milstd1553b_parser/
message.rs

1//! Message types and structures for MIL-STD-1553B protocol
2
3use crate::core::{Address, Word, WordType};
4use crate::error::{ParseError, Result};
5
6/// Sub-address for Read/Write operations
7#[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    /// Create a new sub-address (0-31)
13    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    /// Get the raw sub-address value
24    pub fn value(&self) -> u8 {
25        self.0
26    }
27}
28
29/// Command type in a command word
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub enum CommandType {
33    /// Transmit (Remote Terminal sends data)
34    Transmit,
35    /// Receive (Remote Terminal receives data)
36    Receive,
37}
38
39/// Mode code command (special commands sent to specific addresses)
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42pub enum ModeCode {
43    /// Synchronize (broadcast mode code)
44    Synchronize = 0,
45    /// Transmit Status Word
46    TransmitStatusWord = 1,
47    /// Initiate Self Test
48    InitiateSelfTest = 2,
49    /// Transmit Last Command Word
50    TransmitLastCommandWord = 3,
51    /// Transmit Built-In Test Result
52    TransmitBuiltInTestResult = 4,
53    /// Synchronize (alternate)
54    SynchronizeAlt = 5,
55    /// Transmit Vector Word
56    TransmitVectorWord = 6,
57    /// Synchronize (alternate 2)
58    SynchronizeAlt2 = 7,
59    /// Transmit Last Data Word
60    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/// A MIL-STD-1553B command word
86///
87/// Format:
88/// - Bits 19-16: Address (0-31)
89/// - Bit 15: Transmit/Receive flag
90/// - Bits 14-10: Sub-address or Mode Code
91/// - Bits 9-0: Data word count or mode code data
92#[derive(Debug, Clone, PartialEq, Eq)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
94pub struct Command {
95    /// Address of the target device
96    pub address: Address,
97    /// Transmit or Receive
98    pub command_type: CommandType,
99    /// Sub-address (5 bits)
100    pub sub_address: SubAddress,
101    /// Data word count (10 bits, 0-20, 0 means 32 words)
102    pub word_count: u16,
103}
104
105impl Command {
106    /// Create a new command
107    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    /// Encode command as a word
129    pub fn to_word(&self) -> Result<Word> {
130        let mut word = 0u32;
131
132        // Address (bits 15-12, occupying the high nibble of data)
133        word |= (self.address.value() as u32 & 0x0F) << 12;
134
135        // Transmit/Receive bit (bit 11)
136        word |= match self.command_type {
137            CommandType::Transmit => 0x0800,
138            CommandType::Receive => 0x0000,
139        };
140
141        // Sub-address (bits 10-6)
142        word |= (self.sub_address.value() as u32 & 0x1F) << 6;
143
144        // Word count (bits 5-0)
145        word |= (self.word_count & 0x3F) as u32;
146
147        // Shift to data position (bits 16-1) and add parity
148        let data_in_position = word << 1; // Now in bits 16-1
149        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    /// Decode command from a word
156    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; // Remove start bit
164        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/// A MIL-STD-1553B status word
183///
184/// Format (from Remote Terminal):
185/// - Bits 19-16: Address
186/// - Bits 15-11: Status flags
187/// - Bits 10-0: Message error code (11 bits)
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190pub struct StatusWord {
191    /// Address of the responding device
192    pub address: Address,
193    /// Status flags
194    pub flags: StatusFlags,
195    /// Message error code
196    pub error_code: u16,
197}
198
199/// Status flags in a MIL-STD-1553B status word
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
201#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
202pub struct StatusFlags {
203    /// Reserved flag
204    pub reserved: bool,
205    /// Subsystem flag
206    pub subsystem_flag: bool,
207    /// Busy flag
208    pub busy: bool,
209    /// BCast (broadcast) flag
210    pub broadcast: bool,
211    /// Parity error flag
212    pub parity_error: bool,
213}
214
215impl StatusFlags {
216    /// Create a new status flags struct
217    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    /// Encode flags as bits
228    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    /// Decode flags from bits
249    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    /// Create a new status word
262    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    /// Encode status word as a word
278    pub fn to_word(&self) -> Result<Word> {
279        let mut word = 0u32;
280
281        // Address (bits 15-12)
282        word |= (self.address.value() as u32 & 0x0F) << 12;
283
284        // Status flags (bits 11-7)
285        word |= (self.flags.encode() as u32 & 0x1F) << 7;
286
287        // Error code (bits 6-0)
288        word |= (self.error_code & 0x7F) as u32;
289
290        // Shift to data position (bits 16-1) and add parity
291        let data_in_position = word << 1; // Now in bits 16-1
292        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    /// Decode status word from a word
299    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; // Remove start bit
307        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/// A complete message in MIL-STD-1553B protocol
320#[derive(Debug, Clone, PartialEq, Eq)]
321#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
322pub enum Message {
323    /// Command followed by optional data words
324    CommandData {
325        command: Command,
326        data_words: Vec<Word>,
327    },
328    /// Status word response
329    Status(StatusWord),
330    /// Just a command word (for transmit commands)
331    CommandOnly(Command),
332}
333
334impl Message {
335    /// Get the address associated with this message
336    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    /// Get the number of data words if present
345    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        // Error code limited to 7 bits (0-127) due to word structure
384        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}