Reuse simulator #295

Open
muellerr wants to merge 4 commits from reuse-simulator into main
117 changed files with 240 additions and 218 deletions
+9 -8
View File
@@ -3,17 +3,18 @@ resolver = "2"
members = [
"satrs",
"satrs-mib",
"satrs-example",
"satrs-example/types",
"satrs-example/client",
"satrs-example/minisim",
"examples/obsw-std",
"examples/types",
"examples/client",
"examples/minisim",
"examples/minisim-types",
"satrs-shared",
"tmtc-utils",
"embedded-examples/embedded-client",
"embedded-examples/types",
"examples/embedded-client",
"examples/embedded-types",
]
exclude = [
"embedded-examples/stm32f3-disco-rtic",
"embedded-examples/stm32h7-nucleo-rtic",
"examples/stm32f3-disco-rtic",
"examples/stm32h7-nucleo-rtic",
]
+28 -16
View File
@@ -26,7 +26,7 @@ and [EIVE](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-
# Overview
This project currently contains following crates:
This project currently contains the following crates:
* [`satrs-book`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-book):
Primary information resource in addition to the API documentation, hosted
@@ -34,24 +34,36 @@ This project currently contains following crates:
this first before delving into the example application and the API documentation.
* [`satrs`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs):
Primary crate.
* [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example):
Example of a simple example on-board software using various sat-rs components which can be run
on a host computer or on any system with a standard runtime like a Raspberry Pi.
* [`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-minisim):
Mini-Simulator based on [nexosim](https://github.com/asynchronics/nexosim) which
simulates some physical devices for the `satrs-example` application device handlers.
* [`satrs-mib`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-mib):
Components to build a mission information base from the on-board software directly.
* [`satrs-stm32f3-disco-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/embedded-examples/stm32f3-disco-rtic):
Example of a simple example using low-level sat-rs components on a bare-metal system
with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic)
framework on the STM32F3-Discovery device.
* [`satrs-stm32h-nucleo-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/embedded-examples/stm32h7-nucleo-rtic):
Example of a simple example using sat-rs components on a bare-metal system
with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic)
framework on the STM32H743ZIT device.
Each project has its own `CHANGELOG.md`.
## Examples
All examples and their helper crates are located inside the
[`examples`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples) folder:
* [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples/obsw-std):
Example on-board software using various sat-rs components which can be run on a host computer
or on any system with a standard runtime like a Raspberry Pi.
* [`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples/minisim):
Mini-Simulator based on [nexosim](https://github.com/asynchronics/nexosim) which
simulates some physical devices for the `satrs-example` application device handlers.
* [`client`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples/client): Ground client to command the `satrs-example` application.
* [`types`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples/types): Telecommand and telemetry definitions shared by the
`satrs-example` application and the `client`.
* [`satrs-stm32f3-disco-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples/stm32f3-disco-rtic):
Simple example using low-level sat-rs components on a bare-metal system with constrained
resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic) framework on the
STM32F3-Discovery device.
* [`satrs-stm32h7-nucleo-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples/stm32h7-nucleo-rtic):
Simple example using sat-rs components on a bare-metal system with constrained resources.
This example uses the [RTIC](https://github.com/rtic-rs/rtic) framework on the NUCLEO-H753ZI
board.
* [`embedded-client`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples/embedded-client) and
[`embedded-types`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples/embedded-types): Client and shared telecommand and telemetry
definitions for the embedded examples.
The library crates and the `satrs-example` application have their own `CHANGELOG.md`.
# Related projects
@@ -9,14 +9,13 @@ log = "0.4"
fern = "0.7"
humantime = "2"
serde = { version = "1" }
serde_json = "1"
satrs = { path = "../../satrs" }
satrs-example = { path = ".." }
satrs-minisim = { path = "../minisim" }
satrs-example = { path = "../obsw-std" }
minisim-types = { path = "../minisim-types" }
types = { path = "../types" }
spacepackets = { version = "0.18", default-features = false }
bitbybit = "2"
arbitrary-int = "2"
ctrlc = { version = "3.5" }
postcard = { version = "1" }
postcard = { version = "1", features = ["alloc"] }
anyhow = "1"
@@ -1,11 +1,11 @@
use anyhow::{Context as _, bail};
use arbitrary_int::u11;
use clap::Parser as _;
use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT};
use satrs_minisim::{
use minisim_types::{
SimCtrlReply, SimCtrlRequest, SimReply, SimRequest, SimRequestWithTime, acs::mgm,
udp::SIM_CTRL_PORT,
};
use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT};
use spacepackets::{CcsdsPacketIdAndPsc, SpacePacketHeader};
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
@@ -611,10 +611,10 @@ fn inject_mgm_failure(target_id: types::ComponentId, fault: mgm::SpiFault) -> an
let mut reply_buf = [0u8; 4096];
let ping = SimRequestWithTime::new_with_epoch_time(SimCtrlRequest::Ping);
sim_socket.send_to(&serde_json::to_vec(&ping)?, sim_addr)?;
sim_socket.send_to(&postcard::to_allocvec(&ping)?, sim_addr)?;
match sim_socket.recv(&mut reply_buf) {
Ok(len) => {
let reply: SimReply = serde_json::from_slice(&reply_buf[..len])?;
let reply: SimReply = postcard::from_bytes(&reply_buf[..len])?;
if reply != SimReply::SimCtrl(SimCtrlReply::Pong) {
bail!("unexpected reply while checking minisim connectivity: {reply:?}");
}
@@ -639,7 +639,7 @@ fn inject_mgm_failure(target_id: types::ComponentId, fault: mgm::SpiFault) -> an
id,
request: mgm::Request::SetSpiFault(fault),
});
sim_socket.send_to(&serde_json::to_vec(&request)?, sim_addr)?;
sim_socket.send_to(&postcard::to_allocvec(&request)?, sim_addr)?;
log::info!("injected SPI fault {fault:?} into minisim {target_id:?}");
Ok(())
}
@@ -9,7 +9,7 @@ serialport = "4"
toml = "0.9"
serde = { version = "1", features = ["derive"] }
spacepackets = { version = "0.18" }
embedded-types = { path = "../types" }
embedded-types = { path = "../embedded-types" }
tmtc-utils = { path = "../../tmtc-utils" }
postcard = { version = "1", features = ["alloc"] }
anyhow = "1"
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "minisim-types"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1", default-features = false, features = ["derive"] }
tai-time = { version = "1", default-features = false, features = ["serde"] }
types = { path = "../types" }
[dev-dependencies]
postcard = { version = "1", features = ["alloc"] }
@@ -1,5 +1,7 @@
use nexosim::time::MonotonicTime;
#![no_std]
use serde::{Deserialize, Serialize};
use tai_time::MonotonicTime;
use crate::{
acs::{mgm, mgt},
@@ -223,48 +225,11 @@ pub mod acs {
z: value,
}
}
pub fn from_microtesla(values: SensorValuesMicroTesla) -> Self {
let to_raw = |microtesla: f32| {
(microtesla / (GAUSS_TO_MICROTESLA_FACTOR as f32 * FIELD_LSB_PER_GAUSS_4_SENS))
.round() as i16
};
Self {
x: to_raw(values.x),
y: to_raw(values.y),
z: to_raw(values.z),
}
}
}
impl Reply {
pub fn new(
switch_state: SwitchStateBinary,
sensor_values: SensorValuesMicroTesla,
fault_mode: SpiFaultMode,
) -> Self {
// An injected fault always wins. A switched off device reads back like an
// undriven bus.
let raw = match (fault_mode, switch_state) {
(SpiFaultMode::AllZeros, _) => RawValues::splat(ALL_ZEROS_SENSOR_VAL),
(SpiFaultMode::AllOnes, _) | (SpiFaultMode::None, SwitchStateBinary::Off) => {
RawValues::splat(ALL_ONES_SENSOR_VAL)
}
(SpiFaultMode::None, SwitchStateBinary::On) => {
RawValues::from_microtesla(sensor_values)
}
};
Self {
switch_state,
sensor_values,
raw,
}
}
}
}
pub mod mgt {
use std::time::Duration;
use core::time::Duration;
use serde::{Deserialize, Serialize};
@@ -300,14 +265,16 @@ pub mod udp {
}
#[cfg(test)]
pub mod tests {
mod tests {
extern crate alloc;
use super::*;
#[test]
fn test_request_serde_roundtrip() {
let sim_request = SimRequestWithTime::new_with_epoch_time(SimCtrlRequest::Ping);
let json = serde_json::to_string(&sim_request).unwrap();
let deserialized: SimRequestWithTime = serde_json::from_str(&json).unwrap();
let bytes = postcard::to_allocvec(&sim_request).unwrap();
let deserialized: SimRequestWithTime = postcard::from_bytes(&bytes).unwrap();
assert_eq!(deserialized, sim_request);
}
@@ -315,8 +282,8 @@ pub mod tests {
fn test_reply_serde_roundtrip() {
let sim_reply = SimReply::from(SimCtrlReply::Pong);
assert_eq!(sim_reply.component(), ComponentId::SimCtrl);
let json = serde_json::to_string(&sim_reply).unwrap();
let deserialized: SimReply = serde_json::from_str(&json).unwrap();
let bytes = postcard::to_allocvec(&sim_reply).unwrap();
let deserialized: SimReply = postcard::from_bytes(&bytes).unwrap();
assert_eq!(deserialized, sim_reply);
}
}
@@ -7,18 +7,18 @@ edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
postcard = { version = "1", features = ["alloc"] }
log = "0.4"
thiserror = "2"
fern = "0.7"
strum = { version = "0.28", features = ["derive"] }
num_enum = "0.7"
humantime = "2"
tai-time = { version = "0.3", features = ["serde"] }
nexosim = "1"
satrs = { path = "../../satrs" }
types = { path = "../types" }
minisim-types = { path = "../minisim-types" }
[dev-dependencies]
delegate = "0.13"
@@ -5,7 +5,7 @@ This crate contains a mini-simulator based on the open-source discrete-event sim
[nexosim](https://github.com/asynchronics/nexosim).
Right now, this crate is primarily used together with the
[`satrs-example` application](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example)
[`satrs-example` application](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples/obsw-std)
to simulate the devices connected to the example application.
You can simply run this application using
@@ -1,10 +1,10 @@
use std::f32::consts::PI;
use minisim_types::{acs::mgm, SimReply};
use nexosim::{
model::{Context, Model},
ports::Output,
};
use satrs_minisim::{acs::mgm, SimReply};
use serde::{Deserialize, Serialize};
use types::pcdu::SwitchStateBinary;
@@ -59,7 +59,7 @@ impl MgmModel {
pub async fn send_sensor_values(&mut self, _: (), cx: &Context<Self>) {
let reply = SimReply::Mgm {
id: self.id,
reply: mgm::Reply::new(
reply: create_reply(
self.switch_state,
self.calculate_current_mgm_tuple(current_millis(cx.time())),
self.spi_fault.mode,
@@ -98,11 +98,46 @@ impl MgmModel {
}
}
/// Builds the reply of the simulated LIS3MDL, including the raw register values.
fn create_reply(
switch_state: SwitchStateBinary,
sensor_values: mgm::SensorValuesMicroTesla,
fault_mode: mgm::SpiFaultMode,
) -> mgm::Reply {
// An injected fault always wins. A switched off device reads back like an undriven bus.
let raw = match (fault_mode, switch_state) {
(mgm::SpiFaultMode::AllZeros, _) => mgm::RawValues::splat(mgm::ALL_ZEROS_SENSOR_VAL),
(mgm::SpiFaultMode::AllOnes, _) | (mgm::SpiFaultMode::None, SwitchStateBinary::Off) => {
mgm::RawValues::splat(mgm::ALL_ONES_SENSOR_VAL)
}
(mgm::SpiFaultMode::None, SwitchStateBinary::On) => {
raw_values_from_microtesla(sensor_values)
}
};
mgm::Reply {
switch_state,
sensor_values,
raw,
}
}
fn raw_values_from_microtesla(values: mgm::SensorValuesMicroTesla) -> mgm::RawValues {
let to_raw = |microtesla: f32| {
(microtesla / (mgm::GAUSS_TO_MICROTESLA_FACTOR as f32 * mgm::FIELD_LSB_PER_GAUSS_4_SENS))
.round() as i16
};
mgm::RawValues {
x: to_raw(values.x),
y: to_raw(values.y),
z: to_raw(values.z),
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use satrs_minisim::{acs::mgm, SimReply, SimRequest};
use minisim_types::{acs::mgm, SimReply, SimRequest};
use types::pcdu::{SwitchId, SwitchStateBinary};
use crate::{
@@ -1,11 +1,11 @@
use minisim_types::{
acs::{mgm, mgt},
SimReply,
};
use nexosim::{
model::{schedulable, Context, Model},
ports::Output,
};
use satrs_minisim::{
acs::{mgm, mgt},
SimReply,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use types::pcdu::SwitchStateBinary;
@@ -109,7 +109,7 @@ impl MgtModel {
mod tests {
use std::time::Duration;
use satrs_minisim::{
use minisim_types::{
acs::{mgm, mgt},
eps::PcduRequest,
SimReply, SimRequest, SimRequestWithTime,
@@ -3,16 +3,16 @@ use std::{
time::{Duration, SystemTime},
};
use minisim_types::{
acs::{mgm, mgt},
eps::PcduRequest,
SimCtrlReply, SimCtrlRequest, SimReply, SimRequest, SimRequestWithTime,
};
use nexosim::{
ports::{event_queue, EventQueueReader, EventSinkReader, EventSource, SinkState},
simulation::{EventId, ExecutionError, Mailbox, SimInit, Simulation},
time::{Clock, Deadline, MonotonicTime, SystemClock},
};
use satrs_minisim::{
acs::{mgm, mgt},
eps::PcduRequest,
SimCtrlReply, SimCtrlRequest, SimReply, SimRequest, SimRequestWithTime,
};
use types::pcdu::{SwitchId, SwitchStateBinary};
use crate::{
@@ -1,10 +1,10 @@
use std::time::Duration;
use minisim_types::{eps::PcduReply, SimReply};
use nexosim::{
model::{schedulable, Context, Model},
ports::Output,
};
use satrs_minisim::{eps::PcduReply, SimReply};
use serde::{Deserialize, Serialize};
use types::pcdu::{SwitchId, SwitchMapBinary, SwitchMapBinaryWrapper, SwitchStateBinary};
@@ -76,7 +76,7 @@ pub(crate) mod tests {
use super::*;
use std::time::Duration;
use satrs_minisim::{eps::PcduRequest, SimRequestWithTime};
use minisim_types::{eps::PcduRequest, SimRequestWithTime};
use types::pcdu::SwitchMapBinary;
use crate::test_helpers::SimTestbench;
@@ -1,6 +1,6 @@
use controller::{SimController, ThreadingModel};
use minisim_types::udp::SIM_CTRL_PORT;
use nexosim::time::MonotonicTime;
use satrs_minisim::udp::SIM_CTRL_PORT;
use std::sync::mpsc;
use std::thread;
use udp::SimUdpServer;
@@ -1,11 +1,11 @@
use delegate::delegate;
use std::sync::mpsc;
use minisim_types::{SimReply, SimRequest, SimRequestWithTime};
use nexosim::{
simulation::ExecutionError,
time::{Deadline, MonotonicTime},
};
use satrs_minisim::{SimReply, SimRequest, SimRequestWithTime};
use crate::controller::{SimController, ThreadingModel};
@@ -6,7 +6,7 @@ use std::{
time::Duration,
};
use satrs_minisim::{SimReply, SimRequestWithTime};
use minisim_types::{SimReply, SimRequestWithTime};
// A UDP server which handles all TC received by a client application.
pub struct SimUdpServer {
@@ -89,7 +89,7 @@ impl SimUdpServer {
self.sender_addr = Some(src);
let sim_req = serde_json::from_slice::<SimRequestWithTime>(&self.req_buf[..bytes_read]);
let sim_req = postcard::from_bytes::<SimRequestWithTime>(&self.req_buf[..bytes_read]);
if let Err(e) = sim_req {
log::warn!("received UDP request with invalid format: {}", e);
return processed_requests;
@@ -130,9 +130,7 @@ impl SimUdpServer {
let next_reply_to_send = self.reply_queue.pop_front().unwrap();
self.socket
.send_to(
serde_json::to_string(&next_reply_to_send)
.unwrap()
.as_bytes(),
&postcard::to_allocvec(&next_reply_to_send).unwrap(),
self.sender_addr.unwrap(),
)
.expect("sending reply failed");
@@ -154,7 +152,7 @@ mod tests {
time::Duration,
};
use satrs_minisim::{
use minisim_types::{
eps::{PcduReply, PcduRequest},
SimCtrlReply, SimCtrlRequest, SimReply, SimRequestWithTime,
};
@@ -171,8 +169,8 @@ mod tests {
pub enum ReceptionError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Serde JSON error: {0}")]
SerdeJson(#[from] serde_json::Error),
#[error("postcard error: {0}")]
Postcard(#[from] postcard::Error),
}
pub struct SimUdpTestClient {
@@ -203,7 +201,8 @@ mod tests {
pub fn send_request(&self, sim_request: &SimRequestWithTime) -> std::io::Result<usize> {
self.socket.send(
&serde_json::to_vec(sim_request).expect("conversion of request to vector failed"),
&postcard::to_allocvec(sim_request)
.expect("conversion of request to vector failed"),
)
}
@@ -213,7 +212,7 @@ mod tests {
pub fn recv_sim_reply(&mut self) -> Result<SimReply, ReceptionError> {
let read_len = self.recv_raw()?;
Ok(serde_json::from_slice(&self.reply_buf[0..read_len])?)
Ok(postcard::from_bytes(&self.reply_buf[0..read_len])?)
}
}
struct UdpTestbench {
@@ -302,8 +301,8 @@ mod tests {
panic!("unexpected request server error: {e}");
}
}
ReceptionError::SerdeJson(json_error) => {
panic!("unexpected JSON error: {json_error}");
ReceptionError::Postcard(postcard_error) => {
panic!("unexpected postcard error: {postcard_error}");
}
},
}
@@ -22,15 +22,14 @@ derive-new = "0.7"
cfg-if = "1"
arbitrary-int = "2"
bitbybit = "2"
postcard = "1"
postcard = { version = "1", features = ["alloc"] }
ctrlc = "3"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
satrs = { path = "../satrs", features = ["test_util"] }
types = { path = "./types" }
satrs-minisim = { path = "./minisim" }
satrs-mib = { path = "../satrs-mib" }
satrs = { path = "../../satrs", features = ["test_util"] }
types = { path = "../types" }
minisim-types = { path = "../minisim-types" }
satrs-mib = { path = "../../satrs-mib" }
[features]
# default = ["heap_tmtc"]
@@ -40,7 +40,7 @@ Use `cargo run -p client -- --help` to list all available commands.
## Adding the mini simulator application
This example application features a few device handlers. The
[`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example/minisim)
[`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/examples/minisim)
can be used to simulate the physical devices managed by these device handlers.
The example application will attempt communication with the mini simulator on UDP port 7303.
@@ -1,10 +1,10 @@
use minisim_types::acs::mgm as sim_mgm;
use minisim_types::acs::mgm::{FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR};
use minisim_types::{SimReply, SimRequest, SimRequestWithTime};
use satrs::fdir::{FaultCounterStd, FaultResponse, RecoveryEvent, RecoveryFdir};
use satrs::health::HealthTableMapSync;
use satrs::spacepackets::CcsdsPacketIdAndPsc;
use satrs_example::{HkHelperSingleSet, TimestampHelper, TmtcQueues};
use satrs_minisim::acs::mgm as sim_mgm;
use satrs_minisim::acs::mgm::{FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR};
use satrs_minisim::{SimReply, SimRequest, SimRequestWithTime};
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::time::Duration;
@@ -652,9 +652,9 @@ mod tests {
};
use arbitrary_int::u11;
use minisim_types::acs::mgm as sim_mgm;
use satrs::health::{HealthState, HealthTableProvider};
use satrs::spacepackets::SpacePacketHeader;
use satrs_minisim::acs::mgm as sim_mgm;
use types::{
Apid, ComponentId, TcHeader,
acs::mgm::request::HkRequest,
@@ -2,10 +2,10 @@ use std::collections::VecDeque;
use std::sync::mpsc;
use std::time::{Duration, Instant};
use minisim_types::acs::mgt as sim_mgt;
use minisim_types::{SimReply, SimRequestWithTime};
use satrs::spacepackets::CcsdsPacketIdAndPsc;
use satrs_example::{HkHelperSingleSet, TmtcQueues};
use satrs_minisim::acs::mgt as sim_mgt;
use satrs_minisim::{SimReply, SimRequestWithTime};
use types::acs::mgt::{
self, HkSet,
request::{ModeRequest, Request},
@@ -5,13 +5,13 @@ use std::{
};
use derive_new::new;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use satrs::spacepackets::CcsdsPacketIdAndPsc;
use satrs_example::TimestampHelper;
use satrs_minisim::{
use minisim_types::{
SimReply, SimRequestWithTime,
eps::{PcduReply, PcduRequest},
};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use satrs::spacepackets::CcsdsPacketIdAndPsc;
use satrs_example::TimestampHelper;
use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator as _;
use types::{
@@ -134,7 +134,7 @@ impl SerialInterface for SerialInterfaceToSim {
type Error = ();
fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
let request: PcduRequest = serde_json::from_slice(data).expect("expected a PCDU request");
let request: PcduRequest = postcard::from_bytes(data).expect("expected a PCDU request");
self.sim_request_tx
.send(SimRequestWithTime::new_with_epoch_time(request))
.expect("failed to send request to simulation");
@@ -148,8 +148,8 @@ impl SerialInterface for SerialInterfaceToSim {
loop {
match self.sim_reply_rx.try_recv() {
Ok(reply) => {
let reply = serde_json::to_string(&reply).unwrap();
f(reply.as_bytes());
let reply = postcard::to_allocvec(&reply).unwrap();
f(&reply);
}
Err(e) => match e {
mpsc::TryRecvError::Empty => break,
@@ -175,18 +175,13 @@ impl SerialInterface for SerialInterfaceDummy {
type Error = ();
fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
let pcdu_req: PcduRequest = serde_json::from_slice(data).unwrap();
let pcdu_req: PcduRequest = postcard::from_bytes(data).unwrap();
let switch_map_mut = &mut self.switch_map.borrow_mut().0;
match pcdu_req {
PcduRequest::SwitchDevice { switch, state } => {
match switch_map_mut.entry(switch) {
std::collections::hash_map::Entry::Occupied(mut val) => {
*val.get_mut() = state;
}
std::collections::hash_map::Entry::Vacant(vacant) => {
vacant.insert(state);
}
};
switch_map_mut
.insert(switch, state)
.expect("switch map capacity exceeded");
}
PcduRequest::RequestSwitchInfo => {
let mut reply_deque_mut = self.reply_deque.borrow_mut();
@@ -206,8 +201,8 @@ impl SerialInterface for SerialInterfaceDummy {
return Ok(());
}
loop {
let reply = self.get_next_reply_as_string();
f(reply.as_bytes());
let reply = self.next_reply_serialized();
f(&reply);
if self.reply_queue_empty() {
break;
}
@@ -217,10 +212,10 @@ impl SerialInterface for SerialInterfaceDummy {
}
impl SerialInterfaceDummy {
fn get_next_reply_as_string(&self) -> String {
fn next_reply_serialized(&self) -> Vec<u8> {
let mut reply_deque_mut = self.reply_deque.borrow_mut();
let next_reply = reply_deque_mut.pop_front().unwrap();
serde_json::to_string(&next_reply).unwrap()
postcard::to_allocvec(&next_reply).unwrap()
}
fn reply_queue_empty(&self) -> bool {
@@ -437,8 +432,8 @@ impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
pub fn handle_periodic_commands(&self) {
let pcdu_req = PcduRequest::RequestSwitchInfo;
let pcdu_req_ser = serde_json::to_string(&pcdu_req).unwrap();
if let Err(_e) = self.com_interface.send(pcdu_req_ser.as_bytes()) {
let pcdu_req_ser = postcard::to_allocvec(&pcdu_req).unwrap();
if let Err(_e) = self.com_interface.send(&pcdu_req_ser) {
log::warn!("polling PCDU switch info failed");
if let Err(e) = self.event_tx.send(pcdu::Event::SerialCommError) {
log::warn!("failed to send comm error event: {}", e);
@@ -484,9 +479,9 @@ impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
switch: switch_id,
state,
};
let pcdu_req_ser = serde_json::to_string(&pcdu_req).unwrap();
let pcdu_req_ser = postcard::to_allocvec(&pcdu_req).unwrap();
self.com_interface
.send(pcdu_req_ser.as_bytes())
.send(&pcdu_req_ser)
.expect("failed to send switch request to PCDU");
}
@@ -509,7 +504,7 @@ impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
pub fn poll_and_handle_replies(&mut self) {
if let Err(e) = self.com_interface.try_recv_replies(|reply| {
let sim_reply: SimReply = serde_json::from_slice(reply).expect("invalid reply format");
let sim_reply: SimReply = postcard::from_bytes(reply).expect("invalid reply format");
let SimReply::Pcdu(pcdu_reply) = sim_reply else {
log::warn!("unexpected PCDU SIM reply: {sim_reply:?}");
return;
@@ -559,7 +554,7 @@ mod tests {
pub struct SerialInterfaceTest {
pub inner: SerialInterfaceDummy,
pub send_queue: RefCell<VecDeque<Vec<u8>>>,
pub reply_queue: RefCell<VecDeque<String>>,
pub reply_queue: RefCell<VecDeque<Vec<u8>>>,
/// Makes the next `send` call fail, to exercise comm-error handling.
pub fail_next_send: RefCell<bool>,
}
@@ -584,9 +579,9 @@ mod tests {
return Ok(());
}
loop {
let reply = self.inner.get_next_reply_as_string();
let reply = self.inner.next_reply_serialized();
self.reply_queue.borrow_mut().push_back(reply.clone());
f(reply.as_bytes());
f(&reply);
if self.inner.reply_queue_empty() {
break;
}
@@ -642,7 +637,7 @@ mod tests {
assert_eq!(send_queue_mut.len(), expected_queue_len);
let packet_sent = send_queue_mut.pop_front().unwrap();
drop(send_queue_mut);
let pcdu_req: PcduRequest = serde_json::from_slice(&packet_sent).unwrap();
let pcdu_req: PcduRequest = postcard::from_bytes(&packet_sent).unwrap();
assert_eq!(pcdu_req, PcduRequest::RequestSwitchInfo);
}
@@ -657,7 +652,7 @@ mod tests {
assert_eq!(send_queue_mut.len(), expected_queue_len);
let packet_sent = send_queue_mut.pop_front().unwrap();
drop(send_queue_mut);
let pcdu_req: PcduRequest = serde_json::from_slice(&packet_sent).unwrap();
let pcdu_req: PcduRequest = postcard::from_bytes(&packet_sent).unwrap();
assert_eq!(
pcdu_req,
PcduRequest::SwitchDevice {
@@ -676,7 +671,7 @@ mod tests {
let mut reply_received_mut = self.handler.com_interface.reply_queue.borrow_mut();
assert_eq!(reply_received_mut.len(), expected_queue_len);
let reply_received = reply_received_mut.pop_front().unwrap();
let sim_reply: SimReply = serde_json::from_str(&reply_received).unwrap();
let sim_reply: SimReply = postcard::from_bytes(&reply_received).unwrap();
assert_eq!(
sim_reply,
SimReply::Pcdu(PcduReply::SwitchInfo(expected_map))
@@ -5,9 +5,9 @@ use std::{
time::Duration,
};
use minisim_types::{ComponentId, SimReply, SimRequestWithTime, udp::SIM_CTRL_PORT};
use minisim_types::{SimCtrlReply, SimCtrlRequest};
use satrs::HandlingStatus;
use satrs_minisim::{ComponentId, SimReply, SimRequestWithTime, udp::SIM_CTRL_PORT};
use satrs_minisim::{SimCtrlReply, SimCtrlRequest};
struct SimReplyMap(pub HashMap<ComponentId, mpsc::Sender<SimReply>>);
@@ -35,10 +35,10 @@ pub enum SimClientCreationError {
Io(#[from] std::io::Error),
#[error("timeout when trying to connect to sim UDP server")]
Timeout,
#[error("invalid ping reply when trying connection to UDP sim server")]
InvalidReplyJsonError(#[from] serde_json::Error),
#[error("invalid ping reply when trying connection to UDP sim server: {0}")]
InvalidReply(#[from] postcard::Error),
#[error("invalid sim reply, not pong reply as expected: {0:?}")]
ReplyIsNotPong(SimReply),
ReplyIsNotPong(Box<SimReply>),
}
pub struct SimClientUdp {
@@ -74,14 +74,14 @@ impl SimClientUdp {
reply_buf: &mut [u8],
) -> Result<(), SimClientCreationError> {
let sim_req = SimRequestWithTime::new_with_epoch_time(SimCtrlRequest::Ping);
let sim_req_json = serde_json::to_string(&sim_req).expect("failed to serialize SimRequest");
udp_client.send_to(sim_req_json.as_bytes(), simulator_addr)?;
let sim_req_raw = postcard::to_allocvec(&sim_req).expect("failed to serialize SimRequest");
udp_client.send_to(&sim_req_raw, simulator_addr)?;
match udp_client.recv(reply_buf) {
Ok(reply_len) => {
let sim_reply: SimReply = serde_json::from_slice(&reply_buf[0..reply_len])?;
let sim_reply: SimReply = postcard::from_bytes(&reply_buf[0..reply_len])?;
match sim_reply {
SimReply::SimCtrl(SimCtrlReply::Pong) => Ok(()),
_ => Err(SimClientCreationError::ReplyIsNotPong(sim_reply)),
_ => Err(SimClientCreationError::ReplyIsNotPong(Box::new(sim_reply))),
}
}
Err(e) => {
@@ -102,12 +102,9 @@ impl SimClientUdp {
loop {
match self.sim_request_rx.try_recv() {
Ok(request) => {
let request_json =
serde_json::to_string(&request).expect("failed to serialize SimRequest");
if let Err(e) = self
.udp_client
.send_to(request_json.as_bytes(), self.simulator_addr)
{
let request_raw =
postcard::to_allocvec(&request).expect("failed to serialize SimRequest");
if let Err(e) = self.udp_client.send_to(&request_raw, self.simulator_addr) {
log::error!("error sending data to UDP SIM server: {e}");
break;
} else {
@@ -129,8 +126,8 @@ impl SimClientUdp {
match self.udp_client.recv(&mut self.reply_buf) {
Ok(recvd_bytes) => {
no_data_from_udp_server_received = false;
let sim_reply_result: serde_json::Result<SimReply> =
serde_json::from_slice(&self.reply_buf[0..recvd_bytes]);
let sim_reply_result: postcard::Result<SimReply> =
postcard::from_bytes(&self.reply_buf[0..recvd_bytes]);
match sim_reply_result {
Ok(sim_reply) => {
if let Some(sender) = self.reply_map.0.get(&sim_reply.component()) {
@@ -176,7 +173,6 @@ impl SimClientUdp {
#[cfg(test)]
pub mod tests {
use std::{
collections::HashMap,
net::{SocketAddr, UdpSocket},
sync::{
Arc,
@@ -186,11 +182,13 @@ pub mod tests {
time::Duration,
};
use satrs_minisim::{
use minisim_types::{
ComponentId, SimCtrlReply, SimCtrlRequest, SimReply, SimRequest, SimRequestWithTime,
eps::{PcduReply, PcduRequest},
};
use types::pcdu::SwitchMapBinary;
use super::SimClientUdp;
struct UdpSimTestServer {
@@ -233,10 +231,10 @@ pub mod tests {
loop {
match self.reply_rx.try_recv() {
Ok(sim_reply) => {
let sim_reply_json = serde_json::to_string(&sim_reply)
let sim_reply_raw = postcard::to_allocvec(&sim_reply)
.expect("failed to serialize SimReply");
self.udp_server
.send_to(sim_reply_json.as_bytes(), last_sender)
.send_to(&sim_reply_raw, last_sender)
.expect("failed to send reply to client from UDP server");
no_sim_replies_handled = false;
}
@@ -254,17 +252,17 @@ pub mod tests {
match self.udp_server.recv_from(&mut self.recv_buf) {
Ok((read_bytes, from)) => {
let sim_request: SimRequestWithTime =
serde_json::from_slice(&self.recv_buf[0..read_bytes])
postcard::from_bytes(&self.recv_buf[0..read_bytes])
.expect("failed to deserialize SimRequest");
// For a ping, we perform the reply handling here directly
if sim_request.request == SimRequest::SimCtrl(SimCtrlRequest::Ping) {
no_data_received = false;
self.last_sender = Some(from);
let sim_reply = SimReply::from(SimCtrlReply::Pong);
let sim_reply_json = serde_json::to_string(&sim_reply)
let sim_reply_raw = postcard::to_allocvec(&sim_reply)
.expect("failed to serialize SimReply");
self.udp_server
.send_to(sim_reply_json.as_bytes(), from)
.send_to(&sim_reply_raw, from)
.expect("failed to send reply to client from UDP server");
}
// Forward each SIM request for testing purposes.
@@ -369,7 +367,7 @@ pub mod tests {
);
// We inject the reply ourselves.
let pcdu_reply = PcduReply::SwitchInfo(HashMap::new());
let pcdu_reply = PcduReply::SwitchInfo(SwitchMapBinary::default());
server_sim_reply_tx
.send(SimReply::from(pcdu_reply.clone()))
.expect("sending PCDU reply failed");
@@ -181,8 +181,8 @@ fn main() {
let (mgm_0_spi_interface, mgm_1_spi_interface) = if let Some(sim_client) =
opt_sim_client.as_mut()
{
sim_client.add_reply_recipient(satrs_minisim::ComponentId::Mgm0Lis3Mdl, mgm_0_sim_reply_tx);
sim_client.add_reply_recipient(satrs_minisim::ComponentId::Mgm1Lis3Mdl, mgm_1_sim_reply_tx);
sim_client.add_reply_recipient(minisim_types::ComponentId::Mgm0Lis3Mdl, mgm_0_sim_reply_tx);
sim_client.add_reply_recipient(minisim_types::ComponentId::Mgm1Lis3Mdl, mgm_1_sim_reply_tx);
(
mgm::SpiCommunication::Sim(mgm::SpiSimInterface {
id: mgm::MgmId::_0,
@@ -258,7 +258,7 @@ fn main() {
});
let mgt_com = if let Some(sim_client) = opt_sim_client.as_mut() {
sim_client.add_reply_recipient(satrs_minisim::ComponentId::Mgt, mgt_sim_reply_tx);
sim_client.add_reply_recipient(minisim_types::ComponentId::Mgt, mgt_sim_reply_tx);
mgt::MgtCommunication::Sim(mgt::SimInterface {
sim_request_tx: sim_request_tx.clone(),
sim_reply_rx: mgt_sim_reply_rx,
@@ -299,7 +299,7 @@ fn main() {
);
let pcdu_serial_interface = if let Some(sim_client) = opt_sim_client.as_mut() {
sim_client.add_reply_recipient(satrs_minisim::ComponentId::Pcdu, pcdu_sim_reply_tx);
sim_client.add_reply_recipient(minisim_types::ComponentId::Pcdu, pcdu_sim_reply_tx);
SerialSimInterfaceWrapper::Sim(SerialInterfaceToSim::new(
sim_request_tx.clone(),
pcdu_sim_reply_rx,
@@ -7,7 +7,7 @@ default-run = "satrs-stm32f3-disco-rtic"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
embedded-types = { path = "../types", features = ["defmt"] }
embedded-types = { path = "../embedded-types", features = ["defmt"] }
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
defmt = "1"
@@ -13,7 +13,7 @@ name = "integration"
harness = false
[dependencies]
embedded-types = { path = "../types", features = ["defmt"] }
embedded-types = { path = "../embedded-types", features = ["defmt"] }
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
arbitrary-int = "2"
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "types"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1", default-features = false, features = ["derive"] }
spacepackets = { version = "0.18", default-features = false, features = ["serde"] }
satrs = { path = "../../satrs", default-features = false, features = ["serde"] }
num_enum = { version = "0.7", default-features = false }
strum = { version = "0.28", default-features = false, features = ["derive"] }
postcard = { version = "1", features = ["alloc"] }
thiserror = { version = "2", default-features = false }
bitbybit = "2"
arbitrary-int = "2"
heapless = { version = "0.9", features = ["serde"] }

Some files were not shown because too many files have changed in this diff Show More