Skip to content

Commit

Permalink
Merge pull request #4 from BridgeSource/host_traits
Browse files Browse the repository at this point in the history
Host traits
  • Loading branch information
amcelroy authored Nov 29, 2023
2 parents 5120739 + df0a007 commit 74276f2
Show file tree
Hide file tree
Showing 7 changed files with 460 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 15 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "flem"
version = "0.6.1"
version = "0.6.2"
edition = "2021"
description = "Flexible, Light-weight, Embedded Messaging Protocol"
repository = "https://github.com/BridgeSource/flem-rs.git"
Expand All @@ -11,6 +11,10 @@ license = "MIT"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = []
std = []

[lib]
name = "flem"
crate-type = ["lib"]
Expand All @@ -22,8 +26,16 @@ name = "flem"
path = "examples/example.rs"

[[example]]
name = "traits"
path = "examples/traits.rs"
name = "encode_decode"
path = "examples/encode_decode.rs"

[[example]]
name = "software_host_simple"
path = "examples/software_host_simple.rs"

[[example]]
name = "software_host_complex"
path = "examples/software_host_complex.rs"

[dev-dependencies]
heapless = "0.7"
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
![Flem Build and Tests](https://github.com/amcelroy/flem-rust/actions/workflows/rust.yml/badge.svg)

# FLEM Rust 0.6.0
# FLEM Rust 0.6.2

FLEM stands for Flexible, Light-weight, Embedded Messaging and is a Little
Endian messaging protocol intended for use in communicating with embedded
systems targets over numerous types of buses.

## Changelog

### Changelog 0.6.2
- Added feature = ["std"]
- Added `Channel` trait. This trait requires features = ["std"]. It servers as a set of traits that can be used
to implement different hardware or to emulate a device. It uses the `std` library for threading and `mpsc` channels.
- Added examples to show how to emulate a device use the `Channel` trait.

### Changelog 0.6.1
- Added fmt::Debug trait to Packet that prints the header, checksum, request, response, length, and status.

Expand Down
File renamed without changes.
259 changes: 259 additions & 0 deletions examples/software_host_complex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
use flem::{traits::Channel, DataId, Packet};
use std::{
sync::{
mpsc::{self, Receiver, Sender},
Arc, Mutex,
},
thread,
time::Duration,
};

const PACKET_SIZE: usize = 512;
const PACKET_DEVICE_SIZE: usize = 128;

#[derive(Clone)]
struct FlemSoftwareHost<const PACKET_SIZE: usize> {
listening: Arc<Mutex<bool>>,
flem_packet_handler: Option<fn(&Packet<PACKET_DEVICE_SIZE>) -> Packet<PACKET_DEVICE_SIZE>>,
}

impl<const PACKET_SIZE: usize> FlemSoftwareHost<PACKET_SIZE> {
pub fn new() -> Self {
FlemSoftwareHost {
listening: Arc::new(Mutex::new(false)),
flem_packet_handler: None,
}
}
}

impl<const PACKET_SIZE: usize> Channel<PACKET_SIZE> for FlemSoftwareHost<PACKET_SIZE> {
type Error = ();

fn list_devices(&self) -> Vec<String> {
let mut devices = Vec::<String>::new();
devices.push(String::from("Software Host"));
devices
}

fn connect(&mut self, device: &String) -> Result<(), Self::Error> {
Ok(())
}

fn disconnect(&mut self) -> Result<(), Self::Error> {
Ok(())
}

fn listen(
&mut self,
rx_sleep_time_ms: u64,
tx_sleep_time_ms: u64,
) -> (Sender<Packet<PACKET_SIZE>>, Receiver<Packet<PACKET_SIZE>>) {
// This example is similar to how flem-serial-rs works, except we need to add in a simulated byte-by-byte hardware transmission.
// If there is no need to simulate Rx and Tx hardware, a single thread would work that accepts a packet from `packet_to_transmit`,
// parses it, and sends a response on `validated_packet`.

// Tx packets are marshalled into a single queue, and dispatched over hardware.
let (tx_packet_from_program, packet_to_transmit) =
mpsc::channel::<flem::Packet<PACKET_SIZE>>();

// Rx data is coming off of hardware, usually a byte at a time, and needs to be constructed into a packet and validated before passing back into the program
let (validated_packet, rx_packet_to_program) = mpsc::channel::<flem::Packet<PACKET_SIZE>>();

// This is a simple channel to simulated byte-by-byte transmission over hardware
let (simulated_hardware_host_tx, simulated_hardware_device_rx) = mpsc::channel::<u8>();
let (simulated_hardware_device_tx, simulated_hardware_host_rx) = mpsc::channel::<u8>();

*self.listening.lock().unwrap() = true;

let listening_clone_rx = self.listening.clone();
let listening_clone_tx = self.listening.clone();
let listening_clone_device = self.listening.clone();

// Tx Thread - Transmit packets to the "device"
let tx_handle = thread::spawn(move || {
while *listening_clone_tx.lock().unwrap() {
// Check if there is a packet to transmit, use recv_timeout to prevent a blocking thread
if let Ok(tx_packet) =
packet_to_transmit.recv_timeout(Duration::from_millis(tx_sleep_time_ms))
{
// Send the packet one byte at a time
for byte in tx_packet.bytes() {
simulated_hardware_host_tx.send(*byte).unwrap();
}
println!(
"Raw packet from host to device in bytes: {:?}",
tx_packet.bytes()
);
println!("Host transmitted packet successfully");
}
}
});

let device_flem_handler = self.flem_packet_handler.clone();

let simulated_device_thread = thread::spawn(move || {
let mut packet = flem::Packet::<PACKET_DEVICE_SIZE>::new();

while *listening_clone_device.lock().unwrap() {
// List for data on hardware
if let Ok(byte) =
simulated_hardware_device_rx.recv_timeout(Duration::from_millis(10))
{
match packet.construct(byte) {
Ok(_) => {
// Packet received
println!(
"Packet received on device successfully with checksum {}",
packet.get_checksum()
);

if device_flem_handler.is_none() {
println!("Packet handler not set, working as a loop-back");
for byte in packet.bytes() {
simulated_hardware_device_tx.send(*byte).unwrap();
}
} else {
println!("Packet handler set, calling handler");
let handler = device_flem_handler.as_ref().unwrap();
let response = handler(&packet);
for byte in response.bytes() {
simulated_hardware_device_tx.send(*byte).unwrap();
}
println!(
"Raw packet from device to host in bytes: {:?}",
response.bytes()
);
}

println!("Packet sent from device successfully");

packet.reset_lazy();
}
Err(error) => {
match error {
flem::Status::PacketBuilding => {
// Packet not yet received
}
_ => {
println!("Error parsing packet");
packet.reset_lazy();
}
}
}
}
}
}
});

// Rx Thread - Receive packets from the "device"
let rx_handle = thread::spawn(move || {
let mut packet = flem::Packet::<PACKET_SIZE>::new();

while *listening_clone_rx.lock().unwrap() {
// List for data on hardware
if let Ok(byte) =
simulated_hardware_host_rx.recv_timeout(Duration::from_millis(rx_sleep_time_ms))
{
match packet.construct(byte) {
Ok(_) => {
// Packet received
println!(
"Packet received successfully with checksum {}",
packet.get_checksum()
);
validated_packet.send(packet);

println!("Packet sent to program");

packet.reset_lazy();
}
Err(error) => {
match error {
flem::Status::PacketBuilding => {
// Packet not yet received
}
_ => {
println!("Error parsing packet");
packet.reset_lazy();
}
}
}
}
}
}
});

(tx_packet_from_program, rx_packet_to_program)
}

fn unlisten(&mut self) -> Result<(), Self::Error> {
*self.listening.lock().unwrap() = false;
Ok(())
}
}

fn main() {
let mut host = FlemSoftwareHost::<PACKET_SIZE>::new();

// Our "device" FLEM handler
fn device_flem_handler(packet: &Packet<PACKET_DEVICE_SIZE>) -> Packet<PACKET_DEVICE_SIZE> {
let mut response = Packet::<PACKET_DEVICE_SIZE>::new();

match packet.get_request() {
flem::request::ID => {
let id = DataId::new("Emulated Target", 0, 0, 1, PACKET_DEVICE_SIZE);

// Respond with ID
response.set_request(flem::request::ID);
response.set_response(flem::response::SUCCESS);
response.pack_id(&id, true);
}
_ => {
response.set_request(flem::request::ID);
response.set_response(flem::response::UNKNOWN_REQUEST);
response.pack();
}
}

response
}

// Configure the packet handler
host.flem_packet_handler = Some(device_flem_handler);

let (tx, rx) = host.listen(10, 10);

let mut packet = flem::Packet::<PACKET_SIZE>::new();

packet.set_request(flem::request::ID);
packet.pack();

tx.send(packet).unwrap();

loop {
if let Ok(packet) = rx.recv_timeout(Duration::from_millis(25)) {
println!("Received packet: {:?}", packet);

// Do stuff with the packet
match packet.get_request() {
flem::request::ID => {
let id = DataId::from(&packet.get_data()).unwrap();
println!(
"DataId Message: {}, max packet size: {}, Major: {}, Minor: {}, Patch: {}",
String::from_iter(id.get_name().iter()),
id.get_max_packet_size(),
id.get_major(),
id.get_minor(),
id.get_patch()
);
}
_ => {
// Unknown request
}
}

host.unlisten().unwrap();
break;
}
}
}
Loading

0 comments on commit 74276f2

Please sign in to comment.