1use emcyphal_core::{NodeId, Priority, ServiceId, SubjectId};
4
5use crate::time::Instant;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
9#[cfg_attr(feature = "defmt", derive(defmt::Format))]
10pub enum Mtu {
11 Classic,
12 Fd,
13}
14
15impl From<Mtu> for usize {
16 fn from(value: Mtu) -> Self {
17 match value {
18 Mtu::Classic => 8,
19 Mtu::Fd => 64,
20 }
21 }
22}
23
24#[derive(Debug)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct IncorrectMtu;
27
28impl TryFrom<usize> for Mtu {
29 type Error = IncorrectMtu;
30 fn try_from(value: usize) -> Result<Self, Self::Error> {
31 match value {
32 8 => Ok(Mtu::Classic),
33 64 => Ok(Mtu::Fd),
34 _ => Err(IncorrectMtu),
35 }
36 }
37}
38
39#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub enum DataSpecifier {
43 Message(SubjectId),
44 Request(ServiceId),
45 Response(ServiceId),
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[cfg_attr(feature = "defmt", derive(defmt::Format))]
51pub struct Header {
52 pub priority: Priority,
53 pub data_spec: DataSpecifier,
54 pub source: Option<NodeId>,
55 pub destination: Option<NodeId>,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63pub struct Frame {
64 pub header: Header,
65 pub data: Data,
66 pub timestamp: Instant,
67 pub loop_back: bool,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76pub struct DataLength(u8);
77
78impl DataLength {
79 pub const MAX: usize = 64;
80
81 pub const fn new(value: usize) -> Option<Self> {
82 let floor = Self::new_floor(value);
83 if floor.as_usize() == value {
84 Some(floor)
85 } else {
86 None
87 }
88 }
89
90 pub const fn new_floor(value: usize) -> Self {
91 let floor = match value {
92 0..8 => value,
93 8..24 => value / 4 * 4,
94 24..32 => value / 8 * 8,
95 32..64 => value / 16 * 16,
96 64.. => 64,
97 };
98 Self(floor as u8)
99 }
100
101 pub const fn new_ceil(value: usize) -> Option<Self> {
102 if value <= Self::MAX {
103 let ceil = match value {
104 0..8 => value,
105 8..24 => value.div_ceil(4) * 4,
106 24..32 => value.div_ceil(8) * 8,
107 32.. => value.div_ceil(16) * 16,
108 };
109 Some(Self(ceil as u8))
110 } else {
111 None
112 }
113 }
114
115 pub const fn as_usize(&self) -> usize {
116 self.0 as usize
117 }
118}
119
120impl From<DataLength> for usize {
121 fn from(value: DataLength) -> Self {
122 value.as_usize()
123 }
124}
125
126#[derive(Debug)]
127#[cfg_attr(feature = "defmt", derive(defmt::Format))]
128pub struct InvalidLength;
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135#[cfg_attr(feature = "defmt", derive(defmt::Format))]
136pub struct Data {
137 length: DataLength,
138 bytes: [u8; 64],
139}
140
141impl Data {
142 pub fn new(data: &[u8]) -> Result<Self, InvalidLength> {
144 let length = DataLength::new(data.len()).ok_or(InvalidLength)?;
145 let mut bytes = [0; 64];
146 bytes[..data.len()].copy_from_slice(data);
147
148 Ok(Self { length, bytes })
149 }
150
151 pub fn new_zeros(length: DataLength) -> Self {
152 Self {
153 length,
154 bytes: [0; 64],
155 }
156 }
157
158 pub fn length(&self) -> DataLength {
159 self.length
160 }
161}
162
163impl core::ops::Deref for Data {
164 type Target = [u8];
165
166 fn deref(&self) -> &Self::Target {
167 &self.bytes[..usize::from(self.length)]
168 }
169}
170
171impl core::ops::DerefMut for Data {
172 fn deref_mut(&mut self) -> &mut Self::Target {
173 &mut self.bytes[..usize::from(self.length)]
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 const VALID_CAN_LENGTH: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64];
182
183 fn ceil_length_ref(value: usize) -> Option<usize> {
184 match VALID_CAN_LENGTH.binary_search(&value) {
185 Ok(pos) => Some(VALID_CAN_LENGTH[pos]),
186 Err(pos) => {
187 if pos < VALID_CAN_LENGTH.len() {
188 Some(VALID_CAN_LENGTH[pos])
189 } else {
190 None
191 }
192 }
193 }
194 }
195
196 fn floor_length_ref(value: usize) -> usize {
197 match VALID_CAN_LENGTH.binary_search(&value) {
198 Ok(pos) => VALID_CAN_LENGTH[pos],
199 Err(pos) => VALID_CAN_LENGTH[pos - 1],
200 }
201 }
202
203 #[test]
204 fn test_frame_length() {
205 for len in 0usize..100 {
206 assert_eq!(
207 usize::from(DataLength::new_floor(len)),
208 floor_length_ref(len)
209 );
210 assert_eq!(
211 DataLength::new_ceil(len).map(|len| usize::from(len)),
212 ceil_length_ref(len)
213 );
214 }
215 }
216}