Skip to main content

leodos_protocols/misc/sle/
types.rs

1//! Shared SLE types used across RAF, CLTU, and ISP1.
2
3/// CCSDS Day Segmented (CDS) time code.
4///
5/// 8 bytes: 2-byte day count (epoch 1958-01-01) + 4-byte ms of day
6/// + 2-byte microseconds of millisecond.
7#[derive(Copy, Clone, Debug, PartialEq, Eq)]
8pub struct Time {
9    /// Raw 8-byte CDS time field.
10    pub cds: [u8; 8],
11}
12
13impl Time {
14    /// Size of a CDS time code in bytes.
15    pub const SIZE: usize = 8;
16
17    /// Creates a Time from raw bytes.
18    pub const fn from_bytes(bytes: [u8; 8]) -> Self {
19        Self { cds: bytes }
20    }
21
22    /// Returns the day count since 1958-01-01.
23    pub fn day(&self) -> u16 {
24        u16::from_be_bytes([self.cds[0], self.cds[1]])
25    }
26
27    /// Returns the millisecond of the day.
28    pub fn ms_of_day(&self) -> u32 {
29        u32::from_be_bytes([
30            self.cds[2], self.cds[3], self.cds[4], self.cds[5],
31        ])
32    }
33
34    /// Returns the sub-millisecond microseconds.
35    pub fn microseconds(&self) -> u16 {
36        u16::from_be_bytes([self.cds[6], self.cds[7]])
37    }
38
39    /// Encodes the time into the provided buffer.
40    /// Returns the number of bytes written (always 8).
41    pub fn encode(&self, buf: &mut [u8]) -> Result<usize, SleError> {
42        if buf.len() < Self::SIZE {
43            return Err(SleError::BufferTooSmall);
44        }
45        buf[..Self::SIZE].copy_from_slice(&self.cds);
46        Ok(Self::SIZE)
47    }
48
49    /// Decodes a Time from the provided buffer.
50    /// Returns the Time and number of bytes consumed.
51    pub fn decode(buf: &[u8]) -> Result<(Self, usize), SleError> {
52        if buf.len() < Self::SIZE {
53            return Err(SleError::BufferTooSmall);
54        }
55        let mut cds = [0u8; 8];
56        cds.copy_from_slice(&buf[..Self::SIZE]);
57        Ok((Self { cds }, Self::SIZE))
58    }
59}
60
61/// SLE service types.
62#[derive(Copy, Clone, Debug, PartialEq, Eq)]
63#[repr(u8)]
64pub enum ServiceType {
65    /// Return All Frames — online (real-time) delivery.
66    RafOnline = 0,
67    /// Return All Frames — offline (deferred) delivery.
68    RafOffline = 1,
69    /// Forward CLTU service.
70    FCltu = 2,
71}
72
73impl ServiceType {
74    /// Converts from an integer value.
75    pub fn from_u8(v: u8) -> Result<Self, SleError> {
76        match v {
77            0 => Ok(Self::RafOnline),
78            1 => Ok(Self::RafOffline),
79            2 => Ok(Self::FCltu),
80            _ => Err(SleError::InvalidEnumValue),
81        }
82    }
83}
84
85/// Result of a Bind operation.
86#[derive(Copy, Clone, Debug, PartialEq, Eq)]
87#[repr(u8)]
88pub enum BindResult {
89    /// Bind succeeded.
90    Success = 0,
91    /// Responder denied access (bad credentials).
92    AccessDenied = 1,
93    /// The requested service type is not available.
94    ServiceTypeNotSupported = 2,
95    /// The requested protocol version is not supported.
96    VersionNotSupported = 3,
97}
98
99impl BindResult {
100    /// Converts from an integer value.
101    pub fn from_u8(v: u8) -> Result<Self, SleError> {
102        match v {
103            0 => Ok(Self::Success),
104            1 => Ok(Self::AccessDenied),
105            2 => Ok(Self::ServiceTypeNotSupported),
106            3 => Ok(Self::VersionNotSupported),
107            _ => Err(SleError::InvalidEnumValue),
108        }
109    }
110}
111
112/// Identifies a specific service instance on the provider.
113///
114/// In SLE, service instances are identified by an Object Identifier
115/// (OID) path like `sagr=1.spack=1.rsl-fg=1.raf=onlt1`.
116/// We store the raw bytes of the BER-encoded identifier.
117#[derive(Clone, Debug, PartialEq, Eq)]
118pub struct ServiceInstanceId {
119    /// Raw BER-encoded service instance identifier.
120    /// Max 64 bytes should be enough for any real identifier.
121    buf: [u8; 64],
122    /// Number of valid bytes in `buf`.
123    len: usize,
124}
125
126impl ServiceInstanceId {
127    /// Maximum encoded length.
128    pub const MAX_LEN: usize = 64;
129
130    /// Creates a new ServiceInstanceId from raw bytes.
131    pub fn from_bytes(data: &[u8]) -> Result<Self, SleError> {
132        if data.len() > Self::MAX_LEN {
133            return Err(SleError::BufferTooSmall);
134        }
135        let mut buf = [0u8; Self::MAX_LEN];
136        buf[..data.len()].copy_from_slice(data);
137        Ok(Self {
138            buf,
139            len: data.len(),
140        })
141    }
142
143    /// Returns the raw identifier bytes.
144    pub fn as_bytes(&self) -> &[u8] {
145        &self.buf[..self.len]
146    }
147
148    /// Returns the length of the identifier.
149    pub fn len(&self) -> usize {
150        self.len
151    }
152
153    /// Returns true if the identifier is empty.
154    pub fn is_empty(&self) -> bool {
155        self.len == 0
156    }
157}
158
159/// Errors from SLE encoding/decoding.
160#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
161pub enum SleError {
162    /// Output buffer is too small.
163    #[error("buffer too small")]
164    BufferTooSmall,
165    /// Input data is truncated or malformed.
166    #[error("truncated or malformed input")]
167    Truncated,
168    /// Invalid BER tag encountered.
169    #[error("unexpected BER tag")]
170    UnexpectedTag,
171    /// Invalid enum discriminant.
172    #[error("invalid enum value")]
173    InvalidEnumValue,
174    /// Integer value out of range.
175    #[error("integer out of range")]
176    IntegerOverflow,
177    /// String or identifier exceeds maximum length.
178    #[error("value too long")]
179    TooLong,
180    /// Missing required field in a PDU.
181    #[error("missing required field")]
182    MissingField,
183}