Skip to content

Commit f89a132

Browse files
committed
Open Attribute struct for any path
This idea comes from issue #14 - Rename `Attribute::new()` to `Attribute::from_sys_class()` - Add `Attribute::from_path()` - Add `Attribute::from_path_with_discriminator()` - Add example for `Attribute::from_path_with_discriminator()`
1 parent 584ec6e commit f89a132

File tree

12 files changed

+174
-32
lines changed

12 files changed

+174
-32
lines changed

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ev3dev-lang-rust"
3-
version = "0.10.1"
3+
version = "0.10.2"
44
authors = ["Lars Westermann <lars-westermann@live.de>"]
55

66
description = "Rust language bindings for ev3dev"
@@ -24,8 +24,9 @@ image = { version = "0.23.8", optional = true }
2424
[workspace]
2525
members = [
2626
"ev3dev_lang_rust_derive",
27-
"examples/color-sensor",
27+
"examples/attributes",
2828
"examples/buttons",
29-
"examples/screen",
30-
"examples/infrared-sensor"
29+
"examples/color-sensor",
30+
"examples/infrared-sensor",
31+
"examples/screen"
3132
]

examples/attributes/.cargo/config

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[target.armv5te-unknown-linux-gnueabi]
2+
linker = "/usr/bin/arm-linux-gnueabi-gcc"
3+
4+
[term]
5+
color = "always"

examples/attributes/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "attributes"
3+
version = "0.1.0"
4+
authors = ["Lars Westermann <lars.westermann@tu-dresden.de>"]
5+
6+
[dependencies]
7+
ev3dev-lang-rust = { path = "../.."}

examples/attributes/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.PHONY: all clean
2+
3+
all:
4+
docker run --rm -v $(PWD)/../../:/ev3dev-lang-rust/ -w /ev3dev-lang-rust/examples/attributes pixix4/ev3dev-rust cargo build --release --target armv5te-unknown-linux-gnueabi

examples/attributes/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# `Attribute` example
2+
3+
Simple example for `Attribute` usage
4+
5+
```bash
6+
make
7+
# Binary: ../../target/armv5te-unknown-linux-gnueabi/release/attributes
8+
```

examples/attributes/src/main.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
extern crate ev3dev_lang_rust;
2+
3+
use ev3dev_lang_rust::Attribute;
4+
use ev3dev_lang_rust::Ev3Result;
5+
6+
fn main() -> Ev3Result<()> {
7+
// Get value0 of first connected color sensor.
8+
let color_sensor_value = Attribute::from_path_with_discriminator(
9+
"/sys/class/lego-sensor",
10+
"value0",
11+
"driver_name",
12+
"lego-ev3-color",
13+
)?;
14+
15+
// Get raw rotation count of motor in port `A`.
16+
// See https://github.com/ev3dev/ev3dev/wiki/Internals:-ev3dev-stretch for more infomation.
17+
let rotation_count = Attribute::from_path_with_discriminator(
18+
"/sys/bus/iio/devices",
19+
"in_count0_raw",
20+
"name",
21+
"ev3-tacho",
22+
)?;
23+
24+
loop {
25+
println!(
26+
"value0 of color sensor: {}",
27+
color_sensor_value.get::<i32>()?
28+
);
29+
println!("Raw rotation count: {}", rotation_count.get::<i32>()?);
30+
31+
std::thread::sleep(std::time::Duration::from_secs(1));
32+
}
33+
}

src/attriute.rs

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! A wrapper to a attribute file in the `/sys/class/` directory.
1+
//! A wrapper to a attribute file commonly in the `/sys/class/` directory.
22
use std::error::Error;
33
use std::fs::{self, File, OpenOptions};
44
use std::io::{Read, Seek, SeekFrom, Write};
@@ -7,7 +7,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
77
use std::string::String;
88
use std::sync::{Arc, Mutex};
99

10-
use crate::{Ev3Error, Ev3Result};
10+
use crate::{utils::OrErr, Ev3Error, Ev3Result};
1111

1212
/// The root driver path `/sys/class/`.
1313
const ROOT_PATH: &str = "/sys/class/";
@@ -19,28 +19,98 @@ pub struct Attribute {
1919
}
2020

2121
impl Attribute {
22-
/// Create a new `Attribute` instance that wrappes
23-
/// the file `/sys/class/{class_name}/{name}{attribute_name}`.
24-
pub fn new(class_name: &str, name: &str, attribute_name: &str) -> Ev3Result<Attribute> {
25-
let filename = format!("{}{}/{}/{}", ROOT_PATH, class_name, name, attribute_name);
26-
27-
let stat = fs::metadata(&filename)?;
22+
/// Create a new `Attribute` instance for the given path.
23+
pub fn from_path(path: &str) -> Ev3Result<Attribute> {
24+
let stat = fs::metadata(&path)?;
2825

2926
let mode = stat.permissions().mode();
3027

31-
let readable = mode & 256 == 256;
32-
let writeable = mode & 128 == 128;
28+
// Read permission for group (`ev3dev`)
29+
let readable = mode & 0o040 == 0o040;
30+
let writeable = mode & 0o020 == 0o020;
3331

3432
let file = OpenOptions::new()
3533
.read(readable)
3634
.write(writeable)
37-
.open(&filename)?;
35+
.open(&path)?;
3836

3937
Ok(Attribute {
4038
file: Arc::new(Mutex::new(file)),
4139
})
4240
}
4341

42+
/// Create a new `Attribute` instance that wrappes
43+
/// the file `/sys/class/{class_name}/{name}{attribute_name}`.
44+
pub fn from_sys_class(
45+
class_name: &str,
46+
name: &str,
47+
attribute_name: &str,
48+
) -> Ev3Result<Attribute> {
49+
let path = format!("{}{}/{}/{}", ROOT_PATH, class_name, name, attribute_name);
50+
Attribute::from_path(&path)
51+
}
52+
53+
/// Create a new `Attribute` instance by a discriminator attribute.
54+
/// This can be used to manually access driver files or advances features like raw encoder values.
55+
/// To find the correct file, this function iterates over all directories `$d` in `root_path` and
56+
/// checks if the content of `root_path/$d/discriminator_path` equals `discriminator_value`. When a
57+
/// match is found it returns an Attribute for file `root_path/$d/attribute_path`.
58+
///
59+
/// # Example
60+
/// ```no_run
61+
/// use ev3dev_lang_rust::Attribute;
62+
///
63+
/// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
64+
/// // Get value0 of first connected color sensor.
65+
/// let color_sensor_value = Attribute::from_path_with_discriminator(
66+
/// "/sys/class/lego-sensor",
67+
/// "value0",
68+
/// "driver_name",
69+
/// "lego-ev3-color"
70+
/// )?;
71+
/// println!("value0 of color sensor: {}", color_sensor_value.get::<i32>()?);
72+
///
73+
/// // Get raw rotation count of motor in port `A`.
74+
/// // See https://github.com/ev3dev/ev3dev/wiki/Internals:-ev3dev-stretch for more infomation.
75+
/// let rotation_count = Attribute::from_path_with_discriminator(
76+
/// "/sys/bus/iio/devices",
77+
/// "in_count0_raw",
78+
/// "name",
79+
/// "ev3-tacho"
80+
/// )?;
81+
/// println!("Raw rotation count: {}", rotation_count.get::<i32>()?);
82+
///
83+
/// # Ok(())
84+
/// # }
85+
/// ```
86+
pub fn from_path_with_discriminator(
87+
root_path: &str,
88+
attribute_path: &str,
89+
discriminator_path: &str,
90+
discriminator_value: &str,
91+
) -> Ev3Result<Attribute> {
92+
let paths = fs::read_dir(root_path)?;
93+
94+
for path_result in paths {
95+
let path_buf = path_result?.path();
96+
let current_path = path_buf.to_str().or_err()?;
97+
98+
let discriminator_attribute =
99+
Attribute::from_path(&format!("{}/{}", current_path, discriminator_path))?;
100+
101+
if discriminator_attribute.get::<String>()? == discriminator_value {
102+
return Attribute::from_path(&format!("{}/{}", current_path, attribute_path));
103+
}
104+
}
105+
106+
Err(Ev3Error::InternalError {
107+
msg: format!(
108+
"Attribute `{}` at root path `{}` coult not be found!",
109+
attribute_path, root_path
110+
),
111+
})
112+
}
113+
44114
/// Returns the current value of the wrapped file.
45115
fn get_str(&self) -> Ev3Result<String> {
46116
let mut value = String::new();

src/driver.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl Driver {
3838
pub fn find_name_by_port_and_driver(
3939
class_name: &str,
4040
port: &dyn Port,
41-
driver_name_vec: &Vec<&str>,
41+
driver_name_vec: &[&str],
4242
) -> Ev3Result<String> {
4343
let port_address = port.address();
4444

@@ -48,10 +48,10 @@ impl Driver {
4848
let file_name = path?.file_name();
4949
let name = file_name.to_str().or_err()?;
5050

51-
let address = Attribute::new(class_name, name, "address")?;
51+
let address = Attribute::from_sys_class(class_name, name, "address")?;
5252

5353
if address.get::<String>()?.contains(&port_address) {
54-
let driver = Attribute::new(class_name, name, "driver_name")?;
54+
let driver = Attribute::from_sys_class(class_name, name, "driver_name")?;
5555
let driver_name = driver.get::<String>()?;
5656
if driver_name_vec.iter().any(|n| &driver_name == n) {
5757
return Ok(name.to_owned());
@@ -69,7 +69,7 @@ impl Driver {
6969
///
7070
/// Returns `Ev3Error::NotFound` if no such device exists.
7171
/// Returns `Ev3Error::MultipleMatches` if more then one matching device exists.
72-
pub fn find_name_by_driver(class_name: &str, driver_name_vec: &Vec<&str>) -> Ev3Result<String> {
72+
pub fn find_name_by_driver(class_name: &str, driver_name_vec: &[&str]) -> Ev3Result<String> {
7373
let mut names = Driver::find_names_by_driver(class_name, &driver_name_vec)?;
7474

7575
match names.len() {
@@ -88,15 +88,18 @@ impl Driver {
8888
}
8989

9090
/// Returns the names of the devices with the given `class_name`.
91-
pub fn find_names_by_driver(class_name: &str, driver_name_vec: &Vec<&str>) -> Ev3Result<Vec<String>> {
91+
pub fn find_names_by_driver(
92+
class_name: &str,
93+
driver_name_vec: &[&str],
94+
) -> Ev3Result<Vec<String>> {
9295
let paths = fs::read_dir(format!("{}{}", ROOT_PATH, class_name))?;
9396

9497
let mut found_names = Vec::new();
9598
for path in paths {
9699
let file_name = path?.file_name();
97100
let name = file_name.to_str().or_err()?;
98101

99-
let driver = Attribute::new(class_name, name, "driver_name")?;
102+
let driver = Attribute::from_sys_class(class_name, name, "driver_name")?;
100103

101104
let driver_name = driver.get::<String>()?;
102105
if driver_name_vec.iter().any(|n| &driver_name == n) {
@@ -113,9 +116,11 @@ impl Driver {
113116
let mut attributes = self.attributes.borrow_mut();
114117

115118
if !attributes.contains_key(attribute_name) {
116-
if let Ok(v) =
117-
Attribute::new(self.class_name.as_ref(), self.name.as_ref(), attribute_name)
118-
{
119+
if let Ok(v) = Attribute::from_sys_class(
120+
self.class_name.as_ref(),
121+
self.name.as_ref(),
122+
attribute_name,
123+
) {
119124
attributes.insert(attribute_name.to_owned(), v);
120125
};
121126
};

src/findable.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ macro_rules! findable {
4141
}
4242

4343
/// Try to get a `Self` on the given port. Returns `None` if port is not used or another device is connected.
44+
#[allow(clippy::vec_init_then_push)]
4445
pub fn get(port: $port) -> Ev3Result<Self> {
4546
let mut driver_name_vec = Vec::new();
4647
$(
@@ -54,25 +55,27 @@ macro_rules! findable {
5455
}
5556

5657
/// Try to find a `Self`. Only returns a motor if their is exactly one connected, `Error::NotFound` otherwise.
58+
#[allow(clippy::vec_init_then_push)]
5759
pub fn find() -> Ev3Result<Self> {
5860
let mut driver_name_vec = Vec::new();
5961
$(
6062
driver_name_vec.push($driver_name);
6163
)*
62-
64+
6365
let name =
6466
Driver::find_name_by_driver($class_name, &driver_name_vec).map_err(Self::map_error)?;
6567

6668
Ok(Self::new(Driver::new($class_name, &name)))
6769
}
6870

6971
/// Extract list of connected 'Self'
72+
#[allow(clippy::vec_init_then_push)]
7073
pub fn list() -> Ev3Result<Vec<Self>> {
7174
let mut driver_name_vec = Vec::new();
7275
$(
7376
driver_name_vec.push($driver_name);
7477
)*
75-
78+
7679
Ok(Driver::find_names_by_driver($class_name, &driver_name_vec)?
7780
.iter()
7881
.map(|name| Self::new(Driver::new($class_name, &name)))

src/led.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,11 @@ impl Led {
6565
}
6666
}
6767

68-
let left_red = Attribute::new("leds", left_red_name.as_str(), "brightness")?;
69-
let left_green = Attribute::new("leds", left_green_name.as_str(), "brightness")?;
70-
let right_red = Attribute::new("leds", right_red_name.as_str(), "brightness")?;
71-
let right_green = Attribute::new("leds", right_green_name.as_str(), "brightness")?;
68+
let left_red = Attribute::from_sys_class("leds", left_red_name.as_str(), "brightness")?;
69+
let left_green = Attribute::from_sys_class("leds", left_green_name.as_str(), "brightness")?;
70+
let right_red = Attribute::from_sys_class("leds", right_red_name.as_str(), "brightness")?;
71+
let right_green =
72+
Attribute::from_sys_class("leds", right_green_name.as_str(), "brightness")?;
7273

7374
Ok(Led {
7475
left_red,

0 commit comments

Comments
 (0)