Integration of the mini simulator into the sat-rs example
Some checks are pending
Rust/sat-rs/pipeline/pr-main Build started...

This commit is contained in:
2024-06-03 15:18:23 +02:00
parent a4c433a7be
commit 29167736db
42 changed files with 3578 additions and 697 deletions

View File

@ -1,22 +1,17 @@
use core::time::Duration;
use derive_new::new;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
#[allow(unused_imports)]
pub use std_mod::*;
/// Generic trait for a device capable of switching itself on or off.
pub trait PowerSwitch {
type Error;
fn switch_on(&mut self) -> Result<(), Self::Error>;
fn switch_off(&mut self) -> Result<(), Self::Error>;
fn is_switch_on(&self) -> bool {
self.switch_state() == SwitchState::On
}
fn switch_state(&self) -> SwitchState;
}
use crate::request::MessageMetadata;
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SwitchState {
Off = 0,
On = 1,
@ -26,6 +21,7 @@ pub enum SwitchState {
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SwitchStateBinary {
Off = 0,
On = 1,
@ -63,76 +59,254 @@ impl From<SwitchStateBinary> for SwitchState {
pub type SwitchId = u16;
/// Generic trait for a device capable of turning on and off switches.
pub trait PowerSwitcherCommandSender {
type Error;
pub trait PowerSwitcherCommandSender<SwitchType: Into<u16>> {
type Error: core::fmt::Debug;
fn send_switch_on_cmd(&mut self, switch_id: SwitchId) -> Result<(), Self::Error>;
fn send_switch_off_cmd(&mut self, switch_id: SwitchId) -> Result<(), Self::Error>;
fn send_switch_on_cmd(
&self,
requestor_info: MessageMetadata,
switch_id: SwitchType,
) -> Result<(), Self::Error>;
fn send_switch_off_cmd(
&self,
requestor_info: MessageMetadata,
switch_id: SwitchType,
) -> Result<(), Self::Error>;
}
pub trait PowerSwitchInfo {
type Error;
pub trait PowerSwitchInfo<SwitchType> {
type Error: core::fmt::Debug;
/// Retrieve the switch state
fn get_switch_state(&mut self, switch_id: SwitchId) -> Result<SwitchState, Self::Error>;
fn switch_state(&self, switch_id: SwitchType) -> Result<SwitchState, Self::Error>;
fn get_is_switch_on(&mut self, switch_id: SwitchId) -> Result<bool, Self::Error> {
Ok(self.get_switch_state(switch_id)? == SwitchState::On)
fn is_switch_on(&self, switch_id: SwitchType) -> Result<bool, Self::Error> {
Ok(self.switch_state(switch_id)? == SwitchState::On)
}
/// The maximum delay it will take to change a switch.
///
/// This may take into account the time to send a command, wait for it to be executed, and
/// see the switch changed.
fn switch_delay_ms(&self) -> u32;
fn switch_delay_ms(&self) -> Duration;
}
#[derive(new)]
pub struct SwitchRequest {
switch_id: SwitchId,
target_state: SwitchStateBinary,
}
impl SwitchRequest {
pub fn switch_id(&self) -> SwitchId {
self.switch_id
}
pub fn target_state(&self) -> SwitchStateBinary {
self.target_state
}
}
#[cfg(feature = "std")]
pub mod std_mod {
use std::sync::mpsc;
use crate::{
queue::GenericSendError,
request::{GenericMessage, MessageMetadata},
};
use super::*;
pub type MpscSwitchCmdSender = mpsc::Sender<GenericMessage<SwitchRequest>>;
pub type MpscSwitchCmdSenderBounded = mpsc::SyncSender<GenericMessage<SwitchRequest>>;
impl<SwitchType: Into<u16>> PowerSwitcherCommandSender<SwitchType> for MpscSwitchCmdSender {
type Error = GenericSendError;
fn send_switch_on_cmd(
&self,
requestor_info: MessageMetadata,
switch_id: SwitchType,
) -> Result<(), Self::Error> {
self.send(GenericMessage::new(
requestor_info,
SwitchRequest::new(switch_id.into(), SwitchStateBinary::On),
))
.map_err(|_| GenericSendError::RxDisconnected)
}
fn send_switch_off_cmd(
&self,
requestor_info: MessageMetadata,
switch_id: SwitchType,
) -> Result<(), Self::Error> {
self.send(GenericMessage::new(
requestor_info,
SwitchRequest::new(switch_id.into(), SwitchStateBinary::Off),
))
.map_err(|_| GenericSendError::RxDisconnected)
}
}
impl<SwitchType: Into<u16>> PowerSwitcherCommandSender<SwitchType> for MpscSwitchCmdSenderBounded {
type Error = GenericSendError;
fn send_switch_on_cmd(
&self,
requestor_info: MessageMetadata,
switch_id: SwitchType,
) -> Result<(), Self::Error> {
self.try_send(GenericMessage::new(
requestor_info,
SwitchRequest::new(switch_id.into(), SwitchStateBinary::On),
))
.map_err(|e| match e {
mpsc::TrySendError::Full(_) => GenericSendError::QueueFull(None),
mpsc::TrySendError::Disconnected(_) => GenericSendError::RxDisconnected,
})
}
fn send_switch_off_cmd(
&self,
requestor_info: MessageMetadata,
switch_id: SwitchType,
) -> Result<(), Self::Error> {
self.try_send(GenericMessage::new(
requestor_info,
SwitchRequest::new(switch_id.into(), SwitchStateBinary::Off),
))
.map_err(|e| match e {
mpsc::TrySendError::Full(_) => GenericSendError::QueueFull(None),
mpsc::TrySendError::Disconnected(_) => GenericSendError::RxDisconnected,
})
}
}
}
#[cfg(test)]
mod tests {
#![allow(dead_code)]
// TODO: Add unittests for PowerSwitcherCommandSender impls for mpsc.
use std::sync::mpsc::{self, TryRecvError};
use crate::{queue::GenericSendError, request::GenericMessage, ComponentId};
use super::*;
use std::boxed::Box;
struct Pcdu {
switch_rx: std::sync::mpsc::Receiver<(SwitchId, u16)>,
const TEST_REQ_ID: u32 = 2;
const TEST_SENDER_ID: ComponentId = 5;
const TEST_SWITCH_ID: u16 = 0x1ff;
fn common_checks(request: &GenericMessage<SwitchRequest>) {
assert_eq!(request.requestor_info.sender_id(), TEST_SENDER_ID);
assert_eq!(request.requestor_info.request_id(), TEST_REQ_ID);
assert_eq!(request.message.switch_id(), TEST_SWITCH_ID);
}
#[derive(Eq, PartialEq)]
enum DeviceState {
OFF,
SwitchingPower,
ON,
SETUP,
IDLE,
}
struct MyComplexDevice {
power_switcher: Box<dyn PowerSwitcherCommandSender<Error = ()>>,
power_info: Box<dyn PowerSwitchInfo<Error = ()>>,
switch_id: SwitchId,
some_state: u16,
dev_state: DeviceState,
mode: u32,
submode: u16,
#[test]
fn test_comand_switch_sending_mpsc_regular_on_cmd() {
let (switch_cmd_tx, switch_cmd_rx) = mpsc::channel::<GenericMessage<SwitchRequest>>();
switch_cmd_tx
.send_switch_on_cmd(
MessageMetadata::new(TEST_REQ_ID, TEST_SENDER_ID),
TEST_SWITCH_ID,
)
.expect("sending switch cmd failed");
let request = switch_cmd_rx
.recv()
.expect("receiving switch request failed");
common_checks(&request);
assert_eq!(request.message.target_state(), SwitchStateBinary::On);
}
impl MyComplexDevice {
pub fn periodic_op(&mut self) {
// .. mode command coming in
let mode = 1;
if mode == 1 {
if self.dev_state == DeviceState::OFF {
self.power_switcher
.send_switch_on_cmd(self.switch_id)
.expect("sending siwthc cmd failed");
self.dev_state = DeviceState::SwitchingPower;
}
if self.dev_state == DeviceState::SwitchingPower {
if self.power_info.get_is_switch_on(0).unwrap() {
self.dev_state = DeviceState::ON;
self.mode = 1;
}
}
}
}
#[test]
fn test_comand_switch_sending_mpsc_regular_off_cmd() {
let (switch_cmd_tx, switch_cmd_rx) = mpsc::channel::<GenericMessage<SwitchRequest>>();
switch_cmd_tx
.send_switch_off_cmd(
MessageMetadata::new(TEST_REQ_ID, TEST_SENDER_ID),
TEST_SWITCH_ID,
)
.expect("sending switch cmd failed");
let request = switch_cmd_rx
.recv()
.expect("receiving switch request failed");
common_checks(&request);
assert_eq!(request.message.target_state(), SwitchStateBinary::Off);
}
#[test]
fn test_comand_switch_sending_mpsc_regular_rx_disconnected() {
let (switch_cmd_tx, switch_cmd_rx) = mpsc::channel::<GenericMessage<SwitchRequest>>();
drop(switch_cmd_rx);
let result = switch_cmd_tx.send_switch_off_cmd(
MessageMetadata::new(TEST_REQ_ID, TEST_SENDER_ID),
TEST_SWITCH_ID,
);
assert!(result.is_err());
matches!(result.unwrap_err(), GenericSendError::RxDisconnected);
}
#[test]
fn test_comand_switch_sending_mpsc_sync_on_cmd() {
let (switch_cmd_tx, switch_cmd_rx) = mpsc::sync_channel::<GenericMessage<SwitchRequest>>(3);
switch_cmd_tx
.send_switch_on_cmd(
MessageMetadata::new(TEST_REQ_ID, TEST_SENDER_ID),
TEST_SWITCH_ID,
)
.expect("sending switch cmd failed");
let request = switch_cmd_rx
.recv()
.expect("receiving switch request failed");
common_checks(&request);
assert_eq!(request.message.target_state(), SwitchStateBinary::On);
}
#[test]
fn test_comand_switch_sending_mpsc_sync_off_cmd() {
let (switch_cmd_tx, switch_cmd_rx) = mpsc::sync_channel::<GenericMessage<SwitchRequest>>(3);
switch_cmd_tx
.send_switch_off_cmd(
MessageMetadata::new(TEST_REQ_ID, TEST_SENDER_ID),
TEST_SWITCH_ID,
)
.expect("sending switch cmd failed");
let request = switch_cmd_rx
.recv()
.expect("receiving switch request failed");
common_checks(&request);
assert_eq!(request.message.target_state(), SwitchStateBinary::Off);
}
#[test]
fn test_comand_switch_sending_mpsc_sync_rx_disconnected() {
let (switch_cmd_tx, switch_cmd_rx) = mpsc::sync_channel::<GenericMessage<SwitchRequest>>(1);
drop(switch_cmd_rx);
let result = switch_cmd_tx.send_switch_off_cmd(
MessageMetadata::new(TEST_REQ_ID, TEST_SENDER_ID),
TEST_SWITCH_ID,
);
assert!(result.is_err());
matches!(result.unwrap_err(), GenericSendError::RxDisconnected);
}
#[test]
fn test_comand_switch_sending_mpsc_sync_queue_full() {
let (switch_cmd_tx, switch_cmd_rx) = mpsc::sync_channel::<GenericMessage<SwitchRequest>>(1);
let mut result = switch_cmd_tx.send_switch_off_cmd(
MessageMetadata::new(TEST_REQ_ID, TEST_SENDER_ID),
TEST_SWITCH_ID,
);
assert!(result.is_ok());
result = switch_cmd_tx.send_switch_off_cmd(
MessageMetadata::new(TEST_REQ_ID, TEST_SENDER_ID),
TEST_SWITCH_ID,
);
assert!(result.is_err());
matches!(result.unwrap_err(), GenericSendError::QueueFull(None));
matches!(switch_cmd_rx.try_recv(), Err(TryRecvError::Empty));
}
}