1use crate::core::{Address, Word, WordType};
4use crate::error::{ParseError, Result};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct SubAddress(u8);
10
11impl SubAddress {
12 pub fn new(addr: u8) -> Result<Self> {
14 if addr > 31 {
15 return Err(ParseError::invalid_address(format!(
16 "Sub-address {} out of range [0, 31]",
17 addr
18 )));
19 }
20 Ok(SubAddress(addr))
21 }
22
23 pub fn value(&self) -> u8 {
25 self.0
26 }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub enum CommandType {
33 Transmit,
35 Receive,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42pub enum ModeCode {
43 Synchronize = 0,
45 TransmitStatusWord = 1,
47 InitiateSelfTest = 2,
49 TransmitLastCommandWord = 3,
51 TransmitBuiltInTestResult = 4,
53 SynchronizeAlt = 5,
55 TransmitVectorWord = 6,
57 SynchronizeAlt2 = 7,
59 TransmitLastDataWord = 8,
61}
62
63impl TryFrom<u8> for ModeCode {
64 type Error = ParseError;
65
66 fn try_from(value: u8) -> Result<Self> {
67 match value {
68 0 => Ok(ModeCode::Synchronize),
69 1 => Ok(ModeCode::TransmitStatusWord),
70 2 => Ok(ModeCode::InitiateSelfTest),
71 3 => Ok(ModeCode::TransmitLastCommandWord),
72 4 => Ok(ModeCode::TransmitBuiltInTestResult),
73 5 => Ok(ModeCode::SynchronizeAlt),
74 6 => Ok(ModeCode::TransmitVectorWord),
75 7 => Ok(ModeCode::SynchronizeAlt2),
76 8 => Ok(ModeCode::TransmitLastDataWord),
77 _ => Err(ParseError::invalid_message_type(format!(
78 "Unknown mode code: {}",
79 value
80 ))),
81 }
82 }
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
94pub struct Command {
95 pub address: Address,
97 pub command_type: CommandType,
99 pub sub_address: SubAddress,
101 pub word_count: u16,
103}
104
105impl Command {
106 pub fn new(
108 address: Address,
109 command_type: CommandType,
110 sub_address: SubAddress,
111 word_count: u16,
112 ) -> Result<Self> {
113 if word_count > 32 {
114 return Err(ParseError::invalid_command(format!(
115 "Word count {} exceeds maximum of 32",
116 word_count
117 )));
118 }
119
120 Ok(Command {
121 address,
122 command_type,
123 sub_address,
124 word_count,
125 })
126 }
127
128 pub fn to_word(&self) -> Result<Word> {
130 let mut word = 0u32;
131
132 word |= (self.address.value() as u32 & 0x0F) << 12;
134
135 word |= match self.command_type {
137 CommandType::Transmit => 0x0800,
138 CommandType::Receive => 0x0000,
139 };
140
141 word |= (self.sub_address.value() as u32 & 0x1F) << 6;
143
144 word |= (self.word_count & 0x3F) as u32;
146
147 let data_in_position = word << 1; let parity = Word::calculate_parity(word as u16) as u32;
150 let final_word = data_in_position | (parity << 17);
151
152 Ok(Word::new_unchecked(final_word, WordType::Command))
153 }
154
155 pub fn from_word(word: &Word) -> Result<Self> {
157 if word.word_type() != WordType::Command {
158 return Err(ParseError::invalid_message_type(
159 "Expected command word".to_string(),
160 ));
161 }
162
163 let data = word.data() >> 1; let address = Address::new(((data >> 12) & 0x0F) as u8)?;
165 let command_type = if (data & 0x0800) != 0 {
166 CommandType::Transmit
167 } else {
168 CommandType::Receive
169 };
170 let sub_address = SubAddress::new(((data >> 6) & 0x1F) as u8)?;
171 let word_count = (data & 0x3F) as u16;
172
173 Ok(Command {
174 address,
175 command_type,
176 sub_address,
177 word_count: if word_count == 0 { 32 } else { word_count },
178 })
179 }
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190pub struct StatusWord {
191 pub address: Address,
193 pub flags: StatusFlags,
195 pub error_code: u16,
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq)]
201#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
202pub struct StatusFlags {
203 pub reserved: bool,
205 pub subsystem_flag: bool,
207 pub busy: bool,
209 pub broadcast: bool,
211 pub parity_error: bool,
213}
214
215impl StatusFlags {
216 pub fn new(reserved: bool, subsystem: bool, busy: bool, broadcast: bool, parity: bool) -> Self {
218 StatusFlags {
219 reserved,
220 subsystem_flag: subsystem,
221 busy,
222 broadcast,
223 parity_error: parity,
224 }
225 }
226
227 fn encode(&self) -> u8 {
229 let mut flags = 0u8;
230 if self.reserved {
231 flags |= 0x10;
232 }
233 if self.subsystem_flag {
234 flags |= 0x08;
235 }
236 if self.busy {
237 flags |= 0x04;
238 }
239 if self.broadcast {
240 flags |= 0x02;
241 }
242 if self.parity_error {
243 flags |= 0x01;
244 }
245 flags
246 }
247
248 fn decode(bits: u8) -> Self {
250 StatusFlags {
251 reserved: (bits & 0x10) != 0,
252 subsystem_flag: (bits & 0x08) != 0,
253 busy: (bits & 0x04) != 0,
254 broadcast: (bits & 0x02) != 0,
255 parity_error: (bits & 0x01) != 0,
256 }
257 }
258}
259
260impl StatusWord {
261 pub fn new(address: Address, flags: StatusFlags, error_code: u16) -> Result<Self> {
263 if error_code > 0x7FF {
264 return Err(ParseError::invalid_response(format!(
265 "Error code {} exceeds 11 bits",
266 error_code
267 )));
268 }
269
270 Ok(StatusWord {
271 address,
272 flags,
273 error_code,
274 })
275 }
276
277 pub fn to_word(&self) -> Result<Word> {
279 let mut word = 0u32;
280
281 word |= (self.address.value() as u32 & 0x0F) << 12;
283
284 word |= (self.flags.encode() as u32 & 0x1F) << 7;
286
287 word |= (self.error_code & 0x7F) as u32;
289
290 let data_in_position = word << 1; let parity = Word::calculate_parity(word as u16) as u32;
293 let final_word = data_in_position | (parity << 17);
294
295 Ok(Word::new_unchecked(final_word, WordType::Status))
296 }
297
298 pub fn from_word(word: &Word) -> Result<Self> {
300 if word.word_type() != WordType::Status {
301 return Err(ParseError::invalid_message_type(
302 "Expected status word".to_string(),
303 ));
304 }
305
306 let data = word.data() >> 1; let address = Address::new(((data >> 12) & 0x0F) as u8)?;
308 let flags = StatusFlags::decode(((data >> 7) & 0x1F) as u8);
309 let error_code = (data & 0x7F) as u16;
310
311 Ok(StatusWord {
312 address,
313 flags,
314 error_code,
315 })
316 }
317}
318
319#[derive(Debug, Clone, PartialEq, Eq)]
321#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
322pub enum Message {
323 CommandData {
325 command: Command,
326 data_words: Vec<Word>,
327 },
328 Status(StatusWord),
330 CommandOnly(Command),
332}
333
334impl Message {
335 pub fn address(&self) -> Address {
337 match self {
338 Message::CommandData { command, .. } => command.address,
339 Message::Status(status) => status.address,
340 Message::CommandOnly(command) => command.address,
341 }
342 }
343
344 pub fn data_word_count(&self) -> Option<usize> {
346 match self {
347 Message::CommandData { data_words, .. } => Some(data_words.len()),
348 _ => None,
349 }
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 #[test]
358 fn test_subaddress_creation() {
359 assert!(SubAddress::new(0).is_ok());
360 assert!(SubAddress::new(31).is_ok());
361 assert!(SubAddress::new(32).is_err());
362 }
363
364 #[test]
365 fn test_command_encode_decode() {
366 let cmd = Command::new(
367 Address::new(5).unwrap(),
368 CommandType::Transmit,
369 SubAddress::new(10).unwrap(),
370 16,
371 )
372 .unwrap();
373
374 let word = cmd.to_word().unwrap();
375 let decoded = Command::from_word(&word).unwrap();
376
377 assert_eq!(cmd, decoded);
378 }
379
380 #[test]
381 fn test_status_word_encode_decode() {
382 let flags = StatusFlags::new(false, true, false, false, false);
383 let status = StatusWord::new(Address::new(3).unwrap(), flags, 0x42).unwrap();
385
386 let word = status.to_word().unwrap();
387 let decoded = StatusWord::from_word(&word).unwrap();
388
389 assert_eq!(status, decoded);
390 }
391
392 #[test]
393 fn test_mode_code_conversion() {
394 let code: ModeCode = 1u8.try_into().unwrap();
395 assert_eq!(code, ModeCode::TransmitStatusWord);
396
397 let result: Result<ModeCode> = 99u8.try_into();
398 assert!(result.is_err());
399 }
400}