From ab2e5e587c0c9ef19e6f32b403de4ced3963a3da Mon Sep 17 00:00:00 2001 From: N8BWert Date: Fri, 13 Sep 2024 22:27:46 -0400 Subject: [PATCH 1/2] Add no-std, alloc, and std support + serial pubub and client-server --- Cargo.lock | 9 + Cargo.toml | 17 +- ncomm-clients-and-servers/Cargo.toml | 13 +- ncomm-clients-and-servers/src/lib.rs | 7 + ncomm-clients-and-servers/src/local.rs | 42 +++ ncomm-clients-and-servers/src/serial.rs | 255 +++++++++++++++++ ncomm-clients-and-servers/src/udp.rs | 86 +++++- ncomm-core/Cargo.toml | 6 + ncomm-core/src/client_server.rs | 18 ++ ncomm-core/src/executor.rs | 5 + ncomm-core/src/lib.rs | 5 + ncomm-core/src/update_client_server.rs | 21 ++ ncomm-executors/Cargo.toml | 14 +- ncomm-executors/src/lib.rs | 24 +- ncomm-nodes/Cargo.toml | 13 +- ncomm-nodes/src/lib.rs | 4 + ncomm-publishers-and-subscribers/Cargo.toml | 12 +- ncomm-publishers-and-subscribers/src/lib.rs | 7 + .../src/serial.rs | 269 ++++++++++++++++++ ncomm-update-clients-and-servers/Cargo.toml | 15 +- ncomm-update-clients-and-servers/src/lib.rs | 3 + ncomm-update-clients-and-servers/src/local.rs | 63 ++++ ncomm-update-clients-and-servers/src/udp.rs | 151 ++++++++++ ncomm-utils/Cargo.toml | 1 + ncomm-utils/src/lib.rs | 4 +- ncomm/Cargo.toml | 46 ++- ncomm/src/lib.rs | 4 + 27 files changed, 1072 insertions(+), 42 deletions(-) create mode 100644 ncomm-clients-and-servers/src/serial.rs create mode 100644 ncomm-publishers-and-subscribers/src/serial.rs diff --git a/Cargo.lock b/Cargo.lock index a59213e..6d86ea8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1550,6 +1550,12 @@ dependencies = [ "serde", ] +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -2829,6 +2835,7 @@ name = "ncomm-clients-and-servers" version = "1.0.0" dependencies = [ "crossbeam", + "embedded-io", "ncomm-core", "ncomm-utils", "rand", @@ -2864,6 +2871,7 @@ name = "ncomm-publishers-and-subscribers" version = "1.0.0" dependencies = [ "crossbeam", + "embedded-io", "ncomm-core", "ncomm-utils", "quanta", @@ -2876,6 +2884,7 @@ name = "ncomm-update-clients-and-servers" version = "1.0.0" dependencies = [ "crossbeam", + "embedded-io", "ncomm-core", "ncomm-utils", "rand", diff --git a/Cargo.toml b/Cargo.toml index 4a86fff..c6e7ff9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,14 +40,14 @@ categories = ["science::robotics"] [workspace.dependencies] # NComm -ncomm = { path = "ncomm", version = "1.0.0" } -ncomm-core = { path = "ncomm-core", version = "1.0.0" } -ncomm-utils = { path = "ncomm-utils", version = "1.0.0" } -ncomm-executors = { path = "ncomm-executors", version = "1.0.0" } -ncomm-publishers-and-subscribers = { path = "ncomm-publishers-and-subscribers", version = "1.0.0" } -ncomm-clients-and-servers = { path = "ncomm-clients-and-servers", version = "1.0.0" } -ncomm-update-clients-and-servers = { path = "ncomm-update-clients-and-servers", version = "1.0.0" } -ncomm-nodes = { path = "ncomm-nodes", version = "1.0.0" } +ncomm = { path = "ncomm", version = "1.0.0", default-features = false } +ncomm-core = { path = "ncomm-core", version = "1.0.0", default-features = false } +ncomm-utils = { path = "ncomm-utils", version = "1.0.0", default-features = false } +ncomm-executors = { path = "ncomm-executors", version = "1.0.0", default-features = false } +ncomm-publishers-and-subscribers = { path = "ncomm-publishers-and-subscribers", version = "1.0.0", default-features = false } +ncomm-clients-and-servers = { path = "ncomm-clients-and-servers", version = "1.0.0", default-features = false } +ncomm-update-clients-and-servers = { path = "ncomm-update-clients-and-servers", version = "1.0.0", default-features = false } +ncomm-nodes = { path = "ncomm-nodes", version = "1.0.0", default-features = false } # Outside Dependencies crossbeam = "0.8.4" @@ -59,3 +59,4 @@ rand_distr = "0.4.3" rerun = "0.18.2" re_web_viewer_server = "0.18.2" re_ws_comms = "0.18.2" +embedded-io = "0.6.1" diff --git a/ncomm-clients-and-servers/Cargo.toml b/ncomm-clients-and-servers/Cargo.toml index 9c211e6..57f30a1 100644 --- a/ncomm-clients-and-servers/Cargo.toml +++ b/ncomm-clients-and-servers/Cargo.toml @@ -11,9 +11,16 @@ homepage.workspace = true repository.workspace = true [dependencies] -crossbeam = { workspace = true } -ncomm-core = { workspace = true } -ncomm-utils = { workspace = true } +crossbeam = { workspace = true, optional = true } +ncomm-core = { workspace = true, default-features = false } +ncomm-utils = { workspace = true, default-features = false } +embedded-io = { workspace = true } [dev-dependencies] rand = { workspace = true } + +[features] +default = ["std"] +nostd = ["ncomm-core/nostd", "ncomm-utils/nostd"] +alloc = ["nostd", "ncomm-core/alloc", "ncomm-utils/alloc"] +std = ["dep:crossbeam", "ncomm-core/std", "ncomm-utils/std"] diff --git a/ncomm-clients-and-servers/src/lib.rs b/ncomm-clients-and-servers/src/lib.rs index a23c3a9..5265dda 100644 --- a/ncomm-clients-and-servers/src/lib.rs +++ b/ncomm-clients-and-servers/src/lib.rs @@ -7,7 +7,14 @@ //! #![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(feature = "std")] pub mod local; +#[cfg(feature = "std")] pub mod udp; + +pub mod serial; diff --git a/ncomm-clients-and-servers/src/local.rs b/ncomm-clients-and-servers/src/local.rs index 38d7ce7..e73ee2a 100644 --- a/ncomm-clients-and-servers/src/local.rs +++ b/ncomm-clients-and-servers/src/local.rs @@ -31,6 +31,15 @@ impl Client for LocalClient { Ok(()) } + fn poll_for_response( + &mut self, + ) -> Result, Self::Error> { + match self.rx.try_recv() { + Ok(response) => Ok(Some(response)), + Err(_) => Ok(None), + } + } + fn poll_for_responses(&mut self) -> Vec> { let mut responses = Vec::new(); for response in self.rx.try_iter() { @@ -91,6 +100,15 @@ impl Server for LocalServer { requests } + fn poll_for_request(&mut self) -> Result, Self::Error> { + for (k, (rx, _)) in self.client_map.iter() { + if let Ok(request) = rx.try_recv() { + return Ok(Some((k.clone(), request))); + } + } + Ok(None) + } + fn send_response( &mut self, client_key: Self::Key, @@ -155,4 +173,28 @@ mod tests { assert_eq!(response, original_response); } } + + #[test] + fn test_local_client_server_single() { + let mut server = LocalServer::new(); + let mut client = server.create_client(0u8); + + let original_request = Request::new(); + let original_response = Response::new(original_request); + client.send_request(original_request).unwrap(); + if let Ok(Some((client, request))) = server.poll_for_request() { + assert_eq!(request, original_request); + server + .send_response(client, request, Response::new(request.clone())) + .unwrap() + } else { + assert!(false, "Expected to receive request"); + } + if let Ok(Some((request, response))) = client.poll_for_response() { + assert_eq!(request, original_request); + assert_eq!(response, original_response); + } else { + assert!(false, "Expected to receive response"); + } + } } diff --git a/ncomm-clients-and-servers/src/serial.rs b/ncomm-clients-and-servers/src/serial.rs new file mode 100644 index 0000000..cb7524a --- /dev/null +++ b/ncomm-clients-and-servers/src/serial.rs @@ -0,0 +1,255 @@ +//! +//! Serial Client and Server for communication using embedded-io +//! traits. +//! + +use core::marker::PhantomData; + +use embedded_io::{Error, Read, ReadReady, Write}; + +use ncomm_core::client_server::{Client, Server}; +use ncomm_utils::packing::{Packable, PackingError}; + +/// An Error regarding sending and receiving data via serial +#[derive(Debug)] +pub enum SerialClientServerError { + /// Embedded-IO Error + IOError(Err), + /// An error occurred with pacing the data + PackingError(PackingError), +} + +/// Client that sends requests and receives responses via a serial device. +/// +/// Note: To make this client no_std compatible the client has an internal buffer +/// that is statically allocated, hence the reason for the const BUFFER_SIZE: usize +/// generic +pub struct SerialClient< + Req: Packable, + Res: Packable, + Serial: ReadReady + Read + Write, + Err: Error, + const BUFFER_SIZE: usize, +> { + /// The serial peripheral device + serial_device: Serial, + /// The internal buffer for encoding data + buffer: [u8; BUFFER_SIZE], + /// A marker t bind the type of data + _phantom: PhantomData<(Req, Res)>, +} + +impl + SerialClient +where + Req: Packable, + Res: Packable, + Serial: ReadReady + Read + Write, + Err: Error, +{ + /// Construct a new SerialClient from a serial peripheral + pub fn new(serial_device: Serial, buffer: [u8; BUFFER_SIZE]) -> Self { + assert!( + BUFFER_SIZE >= Req::len() + Res::len(), + "The buffer must be large enough to fit a request and response" + ); + Self { + serial_device, + buffer, + _phantom: PhantomData, + } + } + + /// Destroy the SerialClient, returning the serial peripheral + pub fn destroy(self) -> Serial { + self.serial_device + } +} + +impl Client + for SerialClient +where + Req: Packable, + Res: Packable, + Serial: ReadReady + Read + Write, + Err: Error, +{ + type Request = Req; + type Response = Res; + type Error = SerialClientServerError; + + fn send_request(&mut self, request: Self::Request) -> Result<(), Self::Error> { + self.buffer.iter_mut().for_each(|v| *v = 0); + request + .pack(&mut self.buffer) + .map_err(SerialClientServerError::PackingError)?; + + self.serial_device + .write_all(&self.buffer[..Req::len()]) + .map_err(SerialClientServerError::IOError)?; + + Ok(()) + } + + fn poll_for_response( + &mut self, + ) -> Result, Self::Error> { + if let Ok(ready) = self.serial_device.read_ready() { + if !ready { + return Ok(None); + } + + self.buffer.iter_mut().for_each(|v| *v = 0); + if self.serial_device.read(&mut self.buffer).is_ok() { + if let (Ok(request), Ok(response)) = ( + Req::unpack(&self.buffer[..Req::len()]), + Res::unpack(&self.buffer[Req::len()..]), + ) { + return Ok(Some((request, response))); + } + } + } + Ok(None) + } + + #[cfg(any(feature = "alloc", feature = "std"))] + fn poll_for_responses(&mut self) -> Vec> { + let mut responses = Vec::new(); + + while let Ok(ready) = self.serial_device.read_ready() { + if !ready { + break; + } + + self.buffer.iter_mut().for_each(|v| *v = 0); + if self.serial_device.read(&mut self.buffer).is_ok() { + if let (Ok(request), Ok(response)) = ( + Req::unpack(&self.buffer[..Req::len()]), + Res::unpack(&self.buffer[Req::len()..]), + ) { + responses.push(Ok((request, response))); + } + } + } + + responses + } +} + +/// A serial server capable of receiving requests and sending responses. +/// +/// Note: To make this server no_std compatible the server has an internal buffer +/// that is statically allocated, hence the reason for the const BUFFER_SIZE: usize +/// generic +pub struct SerialServer< + Req: Packable, + Res: Packable, + Serial: ReadReady + Read + Write, + Err: Error, + const BUFFER_SIZE: usize, +> { + /// The serial peripheral device + serial_device: Serial, + /// The internal buffer for sending and receiving data + buffer: [u8; BUFFER_SIZE], + /// A holder for the request and response data type + _phantom: PhantomData<(Req, Res)>, +} + +impl + SerialServer +where + Req: Packable, + Res: Packable, + Serial: ReadReady + Read + Write, + Err: Error, +{ + /// Create a new SerialServer from a serial device peripheral + pub fn new(serial_device: Serial, buffer: [u8; BUFFER_SIZE]) -> Self { + assert!( + buffer.len() >= Req::len() + Res::len(), + "The buffer must be large enough to accommodate a request and response" + ); + Self { + serial_device, + buffer, + _phantom: PhantomData, + } + } + + /// Destroy the SerialServer returning the serial peripheral + pub fn destroy(self) -> Serial { + self.serial_device + } +} + +impl Server + for SerialServer +where + Req: Packable, + Res: Packable, + Serial: ReadReady + Read + Write, + Err: Error, +{ + type Request = Req; + type Response = Res; + type Key = bool; + type Error = SerialClientServerError; + + fn poll_for_request(&mut self) -> Result, Self::Error> { + if let Ok(ready) = self.serial_device.read_ready() { + if !ready { + return Ok(None); + } + + self.buffer.iter_mut().for_each(|v| *v = 0); + if self.serial_device.read(&mut self.buffer).is_ok() { + if let Ok(request) = Req::unpack(&self.buffer[..Req::len()]) { + return Ok(Some((true, request))); + } + } + } + Ok(None) + } + + #[cfg(any(feature = "alloc", feature = "std"))] + fn poll_for_requests(&mut self) -> Vec> { + let mut requests = Vec::new(); + + while let Ok(ready) = self.serial_device.read_ready() { + if !ready { + break; + } + + self.buffer.iter_mut().for_each(|v| *v = 0); + if self.serial_device.read(&mut self.buffer).is_ok() { + if let Ok(request) = Req::unpack(&self.buffer[..Req::len()]) { + requests.push(Ok((true, request))); + } + } + } + + requests + } + + fn send_response( + &mut self, + _client_key: Self::Key, + request: Self::Request, + response: Self::Response, + ) -> Result<(), Self::Error> { + self.buffer.iter_mut().for_each(|v| *v = 0); + request + .pack(&mut self.buffer[..Req::len()]) + .map_err(SerialClientServerError::PackingError)?; + response + .pack(&mut self.buffer[Req::len()..]) + .map_err(SerialClientServerError::PackingError)?; + + self.serial_device + .write_all(&self.buffer[..(Req::len() + Res::len())]) + .map_err(SerialClientServerError::IOError)?; + + Ok(()) + } +} diff --git a/ncomm-clients-and-servers/src/udp.rs b/ncomm-clients-and-servers/src/udp.rs index 1a59b8b..33ad885 100644 --- a/ncomm-clients-and-servers/src/udp.rs +++ b/ncomm-clients-and-servers/src/udp.rs @@ -23,7 +23,7 @@ pub enum UdpClientServerError { /// An error with packing the data PackingError(PackingError), /// Request from an unknown client - UnknownRequester(Data), + UnknownRequester((Data, SocketAddr)), /// The client you are sending data to is unknown UnknownClient, } @@ -71,6 +71,25 @@ impl Client for UdpClient { Ok(()) } + fn poll_for_response( + &mut self, + ) -> Result, Self::Error> { + let mut buffer = vec![0u8; Req::len() + Res::len()]; + let (req, res) = match self.socket.recv(&mut buffer) { + Ok(_received) => ( + Req::unpack(&buffer[..Req::len()]), + Res::unpack(&buffer[Req::len()..]), + ), + Err(_) => return Ok(None), + }; + + if let (Ok(req), Ok(res)) = (req, res) { + Ok(Some((req, res))) + } else { + Ok(None) + } + } + /// Check the UDP socket for incoming Datagrams. /// /// Note: Incoming data will be in the form: @@ -78,7 +97,7 @@ impl Client for UdpClient { fn poll_for_responses(&mut self) -> Vec> { let mut responses = Vec::new(); - let mut buffer = vec![0u8; Res::len() + Res::len()]; + let mut buffer = vec![0u8; Req::len() + Res::len()]; loop { let (req, res) = match self.socket.recv(&mut buffer) { Ok(_received) => ( @@ -151,6 +170,25 @@ impl Server for UdpServer; + fn poll_for_request(&mut self) -> Result, Self::Error> { + let mut buffer = vec![0u8; Req::len()]; + let address = match self.socket.recv_from(&mut buffer) { + Ok((_request_size, address)) => address, + Err(_) => return Ok(None), + }; + + match Req::unpack(&buffer[..]) { + Ok(data) => { + if let Some((k, _)) = self.client_addresses.iter().find(|v| v.1 == address) { + Ok(Some((k.clone(), data))) + } else { + Err(UdpClientServerError::UnknownRequester((data, address))) + } + } + Err(err) => Err(UdpClientServerError::PackingError(err)), + } + } + fn poll_for_requests(&mut self) -> Vec> { let mut requests = Vec::new(); @@ -166,7 +204,7 @@ impl Server for UdpServer requests.push(Err(UdpClientServerError::PackingError(err))), @@ -356,4 +394,46 @@ mod tests { assert_eq!(response.1, client_two_response); } } + + #[test] + fn test_udp_client_server_singular_send() { + let mut server: UdpServer = UdpServer::new_with( + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 7005)), + vec![( + 0, + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 7006)), + )], + ) + .unwrap(); + + let mut client_one: UdpClient = UdpClient::new( + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 7006)), + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 7005)), + ) + .unwrap(); + + let client_one_request = Request::new(); + let client_one_response = Response::new(client_one_request.clone()); + + client_one.send_request(client_one_request).unwrap(); + + sleep(Duration::from_millis(50)); + + if let Ok(Some((k, request))) = server.poll_for_request() { + server + .send_response(k, request, client_one_response.clone()) + .unwrap(); + } else { + assert!(false, "Expected to receive request"); + } + + sleep(Duration::from_millis(50)); + + if let Ok(Some((request, response))) = client_one.poll_for_response() { + assert_eq!(request, client_one_request); + assert_eq!(response, client_one_response); + } else { + assert!(false, "Expected to receive response"); + } + } } diff --git a/ncomm-core/Cargo.toml b/ncomm-core/Cargo.toml index 1103d86..7d81123 100644 --- a/ncomm-core/Cargo.toml +++ b/ncomm-core/Cargo.toml @@ -11,3 +11,9 @@ homepage.workspace = true repository.workspace = true [dependencies] + +[features] +default = ["std"] +nostd = [] +alloc = ["nostd"] +std = [] diff --git a/ncomm-core/src/client_server.rs b/ncomm-core/src/client_server.rs index 631cb37..8f5cc4c 100644 --- a/ncomm-core/src/client_server.rs +++ b/ncomm-core/src/client_server.rs @@ -5,6 +5,11 @@ //! that request something from them (in the form of a request). //! +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::vec::Vec; + /// A common abstraction for all NComm clients to allow for the creation /// of a common method of sending requests and receiving responses. pub trait Client { @@ -19,6 +24,13 @@ pub trait Client { /// Send a request to the server this client is associated with fn send_request(&mut self, request: Self::Request) -> Result<(), Self::Error>; + /// Check for a singular response from the server containing the request and response + /// from the server + #[allow(clippy::type_complexity)] + fn poll_for_response(&mut self) + -> Result, Self::Error>; + + #[cfg(any(feature = "alloc", feature = "std"))] /// Check for a response from the server containing both the sent /// request and the response from the server #[allow(clippy::type_complexity)] @@ -38,6 +50,11 @@ pub trait Server { /// the client type Error; + /// Check for an incoming request from the client + #[allow(clippy::type_complexity)] + fn poll_for_request(&mut self) -> Result, Self::Error>; + + #[cfg(any(feature = "alloc", feature = "std"))] /// Check for incoming requests from the client #[allow(clippy::type_complexity)] fn poll_for_requests(&mut self) -> Vec>; @@ -50,6 +67,7 @@ pub trait Server { response: Self::Response, ) -> Result<(), Self::Error>; + #[cfg(any(feature = "alloc", feature = "std"))] /// Send a collection of responses to specified clients fn send_responses( &mut self, diff --git a/ncomm-core/src/executor.rs b/ncomm-core/src/executor.rs index 0dfc06e..22aa719 100644 --- a/ncomm-core/src/executor.rs +++ b/ncomm-core/src/executor.rs @@ -9,6 +9,11 @@ use crate::node::Node; +#[cfg(feature = "alloc")] +use alloc::boxed::Box; +#[cfg(feature = "std")] +use std::boxed::Box; + /// The current state an executor is in. /// /// This should be taken into account whenever the start or update methods diff --git a/ncomm-core/src/lib.rs b/ncomm-core/src/lib.rs index 3edb9af..529740c 100644 --- a/ncomm-core/src/lib.rs +++ b/ncomm-core/src/lib.rs @@ -5,11 +5,16 @@ #![deny(unsafe_code)] #![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "alloc")] +extern crate alloc; pub mod node; pub use node::Node; +#[cfg(any(feature = "std", feature = "alloc"))] pub mod executor; +#[cfg(any(feature = "std", feature = "alloc"))] pub use executor::{Executor, ExecutorState}; pub mod publisher_subscriber; diff --git a/ncomm-core/src/update_client_server.rs b/ncomm-core/src/update_client_server.rs index 25f1dee..a8c1603 100644 --- a/ncomm-core/src/update_client_server.rs +++ b/ncomm-core/src/update_client_server.rs @@ -6,6 +6,11 @@ //! update the client on. //! +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::vec::Vec; + /// A common abstraction for all NComm update clients pub trait UpdateClient { /// The type of data used as a request by the client @@ -22,10 +27,21 @@ pub trait UpdateClient { #[allow(clippy::type_complexity)] fn send_request(&mut self, request: Self::Request) -> Result<(), Self::Error>; + /// Poll for a singular update from the server + #[allow(clippy::type_complexity)] + fn poll_for_update(&mut self) -> Result, Self::Error>; + + #[cfg(any(feature = "alloc", feature = "std"))] /// Poll for updates from the server #[allow(clippy::type_complexity)] fn poll_for_updates(&mut self) -> Vec>; + /// Poll for a singular response from the server + #[allow(clippy::type_complexity)] + fn poll_for_response(&mut self) + -> Result, Self::Error>; + + #[cfg(any(feature = "alloc", feature = "std"))] /// Poll for responses from the server #[allow(clippy::type_complexity)] fn poll_for_responses(&mut self) -> Vec>; @@ -45,6 +61,11 @@ pub trait UpdateServer { /// to the update client type Error; + /// Check for a singular incoming request from the client + #[allow(clippy::type_complexity)] + fn poll_for_request(&mut self) -> Result, Self::Error>; + + #[cfg(any(feature = "alloc", feature = "std"))] /// Check for incoming requests from the client #[allow(clippy::type_complexity)] fn poll_for_requests(&mut self) -> Vec>; diff --git a/ncomm-executors/Cargo.toml b/ncomm-executors/Cargo.toml index abccc47..2fbf37d 100644 --- a/ncomm-executors/Cargo.toml +++ b/ncomm-executors/Cargo.toml @@ -11,7 +11,13 @@ homepage.workspace = true repository.workspace = true [dependencies] -crossbeam = { workspace = true } -ncomm-core = { workspace = true } -quanta = { workspace = true } -threadpool = { workspace = true } +crossbeam = { workspace = true, optional = true } +ncomm-core = { workspace = true, default-features = false } +quanta = { workspace = true, optional = true } +threadpool = { workspace = true, optional = true } + +[features] +default = ["std"] +nostd = ["ncomm-core/nostd"] +alloc = ["nostd", "ncomm-core/alloc"] +std = ["ncomm-core/std", "dep:crossbeam", "dep:quanta", "dep:threadpool"] diff --git a/ncomm-executors/src/lib.rs b/ncomm-executors/src/lib.rs index f149c79..5c41430 100644 --- a/ncomm-executors/src/lib.rs +++ b/ncomm-executors/src/lib.rs @@ -15,18 +15,34 @@ // To test the internal state of nodes, they need to be force // downcasted into their respective type. #![cfg_attr(test, feature(downcast_unchecked))] +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(feature = "std")] pub mod simple_executor; +#[cfg(feature = "std")] pub use simple_executor::SimpleExecutor; +#[cfg(feature = "std")] pub mod threadpool_executor; +#[cfg(feature = "std")] pub use threadpool_executor::ThreadPoolExecutor; +#[cfg(feature = "std")] pub mod threaded_executor; +#[cfg(feature = "std")] +pub use threaded_executor::ThreadedExecutor; +use core::cmp::{Ord, Ordering}; use ncomm_core::node::Node; -use std::cmp::{Ord, Ordering}; +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, vec::Vec}; +#[cfg(feature = "std")] +use std::{boxed::Box, vec::Vec}; + +#[cfg(any(feature = "alloc", feature = "std"))] /// The NodeWrapper wraps nodes giving them a priority based on the timestamp /// of their next update. /// @@ -38,6 +54,7 @@ pub(crate) struct NodeWrapper { pub node: Box>, } +#[cfg(any(feature = "alloc", feature = "std"))] impl NodeWrapper { /// Destroy the node wrapper returning the node it was wrapping. pub fn destroy(self) -> Box> { @@ -45,26 +62,31 @@ impl NodeWrapper { } } +#[cfg(any(feature = "alloc", feature = "std"))] impl Ord for NodeWrapper { fn cmp(&self, other: &Self) -> Ordering { self.priority.cmp(&other.priority).reverse() } } +#[cfg(any(feature = "alloc", feature = "std"))] impl PartialOrd for NodeWrapper { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } +#[cfg(any(feature = "alloc", feature = "std"))] impl PartialEq for NodeWrapper { fn eq(&self, other: &Self) -> bool { self.priority == other.priority } } +#[cfg(any(feature = "alloc", feature = "std"))] impl Eq for NodeWrapper {} +#[cfg(any(feature = "alloc", feature = "std"))] /// This method performs binary search insertion into the sorted vector /// `vec` with the node `node`. /// diff --git a/ncomm-nodes/Cargo.toml b/ncomm-nodes/Cargo.toml index 08ea214..d83834c 100644 --- a/ncomm-nodes/Cargo.toml +++ b/ncomm-nodes/Cargo.toml @@ -15,10 +15,13 @@ categories.workspace = true rerun = { workspace = true, optional = true } re_web_viewer_server = { workspace = true, optional = true } re_ws_comms = { workspace = true, optional = true } -ncomm-core = { workspace = true } -ncomm-publishers-and-subscribers = { workspace = true } +ncomm-core = { workspace = true, default-features = false } +ncomm-publishers-and-subscribers = { workspace = true, default-features = false } [features] -default = [] -rerun = ["dep:rerun", "ncomm-publishers-and-subscribers/rerun"] -rerun-web-viewer = ["rerun", "rerun/web_viewer", "dep:re_web_viewer_server", "dep:re_ws_comms"] +default = ["std"] +nostd = ["ncomm-core/nostd", "ncomm-publishers-and-subscribers/nostd"] +alloc = ["nostd", "ncomm-core/alloc", "ncomm-publishers-and-subscribers/alloc"] +std = ["ncomm-core/std", "ncomm-publishers-and-subscribers/std"] +rerun = ["std", "dep:rerun", "ncomm-publishers-and-subscribers/rerun"] +rerun-web-viewer = ["std", "rerun", "rerun/web_viewer", "dep:re_web_viewer_server", "dep:re_ws_comms"] diff --git a/ncomm-nodes/src/lib.rs b/ncomm-nodes/src/lib.rs index bdb251c..5f658de 100644 --- a/ncomm-nodes/src/lib.rs +++ b/ncomm-nodes/src/lib.rs @@ -3,6 +3,10 @@ //! tools. //! +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "alloc")] +extern crate alloc; + #[deny(missing_docs)] #[cfg(feature = "rerun")] pub mod rerun; diff --git a/ncomm-publishers-and-subscribers/Cargo.toml b/ncomm-publishers-and-subscribers/Cargo.toml index 05218a6..47d804d 100644 --- a/ncomm-publishers-and-subscribers/Cargo.toml +++ b/ncomm-publishers-and-subscribers/Cargo.toml @@ -12,14 +12,18 @@ repository.workspace = true [dependencies] crossbeam = { workspace = true } -ncomm-core = { workspace = true } -ncomm-utils = { workspace = true } +ncomm-core = { workspace = true, default-features = false } +ncomm-utils = { workspace = true, default-features = false } quanta = { workspace = true } rerun = { workspace = true, optional = true } +embedded-io = { workspace = true } [dev-dependencies] rand = { workspace = true } [features] -default = [] -rerun = ["dep:rerun"] +default = ["std"] +nostd = ["ncomm-core/nostd", "ncomm-utils/nostd"] +alloc = ["nostd", "ncomm-core/alloc", "ncomm-utils/alloc"] +std = ["ncomm-core/std", "ncomm-utils/std"] +rerun = ["std", "dep:rerun"] diff --git a/ncomm-publishers-and-subscribers/src/lib.rs b/ncomm-publishers-and-subscribers/src/lib.rs index 5f718a8..f27af07 100644 --- a/ncomm-publishers-and-subscribers/src/lib.rs +++ b/ncomm-publishers-and-subscribers/src/lib.rs @@ -7,10 +7,17 @@ //! #![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(feature = "std")] pub mod local; +#[cfg(feature = "std")] pub mod udp; #[cfg(feature = "rerun")] pub mod rerun; + +pub mod serial; diff --git a/ncomm-publishers-and-subscribers/src/serial.rs b/ncomm-publishers-and-subscribers/src/serial.rs new file mode 100644 index 0000000..91efa80 --- /dev/null +++ b/ncomm-publishers-and-subscribers/src/serial.rs @@ -0,0 +1,269 @@ +//! +//! NComm Publisher and Subscriber for publishing over Serial using the embedded-io traits. +//! +//! This publisher and subscriber send and receive data over the serial +//! peripherals of whatever system is being utilized. +//! + +use core::marker::PhantomData; + +use embedded_io::{Error, Read, ReadReady, Write}; + +use ncomm_core::publisher_subscriber::{Publisher, Subscriber}; +use ncomm_utils::packing::{Packable, PackingError}; + +/// An Error regarding publishing serial data +#[derive(Debug)] +pub enum SerialPublishError { + /// Embedded-IO Error + IOError(Err), + /// An error occurred with packing the data + PackingError(PackingError), +} + +/// Publisher that publishes data via a serial device. +/// +/// To make this publisher no_std compatible the publisher has an internal buffer +/// that is statically allocated, hence the reason for the const BUFFER_SIZE: usize +/// generic +pub struct SerialPublisher< + Data: Packable, + Serial: Write, + Err: Error, + const BUFFER_SIZE: usize, +> { + /// The serial peripheral device + serial_device: Serial, + /// The internal buffer for encoding data + buffer: [u8; BUFFER_SIZE], + /// A marker to bind the type of data published to the publisher + _phantom: PhantomData, +} + +impl SerialPublisher +where + Data: Packable, + Serial: Write, + Err: Error, +{ + /// Create a new SerialPublisher from the peripheral + pub fn new(serial_device: Serial, buffer: [u8; BUFFER_SIZE]) -> Self { + assert!( + BUFFER_SIZE >= Data::len(), + "The buffer must be large enough to fit encoded data" + ); + Self { + serial_device, + buffer, + _phantom: PhantomData, + } + } + + /// Destroy the SerialPublisher returning the serial peripheral + pub fn destroy(self) -> Serial { + self.serial_device + } +} + +impl Publisher + for SerialPublisher +where + Data: Packable, + Serial: Write, + Err: Error, +{ + type Data = Data; + type Error = SerialPublishError; + + fn publish(&mut self, data: Self::Data) -> Result<(), Self::Error> { + self.buffer.iter_mut().for_each(|v| *v = 0); + data.pack(&mut self.buffer) + .map_err(SerialPublishError::PackingError)?; + + self.serial_device + .write_all(&self.buffer) + .map_err(SerialPublishError::IOError)?; + + Ok(()) + } +} + +/// Serial Subscriber that reads data from a serial line +/// +/// Note: To make this subscriber no_std compatible the subscriber +/// has an internal buffer that is statically allocated, hence the reason +/// for the const BUFFER_SIZE: usize generic +pub struct SerialSubscriber< + Data: Packable, + Serial: ReadReady + Read, + Err: Error, + const BUFFER_SIZE: usize, +> { + /// The serial peripheral device + serial_device: Serial, + /// The internal buffer for decoding data + buffer: [u8; BUFFER_SIZE], + /// The current data stored in the subscriber + data: Option, +} + +impl SerialSubscriber +where + Data: Packable, + Serial: ReadReady + Read, + Err: Error, +{ + /// Create a new SerialSubscriber from the peripheral + pub fn new(serial_device: Serial, buffer: [u8; BUFFER_SIZE]) -> Self { + assert!( + BUFFER_SIZE >= Data::len(), + "The buffer must be large enough to fit encoded data" + ); + Self { + serial_device, + buffer, + data: None, + } + } + + /// Destroy the SerialSubscriber returning the serial peripheral + pub fn destroy(self) -> Serial { + self.serial_device + } +} + +impl Subscriber + for SerialSubscriber +where + Data: Packable, + Serial: ReadReady + Read, + Err: Error, +{ + type Target = Option; + + fn get(&mut self) -> &Self::Target { + let mut new_data = None; + + while let Ok(ready) = self.serial_device.read_ready() { + if !ready { + break; + } + + self.buffer.iter_mut().for_each(|v| *v = 0); + if self.serial_device.read(&mut self.buffer).is_ok() { + if let Ok(data) = Data::unpack(&self.buffer) { + new_data = Some(data); + } + } + } + + if let Some(data) = new_data { + self.data = Some(data); + } + + &self.data + } +} + +/// A serial publisher/subscriber capable of both publishing and subscribing +/// a specific data type. +/// +/// Note: To make this subscriber no_std compatible the subscriber +/// has an internal buffer that is statically allocated, hence the reason +/// for the const BUFFER_SIZE: usize generic +pub struct SerialPublisherSubscriber< + Data: Packable, + Serial: ReadReady + Read + Write, + Err: Error, + const BUFFER_SIZE: usize, +> { + /// The serial peripheral device + serial_device: Serial, + /// The internal buffer for sending and receiving data + buffer: [u8; BUFFER_SIZE], + /// The most recent data received from the subscription + data: Option, +} + +impl + SerialPublisherSubscriber +where + Data: Packable, + Serial: ReadReady + Read + Write, + Err: Error, +{ + /// Create a new SerialPublisherSubscriber from the peripheral + pub fn new(serial_device: Serial, buffer: [u8; BUFFER_SIZE]) -> Self { + assert!( + BUFFER_SIZE >= Data::len(), + "The buffer must be large enough to fit encoded data" + ); + Self { + serial_device, + buffer, + data: None, + } + } + + /// Destroy the SerialPublisherSubscriber returning the serial + /// peripheral + pub fn destroy(self) -> Serial { + self.serial_device + } +} + +impl Publisher + for SerialPublisherSubscriber +where + Data: Packable, + Serial: ReadReady + Read + Write, + Err: Error, +{ + type Data = Data; + type Error = SerialPublishError; + + fn publish(&mut self, data: Self::Data) -> Result<(), Self::Error> { + self.buffer.iter_mut().for_each(|v| *v = 0); + data.pack(&mut self.buffer) + .map_err(SerialPublishError::PackingError)?; + + self.serial_device + .write_all(&self.buffer) + .map_err(SerialPublishError::IOError)?; + + Ok(()) + } +} + +impl Subscriber + for SerialPublisherSubscriber +where + Data: Packable, + Serial: ReadReady + Read + Write, + Err: Error, +{ + type Target = Option; + + fn get(&mut self) -> &Self::Target { + let mut new_data = None; + + while let Ok(ready) = self.serial_device.read_ready() { + if !ready { + break; + } + + self.buffer.iter_mut().for_each(|v| *v = 0); + if self.serial_device.read(&mut self.buffer).is_ok() { + if let Ok(data) = Data::unpack(&self.buffer) { + new_data = Some(data); + } + } + } + + if let Some(data) = new_data { + self.data = Some(data); + } + + &self.data + } +} diff --git a/ncomm-update-clients-and-servers/Cargo.toml b/ncomm-update-clients-and-servers/Cargo.toml index 66ac6d1..4a84891 100644 --- a/ncomm-update-clients-and-servers/Cargo.toml +++ b/ncomm-update-clients-and-servers/Cargo.toml @@ -11,9 +11,16 @@ homepage.workspace = true repository.workspace = true [dependencies] -crossbeam = { workspace = true } -ncomm-core = { workspace = true } -ncomm-utils = { workspace = true } +crossbeam = { workspace = true, optional = true } +ncomm-core = { workspace = true, default-features = false } +ncomm-utils = { workspace = true, default-features = false } +embedded-io = { workspace = true } [dev-dependencies] -rand = { workspace = true } \ No newline at end of file +rand = { workspace = true } + +[features] +default = ["std"] +nostd = ["ncomm-core/nostd", "ncomm-utils/nostd"] +alloc = ["nostd", "ncomm-core/alloc", "ncomm-utils/alloc"] +std = ["ncomm-core/std", "ncomm-utils/std", "dep:crossbeam"] \ No newline at end of file diff --git a/ncomm-update-clients-and-servers/src/lib.rs b/ncomm-update-clients-and-servers/src/lib.rs index 1bdebe1..2d2a04f 100644 --- a/ncomm-update-clients-and-servers/src/lib.rs +++ b/ncomm-update-clients-and-servers/src/lib.rs @@ -7,6 +7,9 @@ //! #![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "alloc")] +extern crate alloc; pub mod local; diff --git a/ncomm-update-clients-and-servers/src/local.rs b/ncomm-update-clients-and-servers/src/local.rs index b8f3653..45617d5 100644 --- a/ncomm-update-clients-and-servers/src/local.rs +++ b/ncomm-update-clients-and-servers/src/local.rs @@ -34,6 +34,13 @@ impl UpdateClient for LocalUpdateClient { Ok(()) } + fn poll_for_update(&mut self) -> Result, Self::Error> { + match self.update_rx.try_recv() { + Ok(update) => Ok(Some(update)), + Err(_) => Ok(None), + } + } + fn poll_for_updates(&mut self) -> Vec> { let mut updates = Vec::new(); for update in self.update_rx.try_iter() { @@ -42,6 +49,15 @@ impl UpdateClient for LocalUpdateClient { updates } + fn poll_for_response( + &mut self, + ) -> Result, Self::Error> { + match self.response_rx.try_recv() { + Ok(response) => Ok(Some(response)), + Err(_) => Ok(None), + } + } + fn poll_for_responses(&mut self) -> Vec> { let mut responses = Vec::new(); for response in self.response_rx.try_iter() { @@ -97,6 +113,15 @@ impl UpdateServer type Key = K; type Error = Infallible; + fn poll_for_request(&mut self) -> Result, Self::Error> { + for (k, (rx, _, _)) in self.client_map.iter() { + if let Ok(request) = rx.try_recv() { + return Ok(Some((k.clone(), request))); + } + } + Ok(None) + } + fn poll_for_requests(&mut self) -> Vec> { let mut requests = Vec::new(); for (k, (rx, _, _)) in self.client_map.iter() { @@ -209,4 +234,42 @@ mod tests { assert_eq!(response, original_response); } } + + #[test] + fn test_local_update_client_server_singular_request() { + let mut server = LocalUpdateServer::new(); + let mut client = server.create_update_client(0u8); + + let original_request = Request::new(); + let original_update = Update::new(original_request.clone()); + let original_response = Response::new(original_request.clone()); + + client.send_request(original_request.clone()).unwrap(); + + if let Ok(Some((client, request))) = server.poll_for_request() { + assert_eq!(request, original_request); + server + .send_update(client, &request, Update::new(request.clone())) + .unwrap(); + server + .send_response(client, request, Response::new(request.clone())) + .unwrap(); + } else { + assert!(false, "Expected a request to be received"); + } + + if let Ok(Some((request, update))) = client.poll_for_update() { + assert_eq!(request, original_request); + assert_eq!(update, original_update); + } else { + assert!(false, "Expected an update to be received"); + } + + if let Ok(Some((request, response))) = client.poll_for_response() { + assert_eq!(request, original_request); + assert_eq!(response, original_response); + } else { + assert!(false, "Expected a response to be received"); + } + } } diff --git a/ncomm-update-clients-and-servers/src/udp.rs b/ncomm-update-clients-and-servers/src/udp.rs index ea4024c..876e1d2 100644 --- a/ncomm-update-clients-and-servers/src/udp.rs +++ b/ncomm-update-clients-and-servers/src/udp.rs @@ -86,6 +86,41 @@ impl UpdateClient Ok(()) } + fn poll_for_update(&mut self) -> Result, Self::Error> { + let mut buffer = vec![0u8; Req::len() + std::cmp::max(Updt::len(), Res::len())]; + loop { + let (req, updt) = match self.socket.recv(&mut buffer) { + Ok(received) => { + if received - Req::len() == Updt::len() { + ( + Req::unpack(&buffer[..Req::len()]), + Updt::unpack(&buffer[Req::len()..]), + ) + } else if received - Req::len() == Res::len() { + let (req, res) = ( + Req::unpack(&buffer[..Req::len()]), + Res::unpack(&buffer[Req::len()..]), + ); + + if let (Ok(req), Ok(res)) = (req, res) { + self.response_buffer.push(Ok((req, res))); + } + buffer.iter_mut().for_each(|v| *v = 0); + continue; + } else { + break; + } + } + Err(_) => break, + }; + + if let (Ok(req), Ok(updt)) = (req, updt) { + return Ok(Some((req, updt))); + } + } + Ok(None) + } + fn poll_for_updates(&mut self) -> Vec> { let mut updates = Vec::new(); updates.append(&mut self.update_buffer); @@ -108,6 +143,7 @@ impl UpdateClient if let (Ok(req), Ok(res)) = (req, res) { self.response_buffer.push(Ok((req, res))); } + buffer.iter_mut().for_each(|v| *v = 0); continue; } else { ( @@ -122,11 +158,50 @@ impl UpdateClient if let (Ok(req), Ok(updt)) = (req, updt) { updates.push(Ok((req, updt))); } + buffer.iter_mut().for_each(|v| *v = 0); } updates } + fn poll_for_response( + &mut self, + ) -> Result, Self::Error> { + let mut buffer = vec![0u8; Req::len() + std::cmp::max(Updt::len(), Res::len())]; + loop { + let (req, res) = match self.socket.recv(&mut buffer) { + Ok(received) => { + if received - Req::len() == Updt::len() { + let (req, updt) = ( + Req::unpack(&buffer[..Req::len()]), + Updt::unpack(&buffer[Req::len()..]), + ); + + if let (Ok(req), Ok(updt)) = (req, updt) { + self.update_buffer.push(Ok((req, updt))); + } + + buffer.iter_mut().for_each(|v| *v = 0); + continue; + } else if received - Req::len() == Res::len() { + ( + Req::unpack(&buffer[..Req::len()]), + Res::unpack(&buffer[Req::len()..]), + ) + } else { + break; + } + } + Err(_) => break, + }; + + if let (Ok(req), Ok(res)) = (req, res) { + return Ok(Some((req, res))); + } + } + Ok(None) + } + fn poll_for_responses(&mut self) -> Vec> { let mut responses = Vec::new(); responses.append(&mut self.response_buffer); @@ -223,6 +298,27 @@ impl Update type Key = K; type Error = UdpUpdateClientServerError; + fn poll_for_request(&mut self) -> Result, Self::Error> { + let mut buffer = vec![0u8; Req::len()]; + let address = match self.socket.recv_from(&mut buffer) { + Ok((_requset_size, address)) => address, + Err(_) => return Ok(None), + }; + + match Req::unpack(&buffer[..]) { + Ok(data) => { + if let Some((k, _)) = self.client_addresses.iter().find(|v| v.1 == address) { + Ok(Some((k.clone(), data))) + } else { + Err(UdpUpdateClientServerError::UnknownRequester(( + data, address, + ))) + } + } + Err(err) => Err(UdpUpdateClientServerError::PackingError(err)), + } + } + fn poll_for_requests(&mut self) -> Vec> { let mut requests = Vec::new(); @@ -517,4 +613,59 @@ mod tests { assert_eq!(response.1, client_two_response); } } + + #[test] + fn test_udp_update_client_server_singular_request() { + let mut server: UdpUpdateServer = + UdpUpdateServer::new_with( + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 7008)), + vec![( + 0, + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 7009)), + )], + ) + .unwrap(); + + let mut client_one: UdpUpdateClient = UdpUpdateClient::new( + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 7009)), + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 7008)), + ) + .unwrap(); + + let client_one_request = Request::new(); + let client_one_update = Update::new(client_one_request.clone()); + let client_one_response = Response::new(client_one_request.clone()); + + client_one.send_request(client_one_request).unwrap(); + + sleep(Duration::from_millis(50)); + + if let Ok(Some((client, request))) = server.poll_for_request() { + assert_eq!(request, client_one_request); + server + .send_update(client, &request, Update::new(request.clone())) + .unwrap(); + server + .send_response(client, request, Response::new(request.clone())) + .unwrap(); + } else { + assert!(false, "Expected a request"); + } + + sleep(Duration::from_millis(50)); + + if let Ok(Some((request, update))) = client_one.poll_for_update() { + assert_eq!(request, client_one_request); + assert_eq!(update, client_one_update); + } else { + assert!(false, "Expected an update"); + } + + if let Ok(Some((request, response))) = client_one.poll_for_response() { + assert_eq!(request, client_one_request); + assert_eq!(response, client_one_response); + } else { + assert!(false, "Expected a response"); + } + } } diff --git a/ncomm-utils/Cargo.toml b/ncomm-utils/Cargo.toml index 29ad254..13b4f5b 100644 --- a/ncomm-utils/Cargo.toml +++ b/ncomm-utils/Cargo.toml @@ -15,4 +15,5 @@ repository.workspace = true [features] default = ["std"] nostd = [] +alloc = [] std = [] \ No newline at end of file diff --git a/ncomm-utils/src/lib.rs b/ncomm-utils/src/lib.rs index b2b785d..52ea40a 100644 --- a/ncomm-utils/src/lib.rs +++ b/ncomm-utils/src/lib.rs @@ -6,7 +6,9 @@ //! are still useful for developing applications utilizing NComm. //! -#![no_std] #![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "alloc")] +extern crate alloc; pub mod packing; diff --git a/ncomm/Cargo.toml b/ncomm/Cargo.toml index 1554124..7d1d503 100644 --- a/ncomm/Cargo.toml +++ b/ncomm/Cargo.toml @@ -12,15 +12,43 @@ authors.workspace = true categories.workspace = true [dependencies] -ncomm-core = { workspace = true } -ncomm-utils = { workspace = true } -ncomm-executors = { workspace = true } -ncomm-publishers-and-subscribers = { workspace = true } -ncomm-clients-and-servers = { workspace = true } -ncomm-update-clients-and-servers = { workspace = true } -ncomm-nodes = { workspace = true } +ncomm-core = { workspace = true, default-features = false } +ncomm-utils = { workspace = true, default-features = false } +ncomm-executors = { workspace = true, default-features = false } +ncomm-publishers-and-subscribers = { workspace = true, default-features = false } +ncomm-clients-and-servers = { workspace = true, default-features = false } +ncomm-update-clients-and-servers = { workspace = true, default-features = false } +ncomm-nodes = { workspace = true, default-features = false } [features] default = [] -rerun = ["ncomm-nodes/rerun", "ncomm-publishers-and-subscribers/rerun"] -rerun-web-viewer = ["rerun", "ncomm-nodes/rerun-web-viewer"] \ No newline at end of file +nostd = [ + "ncomm-core/nostd", + "ncomm-utils/nostd", + "ncomm-executors/nostd", + "ncomm-publishers-and-subscribers/nostd", + "ncomm-clients-and-servers/nostd", + "ncomm-update-clients-and-servers/nostd", + "ncomm-nodes/nostd", +] +alloc = [ + "nostd", + "ncomm-core/alloc", + "ncomm-utils/alloc", + "ncomm-executors/alloc", + "ncomm-publishers-and-subscribers/alloc", + "ncomm-clients-and-servers/alloc", + "ncomm-update-clients-and-servers/alloc", + "ncomm-nodes/alloc", +] +std = [ + "ncomm-core/std", + "ncomm-utils/std", + "ncomm-executors/std", + "ncomm-publishers-and-subscribers/std", + "ncomm-clients-and-servers/std", + "ncomm-update-clients-and-servers/std", + "ncomm-nodes/std", +] +rerun = ["std", "ncomm-nodes/rerun", "ncomm-publishers-and-subscribers/rerun"] +rerun-web-viewer = ["std", "rerun", "ncomm-nodes/rerun-web-viewer"] \ No newline at end of file diff --git a/ncomm/src/lib.rs b/ncomm/src/lib.rs index afff443..8d4ee36 100644 --- a/ncomm/src/lib.rs +++ b/ncomm/src/lib.rs @@ -70,6 +70,10 @@ //! Currently, NComm is only available on Rust with the `std` toolchain. This is fine, but I would love to have `no_std` versions of the communication primitives for embedded development. However, I don't think it is likely I will make `no_std` executors and instead work on adding communication primitive support to current RTOS's like RTIC and Embassy (both of which much better than I could likely create on my own). In general, I would encourage people to add anything they think is missing to this project. I can already see a possibility for building secure network communication primitives and creating standard nodes for data visualization and logging so I encourage people to build whatever they see fit. //! +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "alloc")] +extern crate alloc; + pub mod prelude; /// NComm Clients and Servers From d5d2f04c7fddf5a148d301a3ca935233bed5d852 Mon Sep 17 00:00:00 2001 From: N8BWert Date: Fri, 13 Sep 2024 22:32:43 -0400 Subject: [PATCH 2/2] further increase allowable time for threaded_executor tests --- ncomm-executors/src/threaded_executor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ncomm-executors/src/threaded_executor.rs b/ncomm-executors/src/threaded_executor.rs index 15e33f3..b1df4c7 100644 --- a/ncomm-executors/src/threaded_executor.rs +++ b/ncomm-executors/src/threaded_executor.rs @@ -602,7 +602,7 @@ mod tests { } assert!(Duration::from_millis(95) < end - start); - assert!(end - start < Duration::from_millis(120)); + assert!(end - start < Duration::from_millis(150)); } #[test]