move to CCSDS + serde for sat-rs example
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "models"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
spacepackets = { version = "0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false }
|
||||
satrs = { path = "../../satrs" }
|
||||
num_enum = { version = "0.7" }
|
||||
strum = { version = "0.27", features = ["derive"] }
|
||||
postcard = { version = "1" }
|
||||
thiserror = { version = "2" }
|
||||
bitbybit = "1.4"
|
||||
arbitrary-int = "2"
|
||||
@@ -0,0 +1,130 @@
|
||||
use crate::TmHeader;
|
||||
use serde::Serialize;
|
||||
use spacepackets::{
|
||||
CcsdsPacketCreationError, CcsdsPacketCreatorWithReservedData, SpHeader, SpacePacketHeader,
|
||||
ccsds_packet_len_for_user_data_len_with_checksum,
|
||||
};
|
||||
|
||||
use crate::TcHeader;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CcsdsTcPacketOwned {
|
||||
pub sp_header: SpacePacketHeader,
|
||||
pub tc_header: TcHeader,
|
||||
pub payload: alloc::vec::Vec<u8>,
|
||||
}
|
||||
|
||||
impl CcsdsTcPacketOwned {
|
||||
pub fn new_with_request<R: serde::Serialize>(
|
||||
sp_header: SpacePacketHeader,
|
||||
tc_header: TcHeader,
|
||||
request: R,
|
||||
) -> Self {
|
||||
let request_serialized = postcard::to_allocvec(&request).unwrap();
|
||||
Self::new(sp_header, tc_header, request_serialized)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
sp_header: SpacePacketHeader,
|
||||
tc_header: TcHeader,
|
||||
payload: alloc::vec::Vec<u8>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sp_header,
|
||||
tc_header,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, CcsdsCreationError> {
|
||||
let response_len =
|
||||
postcard::experimental::serialized_size(&self.tc_header)? + self.payload.len();
|
||||
let mut ccsds_tc = CcsdsPacketCreatorWithReservedData::new_tc_with_checksum(
|
||||
self.sp_header,
|
||||
response_len,
|
||||
buf,
|
||||
)?;
|
||||
let user_data = ccsds_tc.packet_data_mut();
|
||||
let ser_len = postcard::to_slice(&self.tc_header, user_data)?.len();
|
||||
user_data[ser_len..ser_len + self.payload.len()].copy_from_slice(&self.payload);
|
||||
let ccsds_packet_len = ccsds_tc.finish();
|
||||
Ok(ccsds_packet_len)
|
||||
}
|
||||
|
||||
pub fn len_written(&self) -> usize {
|
||||
ccsds_packet_len_for_user_data_len_with_checksum(
|
||||
postcard::experimental::serialized_size(&self.tc_header).unwrap() as usize
|
||||
+ postcard::experimental::serialized_size(&self.payload).unwrap() as usize,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> alloc::vec::Vec<u8> {
|
||||
let mut buf = alloc::vec![0u8; self.len_written()];
|
||||
let len = self.write_to_bytes(&mut buf).unwrap();
|
||||
buf.truncate(len);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CcsdsCreationError {
|
||||
#[error("CCSDS packet creation error: {0}")]
|
||||
CcsdsPacketCreation(#[from] CcsdsPacketCreationError),
|
||||
#[error("postcard error: {0}")]
|
||||
Postcard(#[from] postcard::Error),
|
||||
#[error("timestamp generation error")]
|
||||
Time,
|
||||
}
|
||||
|
||||
/// Unserialized owned TM packet which can be cloned and sent around.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CcsdsTmPacketOwned {
|
||||
pub sp_header: SpacePacketHeader,
|
||||
pub tm_header: TmHeader,
|
||||
pub payload: alloc::vec::Vec<u8>,
|
||||
}
|
||||
|
||||
impl CcsdsTmPacketOwned {
|
||||
pub fn new_with_serde_payload(
|
||||
sp_header: SpHeader,
|
||||
tm_header: &TmHeader,
|
||||
payload: &impl Serialize,
|
||||
) -> Result<Self, postcard::Error> {
|
||||
Ok(CcsdsTmPacketOwned {
|
||||
sp_header,
|
||||
tm_header: *tm_header,
|
||||
payload: postcard::to_allocvec(&payload)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, CcsdsCreationError> {
|
||||
let response_len =
|
||||
postcard::experimental::serialized_size(&self.tm_header)? + self.payload.len();
|
||||
let mut ccsds_tm = CcsdsPacketCreatorWithReservedData::new_tm_with_checksum(
|
||||
self.sp_header,
|
||||
response_len,
|
||||
buf,
|
||||
)?;
|
||||
let user_data = ccsds_tm.packet_data_mut();
|
||||
let ser_len = postcard::to_slice(&self.tm_header, user_data)?.len();
|
||||
user_data[ser_len..ser_len + self.payload.len()].copy_from_slice(&self.payload);
|
||||
let ccsds_packet_len = ccsds_tm.finish();
|
||||
Ok(ccsds_packet_len)
|
||||
}
|
||||
|
||||
pub fn len_written(&self) -> usize {
|
||||
ccsds_packet_len_for_user_data_len_with_checksum(
|
||||
postcard::experimental::serialized_size(&self.tm_header).unwrap() as usize
|
||||
+ postcard::experimental::serialized_size(&self.payload).unwrap() as usize,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> alloc::vec::Vec<u8> {
|
||||
let mut buf = alloc::vec![0u8; self.len_written()];
|
||||
let len = self.write_to_bytes(&mut buf).unwrap();
|
||||
buf.truncate(len);
|
||||
buf
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
use crate::Message;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum Event {
|
||||
TestEvent,
|
||||
}
|
||||
|
||||
impl Message for Event {
|
||||
fn message_type(&self) -> crate::MessageType {
|
||||
crate::MessageType::Event
|
||||
}
|
||||
}
|
||||
|
||||
pub mod request {
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum Request {
|
||||
Ping,
|
||||
TestEvent,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
use crate::Message;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum Response {
|
||||
Ok,
|
||||
}
|
||||
|
||||
impl Message for Response {
|
||||
fn message_type(&self) -> crate::MessageType {
|
||||
match self {
|
||||
Response::Ok => crate::MessageType::Verification,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
extern crate alloc;
|
||||
use spacepackets::{
|
||||
CcsdsPacketIdAndPsc,
|
||||
time::cds::{CdsTime, MIN_CDS_FIELD_LEN},
|
||||
};
|
||||
|
||||
pub mod ccsds;
|
||||
pub mod control;
|
||||
pub mod mgm;
|
||||
pub mod pcdu;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
num_enum::TryFromPrimitive,
|
||||
num_enum::IntoPrimitive,
|
||||
)]
|
||||
#[repr(u64)]
|
||||
pub enum ComponentId {
|
||||
Pcdu,
|
||||
Controller,
|
||||
|
||||
AcsSubsystem,
|
||||
AcsMgmAssembly,
|
||||
AcsMgm0,
|
||||
AcsMgm1,
|
||||
|
||||
EpsSubsystem,
|
||||
EpsPcdu,
|
||||
|
||||
UdpServer,
|
||||
TcpServer,
|
||||
EventManager,
|
||||
|
||||
Ground,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, strum::EnumIter)]
|
||||
#[bitbybit::bitenum(u11)]
|
||||
pub enum Apid {
|
||||
Tmtc = 1,
|
||||
Cfdp = 2,
|
||||
|
||||
Acs = 3,
|
||||
Eps = 6,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Event {
|
||||
ControllerEvent(control::Event),
|
||||
}
|
||||
|
||||
impl Message for Event {
|
||||
fn message_type(&self) -> MessageType {
|
||||
MessageType::Event
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub struct TmHeader {
|
||||
pub sender_id: ComponentId,
|
||||
pub target_id: ComponentId,
|
||||
pub message_type: MessageType,
|
||||
/// Telemetry can either be sent unsolicited, or as a response to telecommands.
|
||||
pub tc_id: Option<CcsdsPacketIdAndPsc>,
|
||||
/// Raw CDS short timestamp.
|
||||
pub timestamp: Option<[u8; 7]>,
|
||||
}
|
||||
|
||||
impl TmHeader {
|
||||
pub fn new(
|
||||
sender_id: ComponentId,
|
||||
target_id: ComponentId,
|
||||
message_type: MessageType,
|
||||
tc_id: Option<CcsdsPacketIdAndPsc>,
|
||||
cds_timestamp: &CdsTime,
|
||||
) -> Self {
|
||||
// Can not fail, CDS short always requires 7 bytes.
|
||||
let mut stamp_buf: [u8; MIN_CDS_FIELD_LEN] = [0; MIN_CDS_FIELD_LEN];
|
||||
cds_timestamp.write_to_bytes(&mut stamp_buf).unwrap();
|
||||
Self {
|
||||
sender_id,
|
||||
target_id,
|
||||
tc_id,
|
||||
message_type,
|
||||
timestamp: Some(stamp_buf),
|
||||
}
|
||||
}
|
||||
pub fn new_for_unsolicited_tm(
|
||||
sender_id: ComponentId,
|
||||
target_id: ComponentId,
|
||||
message_type: MessageType,
|
||||
cds_timestamp: &CdsTime,
|
||||
) -> Self {
|
||||
Self::new(sender_id, target_id, message_type, None, cds_timestamp)
|
||||
}
|
||||
|
||||
pub fn new_for_tc_response(
|
||||
sender_id: ComponentId,
|
||||
target_id: ComponentId,
|
||||
message_type: MessageType,
|
||||
tc_id: CcsdsPacketIdAndPsc,
|
||||
cds_timestamp: &CdsTime,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
sender_id,
|
||||
target_id,
|
||||
message_type,
|
||||
Some(tc_id),
|
||||
cds_timestamp,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_bytes_postcard(data: &[u8]) -> Result<(Self, &[u8]), postcard::Error> {
|
||||
postcard::take_from_bytes::<TmHeader>(data)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub struct TcHeader {
|
||||
pub target_id: ComponentId,
|
||||
pub request_type: MessageType,
|
||||
}
|
||||
|
||||
impl TcHeader {
|
||||
pub fn new(target_id: ComponentId, request_type: MessageType) -> Self {
|
||||
Self {
|
||||
target_id,
|
||||
request_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum MessageType {
|
||||
Ping,
|
||||
Mode,
|
||||
Hk,
|
||||
Action,
|
||||
Event,
|
||||
Verification,
|
||||
}
|
||||
|
||||
pub trait Message {
|
||||
fn message_type(&self) -> MessageType;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum DeviceMode {
|
||||
Off = 0,
|
||||
On = 1,
|
||||
Normal = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum HkRequestType {
|
||||
OneShot,
|
||||
EnablePeriodic(core::time::Duration),
|
||||
DisablePeriodic,
|
||||
ModifyInterval(core::time::Duration),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
@@ -0,0 +1,52 @@
|
||||
pub mod request {
|
||||
use crate::HkRequestType;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
pub enum HkId {
|
||||
Sensor,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
pub struct HkRequest {
|
||||
pub id: HkId,
|
||||
pub req_type: HkRequestType,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum Request {
|
||||
Ping,
|
||||
Hk(HkRequest),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct MgmData {
|
||||
pub valid: bool,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
use crate::{Message, mgm::MgmData};
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum HkResponse {
|
||||
MgmData(MgmData),
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum Response {
|
||||
Ok,
|
||||
Hk(HkResponse),
|
||||
}
|
||||
|
||||
impl Message for Response {
|
||||
fn message_type(&self) -> crate::MessageType {
|
||||
match self {
|
||||
Response::Ok => crate::MessageType::Verification,
|
||||
Response::Hk(_hk_response) => crate::MessageType::Hk,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use strum::IntoEnumIterator as _;
|
||||
|
||||
#[bitbybit::bitfield(u16, debug, default = 0x0)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct SwitchesBitfield {
|
||||
#[bit(2, rw)]
|
||||
magnetorquer: bool,
|
||||
#[bit(1, rw)]
|
||||
mgm1: bool,
|
||||
#[bit(0, rw)]
|
||||
mgm0: bool,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
Hash,
|
||||
strum::EnumIter,
|
||||
num_enum::IntoPrimitive,
|
||||
num_enum::TryFromPrimitive,
|
||||
)]
|
||||
#[repr(u16)]
|
||||
pub enum SwitchId {
|
||||
Mgm0 = 0,
|
||||
Mgm1 = 1,
|
||||
Mgt = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum SwitchState {
|
||||
Off = 0,
|
||||
On = 1,
|
||||
Unknown = 2,
|
||||
Faulty = 3,
|
||||
}
|
||||
|
||||
impl From<SwitchStateBinary> for SwitchState {
|
||||
fn from(value: SwitchStateBinary) -> Self {
|
||||
match value {
|
||||
SwitchStateBinary::Off => SwitchState::Off,
|
||||
SwitchStateBinary::On => SwitchState::On,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum SwitchStateBinary {
|
||||
Off = 0,
|
||||
On = 1,
|
||||
}
|
||||
|
||||
pub type SwitchMapBinary = HashMap<SwitchId, SwitchStateBinary>;
|
||||
|
||||
pub struct SwitchMapBinaryWrapper(pub SwitchMapBinary);
|
||||
|
||||
impl Default for SwitchMapBinaryWrapper {
|
||||
fn default() -> Self {
|
||||
let mut switch_map = SwitchMapBinary::default();
|
||||
for entry in SwitchId::iter() {
|
||||
switch_map.insert(entry, SwitchStateBinary::Off);
|
||||
}
|
||||
Self(switch_map)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SwitchRequest {
|
||||
pub switch_id: SwitchId,
|
||||
pub target_state: SwitchStateBinary,
|
||||
}
|
||||
|
||||
impl SwitchRequest {
|
||||
pub fn new(switch_id: SwitchId, target_state: SwitchStateBinary) -> Self {
|
||||
Self {
|
||||
switch_id,
|
||||
target_state,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn switch_id(&self) -> SwitchId {
|
||||
self.switch_id
|
||||
}
|
||||
|
||||
pub fn target_state(&self) -> SwitchStateBinary {
|
||||
self.target_state
|
||||
}
|
||||
}
|
||||
|
||||
pub mod request {
|
||||
use super::*;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum Request {
|
||||
Ping,
|
||||
GetSwitches,
|
||||
EnableSwitches(SwitchesBitfield),
|
||||
DisableSwitches(SwitchesBitfield),
|
||||
}
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
use super::*;
|
||||
use crate::Message;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum Response {
|
||||
Ok,
|
||||
Switches(SwitchesBitfield),
|
||||
}
|
||||
|
||||
impl Message for Response {
|
||||
fn message_type(&self) -> crate::MessageType {
|
||||
match self {
|
||||
Response::Ok => crate::MessageType::Verification,
|
||||
Response::Switches(_switches) => crate::MessageType::Action,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user