demo
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
.idea
|
||||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async_cyclic_app_demo"
|
||||||
|
version = "0.1.0"
|
||||||
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "async_cyclic_app_demo"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
publish = ["gitea"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
149
src/main.rs
Normal file
149
src/main.rs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
use crate::wait_step::wait_step;
|
||||||
|
use crate::MessageFromDevice::{CyclicData, NoOpResponse, SetConfigAResponse};
|
||||||
|
use crate::MessageToDevice::{NoOp, SetConfigA};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::pin::pin;
|
||||||
|
use std::task::{Context, Poll, Waker};
|
||||||
|
|
||||||
|
mod wait_step;
|
||||||
|
|
||||||
|
struct Device {
|
||||||
|
rx_stream: VecDeque<Result<Option<MessageFromDevice>, Box<dyn Error>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device {
|
||||||
|
fn tx(&mut self, message_to_device: MessageToDevice) {
|
||||||
|
// This doesn't actually need to do anything
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rx(&'_ mut self) -> Result<Option<MessageFromDevice>, Box<dyn Error>> {
|
||||||
|
self.rx_stream
|
||||||
|
.pop_front()
|
||||||
|
.unwrap_or_else(|| Err("I/O Error".into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MessageToDevice {
|
||||||
|
NoOp,
|
||||||
|
SetConfigA,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MessageFromDevice {
|
||||||
|
NoOpResponse,
|
||||||
|
SetConfigAResponse,
|
||||||
|
CyclicData {
|
||||||
|
tlm: i32
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn application(mut device: Device) -> Result<(), Box<dyn Error>> {
|
||||||
|
// Startup Logic
|
||||||
|
println!("Future Startup");
|
||||||
|
|
||||||
|
// Wait (i.e. we've completed initialization)
|
||||||
|
wait_step().await;
|
||||||
|
|
||||||
|
let mut tlm_value = 0;
|
||||||
|
|
||||||
|
// loop: i.e. always try to reconnect if needed
|
||||||
|
loop {
|
||||||
|
// Send a NoOp request
|
||||||
|
device.tx(NoOp);
|
||||||
|
|
||||||
|
println!("Sent Tx!");
|
||||||
|
// Wait until the next cycle
|
||||||
|
wait_step().await;
|
||||||
|
|
||||||
|
match device.rx()? {
|
||||||
|
None => continue, // Didn't hear back from the device, so back to the top of the loop (attempt reconnect)
|
||||||
|
Some(_) => {}, // We don't care what data the device sent us
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Comms Restored!");
|
||||||
|
|
||||||
|
// Yay we can hear from the device!
|
||||||
|
device.tx(SetConfigA);
|
||||||
|
println!("Configuring Device!");
|
||||||
|
let mut configured = false;
|
||||||
|
for _ in 0..3 { // 3 cycles to Rx the response
|
||||||
|
match device.rx()? {
|
||||||
|
Some(SetConfigAResponse) => {
|
||||||
|
configured = true;
|
||||||
|
break
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
wait_step().await;
|
||||||
|
}
|
||||||
|
if !configured {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Configured Device!");
|
||||||
|
|
||||||
|
// Configuring is done - we now just need to listen from the device
|
||||||
|
// and monitor for loss of comms
|
||||||
|
let mut missed_message_count = 0;
|
||||||
|
while missed_message_count < 3{
|
||||||
|
wait_step().await;
|
||||||
|
match device.rx()? {
|
||||||
|
None => missed_message_count += 1,
|
||||||
|
Some(CyclicData {tlm}) => tlm_value = tlm,
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
println!("Latest Tlm: {tlm_value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Loss of Comms!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut device = Device { rx_stream: Default::default() };
|
||||||
|
// The device hasn't turned on yet (but no IO errors)
|
||||||
|
device.rx_stream.push_back(Ok(None));
|
||||||
|
device.rx_stream.push_back(Ok(None));
|
||||||
|
// We received the NoOp command
|
||||||
|
device.rx_stream.push_back(Ok(Some(NoOpResponse)));
|
||||||
|
// Set Config A
|
||||||
|
// No immediate response
|
||||||
|
device.rx_stream.push_back(Ok(None));
|
||||||
|
device.rx_stream.push_back(Ok(Some(SetConfigAResponse)));
|
||||||
|
// Monitoring
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 1 })));
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 2 })));
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 3 })));
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 4 })));
|
||||||
|
// Loss of comms
|
||||||
|
device.rx_stream.push_back(Ok(None));
|
||||||
|
device.rx_stream.push_back(Ok(None));
|
||||||
|
device.rx_stream.push_back(Ok(None));
|
||||||
|
// No NoOp Response but comms restored due to cyclic data
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 5 })));
|
||||||
|
// Attempting to reconfigure
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 6 })));
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 7 })));
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 8 })));
|
||||||
|
// Failed back to loss of comms
|
||||||
|
// We received the NoOp command
|
||||||
|
device.rx_stream.push_back(Ok(Some(NoOpResponse)));
|
||||||
|
// Configured successfully
|
||||||
|
device.rx_stream.push_back(Ok(Some(SetConfigAResponse)));
|
||||||
|
// Normal Telemetry
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 9 })));
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 10 })));
|
||||||
|
device.rx_stream.push_back(Ok(Some(CyclicData { tlm: 11 })));
|
||||||
|
|
||||||
|
println!("Startup");
|
||||||
|
let mut application_future = pin!(application(device));
|
||||||
|
// Poll once to get to the end of initialization
|
||||||
|
let _ = application_future.as_mut().poll(&mut Context::from_waker(Waker::noop()));
|
||||||
|
println!("Init Complete");
|
||||||
|
|
||||||
|
while let Poll::Pending = application_future.as_mut().poll(&mut Context::from_waker(Waker::noop())) {
|
||||||
|
println!("step()");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Complete");
|
||||||
|
}
|
||||||
28
src/wait_step.rs
Normal file
28
src/wait_step.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
struct WaitStep {
|
||||||
|
should_wait: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for WaitStep {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
if self.should_wait {
|
||||||
|
// Required to call wake by poll spec
|
||||||
|
// This implementation won't actually care
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
|
||||||
|
self.should_wait = false;
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_step() -> impl Future<Output=()> {
|
||||||
|
WaitStep { should_wait: true }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user