start integrating sim in example
Some checks failed
Rust/sat-rs/pipeline/head There was a failure building this commit

This commit is contained in:
2024-05-08 20:38:45 +02:00
parent 3746e9ebb0
commit c20163b10a
13 changed files with 510 additions and 205 deletions

View File

@ -6,14 +6,17 @@ use asynchronix::{
};
use satrs::power::SwitchStateBinary;
use satrs_minisim::{
acs::{MgmReply, MgmSensorValues, MgtDipole, MgtHkSet, MgtReply, MGT_GEN_MAGNETIC_FIELD},
acs::{
lis3mdl::MgmLis3MdlReply, MgmReplyCommon, MgmReplyProvider, MgmSensorValuesMicroTesla,
MgtDipole, MgtHkSet, MgtReply, MGT_GEN_MAGNETIC_FIELD,
},
SimReply,
};
use crate::time::current_millis;
// Earth magnetic field varies between -30 uT and 30 uT
const AMPLITUDE_MGM: f32 = 0.03;
const AMPLITUDE_MGM_UT: f32 = 30.0;
// Lets start with a simple frequency here.
const FREQUENCY_MGM: f32 = 1.0;
const PHASE_X: f32 = 0.0;
@ -31,30 +34,34 @@ const PHASE_Z: f32 = 0.2;
/// 2. It would sample the magnetic field at a high fixed rate. This might not be possible for
/// a general purpose OS, but self self-sampling at a relatively high rate (20-40 ms) might
/// stil lbe possible.
pub struct MagnetometerModel {
pub struct MagnetometerModel<ReplyProvider: MgmReplyProvider> {
pub switch_state: SwitchStateBinary,
pub periodicity: Duration,
pub external_mag_field: Option<MgmSensorValues>,
pub external_mag_field: Option<MgmSensorValuesMicroTesla>,
pub reply_sender: mpsc::Sender<SimReply>,
pub phatom: std::marker::PhantomData<ReplyProvider>,
}
impl MagnetometerModel {
pub fn new(periodicity: Duration, reply_sender: mpsc::Sender<SimReply>) -> Self {
impl MagnetometerModel<MgmLis3MdlReply> {
pub fn new_for_lis3mdl(periodicity: Duration, reply_sender: mpsc::Sender<SimReply>) -> Self {
Self {
switch_state: SwitchStateBinary::Off,
periodicity,
external_mag_field: None,
reply_sender,
phatom: std::marker::PhantomData,
}
}
}
impl<ReplyProvider: MgmReplyProvider> MagnetometerModel<ReplyProvider> {
pub async fn switch_device(&mut self, switch_state: SwitchStateBinary) {
self.switch_state = switch_state;
}
pub async fn send_sensor_values(&mut self, _: (), scheduler: &Scheduler<Self>) {
self.reply_sender
.send(SimReply::new(MgmReply {
.send(ReplyProvider::create_mgm_reply(MgmReplyCommon {
switch_state: self.switch_state,
sensor_values: self.calculate_current_mgm_tuple(current_millis(scheduler.time())),
}))
@ -63,23 +70,23 @@ impl MagnetometerModel {
// Devices like magnetorquers generate a strong magnetic field which overrides the default
// model for the measured magnetic field.
pub async fn apply_external_magnetic_field(&mut self, field: MgmSensorValues) {
pub async fn apply_external_magnetic_field(&mut self, field: MgmSensorValuesMicroTesla) {
self.external_mag_field = Some(field);
}
fn calculate_current_mgm_tuple(&self, time_ms: u64) -> MgmSensorValues {
fn calculate_current_mgm_tuple(&self, time_ms: u64) -> MgmSensorValuesMicroTesla {
if SwitchStateBinary::On == self.switch_state {
if let Some(ext_field) = self.external_mag_field {
return ext_field;
}
let base_sin_val = 2.0 * PI * FREQUENCY_MGM * (time_ms as f32 / 1000.0);
return MgmSensorValues {
x: AMPLITUDE_MGM * (base_sin_val + PHASE_X).sin(),
y: AMPLITUDE_MGM * (base_sin_val + PHASE_Y).sin(),
z: AMPLITUDE_MGM * (base_sin_val + PHASE_Z).sin(),
return MgmSensorValuesMicroTesla {
x: AMPLITUDE_MGM_UT * (base_sin_val + PHASE_X).sin(),
y: AMPLITUDE_MGM_UT * (base_sin_val + PHASE_Y).sin(),
z: AMPLITUDE_MGM_UT * (base_sin_val + PHASE_Z).sin(),
};
}
MgmSensorValues {
MgmSensorValuesMicroTesla {
x: 0.0,
y: 0.0,
z: 0.0,
@ -87,13 +94,13 @@ impl MagnetometerModel {
}
}
impl Model for MagnetometerModel {}
impl<ReplyProvider: MgmReplyProvider> Model for MagnetometerModel<ReplyProvider> {}
pub struct MagnetorquerModel {
switch_state: SwitchStateBinary,
torquing: bool,
torque_dipole: MgtDipole,
pub gen_magnetic_field: Output<MgmSensorValues>,
pub gen_magnetic_field: Output<MgmSensorValuesMicroTesla>,
reply_sender: mpsc::Sender<SimReply>,
}
@ -153,7 +160,7 @@ impl MagnetorquerModel {
.unwrap();
}
fn calc_magnetic_field(&self, _: MgtDipole) -> MgmSensorValues {
fn calc_magnetic_field(&self, _: MgtDipole) -> MgmSensorValuesMicroTesla {
// Simplified model: Just returns some fixed magnetic field for now.
// Later, we could make this more fancy by incorporating the commanded dipole.
MGT_GEN_MAGNETIC_FIELD
@ -179,7 +186,10 @@ pub mod tests {
use satrs::power::SwitchStateBinary;
use satrs_minisim::{
acs::{MgmReply, MgmRequest, MgtDipole, MgtHkSet, MgtReply, MgtRequest},
acs::{
lis3mdl::{self, MgmLis3MdlReply},
MgmRequest, MgtDipole, MgtHkSet, MgtReply, MgtRequest,
},
eps::PcduSwitch,
SerializableSimMsgPayload, SimMessageProvider, SimRequest, SimTarget,
};
@ -198,13 +208,13 @@ pub mod tests {
let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap();
assert_eq!(sim_reply.target(), SimTarget::Mgm);
let reply = MgmReply::from_sim_message(&sim_reply)
assert_eq!(sim_reply.component(), SimTarget::MgmLis3Mdl);
let reply = MgmLis3MdlReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values");
assert_eq!(reply.switch_state, SwitchStateBinary::Off);
assert_eq!(reply.sensor_values.x, 0.0);
assert_eq!(reply.sensor_values.y, 0.0);
assert_eq!(reply.sensor_values.z, 0.0);
assert_eq!(reply.common.switch_state, SwitchStateBinary::Off);
assert_eq!(reply.common.sensor_values.x, 0.0);
assert_eq!(reply.common.sensor_values.y, 0.0);
assert_eq!(reply.common.sensor_values.z, 0.0);
}
#[test]
@ -221,8 +231,8 @@ pub mod tests {
let mut sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some());
let mut sim_reply = sim_reply_res.unwrap();
assert_eq!(sim_reply.target(), SimTarget::Mgm);
let first_reply = MgmReply::from_sim_message(&sim_reply)
assert_eq!(sim_reply.component(), SimTarget::MgmLis3Mdl);
let first_reply = MgmLis3MdlReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values");
sim_testbench.step_by(Duration::from_millis(50));
@ -236,8 +246,24 @@ pub mod tests {
assert!(sim_reply_res.is_some());
sim_reply = sim_reply_res.unwrap();
let second_reply = MgmReply::from_sim_message(&sim_reply)
let second_reply = MgmLis3MdlReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values");
let x_conv_back = second_reply.raw.x as f32
* lis3mdl::FIELD_LSB_PER_GAUSS_4_SENS
* lis3mdl::GAUSS_TO_MICROTESLA_FACTOR as f32;
let y_conv_back = second_reply.raw.y as f32
* lis3mdl::FIELD_LSB_PER_GAUSS_4_SENS
* lis3mdl::GAUSS_TO_MICROTESLA_FACTOR as f32;
let z_conv_back = second_reply.raw.z as f32
* lis3mdl::FIELD_LSB_PER_GAUSS_4_SENS
* lis3mdl::GAUSS_TO_MICROTESLA_FACTOR as f32;
let diff_x = (second_reply.common.sensor_values.x - x_conv_back).abs();
assert!(diff_x < 0.01, "diff x too large: {}", diff_x);
let diff_y = (second_reply.common.sensor_values.y - y_conv_back).abs();
assert!(diff_y < 0.01, "diff y too large: {}", diff_y);
let diff_z = (second_reply.common.sensor_values.z - z_conv_back).abs();
assert!(diff_z < 0.01, "diff z too large: {}", diff_z);
// assert_eq!(second_reply.raw_reply, SwitchStateBinary::On);
// Check that the values are changing.
assert!(first_reply != second_reply);
}

View File

@ -5,10 +5,10 @@ use asynchronix::{
time::{Clock, MonotonicTime, SystemClock},
};
use satrs_minisim::{
acs::{MgmRequest, MgtRequest},
acs::{lis3mdl::MgmLis3MdlReply, MgmRequestLis3Mdl, MgtRequest},
eps::PcduRequest,
SerializableSimMsgPayload, SimCtrlReply, SimCtrlRequest, SimMessageProvider, SimReply,
SimRequest, SimRequestError, SimTarget,
SerializableSimMsgPayload, SimComponent, SimCtrlReply, SimCtrlRequest, SimMessageProvider,
SimReply, SimRequest, SimRequestError,
};
use crate::{
@ -22,7 +22,7 @@ pub struct SimController {
pub request_receiver: mpsc::Receiver<SimRequest>,
pub reply_sender: mpsc::Sender<SimReply>,
pub simulation: Simulation,
pub mgm_addr: Address<MagnetometerModel>,
pub mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
pub pcdu_addr: Address<PcduModel>,
pub mgt_addr: Address<MagnetorquerModel>,
}
@ -33,7 +33,7 @@ impl SimController {
request_receiver: mpsc::Receiver<SimRequest>,
reply_sender: mpsc::Sender<SimReply>,
simulation: Simulation,
mgm_addr: Address<MagnetometerModel>,
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
pcdu_addr: Address<PcduModel>,
mgt_addr: Address<MagnetorquerModel>,
) -> Self {
@ -70,11 +70,11 @@ impl SimController {
if request.timestamp < old_timestamp {
log::warn!("stale data with timestamp {:?} received", request.timestamp);
}
if let Err(e) = match request.target() {
SimTarget::SimCtrl => self.handle_ctrl_request(&request),
SimTarget::Mgm => self.handle_mgm_request(&request),
SimTarget::Mgt => self.handle_mgt_request(&request),
SimTarget::Pcdu => self.handle_pcdu_request(&request),
if let Err(e) = match request.component() {
SimComponent::SimCtrl => self.handle_ctrl_request(&request),
SimComponent::MgmLis3Mdl => self.handle_mgm_request(&request),
SimComponent::Mgt => self.handle_mgt_request(&request),
SimComponent::Pcdu => self.handle_pcdu_request(&request),
} {
self.handle_invalid_request_with_valid_target(e, &request)
}
@ -100,10 +100,11 @@ impl SimController {
}
Ok(())
}
fn handle_mgm_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
let mgm_request = MgmRequest::from_sim_message(request)?;
let mgm_request = MgmRequestLis3Mdl::from_sim_message(request)?;
match mgm_request {
MgmRequest::RequestSensorData => {
MgmRequestLis3Mdl::RequestSensorData => {
self.simulation.send_event(
MagnetometerModel::send_sensor_values,
(),
@ -156,7 +157,7 @@ impl SimController {
) {
log::warn!(
"received invalid {:?} request: {:?}",
request.target(),
request.component(),
error
);
self.reply_sender
@ -183,7 +184,7 @@ mod tests {
let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap();
assert_eq!(sim_reply.target(), SimTarget::SimCtrl);
assert_eq!(sim_reply.component(), SimComponent::SimCtrl);
let reply = SimCtrlReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values");
assert_eq!(reply, SimCtrlReply::Pong);

View File

@ -76,7 +76,7 @@ pub(crate) mod tests {
use std::time::Duration;
use satrs_minisim::{
eps::PcduRequest, SerializableSimMsgPayload, SimMessageProvider, SimRequest, SimTarget,
eps::PcduRequest, SerializableSimMsgPayload, SimComponent, SimMessageProvider, SimRequest,
};
use crate::test_helpers::SimTestbench;
@ -122,7 +122,7 @@ pub(crate) mod tests {
let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap();
assert_eq!(sim_reply.target(), SimTarget::Pcdu);
assert_eq!(sim_reply.component(), SimComponent::Pcdu);
let pcdu_reply = PcduReply::from_sim_message(&sim_reply)
.expect("failed to deserialize PCDU switch info");
match pcdu_reply {
@ -157,7 +157,7 @@ pub(crate) mod tests {
let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap();
assert_eq!(sim_reply.target(), SimTarget::Pcdu);
assert_eq!(sim_reply.component(), SimComponent::Pcdu);
let pcdu_reply = PcduReply::from_sim_message(&sim_reply)
.expect("failed to deserialize PCDU switch info");
match pcdu_reply {

View File

@ -1,19 +1,17 @@
use asynchronix::time::MonotonicTime;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub const SIM_CTRL_UDP_PORT: u16 = 7303;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SimTarget {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
pub enum SimComponent {
SimCtrl,
Mgm,
MgmLis3Mdl,
Mgt,
Pcdu,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SimMessage {
pub target: SimTarget,
pub target: SimComponent,
pub payload: String,
}
@ -37,10 +35,10 @@ pub enum SimMessageType {
pub trait SerializableSimMsgPayload<P: SimMessageProvider>:
Serialize + DeserializeOwned + Sized
{
const TARGET: SimTarget;
const TARGET: SimComponent;
fn from_sim_message(sim_message: &P) -> Result<Self, SimMessageError<P>> {
if sim_message.target() == Self::TARGET {
if sim_message.component() == Self::TARGET {
return Ok(serde_json::from_str(sim_message.payload())?);
}
Err(SimMessageError::TargetRequestMissmatch(sim_message.clone()))
@ -49,7 +47,7 @@ pub trait SerializableSimMsgPayload<P: SimMessageProvider>:
pub trait SimMessageProvider: Serialize + DeserializeOwned + Clone + Sized {
fn msg_type(&self) -> SimMessageType;
fn target(&self) -> SimTarget;
fn component(&self) -> SimComponent;
fn payload(&self) -> &String;
fn from_raw_data(data: &[u8]) -> serde_json::Result<Self> {
serde_json::from_slice(data)
@ -78,7 +76,7 @@ impl SimRequest {
}
impl SimMessageProvider for SimRequest {
fn target(&self) -> SimTarget {
fn component(&self) -> SimComponent {
self.inner.target
}
fn payload(&self) -> &String {
@ -109,7 +107,7 @@ impl SimReply {
}
impl SimMessageProvider for SimReply {
fn target(&self) -> SimTarget {
fn component(&self) -> SimComponent {
self.inner.target
}
fn payload(&self) -> &String {
@ -126,7 +124,7 @@ pub enum SimCtrlRequest {
}
impl SerializableSimMsgPayload<SimRequest> for SimCtrlRequest {
const TARGET: SimTarget = SimTarget::SimCtrl;
const TARGET: SimComponent = SimComponent::SimCtrl;
}
pub type SimReplyError = SimMessageError<SimReply>;
@ -151,7 +149,7 @@ pub enum SimCtrlReply {
}
impl SerializableSimMsgPayload<SimReply> for SimCtrlReply {
const TARGET: SimTarget = SimTarget::SimCtrl;
const TARGET: SimComponent = SimComponent::SimCtrl;
}
impl From<SimRequestError> for SimCtrlReply {
@ -184,7 +182,7 @@ pub mod eps {
}
impl SerializableSimMsgPayload<SimRequest> for PcduRequest {
const TARGET: SimTarget = SimTarget::Pcdu;
const TARGET: SimComponent = SimComponent::Pcdu;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -193,7 +191,7 @@ pub mod eps {
}
impl SerializableSimMsgPayload<SimReply> for PcduReply {
const TARGET: SimTarget = SimTarget::Pcdu;
const TARGET: SimComponent = SimComponent::Pcdu;
}
}
@ -204,41 +202,104 @@ pub mod acs {
use super::*;
pub trait MgmReplyProvider: Send + 'static {
fn create_mgm_reply(common: MgmReplyCommon) -> SimReply;
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum MgmRequest {
pub enum MgmRequestLis3Mdl {
RequestSensorData,
}
impl SerializableSimMsgPayload<SimRequest> for MgmRequest {
const TARGET: SimTarget = SimTarget::Mgm;
impl SerializableSimMsgPayload<SimRequest> for MgmRequestLis3Mdl {
const TARGET: SimComponent = SimComponent::MgmLis3Mdl;
}
// 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
// simplify this now and generate the signed float values directly.
// simplify this now and generate the signed float values directly. The unit is micro tesla.
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct MgmSensorValues {
pub struct MgmSensorValuesMicroTesla {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct MgmReply {
pub struct MgmReplyCommon {
pub switch_state: SwitchStateBinary,
pub sensor_values: MgmSensorValues,
pub sensor_values: MgmSensorValuesMicroTesla,
}
impl SerializableSimMsgPayload<SimReply> for MgmReply {
const TARGET: SimTarget = SimTarget::Mgm;
}
pub const MGT_GEN_MAGNETIC_FIELD: MgmSensorValues = MgmSensorValues {
x: 0.03,
y: -0.03,
z: 0.03,
pub const MGT_GEN_MAGNETIC_FIELD: MgmSensorValuesMicroTesla = MgmSensorValuesMicroTesla {
x: 30.0,
y: -30.0,
z: 30.0,
};
pub mod lis3mdl {
use super::*;
// Field data register scaling
pub const GAUSS_TO_MICROTESLA_FACTOR: u32 = 100;
pub const FIELD_LSB_PER_GAUSS_4_SENS: f32 = 1.0 / 6842.0;
pub const FIELD_LSB_PER_GAUSS_8_SENS: f32 = 1.0 / 3421.0;
pub const FIELD_LSB_PER_GAUSS_12_SENS: f32 = 1.0 / 2281.0;
pub const FIELD_LSB_PER_GAUSS_16_SENS: f32 = 1.0 / 1711.0;
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct MgmLis3RawValues {
pub x: i16,
pub y: i16,
pub z: i16,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct MgmLis3MdlReply {
pub common: MgmReplyCommon,
// Raw sensor values which are transmitted by the LIS3 device in little-endian
// order.
pub raw: MgmLis3RawValues,
}
impl MgmLis3MdlReply {
pub fn new(common: MgmReplyCommon) -> Self {
let mut raw_reply: [u8; 7] = [0; 7];
let raw_x: i16 = (common.sensor_values.x
/ (GAUSS_TO_MICROTESLA_FACTOR as f32 * FIELD_LSB_PER_GAUSS_4_SENS))
.round() as i16;
let raw_y: i16 = (common.sensor_values.y
/ (GAUSS_TO_MICROTESLA_FACTOR as f32 * FIELD_LSB_PER_GAUSS_4_SENS))
.round() as i16;
let raw_z: i16 = (common.sensor_values.z
/ (GAUSS_TO_MICROTESLA_FACTOR as f32 * FIELD_LSB_PER_GAUSS_4_SENS))
.round() as i16;
// The first byte is a dummy byte.
raw_reply[1..3].copy_from_slice(&raw_x.to_be_bytes());
raw_reply[3..5].copy_from_slice(&raw_y.to_be_bytes());
raw_reply[5..7].copy_from_slice(&raw_z.to_be_bytes());
Self {
common,
raw: MgmLis3RawValues {
x: raw_x,
y: raw_y,
z: raw_z,
},
}
}
}
impl SerializableSimMsgPayload<SimReply> for MgmLis3MdlReply {
const TARGET: SimComponent = SimComponent::MgmLis3Mdl;
}
impl MgmReplyProvider for MgmLis3MdlReply {
fn create_mgm_reply(common: MgmReplyCommon) -> SimReply {
SimReply::new(Self::new(common))
}
}
}
// Simple model using i16 values.
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MgtDipole {
@ -262,7 +323,7 @@ pub mod acs {
}
impl SerializableSimMsgPayload<SimRequest> for MgtRequest {
const TARGET: SimTarget = SimTarget::Mgt;
const TARGET: SimComponent = SimComponent::Mgt;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -279,78 +340,12 @@ pub mod acs {
}
impl SerializableSimMsgPayload<SimReply> for MgtReply {
const TARGET: SimTarget = SimTarget::Mgm;
const TARGET: SimComponent = SimComponent::MgmLis3Mdl;
}
}
pub mod udp {
use std::{
net::{SocketAddr, UdpSocket},
time::Duration,
};
use thiserror::Error;
use crate::{SimReply, SimRequest};
#[derive(Error, Debug)]
pub enum ReceptionError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Serde JSON error: {0}")]
SerdeJson(#[from] serde_json::Error),
}
pub struct SimUdpClient {
socket: UdpSocket,
pub reply_buf: [u8; 4096],
}
impl SimUdpClient {
pub fn new(
server_addr: &SocketAddr,
non_blocking: bool,
read_timeot_ms: Option<u64>,
) -> std::io::Result<Self> {
let socket = UdpSocket::bind("127.0.0.1:0")?;
socket.set_nonblocking(non_blocking)?;
socket
.connect(server_addr)
.expect("could not connect to server addr");
if let Some(read_timeout) = read_timeot_ms {
// Set a read timeout so the test does not hang on failures.
socket.set_read_timeout(Some(Duration::from_millis(read_timeout)))?;
}
Ok(Self {
socket,
reply_buf: [0; 4096],
})
}
pub fn set_nonblocking(&self, non_blocking: bool) -> std::io::Result<()> {
self.socket.set_nonblocking(non_blocking)
}
pub fn set_read_timeout(&self, read_timeout_ms: u64) -> std::io::Result<()> {
self.socket
.set_read_timeout(Some(Duration::from_millis(read_timeout_ms)))
}
pub fn send_request(&self, sim_request: &SimRequest) -> std::io::Result<usize> {
self.socket.send(
&serde_json::to_vec(sim_request).expect("conversion of request to vector failed"),
)
}
pub fn recv_raw(&mut self) -> std::io::Result<usize> {
self.socket.recv(&mut self.reply_buf)
}
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])?)
}
}
pub const SIM_CTRL_PORT: u16 = 7303;
}
#[cfg(test)]
@ -363,7 +358,7 @@ pub mod tests {
}
impl SerializableSimMsgPayload<SimRequest> for DummyRequest {
const TARGET: SimTarget = SimTarget::SimCtrl;
const TARGET: SimComponent = SimComponent::SimCtrl;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -372,13 +367,13 @@ pub mod tests {
}
impl SerializableSimMsgPayload<SimReply> for DummyReply {
const TARGET: SimTarget = SimTarget::SimCtrl;
const TARGET: SimComponent = SimComponent::SimCtrl;
}
#[test]
fn test_basic_request() {
let sim_request = SimRequest::new_with_epoch_time(DummyRequest::Ping);
assert_eq!(sim_request.target(), SimTarget::SimCtrl);
assert_eq!(sim_request.component(), SimComponent::SimCtrl);
assert_eq!(sim_request.msg_type(), SimMessageType::Request);
let dummy_request =
DummyRequest::from_sim_message(&sim_request).expect("deserialization failed");
@ -388,7 +383,7 @@ pub mod tests {
#[test]
fn test_basic_reply() {
let sim_reply = SimReply::new(DummyReply::Pong);
assert_eq!(sim_reply.target(), SimTarget::SimCtrl);
assert_eq!(sim_reply.component(), SimComponent::SimCtrl);
assert_eq!(sim_reply.msg_type(), SimMessageType::Reply);
let dummy_request =
DummyReply::from_sim_message(&sim_reply).expect("deserialization failed");

View File

@ -3,7 +3,8 @@ use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::time::{MonotonicTime, SystemClock};
use controller::SimController;
use eps::PcduModel;
use satrs_minisim::{SimReply, SimRequest, SIM_CTRL_UDP_PORT};
use satrs_minisim::udp::SIM_CTRL_PORT;
use satrs_minisim::{SimReply, SimRequest};
use std::sync::mpsc;
use std::thread;
use std::time::{Duration, SystemTime};
@ -30,7 +31,8 @@ fn create_sim_controller(
request_receiver: mpsc::Receiver<SimRequest>,
) -> SimController {
// Instantiate models and their mailboxes.
let mgm_model = MagnetometerModel::new(Duration::from_millis(50), reply_sender.clone());
let mgm_model =
MagnetometerModel::new_for_lis3mdl(Duration::from_millis(50), reply_sender.clone());
let mgm_mailbox = Mailbox::new();
let mgm_addr = mgm_mailbox.address();
@ -112,9 +114,9 @@ fn main() {
});
let mut udp_server =
SimUdpServer::new(SIM_CTRL_UDP_PORT, request_sender, reply_receiver, 200, None)
SimUdpServer::new(SIM_CTRL_PORT, request_sender, reply_receiver, 200, None)
.expect("could not create UDP request server");
log::info!("starting UDP server on port {}", SIM_CTRL_UDP_PORT);
log::info!("starting UDP server on port {}", SIM_CTRL_PORT);
// This thread manages the simulator UDP server.
let udp_tc_thread = thread::spawn(move || {
udp_server.run();