From 04e484870b6356b7a20997461f59ac1955ba7eb5 Mon Sep 17 00:00:00 2001 From: Marcelo Freitas Date: Sun, 18 Dec 2022 13:15:36 +0900 Subject: [PATCH 1/5] feat: uses github created branch in echonet-lite-rs --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 48a2c44..55fafb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ dependencies = [ [[package]] name = "echonet-lite" version = "0.1.4" -source = "git+https://github.com/marcelocf/echonet-lite-rs.git?branch=marcelocf/1/singlefunctionlighting#362f39dbd5b74fa98c05356babd071b4082d9285" +source = "git+https://github.com/marcelocf/echonet-lite-rs.git?branch=1-singlefunctionlighting-support#362f39dbd5b74fa98c05356babd071b4082d9285" dependencies = [ "core2", "num", diff --git a/Cargo.toml b/Cargo.toml index dd91397..2030e4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -echonet-lite = { git = "https://github.com/marcelocf/echonet-lite-rs.git", branch = "marcelocf/1/singlefunctionlighting" } +echonet-lite = { git = "https://github.com/marcelocf/echonet-lite-rs.git", branch = "1-singlefunctionlighting-support" } tracing = "0.1" tracing-subscriber = "0.3" From f232ff8013d309a4225e5facaecf91c138618a02 Mon Sep 17 00:00:00 2001 From: Marcelo Freitas Date: Sun, 26 Mar 2023 12:37:24 +0900 Subject: [PATCH 2/5] idk: just commiting an old running state I was playing with it, not ready, and moved on to other things. Now I want to focus on having the core foundation on, and I don't remember where I was. So committing so I don't lose important work that I might have forgotten. Sorry for dirty commit :( --- src/{ => echonet}/discovery.rs | 10 ++++ src/echonet/mod.rs | 92 ++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- src/main.rs | 4 +- 4 files changed, 105 insertions(+), 3 deletions(-) rename src/{ => echonet}/discovery.rs (86%) create mode 100644 src/echonet/mod.rs diff --git a/src/discovery.rs b/src/echonet/discovery.rs similarity index 86% rename from src/discovery.rs rename to src/echonet/discovery.rs index ccd3204..63ee1e8 100644 --- a/src/discovery.rs +++ b/src/echonet/discovery.rs @@ -11,6 +11,16 @@ use tracing::info; const EL_MULTICAST_ADDR: Ipv4Addr = Ipv4Addr::new(224, 0, 23, 0); +struct Discovery {} + +impl super::Listener for Discovery { + // do something with the incoming packet + // returns a vectors of new discovery packets to send. + fn process(_request: super::Request) -> Vec { + vec![] + } +} + pub fn find() -> io::Result<()> { let socket = UdpSocket::bind("0.0.0.0:3610")?; socket.set_read_timeout(Some(Duration::from_secs(2)))?; diff --git a/src/echonet/mod.rs b/src/echonet/mod.rs new file mode 100644 index 0000000..646401d --- /dev/null +++ b/src/echonet/mod.rs @@ -0,0 +1,92 @@ +use echonet_lite::{prelude::ClassPacket, ElPacket}; +use std::{ + io, + net::{IpAddr, Ipv4Addr}, +}; +use tokio::net::UdpSocket; +use tracing::info; + +pub mod discovery; + +pub const EL_MULTICAST_ADDR: Ipv4Addr = Ipv4Addr::new(224, 0, 23, 0); + +// Represents an Echonet Lite request. Including the packet and source address +pub struct Request { + packet: ElPacket, + source: IpAddr, +} + +impl Request { + fn new(packet: ElPacket, source: IpAddr) -> Self { + Request { packet, source } + } +} + +impl From for ElPacket { + fn from(req: Request) -> ElPacket { + req.packet + } +} + +impl From for IpAddr { + fn from(req: Request) -> Self { + req.source + } +} + +// Response to echonet lite requests. +pub struct Response { + packet: ElPacket, + destination: IpAddr, +} + +impl Response { + fn new(packet: ElPacket, destination: IpAddr) -> Self { + Self { + packet, + destination, + } + } +} + +// Represents an Echonet Lite server + +pub struct Server { + socket: UdpSocket, +} + +trait Listener { + // a listener can have multiple responses + // for example, when sending a discovery package it can trigger multiple + // different messages + fn process(request: Request) -> Vec; +} + +// Server implementation using Tokio for async code +impl Server { + async fn new() -> io::Result { + let socket = UdpSocket::bind("0.0.0.0:3610").await?; + socket.set_multicast_loop_v4(true)?; + socket.join_multicast_v4(EL_MULTICAST_ADDR, [0, 0, 0, 0].into())?; + + Ok(Server { socket }) + } + + async fn listen(self) -> io::Result<()> { + loop { + let mut buffer = [0u8; 1024]; + match self.socket.recv_from(&mut buffer).await { + Err(_) => break, + Ok((_, src_addr)) => { + if let Ok((_, packet)) = ElPacket::from_bytes(&buffer) { + let obj: ClassPacket = packet.into(); + info!("got response from {}", src_addr); + info!("{:}", obj); + } + } + } + } + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index fc4b5cb..9089290 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1 @@ -pub mod discovery; +pub mod echonet; diff --git a/src/main.rs b/src/main.rs index 502a3c2..d8f367e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ -use rustynet::discovery::find; +use rustynet::echonet::discovery::find; use tracing::info; use tracing_subscriber; fn main() { tracing_subscriber::fmt::init(); - find().expect("omg"); + find().expect("Unable to find devices"); info!("Hello, world!"); } From 24da0ddc996ad42f7612a145364f0c48134a623c Mon Sep 17 00:00:00 2001 From: Marcelo Freitas Date: Sun, 26 Mar 2023 18:07:59 +0900 Subject: [PATCH 3/5] feat: mapping Echonet Property Codes --- Cargo.lock | 283 +++++++++++++++++++++++++++++++++++---- Cargo.toml | 4 +- src/echonet/data.rs | 159 ++++++++++++++++++++++ src/echonet/discovery.rs | 12 +- src/echonet/mod.rs | 5 + src/error.rs | 9 ++ src/lib.rs | 3 + src/main.rs | 6 +- 8 files changed, 450 insertions(+), 31 deletions(-) create mode 100644 src/echonet/data.rs create mode 100644 src/error.rs diff --git a/Cargo.lock b/Cargo.lock index 55fafb4..4e66a86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "cfg-if" version = "1.0.0" @@ -25,8 +37,9 @@ dependencies = [ [[package]] name = "echonet-lite" -version = "0.1.4" -source = "git+https://github.com/marcelocf/echonet-lite-rs.git?branch=1-singlefunctionlighting-support#362f39dbd5b74fa98c05356babd071b4082d9285" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4bd2093f936783e9b4b12b20e85ded4f93a9d1bf3fe19e80927ccc14ec05574" dependencies = [ "core2", "num", @@ -48,6 +61,15 @@ dependencies = [ "wasi", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -56,9 +78,19 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.138" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "lock_api" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] [[package]] name = "log" @@ -75,6 +107,18 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -112,9 +156,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits", ] @@ -172,11 +216,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "overload" @@ -184,6 +238,29 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + [[package]] name = "phf" version = "0.9.0" @@ -242,24 +319,24 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-hack" -version = "0.5.19" +version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.48" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d89e5dba24725ae5678020bf8f1357a9aa7ff10736b551adbcd3f8d17d766f" +checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d0f47a940e895261e77dc200d5eadfc6ef644c179c6f5edfc105e3a2292c8" +checksum = "50686e0021c4136d1d453b2dfe059902278681512a34d4248435dc34b6b5c8ec" dependencies = [ "proc-macro2", ] @@ -294,29 +371,46 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + [[package]] name = "rustynet" version = "0.1.0" dependencies = [ "echonet-lite", + "thiserror", + "tokio", "tracing", "tracing-subscriber", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" -version = "1.0.151" +version = "1.0.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" +checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.151" +version = "1.0.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" +checksum = "d071a94a3fac4aff69d023a7f411e33f40f3483f8c5190b1953822b6b76d7630" dependencies = [ "proc-macro2", "quote", @@ -325,9 +419,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc" dependencies = [ "proc-macro2", "quote", @@ -343,6 +437,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -355,26 +458,88 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "syn" -version = "1.0.106" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ee3a69cd2c7e06684677e5629b3878b253af05e4714964204279c6bc02cf0b" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing" version = "0.1.37" @@ -435,9 +600,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "valuable" @@ -472,3 +637,69 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/Cargo.toml b/Cargo.toml index 2030e4d..456c718 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -echonet-lite = { git = "https://github.com/marcelocf/echonet-lite-rs.git", branch = "1-singlefunctionlighting-support" } +echonet-lite = "0.1" +thiserror = "1.0" +tokio = { version = "1", features = ["full"] } tracing = "0.1" tracing-subscriber = "0.3" diff --git a/src/echonet/data.rs b/src/echonet/data.rs new file mode 100644 index 0000000..bd20c18 --- /dev/null +++ b/src/echonet/data.rs @@ -0,0 +1,159 @@ +//! Contains data types that extends he echonet-lite-rs types. +//! Special attention on handling [`Prorperty`] + +/// Properties common to wide variety of devices. +#[repr(u8)] +pub(crate) enum GeneralPropertyCode { + /// Indicates the node operating status + /// accessRule: + /// get: required + /// set: notApplicable + /// inf: required + /// states: + /// 0x30u8: Booting + /// 0x31u8: Not booting + OperatingStatus = 0x80u8, + + /// Indicates ECHONET Lite versionused by communication middleware and message types supported by communication middleware + /// 1st byte: major version, 2nd byte: minor version, 3rd data: bitmap or data format, 4th byte: 0x00. + /// accessRule: + /// get: required + /// set: notApplicable + /// inf: optional + VersionInformation = 0x81u8, + + /// Number to identify the node implementing the device object in the domain + /// 1st data is 0xFE, 2nd to 4th data is manufacture code. The rest should be unique to each device. + /// accesRule: + /// get: required + /// set: notApplicable + /// inf: optional + IndentificationNumber = 0x83u8, + + /// indicates whether a fault has occurred or not + /// accessRule: + /// get: optional + /// set: notApplicable + /// inf: optional + /// states: YES/NO + FaultStatus = 0x88u8, + + /// Fault description + /// accessRule: + /// get: optional + /// set: notApplicable + /// inf:optional + /// data: number between 0 and 1004. + FaultDescription = 0x89u8, + + /// 3-byte Manufacturer code + /// accesRule: + /// get: required + /// set: notApplicable + /// inf: optional + ManufacturerCode = 0x8Au8, + + /// 3-byte business facility code + /// accessRule: + /// get: optional + /// set: notApplicable + /// inf: optional + BusinessFacilityCode = 0x8Bu8, + + /// Identifies the product using ASCII code + /// accessRule: + /// get: optional + /// set: notApplicable + /// inf: optional + /// raw_12 + ProductCode = 0x8Cu8, + + /// Indicates the product number using ASCII code + /// accesRule: + /// get: optional + /// set: notApplicable + /// inf: optional + /// raw_12 + SerialNumber = 0x8Du8, + + /// 4-byte production date code + /// accessRule: + /// get: optional + /// set: notApplicable + /// inf: optional + ProductionDate = 0x8Eu8, + + /// Enumeration of EPC in case of the count is than 16, or bitmap in case of the count is more than 15. + /// 1st byte is the count of property. + /// accesRule: + /// get: required + /// set: notApplicable + /// inf: optional + StatusChangeAnnouncementPropertyMap = 0x9Du8, + + /// Enumeration of EPC in case of the count is less than 16, or bitmap in case of the count is more than 15. + /// 1st byte is count of property. + /// accessRule: + /// get: required + /// set: notApplicable + /// inf: optional + SetPropertyMap = 0x9Eu8, + + /// Enumeration of EPC in case of the count is less than 16, or bitmap in case of the count is more than 15. + /// 1st byte is count of property. + /// accessRule: + /// get: required + /// set: notApplicable + /// inf: optional + GetPropertyMap = 0x9Fu8, +} + +/// Property code for profile node 0x0EF0 +#[repr(u8)] +pub(crate) enum ProfilePropertyCode { + /// 2 byte data to identify each node in a domain + /// accessRule: + /// get: optional + /// set: optional + /// inf: optional + UniqueIdentifierData = 0xBFu8, + + /// Total number of instances held by self-node + /// accessRule: + /// get: required + /// set: notApplicable + /// inf: optional + /// 3-byte data for integer. excluding node profile object instance. + SelfNodeInstances = 0xD3u8, + + /// Total number of classes held by self-node + /// accessRule: + /// get: required + /// set: notApplicable + /// inf: optional + /// Including node profile class + SelfNodeClasses = 0xD4u8, + + /// Instance list when self-node instance configuration is changed. + /// Number of instances + Instance list + /// accessRule: + /// get: Optional + /// set: notApplicable + /// inf: required + InstanceListNotification = 0xD5u8, + + /// Number of Instances + Instance List + /// Instance list is an array of EOJ. (3 bytes) + /// accessRule: + /// get: required + /// set: notApplicable + /// inf: optional + SelfNodeInstanceListS = 0xd6u8, + + /// Number of classess + class list, excluding node profile class + /// accessRule: + /// get: required + /// set: notApplicable + /// inf: optional + SelfNodeClassListS = 0xD7u8, +} diff --git a/src/echonet/discovery.rs b/src/echonet/discovery.rs index 63ee1e8..d1b4fcc 100644 --- a/src/echonet/discovery.rs +++ b/src/echonet/discovery.rs @@ -4,11 +4,13 @@ use echonet_lite as el; use el::prelude::*; -use std::io; use std::net::{Ipv4Addr, UdpSocket}; use std::time::Duration; +use tokio::io; use tracing::info; +use super::data::ProfilePropertyCode; + const EL_MULTICAST_ADDR: Ipv4Addr = Ipv4Addr::new(224, 0, 23, 0); struct Discovery {} @@ -32,7 +34,13 @@ pub fn find() -> io::Result<()> { .seoj([0x05u8, 0xFFu8, 0x01u8]) .deoj([0x0Eu8, 0xF0u8, 0x01u8]) .esv(el::ServiceCode::Get) - .props(el::props!([0x80, []], [0xD6, []])) + .props(el::props!( + [ProfilePropertyCode::UniqueIdentifierData as u8, []], + [ProfilePropertyCode::SelfNodeClasses as u8, []], + [ProfilePropertyCode::SelfNodeClassListS as u8, []], + [ProfilePropertyCode::SelfNodeInstances as u8, []], + [ProfilePropertyCode::SelfNodeInstanceListS as u8, []] + )) .build(); let bytes = packet.serialize().expect("fail to serialize"); diff --git a/src/echonet/mod.rs b/src/echonet/mod.rs index 646401d..3c2d9f4 100644 --- a/src/echonet/mod.rs +++ b/src/echonet/mod.rs @@ -6,10 +6,15 @@ use std::{ use tokio::net::UdpSocket; use tracing::info; +pub mod data; pub mod discovery; pub const EL_MULTICAST_ADDR: Ipv4Addr = Ipv4Addr::new(224, 0, 23, 0); +// TODO: this probably need the following refactoring: +// mod echonet::io{Request, Response,server::{Server}} +// and then all the complex types within their own modules + // Represents an Echonet Lite request. Including the packet and source address pub struct Request { packet: ElPacket, diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..4f47af6 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,9 @@ +use thiserror::Error; +use tokio::io; + +#[derive(Error, Debug)] +pub enum Error { + /// Catches all io Errors. + #[error(transparent)] + Io(#[from] io::Error), +} diff --git a/src/lib.rs b/src/lib.rs index 9089290..a66f040 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,4 @@ pub mod echonet; +pub mod error; + +pub type Result = core::result::Result; diff --git a/src/main.rs b/src/main.rs index d8f367e..0d4778a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,10 @@ use rustynet::echonet::discovery::find; use tracing::info; use tracing_subscriber; -fn main() { +#[tokio::main] +async fn main() -> rustynet::Result<()> { tracing_subscriber::fmt::init(); + info!("Scanning for devices"); find().expect("Unable to find devices"); - info!("Hello, world!"); + Ok(()) } From dfd7dc130224f5ab2fa5670d611020f956b81809 Mon Sep 17 00:00:00 2001 From: Marcelo Freitas Date: Mon, 27 Mar 2023 21:42:15 +0900 Subject: [PATCH 4/5] feat: can read instance property --- Cargo.lock | 16 ++++++++++++ Cargo.toml | 1 + src/echonet/discovery.rs | 16 +++++++++--- src/echonet/mod.rs | 1 + src/echonet/profile.rs | 56 ++++++++++++++++++++++++++++++++++++++++ src/error.rs | 6 +++++ 6 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 src/echonet/profile.rs diff --git a/Cargo.lock b/Cargo.lock index 4e66a86..0345e22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,12 @@ dependencies = [ "serde_repr", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "getrandom" version = "0.2.8" @@ -70,6 +76,15 @@ dependencies = [ "libc", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -385,6 +400,7 @@ name = "rustynet" version = "0.1.0" dependencies = [ "echonet-lite", + "itertools", "thiserror", "tokio", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 456c718..4df4250 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] echonet-lite = "0.1" +itertools = "0.10" thiserror = "1.0" tokio = { version = "1", features = ["full"] } tracing = "0.1" diff --git a/src/echonet/discovery.rs b/src/echonet/discovery.rs index d1b4fcc..2d4c843 100644 --- a/src/echonet/discovery.rs +++ b/src/echonet/discovery.rs @@ -3,12 +3,15 @@ // that went innactive. use echonet_lite as el; -use el::prelude::*; +use el::{prelude::*, props}; use std::net::{Ipv4Addr, UdpSocket}; use std::time::Duration; use tokio::io; use tracing::info; +use crate::echonet::profile::InstanceList; +use crate::error::Error; + use super::data::ProfilePropertyCode; const EL_MULTICAST_ADDR: Ipv4Addr = Ipv4Addr::new(224, 0, 23, 0); @@ -23,7 +26,7 @@ impl super::Listener for Discovery { } } -pub fn find() -> io::Result<()> { +pub fn find() -> Result<(), Error> { let socket = UdpSocket::bind("0.0.0.0:3610")?; socket.set_read_timeout(Some(Duration::from_secs(2)))?; socket.set_multicast_loop_v4(true)?; @@ -52,9 +55,16 @@ pub fn find() -> io::Result<()> { Ok((_, src_addr)) => { if let Ok((_, response)) = el::ElPacket::from_bytes(&buffer) { if response.is_response_for(&packet) { - let obj: ClassPacket = response.into(); + let obj: ClassPacket = response.clone().into(); info!("got response from {}", src_addr); info!("{:}", obj); + if let ClassPacket::Profile(_) = obj { + let instances = InstanceList::from(response.props)?; + for instance in instances { + let obj = ClassPacket::new(instance.clone(), props![]); + info!(" * instance: {obj}") + } + } } } } diff --git a/src/echonet/mod.rs b/src/echonet/mod.rs index 3c2d9f4..63665e4 100644 --- a/src/echonet/mod.rs +++ b/src/echonet/mod.rs @@ -8,6 +8,7 @@ use tracing::info; pub mod data; pub mod discovery; +pub mod profile; pub const EL_MULTICAST_ADDR: Ipv4Addr = Ipv4Addr::new(224, 0, 23, 0); diff --git a/src/echonet/profile.rs b/src/echonet/profile.rs new file mode 100644 index 0000000..acab76b --- /dev/null +++ b/src/echonet/profile.rs @@ -0,0 +1,56 @@ +//! Useful tools for managing profile types. +use std::ops::Deref; + +use super::data::ProfilePropertyCode; +use crate::error::Error; +use echonet_lite::{EchonetObject, Properties}; +use itertools::Itertools; + +#[derive(Clone, Debug)] +pub struct InstanceList(Vec); + +impl InstanceList { + /// Converts from a list of properties into a list of instances, erroring in case + /// the property type is not available *or* malformed. + pub fn from(properties: Properties) -> Result { + let prop = properties + .iter() + .find(|&p| (ProfilePropertyCode::SelfNodeInstanceListS as u8).eq(&p.epc)) + .ok_or_else(|| Error::UnsetProperty("InstanceList".to_owned()))?; + + let mut instances: Vec = Vec::new(); + + let mut it = prop.edt.iter(); + + let count = it.next().map(|&v| v as usize).unwrap_or(0); + + while let Some((a, b, c)) = it.next_tuple() { + instances.push([*a, *b, *c].into()); + } + + if !count.eq(&instances.len()) { + Err(Error::MalformedProperty(format!( + "property count doesn't match: {count} != {}", + instances.len() + )))?; + } + + Ok(InstanceList(instances)) + } +} + +impl IntoIterator for InstanceList { + type Item = EchonetObject; + type IntoIter = as IntoIterator>::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Deref for InstanceList { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/src/error.rs b/src/error.rs index 4f47af6..b752f36 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,4 +6,10 @@ pub enum Error { /// Catches all io Errors. #[error(transparent)] Io(#[from] io::Error), + + #[error("Malformed property: {0}")] + MalformedProperty(String), + + #[error("Tried to access an unset property: {0}")] + UnsetProperty(String), } From 329f6d2ab154a1ea6e1b1d7dea9bdd6e5d38607a Mon Sep 17 00:00:00 2001 From: Marcelo Freitas Date: Mon, 27 Mar 2023 22:39:08 +0900 Subject: [PATCH 5/5] neat: clippy --- src/echonet/discovery.rs | 4 ++-- src/main.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/echonet/discovery.rs b/src/echonet/discovery.rs index 2d4c843..be587a9 100644 --- a/src/echonet/discovery.rs +++ b/src/echonet/discovery.rs @@ -6,7 +6,7 @@ use echonet_lite as el; use el::{prelude::*, props}; use std::net::{Ipv4Addr, UdpSocket}; use std::time::Duration; -use tokio::io; + use tracing::info; use crate::echonet::profile::InstanceList; @@ -61,7 +61,7 @@ pub fn find() -> Result<(), Error> { if let ClassPacket::Profile(_) = obj { let instances = InstanceList::from(response.props)?; for instance in instances { - let obj = ClassPacket::new(instance.clone(), props![]); + let obj = ClassPacket::new(instance, props![]); info!(" * instance: {obj}") } } diff --git a/src/main.rs b/src/main.rs index 0d4778a..9354a64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use rustynet::echonet::discovery::find; use tracing::info; -use tracing_subscriber; + #[tokio::main] async fn main() -> rustynet::Result<()> {