First version of asynchronix based mini simulator #139

Merged
muellerr merged 3 commits from init-minisim into main 2024-03-11 10:41:24 +01:00
5 changed files with 233 additions and 130 deletions
Showing only changes of commit 7387be3bc3 - Show all commits

View File

@ -7,7 +7,7 @@ use asynchronix::{
use satrs::power::SwitchStateBinary; use satrs::power::SwitchStateBinary;
use satrs_minisim::{ use satrs_minisim::{
acs::{MgmReply, MgmSensorValues, MgtDipole, MgtHkSet, MgtReply, MGT_GEN_MAGNETIC_FIELD}, acs::{MgmReply, MgmSensorValues, MgtDipole, MgtHkSet, MgtReply, MGT_GEN_MAGNETIC_FIELD},
SimReply, SimTarget, SimReply,
}; };
use crate::time::current_millis; use crate::time::current_millis;
@ -54,14 +54,10 @@ impl MagnetometerModel {
pub async fn send_sensor_values(&mut self, _: (), scheduler: &Scheduler<Self>) { pub async fn send_sensor_values(&mut self, _: (), scheduler: &Scheduler<Self>) {
self.reply_sender self.reply_sender
.send(SimReply::new( .send(SimReply::new(MgmReply {
SimTarget::Mgm, switch_state: self.switch_state,
MgmReply { sensor_values: self.calculate_current_mgm_tuple(current_millis(scheduler.time())),
switch_state: self.switch_state, }))
sensor_values: self
.calculate_current_mgm_tuple(current_millis(scheduler.time())),
},
))
.expect("sending MGM sensor values failed"); .expect("sending MGM sensor values failed");
} }
@ -149,12 +145,11 @@ impl MagnetorquerModel {
} }
pub fn send_housekeeping_data(&mut self) { pub fn send_housekeeping_data(&mut self) {
let mgt_reply = MgtReply::Hk(MgtHkSet {
dipole: self.torque_dipole,
torquing: self.torquing,
});
self.reply_sender self.reply_sender
.send(SimReply::new(SimTarget::Mgt, mgt_reply)) .send(SimReply::new(MgtReply::Hk(MgtHkSet {
dipole: self.torque_dipole,
torquing: self.torquing,
})))
.unwrap(); .unwrap();
} }
@ -186,7 +181,7 @@ pub mod tests {
use satrs_minisim::{ use satrs_minisim::{
acs::{MgmReply, MgmRequest, MgtDipole, MgtHkSet, MgtReply, MgtRequest}, acs::{MgmReply, MgmRequest, MgtDipole, MgtHkSet, MgtReply, MgtRequest},
eps::PcduSwitch, eps::PcduSwitch,
SimRequest, SimTarget, SerializableSimMsgPayload, SimMessageProvider, SimRequest, SimTarget,
}; };
use crate::{eps::tests::switch_device_on, test_helpers::SimTestbench}; use crate::{eps::tests::switch_device_on, test_helpers::SimTestbench};
@ -194,8 +189,7 @@ pub mod tests {
#[test] #[test]
fn test_basic_mgm_request() { fn test_basic_mgm_request() {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
let mgm_request = MgmRequest::RequestSensorData; let request = SimRequest::new(MgmRequest::RequestSensorData);
let request = SimRequest::new(SimTarget::Mgm, mgm_request);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
@ -205,7 +199,7 @@ pub mod tests {
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap(); let sim_reply = sim_reply.unwrap();
assert_eq!(sim_reply.target(), SimTarget::Mgm); assert_eq!(sim_reply.target(), SimTarget::Mgm);
let reply: MgmReply = serde_json::from_str(sim_reply.reply()) let reply = MgmReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values"); .expect("failed to deserialize MGM sensor values");
assert_eq!(reply.switch_state, SwitchStateBinary::Off); assert_eq!(reply.switch_state, SwitchStateBinary::Off);
assert_eq!(reply.sensor_values.x, 0.0); assert_eq!(reply.sensor_values.x, 0.0);
@ -218,8 +212,7 @@ pub mod tests {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
switch_device_on(&mut sim_testbench, PcduSwitch::Mgm); switch_device_on(&mut sim_testbench, PcduSwitch::Mgm);
let mgm_request = MgmRequest::RequestSensorData; let mut request = SimRequest::new(MgmRequest::RequestSensorData);
let mut request = SimRequest::new(SimTarget::Mgm, mgm_request);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
@ -229,12 +222,11 @@ pub mod tests {
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
let mut sim_reply = sim_reply_res.unwrap(); let mut sim_reply = sim_reply_res.unwrap();
assert_eq!(sim_reply.target(), SimTarget::Mgm); assert_eq!(sim_reply.target(), SimTarget::Mgm);
let first_reply: MgmReply = serde_json::from_str(sim_reply.reply()) let first_reply = MgmReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values"); .expect("failed to deserialize MGM sensor values");
let mgm_request = MgmRequest::RequestSensorData;
sim_testbench.step_by(Duration::from_millis(50)); sim_testbench.step_by(Duration::from_millis(50));
request = SimRequest::new(SimTarget::Mgm, mgm_request); request = SimRequest::new(MgmRequest::RequestSensorData);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
@ -244,7 +236,7 @@ pub mod tests {
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
sim_reply = sim_reply_res.unwrap(); sim_reply = sim_reply_res.unwrap();
let second_reply: MgmReply = serde_json::from_str(sim_reply.reply()) let second_reply = MgmReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values"); .expect("failed to deserialize MGM sensor values");
// Check that the values are changing. // Check that the values are changing.
assert!(first_reply != second_reply); assert!(first_reply != second_reply);
@ -253,8 +245,7 @@ pub mod tests {
#[test] #[test]
fn test_basic_mgt_request_is_off() { fn test_basic_mgt_request_is_off() {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
let mgt_request = MgtRequest::RequestHk; let request = SimRequest::new(MgtRequest::RequestHk);
let request = SimRequest::new(SimTarget::Mgt, mgt_request);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
@ -268,8 +259,8 @@ pub mod tests {
fn test_basic_mgt_request_is_on() { fn test_basic_mgt_request_is_on() {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
switch_device_on(&mut sim_testbench, PcduSwitch::Mgt); switch_device_on(&mut sim_testbench, PcduSwitch::Mgt);
let mgt_request = MgtRequest::RequestHk; let request = SimRequest::new(MgtRequest::RequestHk);
let request = SimRequest::new(SimTarget::Mgt, mgt_request);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
@ -278,7 +269,7 @@ pub mod tests {
let sim_reply_res = sim_testbench.try_receive_next_reply(); let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
let sim_reply = sim_reply_res.unwrap(); let sim_reply = sim_reply_res.unwrap();
let mgt_reply: MgtReply = serde_json::from_str(sim_reply.reply()) let mgt_reply = MgtReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values"); .expect("failed to deserialize MGM sensor values");
match mgt_reply { match mgt_reply {
MgtReply::Hk(hk) => { MgtReply::Hk(hk) => {
@ -290,8 +281,7 @@ pub mod tests {
} }
fn check_mgt_hk(sim_testbench: &mut SimTestbench, expected_hk_set: MgtHkSet) { fn check_mgt_hk(sim_testbench: &mut SimTestbench, expected_hk_set: MgtHkSet) {
let mgt_request = MgtRequest::RequestHk; let request = SimRequest::new(MgtRequest::RequestHk);
let request = SimRequest::new(SimTarget::Mgt, mgt_request);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
@ -300,7 +290,7 @@ pub mod tests {
let sim_reply_res = sim_testbench.try_receive_next_reply(); let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
let sim_reply = sim_reply_res.unwrap(); let sim_reply = sim_reply_res.unwrap();
let mgt_reply: MgtReply = serde_json::from_str(sim_reply.reply()) let mgt_reply = MgtReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values"); .expect("failed to deserialize MGM sensor values");
match mgt_reply { match mgt_reply {
MgtReply::Hk(hk) => { MgtReply::Hk(hk) => {
@ -319,11 +309,10 @@ pub mod tests {
y: 200, y: 200,
z: 1000, z: 1000,
}; };
let mgt_request = MgtRequest::ApplyTorque { let request = SimRequest::new(MgtRequest::ApplyTorque {
duration: Duration::from_millis(100), duration: Duration::from_millis(100),
dipole: commanded_dipole, dipole: commanded_dipole,
}; });
let request = SimRequest::new(SimTarget::Mgt, mgt_request);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");

View File

@ -7,7 +7,8 @@ use asynchronix::{
use satrs_minisim::{ use satrs_minisim::{
acs::{MgmRequest, MgtRequest}, acs::{MgmRequest, MgtRequest},
eps::PcduRequest, eps::PcduRequest,
RequestError, SimCtrlReply, SimCtrlRequest, SimReply, SimRequest, SimTarget, SerializableSimMsgPayload, SimCtrlReply, SimCtrlRequest, SimMessageProvider, SimReply,
SimRequest, SimRequestError, SimTarget,
}; };
use crate::{ use crate::{
@ -86,19 +87,19 @@ impl SimController {
} }
} }
fn handle_ctrl_request(&mut self, request: &SimRequest) -> serde_json::Result<()> { fn handle_ctrl_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
let sim_ctrl_request: SimCtrlRequest = serde_json::from_str(request.request())?; let sim_ctrl_request = SimCtrlRequest::from_sim_message(request)?;
match sim_ctrl_request { match sim_ctrl_request {
SimCtrlRequest::Ping => { SimCtrlRequest::Ping => {
self.reply_sender self.reply_sender
.send(SimReply::new(SimTarget::SimCtrl, SimCtrlReply::Pong)) .send(SimReply::new(SimCtrlReply::Pong))
.expect("sending reply from sim controller failed"); .expect("sending reply from sim controller failed");
} }
} }
Ok(()) Ok(())
} }
fn handle_mgm_request(&mut self, request: &SimRequest) -> serde_json::Result<()> { fn handle_mgm_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
let mgm_request: MgmRequest = serde_json::from_str(request.request())?; let mgm_request = MgmRequest::from_sim_message(request)?;
match mgm_request { match mgm_request {
MgmRequest::RequestSensorData => { MgmRequest::RequestSensorData => {
self.simulation.send_event( self.simulation.send_event(
@ -111,8 +112,8 @@ impl SimController {
Ok(()) Ok(())
} }
fn handle_pcdu_request(&mut self, request: &SimRequest) -> serde_json::Result<()> { fn handle_pcdu_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
let pcdu_request: PcduRequest = serde_json::from_str(request.request())?; let pcdu_request = PcduRequest::from_sim_message(request)?;
match pcdu_request { match pcdu_request {
PcduRequest::RequestSwitchInfo => { PcduRequest::RequestSwitchInfo => {
self.simulation self.simulation
@ -129,8 +130,8 @@ impl SimController {
Ok(()) Ok(())
} }
fn handle_mgt_request(&mut self, request: &SimRequest) -> serde_json::Result<()> { fn handle_mgt_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
let mgt_request: MgtRequest = serde_json::from_str(request.request())?; let mgt_request = MgtRequest::from_sim_message(request)?;
match mgt_request { match mgt_request {
MgtRequest::ApplyTorque { duration, dipole } => self.simulation.send_event( MgtRequest::ApplyTorque { duration, dipole } => self.simulation.send_event(
MagnetorquerModel::apply_torque, MagnetorquerModel::apply_torque,
@ -148,7 +149,7 @@ impl SimController {
fn handle_invalid_request_with_valid_target( fn handle_invalid_request_with_valid_target(
&self, &self,
error: serde_json::Error, error: SimRequestError,
request: &SimRequest, request: &SimRequest,
) { ) {
log::warn!( log::warn!(
@ -157,10 +158,7 @@ impl SimController {
error error
); );
self.reply_sender self.reply_sender
.send(SimReply::new( .send(SimReply::new(SimCtrlReply::from(error)))
SimTarget::SimCtrl,
SimCtrlReply::from(RequestError::TargetRequestMissmatch(request.clone())),
))
.expect("sending reply from sim controller failed"); .expect("sending reply from sim controller failed");
} }
} }
@ -174,8 +172,7 @@ mod tests {
#[test] #[test]
fn test_basic_ping() { fn test_basic_ping() {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
let sim_ctrl_request = SimCtrlRequest::Ping; let request = SimRequest::new(SimCtrlRequest::Ping);
let request = SimRequest::new(SimTarget::SimCtrl, sim_ctrl_request);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending sim ctrl request failed"); .expect("sending sim ctrl request failed");
@ -185,13 +182,8 @@ mod tests {
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap(); let sim_reply = sim_reply.unwrap();
assert_eq!(sim_reply.target(), SimTarget::SimCtrl); assert_eq!(sim_reply.target(), SimTarget::SimCtrl);
let reply: SimCtrlReply = serde_json::from_str(sim_reply.reply()) let reply = SimCtrlReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values"); .expect("failed to deserialize MGM sensor values");
assert_eq!(reply, SimCtrlReply::Pong); assert_eq!(reply, SimCtrlReply::Pong);
} }
#[test]
fn test_invalid_request() {
// TODO: Implement this test. Check for the expected reply.
}
} }

View File

@ -7,7 +7,7 @@ use asynchronix::{
use satrs::power::SwitchStateBinary; use satrs::power::SwitchStateBinary;
use satrs_minisim::{ use satrs_minisim::{
eps::{PcduReply, PcduSwitch, SwitchMap}, eps::{PcduReply, PcduSwitch, SwitchMap},
SimReply, SimTarget, SimReply,
}; };
pub const SWITCH_INFO_DELAY_MS: u64 = 10; pub const SWITCH_INFO_DELAY_MS: u64 = 10;
@ -44,8 +44,7 @@ impl PcduModel {
} }
pub fn send_switch_info(&mut self) { pub fn send_switch_info(&mut self) {
let switch_info = PcduReply::SwitchInfo(self.switcher_map.clone()); let reply = SimReply::new(PcduReply::SwitchInfo(self.switcher_map.clone()));
let reply = SimReply::new(SimTarget::Pcdu, switch_info);
self.reply_sender.send(reply).unwrap(); self.reply_sender.send(reply).unwrap();
} }
@ -76,7 +75,9 @@ pub(crate) mod tests {
use super::*; use super::*;
use std::time::Duration; use std::time::Duration;
use satrs_minisim::{eps::PcduRequest, SimRequest, SimTarget}; use satrs_minisim::{
eps::PcduRequest, SerializableSimMsgPayload, SimMessageProvider, SimRequest, SimTarget,
};
use crate::test_helpers::SimTestbench; use crate::test_helpers::SimTestbench;
@ -85,11 +86,10 @@ pub(crate) mod tests {
switch: PcduSwitch, switch: PcduSwitch,
target: SwitchStateBinary, target: SwitchStateBinary,
) { ) {
let pcdu_request = PcduRequest::SwitchDevice { let request = SimRequest::new(PcduRequest::SwitchDevice {
switch, switch,
state: target, state: target,
}; });
let request = SimRequest::new(SimTarget::Pcdu, pcdu_request);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM switch request failed"); .expect("sending MGM switch request failed");
@ -113,8 +113,7 @@ pub(crate) mod tests {
} }
fn check_switch_state(sim_testbench: &mut SimTestbench, expected_switch_map: &SwitchMap) { fn check_switch_state(sim_testbench: &mut SimTestbench, expected_switch_map: &SwitchMap) {
let pcdu_request = PcduRequest::RequestSwitchInfo; let request = SimRequest::new(PcduRequest::RequestSwitchInfo);
let request = SimRequest::new(SimTarget::Pcdu, pcdu_request);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
@ -124,7 +123,7 @@ pub(crate) mod tests {
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap(); let sim_reply = sim_reply.unwrap();
assert_eq!(sim_reply.target(), SimTarget::Pcdu); assert_eq!(sim_reply.target(), SimTarget::Pcdu);
let pcdu_reply: PcduReply = serde_json::from_str(&sim_reply.reply()) let pcdu_reply = PcduReply::from_sim_message(&sim_reply)
.expect("failed to deserialize PCDU switch info"); .expect("failed to deserialize PCDU switch info");
match pcdu_reply { match pcdu_reply {
PcduReply::SwitchInfo(switch_map) => { PcduReply::SwitchInfo(switch_map) => {
@ -144,8 +143,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_pcdu_switcher_request() { fn test_pcdu_switcher_request() {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
let pcdu_request = PcduRequest::RequestSwitchInfo; let request = SimRequest::new(PcduRequest::RequestSwitchInfo);
let request = SimRequest::new(SimTarget::Pcdu, pcdu_request);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
@ -160,7 +158,7 @@ pub(crate) mod tests {
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap(); let sim_reply = sim_reply.unwrap();
assert_eq!(sim_reply.target(), SimTarget::Pcdu); assert_eq!(sim_reply.target(), SimTarget::Pcdu);
let pcdu_reply: PcduReply = serde_json::from_str(&sim_reply.reply()) let pcdu_reply = PcduReply::from_sim_message(&sim_reply)
.expect("failed to deserialize PCDU switch info"); .expect("failed to deserialize PCDU switch info");
match pcdu_reply { match pcdu_reply {
PcduReply::SwitchInfo(switch_map) => { PcduReply::SwitchInfo(switch_map) => {

View File

@ -1,4 +1,4 @@
use serde::{Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SimTarget { pub enum SimTarget {
@ -9,48 +9,100 @@ pub enum SimTarget {
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SimRequest { pub struct SimMessage {
target: SimTarget, pub target: SimTarget,
request: String, pub payload: String,
} }
impl SimRequest { /// A generic simulation request type. Right now, the payload data is expected to be
pub fn new<T: Serialize>(device: SimTarget, request: T) -> Self { /// JSON, which might be changed in the future.
Self { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
target: device, pub struct SimRequest {
request: serde_json::to_string(&request).unwrap(), inner: SimMessage,
}
}
pub fn target(&self) -> SimTarget {
self.target
}
pub fn request(&self) -> &String {
&self.request
}
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SimMessageType {
Request,
Reply,
}
/// Generic trait implemented by simulation request or reply payloads. It ties the request or
/// reply to a specific target and provides an API which does boilerplate tasks like checking the
/// validity of the target.
pub trait SerializableSimMsgPayload<P: SimMessageProvider>:
Serialize + DeserializeOwned + Sized
{
const TARGET: SimTarget;
fn from_sim_message(sim_message: &P) -> Result<Self, SimMessageError<P>> {
if sim_message.target() == Self::TARGET {
return Ok(serde_json::from_str(sim_message.payload())?);
}
Err(SimMessageError::TargetRequestMissmatch(sim_message.clone()))
}
}
pub trait SimMessageProvider: Serialize + DeserializeOwned + Clone + Sized {
fn msg_type(&self) -> SimMessageType;
fn target(&self) -> SimTarget;
fn payload(&self) -> &String;
fn from_raw_data(data: &[u8]) -> serde_json::Result<Self> {
serde_json::from_slice(data)
}
}
impl SimRequest {
pub fn new<T: SerializableSimMsgPayload<SimRequest>>(serializable_request: T) -> Self {
Self {
inner: SimMessage {
target: T::TARGET,
payload: serde_json::to_string(&serializable_request).unwrap(),
},
}
}
}
impl SimMessageProvider for SimRequest {
fn target(&self) -> SimTarget {
self.inner.target
}
fn payload(&self) -> &String {
&self.inner.payload
}
fn msg_type(&self) -> SimMessageType {
SimMessageType::Request
}
}
/// A generic simulation reply type. Right now, the payload data is expected to be
/// JSON, which might be changed inthe future.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SimReply { pub struct SimReply {
target: SimTarget, inner: SimMessage,
reply: String,
} }
impl SimReply { impl SimReply {
pub fn new<T: Serialize>(device: SimTarget, reply: T) -> Self { pub fn new<T: SerializableSimMsgPayload<SimReply>>(serializable_reply: T) -> Self {
Self { Self {
target: device, inner: SimMessage {
reply: serde_json::to_string(&reply).unwrap(), target: T::TARGET,
payload: serde_json::to_string(&serializable_reply).unwrap(),
},
} }
} }
}
pub fn target(&self) -> SimTarget { impl SimMessageProvider for SimReply {
self.target fn target(&self) -> SimTarget {
self.inner.target
} }
fn payload(&self) -> &String {
pub fn reply(&self) -> &String { &self.inner.payload
&self.reply }
fn msg_type(&self) -> SimMessageType {
SimMessageType::Reply
} }
} }
@ -59,19 +111,37 @@ pub enum SimCtrlRequest {
Ping, Ping,
} }
impl SerializableSimMsgPayload<SimRequest> for SimCtrlRequest {
const TARGET: SimTarget = SimTarget::SimCtrl;
}
pub type SimReplyError = SimMessageError<SimReply>;
pub type SimRequestError = SimMessageError<SimRequest>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum RequestError { pub enum SimMessageError<P> {
TargetRequestMissmatch(SimRequest), SerdeJson(String),
TargetRequestMissmatch(P),
}
impl<P> From<serde_json::Error> for SimMessageError<P> {
fn from(error: serde_json::Error) -> SimMessageError<P> {
SimMessageError::SerdeJson(error.to_string())
}
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SimCtrlReply { pub enum SimCtrlReply {
Pong, Pong,
InvalidRequest(RequestError), InvalidRequest(SimRequestError),
} }
impl From<RequestError> for SimCtrlReply { impl SerializableSimMsgPayload<SimReply> for SimCtrlReply {
fn from(error: RequestError) -> Self { const TARGET: SimTarget = SimTarget::SimCtrl;
}
impl From<SimRequestError> for SimCtrlReply {
fn from(error: SimRequestError) -> Self {
SimCtrlReply::InvalidRequest(error) SimCtrlReply::InvalidRequest(error)
} }
} }
@ -99,10 +169,18 @@ pub mod eps {
RequestSwitchInfo, RequestSwitchInfo,
} }
impl SerializableSimMsgPayload<SimRequest> for PcduRequest {
const TARGET: SimTarget = SimTarget::Pcdu;
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PcduReply { pub enum PcduReply {
SwitchInfo(SwitchMap), SwitchInfo(SwitchMap),
} }
impl SerializableSimMsgPayload<SimReply> for PcduReply {
const TARGET: SimTarget = SimTarget::Pcdu;
}
} }
pub mod acs { pub mod acs {
@ -117,6 +195,10 @@ pub mod acs {
RequestSensorData, RequestSensorData,
} }
impl SerializableSimMsgPayload<SimRequest> for MgmRequest {
const TARGET: SimTarget = SimTarget::Mgm;
}
// Normally, small magnetometers generate their output as a signed 16 bit raw format or something // Normally, small magnetometers generate their output as a signed 16 bit raw format or something
// similar which needs to be converted to a signed float value with physical units. We will // similar which needs to be converted to a signed float value with physical units. We will
// simplify this now and generate the signed float values directly. // simplify this now and generate the signed float values directly.
@ -133,6 +215,10 @@ pub mod acs {
pub sensor_values: MgmSensorValues, pub sensor_values: MgmSensorValues,
} }
impl SerializableSimMsgPayload<SimReply> for MgmReply {
const TARGET: SimTarget = SimTarget::Mgm;
}
pub const MGT_GEN_MAGNETIC_FIELD: MgmSensorValues = MgmSensorValues { pub const MGT_GEN_MAGNETIC_FIELD: MgmSensorValues = MgmSensorValues {
x: 0.03, x: 0.03,
y: -0.03, y: -0.03,
@ -161,6 +247,10 @@ pub mod acs {
RequestHk, RequestHk,
} }
impl SerializableSimMsgPayload<SimRequest> for MgtRequest {
const TARGET: SimTarget = SimTarget::Mgt;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MgtHkSet { pub struct MgtHkSet {
pub dipole: MgtDipole, pub dipole: MgtDipole,
@ -173,6 +263,10 @@ pub mod acs {
Nak(MgtRequestType), Nak(MgtRequestType),
Hk(MgtHkSet), Hk(MgtHkSet),
} }
impl SerializableSimMsgPayload<SimReply> for MgtReply {
const TARGET: SimTarget = SimTarget::Mgm;
}
} }
pub mod udp { pub mod udp {
@ -244,3 +338,46 @@ pub mod udp {
} }
} }
} }
#[cfg(test)]
pub mod tests {
use super::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DummyRequest {
Ping,
}
impl SerializableSimMsgPayload<SimRequest> for DummyRequest {
const TARGET: SimTarget = SimTarget::SimCtrl;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DummyReply {
Pong,
}
impl SerializableSimMsgPayload<SimReply> for DummyReply {
const TARGET: SimTarget = SimTarget::SimCtrl;
}
#[test]
fn test_basic_request() {
let sim_request = SimRequest::new(DummyRequest::Ping);
assert_eq!(sim_request.target(), SimTarget::SimCtrl);
assert_eq!(sim_request.msg_type(), SimMessageType::Request);
let dummy_request =
DummyRequest::from_sim_message(&sim_request).expect("deserialization failed");
assert_eq!(dummy_request, DummyRequest::Ping);
}
#[test]
fn test_basic_reply() {
let sim_reply = SimReply::new(DummyReply::Pong);
assert_eq!(sim_reply.target(), SimTarget::SimCtrl);
assert_eq!(sim_reply.msg_type(), SimMessageType::Reply);
let dummy_request =
DummyReply::from_sim_message(&sim_reply).expect("deserialization failed");
assert_eq!(dummy_request, DummyReply::Pong);
}
}

View File

@ -6,7 +6,7 @@ use std::{
time::Duration, time::Duration,
}; };
use satrs_minisim::{SimReply, SimRequest}; use satrs_minisim::{SimMessageProvider, SimReply, SimRequest};
// A UDP server which handles all TC received by a client application. // A UDP server which handles all TC received by a client application.
pub struct SimUdpServer { pub struct SimUdpServer {
@ -90,11 +90,7 @@ impl SimUdpServer {
self.sender_addr = Some(src); self.sender_addr = Some(src);
// Convert the buffer into a string slice and print the message. let sim_req = SimRequest::from_raw_data(&self.req_buf[..bytes_read]);
let req_string = std::str::from_utf8(&self.req_buf[..bytes_read])
.expect("Could not write buffer as string");
log::info!("Received request from {}: {}", src, req_string);
let sim_req: serde_json::Result<SimRequest> = serde_json::from_str(req_string);
if sim_req.is_err() { if sim_req.is_err() {
log::warn!( log::warn!(
"received UDP request with invalid format: {}", "received UDP request with invalid format: {}",
@ -164,7 +160,7 @@ mod tests {
use satrs_minisim::{ use satrs_minisim::{
eps::{PcduReply, PcduRequest}, eps::{PcduReply, PcduRequest},
udp::{ReceptionError, SimUdpClient}, udp::{ReceptionError, SimUdpClient},
SimCtrlReply, SimCtrlRequest, SimReply, SimRequest, SimTarget, SimCtrlReply, SimCtrlRequest, SimReply, SimRequest,
}; };
use crate::eps::tests::get_all_off_switch_map; use crate::eps::tests::get_all_off_switch_map;
@ -274,7 +270,7 @@ mod tests {
UdpTestbench::new(true, Some(SERVER_WAIT_TIME_MS), 10) UdpTestbench::new(true, Some(SERVER_WAIT_TIME_MS), 10)
.expect("could not create testbench"); .expect("could not create testbench");
let server_thread = std::thread::spawn(move || udp_server.run()); let server_thread = std::thread::spawn(move || udp_server.run());
let sim_request = SimRequest::new(SimTarget::Pcdu, PcduRequest::RequestSwitchInfo); let sim_request = SimRequest::new(PcduRequest::RequestSwitchInfo);
udp_testbench udp_testbench
.send_request(&sim_request) .send_request(&sim_request)
.expect("sending request failed"); .expect("sending request failed");
@ -296,13 +292,10 @@ mod tests {
.expect("could not create testbench"); .expect("could not create testbench");
let server_thread = std::thread::spawn(move || udp_server.run()); let server_thread = std::thread::spawn(move || udp_server.run());
udp_testbench udp_testbench
.send_request(&SimRequest::new(SimTarget::SimCtrl, SimCtrlRequest::Ping)) .send_request(&SimRequest::new(SimCtrlRequest::Ping))
.expect("sending request failed"); .expect("sending request failed");
let sim_reply = SimReply::new( let sim_reply = SimReply::new(PcduReply::SwitchInfo(get_all_off_switch_map()));
SimTarget::Pcdu,
PcduReply::SwitchInfo(get_all_off_switch_map()),
);
udp_testbench.send_reply(&sim_reply); udp_testbench.send_reply(&sim_reply);
udp_testbench.check_next_sim_reply(&sim_reply); udp_testbench.check_next_sim_reply(&sim_reply);
@ -323,14 +316,11 @@ mod tests {
// Send a ping so that the server knows the address of the client. // Send a ping so that the server knows the address of the client.
// Do not check that the request arrives on the receiver side, is done by other test. // Do not check that the request arrives on the receiver side, is done by other test.
udp_testbench udp_testbench
.send_request(&SimRequest::new(SimTarget::SimCtrl, SimCtrlRequest::Ping)) .send_request(&SimRequest::new(SimCtrlRequest::Ping))
.expect("sending request failed"); .expect("sending request failed");
// Send a reply to the server, ensure it gets forwarded to the client. // Send a reply to the server, ensure it gets forwarded to the client.
let sim_reply = SimReply::new( let sim_reply = SimReply::new(PcduReply::SwitchInfo(get_all_off_switch_map()));
SimTarget::Pcdu,
PcduReply::SwitchInfo(get_all_off_switch_map()),
);
udp_testbench.send_reply(&sim_reply); udp_testbench.send_reply(&sim_reply);
std::thread::sleep(Duration::from_millis(SERVER_WAIT_TIME_MS)); std::thread::sleep(Duration::from_millis(SERVER_WAIT_TIME_MS));
@ -349,10 +339,7 @@ mod tests {
let server_thread = std::thread::spawn(move || udp_server.run()); let server_thread = std::thread::spawn(move || udp_server.run());
// Send a reply to the server. The client is not connected, so it won't get forwarded. // Send a reply to the server. The client is not connected, so it won't get forwarded.
let sim_reply = SimReply::new( let sim_reply = SimReply::new(PcduReply::SwitchInfo(get_all_off_switch_map()));
SimTarget::Pcdu,
PcduReply::SwitchInfo(get_all_off_switch_map()),
);
udp_testbench.send_reply(&sim_reply); udp_testbench.send_reply(&sim_reply);
std::thread::sleep(Duration::from_millis(10)); std::thread::sleep(Duration::from_millis(10));
@ -360,7 +347,7 @@ mod tests {
// Connect by sending a ping. // Connect by sending a ping.
udp_testbench udp_testbench
.send_request(&SimRequest::new(SimTarget::SimCtrl, SimCtrlRequest::Ping)) .send_request(&SimRequest::new(SimCtrlRequest::Ping))
.expect("sending request failed"); .expect("sending request failed");
std::thread::sleep(Duration::from_millis(SERVER_WAIT_TIME_MS)); std::thread::sleep(Duration::from_millis(SERVER_WAIT_TIME_MS));
@ -379,7 +366,7 @@ mod tests {
let server_thread = std::thread::spawn(move || udp_server.run()); let server_thread = std::thread::spawn(move || udp_server.run());
// The server only caches up to 3 replies. // The server only caches up to 3 replies.
let sim_reply = SimReply::new(SimTarget::SimCtrl, SimCtrlReply::Pong); let sim_reply = SimReply::new(SimCtrlReply::Pong);
for _ in 0..4 { for _ in 0..4 {
udp_testbench.send_reply(&sim_reply); udp_testbench.send_reply(&sim_reply);
} }
@ -389,7 +376,7 @@ mod tests {
// Connect by sending a ping. // Connect by sending a ping.
udp_testbench udp_testbench
.send_request(&SimRequest::new(SimTarget::SimCtrl, SimCtrlRequest::Ping)) .send_request(&SimRequest::new(SimCtrlRequest::Ping))
.expect("sending request failed"); .expect("sending request failed");
std::thread::sleep(Duration::from_millis(SERVER_WAIT_TIME_MS)); std::thread::sleep(Duration::from_millis(SERVER_WAIT_TIME_MS));