rustmod_core/pdu/
function_code.rs1use crate::DecodeError;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4#[cfg_attr(feature = "defmt", derive(defmt::Format))]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub enum FunctionCode {
7 ReadCoils,
8 ReadDiscreteInputs,
9 ReadHoldingRegisters,
10 ReadInputRegisters,
11 WriteSingleCoil,
12 WriteSingleRegister,
13 WriteMultipleCoils,
14 WriteMultipleRegisters,
15 MaskWriteRegister,
16 ReadWriteMultipleRegisters,
17 Custom(u8),
18}
19
20impl FunctionCode {
21 pub const fn as_u8(self) -> u8 {
22 match self {
23 Self::ReadCoils => 0x01,
24 Self::ReadDiscreteInputs => 0x02,
25 Self::ReadHoldingRegisters => 0x03,
26 Self::ReadInputRegisters => 0x04,
27 Self::WriteSingleCoil => 0x05,
28 Self::WriteSingleRegister => 0x06,
29 Self::WriteMultipleCoils => 0x0F,
30 Self::WriteMultipleRegisters => 0x10,
31 Self::MaskWriteRegister => 0x16,
32 Self::ReadWriteMultipleRegisters => 0x17,
33 Self::Custom(code) => code,
34 }
35 }
36
37 pub fn from_u8(value: u8) -> Result<Self, DecodeError> {
38 if Self::is_exception(value) {
39 return Err(DecodeError::InvalidFunctionCode);
40 }
41 match value {
42 0x01 => Ok(Self::ReadCoils),
43 0x02 => Ok(Self::ReadDiscreteInputs),
44 0x03 => Ok(Self::ReadHoldingRegisters),
45 0x04 => Ok(Self::ReadInputRegisters),
46 0x05 => Ok(Self::WriteSingleCoil),
47 0x06 => Ok(Self::WriteSingleRegister),
48 0x0F => Ok(Self::WriteMultipleCoils),
49 0x10 => Ok(Self::WriteMultipleRegisters),
50 0x16 => Ok(Self::MaskWriteRegister),
51 0x17 => Ok(Self::ReadWriteMultipleRegisters),
52 _ => Ok(Self::Custom(value)),
53 }
54 }
55
56 pub const fn is_exception(value: u8) -> bool {
57 (value & 0x80) != 0
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::FunctionCode;
64 use crate::DecodeError;
65
66 #[test]
67 fn parses_known_codes() {
68 assert_eq!(FunctionCode::from_u8(0x03).unwrap(), FunctionCode::ReadHoldingRegisters);
69 assert_eq!(FunctionCode::from_u8(0x10).unwrap(), FunctionCode::WriteMultipleRegisters);
70 assert_eq!(FunctionCode::from_u8(0x16).unwrap(), FunctionCode::MaskWriteRegister);
71 assert_eq!(
72 FunctionCode::from_u8(0x17).unwrap(),
73 FunctionCode::ReadWriteMultipleRegisters
74 );
75 }
76
77 #[test]
78 fn preserves_custom_codes() {
79 assert_eq!(FunctionCode::from_u8(0x41).unwrap(), FunctionCode::Custom(0x41));
80 }
81
82 #[test]
83 fn rejects_exception_bit_codes() {
84 assert_eq!(FunctionCode::from_u8(0x83).unwrap_err(), DecodeError::InvalidFunctionCode);
85 }
86
87 #[test]
88 fn exception_bit_is_detected() {
89 assert!(FunctionCode::is_exception(0x83));
90 assert!(!FunctionCode::is_exception(0x03));
91 }
92}