1pub fn crc(data: &[u8]) -> u16 {
3 let mut crc = 0xffff;
4
5 for &byte in data {
6 crc ^= u16::from(byte);
7 for _ in 0..8 {
8 if (crc & 0x0001) != 0 {
9 crc >>= 1;
10 crc ^= 0xa001;
11 } else {
12 crc >>= 1;
13 }
14 }
15 }
16
17 crc
18}
19
20macro_rules! modbus_message_impl {
22 ($function_code:expr, $($field_name:ident: $field_type:ty),*) => {
23 $crate::util::modbus_message_impl!($function_code, $($field_name: $field_type),*; );
24 };
25 ($function_code:expr, $($field_name:ident: $field_type:ty),*; $($prologue:stmt)*) => {
26 #[allow(dead_code)]
27 pub(crate) const FUNCTION: u8 = $function_code;
28
29 #[allow(dead_code)]
30 #[inline]
31 fn new_inner(addr: u8, $($field_name: $field_type),*) -> Self {
32 $($prologue)*
33
34 let mut message = Self {
35 addr,
36 function: $function_code,
37 $(
38 $field_name,
39 )*
40 crc: Default::default(),
41 };
42
43 message.crc = message.calculate_crc().into();
44 message
45 }
46
47 #[allow(dead_code)]
48 fn new_with_inner(addr: u8, f: impl FnOnce(&mut Self)) -> Self {
49 $($prologue)*
50
51 let mut message = <Self as zerocopy::FromZeros>::new_zeroed();
52 message.addr = addr;
53 message.function = $function_code;
54 f(&mut message);
55 message.crc = message.calculate_crc().into();
56 message
57 }
58
59 pub(crate) fn calculate_crc(&self) -> u16 {
60 let bytes = self.as_bytes();
61 crate::crc(&bytes[..bytes.len() - 2])
63 }
64
65 pub fn validate_crc(&self) -> Result<(), $crate::CrcError> {
67 if self.crc.get() == self.calculate_crc() {
68 Ok(())
69 } else {
70 Err($crate::CrcError)
71 }
72 }
73
74 pub fn update_crc(&mut self) {
76 self.crc = self.calculate_crc().into();
77 }
78
79 pub fn address(&self) -> u8 {
81 self.addr
82 }
83
84 pub(crate) fn function(&self) -> u8 {
86 self.function
87 }
88 };
89}
90
91macro_rules! modbus_message {
93 (
94 $(#[$outer:meta])*
95 $name:ident {
96 function_code: $function_code:expr,
97 $(
98 $field_name:ident: $field_type:ty
99 ),* $(,)?
100 }
101 ) => {
102 $(#[$outer])*
103 #[derive(IntoBytes, Immutable, FromBytes, KnownLayout)]
104 #[repr(C)]
105 pub struct $name {
106 addr: u8,
107 function: u8,
108 $(
109 pub(crate) $field_name: $field_type,
110 )*
111 crc: zerocopy::little_endian::U16,
112 }
113
114 impl $name {
115 $crate::util::modbus_message_impl!($function_code, $($field_name: $field_type),*);
116 }
117 };
118
119 (
121 $(#[$outer:meta])*
122 $name:ident<const N: usize> {
123 function_code: $function_code:expr,
124 $(
125 $field_name:ident: $field_type:ty
126 ),* $(,)?
127 }
128 ) => {
129 $(#[$outer])*
130 #[derive(IntoBytes, Immutable, FromBytes, KnownLayout)]
131 #[repr(C)]
132 pub struct $name<const N: usize> {
133 addr: u8,
134 function: u8,
135 $(
136 pub(crate) $field_name: $field_type,
137 )*
138 crc: zerocopy::little_endian::U16,
139 }
140
141 impl<const N: usize> $name<N> {
142 $crate::util::modbus_message_impl!(
143 $function_code,
144 $($field_name: $field_type),*;
145 const { assert!(N <= 127, "N must be less than or equal to 127") }
146 );
147 }
148 };
149}
150
151pub(crate) use {modbus_message, modbus_message_impl};
152
153#[cfg(test)]
154mod tests {
155 use hex_literal::hex;
156
157 #[test]
158 fn crc() {
159 assert_eq!(super::crc(&hex!("00 06 00 00 00 17")), 0x15c8);
160 }
161}