1use crate::errors::MbusError;
13use crate::function_codes::public::FunctionCode;
14use crate::transport::{SerialMode, TransportType, UnitIdOrSlaveAddr, checksum};
15use heapless::Vec;
16
17pub const MAX_PDU_DATA_LEN: usize = 252;
19
20pub const MODBUS_PROTOCOL_ID: u16 = 0x0000;
22
23pub const MAX_ADU_FRAME_LEN_TCP_RTU: usize = 260;
25
26pub const MAX_ADU_FRAME_LEN_ASCII: usize = 513;
28
29#[cfg(feature = "serial-ascii")]
34pub const MAX_ADU_FRAME_LEN: usize = MAX_ADU_FRAME_LEN_ASCII;
35
36#[cfg(not(feature = "serial-ascii"))]
38pub const MAX_ADU_FRAME_LEN: usize = MAX_ADU_FRAME_LEN_TCP_RTU;
39
40#[cfg(test)]
41mod frame_len_tests {
42 use super::*;
43
44 #[test]
45 #[cfg(feature = "serial-ascii")]
46 fn test_max_adu_frame_len_ascii_enabled() {
47 assert_eq!(MAX_ADU_FRAME_LEN, 513);
48 }
49
50 #[test]
51 #[cfg(not(feature = "serial-ascii"))]
52 fn test_max_adu_frame_len_ascii_disabled() {
53 assert_eq!(MAX_ADU_FRAME_LEN, 260);
54 }
55}
56
57pub const MBAP_HEADER_SIZE: usize = 7;
59
60pub const MIN_RTU_ADU_LEN: usize = 4;
62
63pub const MIN_ASCII_ADU_LEN: usize = 9;
65
66pub const RTU_CRC_SIZE: usize = 2;
68
69pub const MBAP_TXN_ID_OFFSET_1B: usize = 0;
71pub const MBAP_TXN_ID_OFFSET_2B: usize = MBAP_TXN_ID_OFFSET_1B + 1;
73pub const MBAP_PROTO_ID_OFFSET_1B: usize = 2;
75pub const MBAP_PROTO_ID_OFFSET_2B: usize = MBAP_PROTO_ID_OFFSET_1B + 1;
77pub const MBAP_LENGTH_OFFSET_1B: usize = 4;
79pub const MBAP_LENGTH_OFFSET_2B: usize = MBAP_LENGTH_OFFSET_1B + 1;
81pub const MBAP_UNIT_ID_OFFSET: usize = 6;
83
84pub const ASCII_START_SIZE: usize = 1;
86
87pub const ASCII_END_SIZE: usize = 2;
89
90pub const ERROR_BIT_MASK: u8 = 0x80;
92
93pub const FUNCTION_CODE_MASK: u8 = 0x7F;
95
96pub const PDU_ADDRESS_OFFSET_1B: usize = 0;
98pub const PDU_ADDRESS_OFFSET_2B: usize = PDU_ADDRESS_OFFSET_1B + 1;
100pub const PDU_QUANTITY_OFFSET_1B: usize = 2;
102pub const PDU_QUANTITY_OFFSET_2B: usize = PDU_QUANTITY_OFFSET_1B + 1;
104
105pub const PDU_AND_MASK_OFFSET_1B: usize = 2;
107pub const PDU_AND_MASK_OFFSET_2B: usize = PDU_AND_MASK_OFFSET_1B + 1;
109pub const PDU_OR_MASK_OFFSET_1B: usize = 4;
111pub const PDU_OR_MASK_OFFSET_2B: usize = PDU_OR_MASK_OFFSET_1B + 1;
113
114pub const PDU_BYTE_COUNT_OFFSET: usize = 4;
116
117pub const PDU_FC17_WRITE_ADDRESS_OFFSET_1B: usize = 4;
119pub const PDU_FC17_WRITE_ADDRESS_OFFSET_2B: usize = PDU_FC17_WRITE_ADDRESS_OFFSET_1B + 1;
121pub const PDU_FC17_WRITE_QUANTITY_OFFSET_1B: usize = 6;
123pub const PDU_FC17_WRITE_QUANTITY_OFFSET_2B: usize = PDU_FC17_WRITE_QUANTITY_OFFSET_1B + 1;
125pub const PDU_FC17_WRITE_BYTE_COUNT_OFFSET: usize = 8;
127pub const PDU_FC17_WRITE_VALUES_OFFSET: usize = 9;
129
130pub const PDU_SUB_FUNCTION_OFFSET_1B: usize = 0;
132pub const PDU_SUB_FUNCTION_OFFSET_2B: usize = 1;
134
135pub const PDU_MEI_TYPE_OFFSET: usize = 0;
137pub const PDU_MEI_READ_CODE_OFFSET: usize = 1;
139pub const PDU_MEI_CONFORMITY_LEVEL_OFFSET: usize = 2;
141pub const PDU_MEI_MORE_FOLLOWS_OFFSET: usize = 3;
143pub const PDU_MEI_NEXT_OBJECT_ID_OFFSET: usize = 4;
145pub const PDU_MEI_NUM_OBJECTS_OFFSET: usize = 5;
147pub const PDU_MEI_OBJECTS_DATA_OFFSET: usize = 6;
149
150pub const PDU_FIFO_BYTE_COUNT_OFFSET_1B: usize = 0;
152pub const PDU_FIFO_BYTE_COUNT_OFFSET_2B: usize = 1;
154pub const PDU_FIFO_COUNT_OFFSET_1B: usize = 2;
156pub const PDU_FIFO_COUNT_OFFSET_2B: usize = 3;
158pub const PDU_FIFO_VALUES_OFFSET: usize = 4;
160
161#[inline]
169pub fn is_exception_code(function_code_byte: u8) -> bool {
170 function_code_byte & ERROR_BIT_MASK != 0
171}
172
173#[inline]
181pub fn clear_exception_bit(function_code_byte: u8) -> u8 {
182 function_code_byte & FUNCTION_CODE_MASK
183}
184
185#[derive(Debug, Clone)]
190pub struct Pdu {
191 function_code: FunctionCode,
193 error_code: Option<u8>,
195 data: heapless::Vec<u8, MAX_PDU_DATA_LEN>,
197 data_len: u8,
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub struct ReadWindow {
204 pub address: u16,
206 pub quantity: u16,
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub struct WriteSingleU16Fields {
213 pub address: u16,
215 pub value: u16,
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221pub struct WriteMultipleFields<'a> {
222 pub address: u16,
224 pub quantity: u16,
226 pub byte_count: u8,
228 pub values: &'a [u8],
230}
231
232#[derive(Debug, Clone, Copy, PartialEq, Eq)]
234pub struct MaskWriteRegisterFields {
235 pub address: u16,
237 pub and_mask: u16,
239 pub or_mask: u16,
241}
242
243#[derive(Debug, Clone, Copy, PartialEq, Eq)]
245pub struct ByteCountPayload<'a> {
246 pub byte_count: u8,
248 pub payload: &'a [u8],
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq)]
254pub struct ReadWriteMultipleFields<'a> {
255 pub read_address: u16,
257 pub read_quantity: u16,
259 pub write_address: u16,
261 pub write_quantity: u16,
263 pub write_byte_count: u8,
265 pub write_values: &'a [u8],
267}
268
269#[derive(Debug, Clone, Copy, PartialEq, Eq)]
271pub struct SubFunctionPayload<'a> {
272 pub sub_function: u16,
274 pub payload: &'a [u8],
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq)]
280pub struct U16PairFields {
281 pub first: u16,
283 pub second: u16,
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq)]
289pub struct MeiTypePayload<'a> {
290 pub mei_type_byte: u8,
292 pub payload: &'a [u8],
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub struct FifoPayload<'a> {
299 pub fifo_byte_count: u16,
301 pub fifo_count: u16,
303 pub values: &'a [u8],
305}
306
307#[derive(Debug, Clone, PartialEq, Eq)]
312pub struct ReadDeviceIdPduFields {
313 pub mei_type_byte: u8,
315 pub read_device_id_code_byte: u8,
317 pub conformity_level_byte: u8,
319 pub more_follows: bool,
321 pub next_object_id_byte: u8,
323 pub number_of_objects: u8,
325 pub objects_data: [u8; MAX_PDU_DATA_LEN],
327 pub payload_len: usize,
329}
330
331#[derive(Debug, Clone, Copy)]
333pub struct MbapHeader {
334 pub transaction_id: u16,
336 pub protocol_id: u16,
338 pub length: u16,
340 pub unit_id: u8,
342}
343
344impl MbapHeader {
345 pub fn new(transaction_id: u16, length: u16, unit_id: u8) -> Self {
356 Self {
357 transaction_id,
358 protocol_id: 0, length,
360 unit_id,
361 }
362 }
363}
364
365#[derive(Debug, Clone, Copy, PartialEq, Eq)]
367pub struct SlaveAddress(u8);
368
369impl SlaveAddress {
370 pub fn new(address: u8) -> Result<Self, MbusError> {
372 if !(0..=247).contains(&address) {
373 return Err(MbusError::InvalidSlaveAddress);
374 }
375 Ok(Self(address))
376 }
377
378 pub fn address(&self) -> u8 {
380 self.0
381 }
382}
383
384#[derive(Debug, Clone, Copy)]
386#[allow(dead_code)]
387pub enum AdditionalAddress {
388 MbapHeader(MbapHeader),
390 SlaveAddress(SlaveAddress),
392}
393
394#[derive(Debug, Clone)]
396pub struct ModbusMessage {
397 pub additional_address: AdditionalAddress,
399 pub pdu: Pdu,
401 }
403
404impl ModbusMessage {
405 pub fn new(additional_address: AdditionalAddress, pdu: Pdu) -> Self {
414 Self {
415 additional_address,
416 pdu,
417 }
418 }
419
420 pub fn additional_address(&self) -> &AdditionalAddress {
422 &self.additional_address
423 }
424
425 pub fn pdu(&self) -> &Pdu {
431 &self.pdu
432 }
433
434 pub fn unit_id_or_slave_addr(&self) -> UnitIdOrSlaveAddr {
442 match self.additional_address {
443 AdditionalAddress::MbapHeader(header) => match header.unit_id {
444 0 => UnitIdOrSlaveAddr::new_broadcast_address(),
445 unit_id => {
446 UnitIdOrSlaveAddr::try_from(unit_id).unwrap_or(UnitIdOrSlaveAddr::default())
447 }
448 },
449 AdditionalAddress::SlaveAddress(slave_address) => match slave_address.address() {
450 0 => UnitIdOrSlaveAddr::new_broadcast_address(),
451 address => {
452 UnitIdOrSlaveAddr::try_from(address).unwrap_or(UnitIdOrSlaveAddr::default())
453 }
454 },
455 }
456 }
457
458 pub fn transaction_id(&self) -> u16 {
463 match self.additional_address {
464 AdditionalAddress::MbapHeader(header) => header.transaction_id,
465 AdditionalAddress::SlaveAddress(_) => 0,
466 }
467 }
468
469 pub fn function_code(&self) -> FunctionCode {
471 self.pdu.function_code()
472 }
473
474 pub fn data(&self) -> &heapless::Vec<u8, MAX_PDU_DATA_LEN> {
476 self.pdu.data()
477 }
478
479 pub fn data_len(&self) -> u8 {
481 self.pdu.data_len()
482 }
483
484 pub fn to_bytes(&self) -> Result<Vec<u8, MAX_ADU_FRAME_LEN>, MbusError> {
493 let mut adu_bytes = Vec::new();
494
495 match &self.additional_address {
496 AdditionalAddress::MbapHeader(header) => {
497 adu_bytes
499 .extend_from_slice(&header.transaction_id.to_be_bytes())
500 .map_err(|_| MbusError::Unexpected)?;
501 adu_bytes
502 .extend_from_slice(&header.protocol_id.to_be_bytes())
503 .map_err(|_| MbusError::Unexpected)?;
504 adu_bytes
505 .extend_from_slice(&header.length.to_be_bytes())
506 .map_err(|_| MbusError::Unexpected)?;
507 adu_bytes
508 .push(header.unit_id)
509 .map_err(|_| MbusError::Unexpected)?;
510 }
511 AdditionalAddress::SlaveAddress(address) => {
512 adu_bytes
513 .push(address.address())
514 .map_err(|_| MbusError::Unexpected)?;
515 }
516 }
517
518 let pdu_bytes = self.pdu.to_bytes()?;
519 adu_bytes
520 .extend_from_slice(&pdu_bytes)
521 .map_err(|_| MbusError::Unexpected)?;
522
523 Ok(adu_bytes)
524 }
525
526 pub fn from_bytes(bytes: &[u8]) -> Result<Self, MbusError> {
536 if bytes.len() < MBAP_HEADER_SIZE + 1 {
538 return Err(MbusError::InvalidAduLength); }
540
541 let transaction_id =
544 u16::from_be_bytes([bytes[MBAP_TXN_ID_OFFSET_1B], bytes[MBAP_TXN_ID_OFFSET_2B]]);
545 let protocol_id = u16::from_be_bytes([
547 bytes[MBAP_PROTO_ID_OFFSET_1B],
548 bytes[MBAP_PROTO_ID_OFFSET_2B],
549 ]);
550 let length =
552 u16::from_be_bytes([bytes[MBAP_LENGTH_OFFSET_1B], bytes[MBAP_LENGTH_OFFSET_2B]]);
553 let unit_id = bytes[MBAP_UNIT_ID_OFFSET];
555
556 if protocol_id != MODBUS_PROTOCOL_ID {
558 return Err(MbusError::BasicParseError); }
560
561 const INITIAL_FRAME_LEN: usize = MBAP_HEADER_SIZE - 1; let expected_total_len_from_header = length as usize + INITIAL_FRAME_LEN; if bytes.len() < expected_total_len_from_header {
570 return Err(MbusError::InvalidPduLength);
571 }
572
573 let frame_bytes = &bytes[..expected_total_len_from_header];
575 let pdu_bytes_slice = &frame_bytes[MBAP_HEADER_SIZE..];
577
578 let pdu = Pdu::from_bytes(pdu_bytes_slice)?;
580
581 let additional_addr = AdditionalAddress::MbapHeader(MbapHeader {
582 transaction_id,
583 protocol_id,
584 length,
585 unit_id,
586 });
587
588 Ok(ModbusMessage::new(additional_addr, pdu))
589 }
590
591 pub fn to_ascii_bytes(&self) -> Result<Vec<u8, MAX_ADU_FRAME_LEN>, MbusError> {
602 let mut binary_data = self.to_bytes()?;
603
604 let lrc = checksum::lrc(&binary_data);
606
607 binary_data
609 .push(lrc)
610 .map_err(|_| MbusError::BufferTooSmall)?;
611
612 let mut ascii_data = Vec::new();
613
614 ascii_data
616 .push(b':')
617 .map_err(|_| MbusError::BufferTooSmall)?;
618
619 for byte in binary_data {
620 let high = (byte >> 4) & 0x0F;
621 let low = byte & 0x0F;
622
623 ascii_data
624 .push(nibble_to_hex(high))
625 .map_err(|_| MbusError::BufferTooSmall)?;
626 ascii_data
627 .push(nibble_to_hex(low))
628 .map_err(|_| MbusError::BufferTooSmall)?;
629 }
630
631 ascii_data
633 .push(b'\r')
634 .map_err(|_| MbusError::BufferTooSmall)?;
635 ascii_data
636 .push(b'\n')
637 .map_err(|_| MbusError::BufferTooSmall)?;
638
639 Ok(ascii_data)
640 }
641
642 pub fn from_rtu_bytes(frame: &[u8]) -> Result<Self, MbusError> {
654 if frame.len() < MIN_RTU_ADU_LEN {
657 return Err(MbusError::InvalidAduLength);
658 }
659
660 let data_len = frame.len() - RTU_CRC_SIZE;
662 let data_to_check = &frame[..data_len];
663
664 let received_crc = u16::from_le_bytes([frame[data_len], frame[data_len + 1]]);
666 let calculated_crc = checksum::crc16(data_to_check);
667
668 if calculated_crc != received_crc {
670 return Err(MbusError::ChecksumError); }
672
673 let slave_address = SlaveAddress::new(frame[0])?;
675
676 let pdu_bytes = &data_to_check[1..];
678 let pdu = Pdu::from_bytes(pdu_bytes)?;
679
680 Ok(ModbusMessage::new(
681 AdditionalAddress::SlaveAddress(slave_address),
682 pdu,
683 ))
684 }
685
686 pub fn from_ascii_bytes(frame: &[u8]) -> Result<Self, MbusError> {
701 if frame.len() < MIN_ASCII_ADU_LEN {
704 return Err(MbusError::InvalidAduLength);
705 }
706
707 if frame[0] != b':' {
709 return Err(MbusError::BasicParseError); }
711 if frame[frame.len() - 2] != b'\r' || frame[frame.len() - 1] != b'\n' {
712 return Err(MbusError::BasicParseError); }
714
715 let hex_content = &frame[ASCII_START_SIZE..frame.len() - ASCII_END_SIZE];
717 if !hex_content.len().is_multiple_of(2) {
718 return Err(MbusError::BasicParseError); }
720
721 let mut binary_data: Vec<u8, 260> = Vec::new();
724 for chunk in hex_content.chunks(2) {
725 let byte = hex_pair_to_byte(chunk[0], chunk[1])?;
726 binary_data
727 .push(byte)
728 .map_err(|_| MbusError::BufferTooSmall)?;
729 }
730
731 if binary_data.len() < 2 {
733 return Err(MbusError::InvalidAduLength);
734 }
735
736 let data_len = binary_data.len() - 1;
737 let data_to_check = &binary_data[..data_len];
738 let received_lrc = binary_data[data_len];
739
740 let calculated_lrc = checksum::lrc(data_to_check);
741
742 if calculated_lrc != received_lrc {
743 return Err(MbusError::ChecksumError); }
745
746 let slave_address = SlaveAddress::new(binary_data[0])?;
747 let pdu_bytes = &binary_data[1..data_len];
748 let pdu = Pdu::from_bytes(pdu_bytes)?;
749
750 Ok(ModbusMessage::new(
751 AdditionalAddress::SlaveAddress(slave_address),
752 pdu,
753 ))
754 }
755}
756
757impl Pdu {
758 pub fn new(
769 function_code: FunctionCode,
770 data: heapless::Vec<u8, MAX_PDU_DATA_LEN>,
771 data_len: u8,
772 ) -> Self {
773 Self {
774 function_code,
775 error_code: None, data, data_len,
778 }
779 }
780
781 pub fn function_code(&self) -> FunctionCode {
783 self.function_code
784 }
785
786 pub fn data(&self) -> &Vec<u8, MAX_PDU_DATA_LEN> {
788 &self.data
789 }
790
791 pub fn data_len(&self) -> u8 {
793 self.data_len
794 }
795
796 pub fn error_code(&self) -> Option<u8> {
798 self.error_code
799 }
800
801 pub fn build_read_window(
807 fc: FunctionCode,
808 address: u16,
809 quantity: u16,
810 ) -> Result<Self, MbusError> {
811 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
812 data.extend_from_slice(&address.to_be_bytes())
813 .map_err(|_| MbusError::BufferLenMissmatch)?;
814 data.extend_from_slice(&quantity.to_be_bytes())
815 .map_err(|_| MbusError::BufferLenMissmatch)?;
816 Ok(Pdu::new(fc, data, 4))
817 }
818
819 pub fn build_write_single_u16(
823 fc: FunctionCode,
824 address: u16,
825 value: u16,
826 ) -> Result<Self, MbusError> {
827 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
828 data.extend_from_slice(&address.to_be_bytes())
829 .map_err(|_| MbusError::BufferLenMissmatch)?;
830 data.extend_from_slice(&value.to_be_bytes())
831 .map_err(|_| MbusError::BufferLenMissmatch)?;
832 Ok(Pdu::new(fc, data, 4))
833 }
834
835 pub fn build_write_multiple(
842 fc: FunctionCode,
843 address: u16,
844 quantity: u16,
845 values: &[u8],
846 ) -> Result<Self, MbusError> {
847 let byte_count = values.len();
848 if byte_count > u8::MAX as usize {
849 return Err(MbusError::InvalidByteCount);
850 }
851 let data_len = 5 + byte_count;
852 if data_len > MAX_PDU_DATA_LEN {
853 return Err(MbusError::BufferTooSmall);
854 }
855 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
856 data.extend_from_slice(&address.to_be_bytes())
857 .map_err(|_| MbusError::BufferLenMissmatch)?;
858 data.extend_from_slice(&quantity.to_be_bytes())
859 .map_err(|_| MbusError::BufferLenMissmatch)?;
860 data.push(byte_count as u8)
861 .map_err(|_| MbusError::BufferLenMissmatch)?;
862 data.extend_from_slice(values)
863 .map_err(|_| MbusError::BufferLenMissmatch)?;
864 Ok(Pdu::new(fc, data, data_len as u8))
865 }
866
867 pub fn build_mask_write_register(
871 address: u16,
872 and_mask: u16,
873 or_mask: u16,
874 ) -> Result<Self, MbusError> {
875 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
876 data.extend_from_slice(&address.to_be_bytes())
877 .map_err(|_| MbusError::BufferLenMissmatch)?;
878 data.extend_from_slice(&and_mask.to_be_bytes())
879 .map_err(|_| MbusError::BufferLenMissmatch)?;
880 data.extend_from_slice(&or_mask.to_be_bytes())
881 .map_err(|_| MbusError::BufferLenMissmatch)?;
882 Ok(Pdu::new(FunctionCode::MaskWriteRegister, data, 6))
883 }
884
885 pub fn build_read_write_multiple(
890 read_address: u16,
891 read_quantity: u16,
892 write_address: u16,
893 write_quantity: u16,
894 write_values: &[u8],
895 ) -> Result<Self, MbusError> {
896 let byte_count = write_values.len();
897 if byte_count > u8::MAX as usize {
898 return Err(MbusError::InvalidByteCount);
899 }
900 let data_len = 9 + byte_count;
901 if data_len > MAX_PDU_DATA_LEN {
902 return Err(MbusError::BufferTooSmall);
903 }
904 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
905 data.extend_from_slice(&read_address.to_be_bytes())
906 .map_err(|_| MbusError::BufferLenMissmatch)?;
907 data.extend_from_slice(&read_quantity.to_be_bytes())
908 .map_err(|_| MbusError::BufferLenMissmatch)?;
909 data.extend_from_slice(&write_address.to_be_bytes())
910 .map_err(|_| MbusError::BufferLenMissmatch)?;
911 data.extend_from_slice(&write_quantity.to_be_bytes())
912 .map_err(|_| MbusError::BufferLenMissmatch)?;
913 data.push(byte_count as u8)
914 .map_err(|_| MbusError::BufferLenMissmatch)?;
915 data.extend_from_slice(write_values)
916 .map_err(|_| MbusError::BufferLenMissmatch)?;
917 Ok(Pdu::new(
918 FunctionCode::ReadWriteMultipleRegisters,
919 data,
920 data_len as u8,
921 ))
922 }
923
924 pub fn build_sub_function(
928 fc: FunctionCode,
929 sub_function: u16,
930 words: &[u16],
931 ) -> Result<Self, MbusError> {
932 let data_len = 2 + words.len() * 2;
933 if data_len > MAX_PDU_DATA_LEN {
934 return Err(MbusError::BufferTooSmall);
935 }
936 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
937 data.extend_from_slice(&sub_function.to_be_bytes())
938 .map_err(|_| MbusError::BufferLenMissmatch)?;
939 for &word in words {
940 data.extend_from_slice(&word.to_be_bytes())
941 .map_err(|_| MbusError::BufferLenMissmatch)?;
942 }
943 Ok(Pdu::new(fc, data, data_len as u8))
944 }
945
946 pub fn build_mei_type(
950 fc: FunctionCode,
951 mei_type: u8,
952 payload: &[u8],
953 ) -> Result<Self, MbusError> {
954 let data_len = 1 + payload.len();
955 if data_len > MAX_PDU_DATA_LEN {
956 return Err(MbusError::BufferTooSmall);
957 }
958 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
959 data.push(mei_type)
960 .map_err(|_| MbusError::BufferLenMissmatch)?;
961 data.extend_from_slice(payload)
962 .map_err(|_| MbusError::BufferLenMissmatch)?;
963 Ok(Pdu::new(fc, data, data_len as u8))
964 }
965
966 pub fn build_u16_payload(fc: FunctionCode, value: u16) -> Result<Self, MbusError> {
970 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
971 data.extend_from_slice(&value.to_be_bytes())
972 .map_err(|_| MbusError::BufferLenMissmatch)?;
973 Ok(Pdu::new(fc, data, 2))
974 }
975
976 pub fn build_byte_payload(fc: FunctionCode, byte: u8) -> Result<Self, MbusError> {
980 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
981 data.push(byte).map_err(|_| MbusError::BufferLenMissmatch)?;
982 Ok(Pdu::new(fc, data, 1))
983 }
984
985 pub fn build_empty(fc: FunctionCode) -> Self {
989 Pdu::new(fc, heapless::Vec::new(), 0)
990 }
991
992 pub fn build_byte_count_payload(fc: FunctionCode, payload: &[u8]) -> Result<Self, MbusError> {
999 let byte_count = u8::try_from(payload.len()).map_err(|_| MbusError::InvalidByteCount)?;
1000 let data_len = 1 + payload.len();
1001 if data_len > MAX_PDU_DATA_LEN {
1002 return Err(MbusError::BufferTooSmall);
1003 }
1004 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
1005 data.push(byte_count)
1006 .map_err(|_| MbusError::BufferLenMissmatch)?;
1007 data.extend_from_slice(payload)
1008 .map_err(|_| MbusError::BufferLenMissmatch)?;
1009 Ok(Pdu::new(fc, data, data_len as u8))
1010 }
1011
1012 #[cfg(feature = "diagnostics")]
1017 pub fn build_diagnostics(sub_function: u16, result: u16) -> Result<Self, MbusError> {
1018 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
1019 data.extend_from_slice(&sub_function.to_be_bytes())
1020 .map_err(|_| MbusError::BufferLenMissmatch)?;
1021 data.extend_from_slice(&result.to_be_bytes())
1022 .map_err(|_| MbusError::BufferLenMissmatch)?;
1023 Ok(Pdu::new(FunctionCode::Diagnostics, data, 4))
1024 }
1025
1026 #[cfg(feature = "fifo")]
1032 pub fn build_fifo_payload(app_payload: &[u8]) -> Result<Self, MbusError> {
1033 let byte_count =
1034 u16::try_from(app_payload.len()).map_err(|_| MbusError::InvalidByteCount)?;
1035 let data_len = 2 + app_payload.len();
1036 if data_len > MAX_PDU_DATA_LEN {
1037 return Err(MbusError::BufferTooSmall);
1038 }
1039 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
1040 data.extend_from_slice(&byte_count.to_be_bytes())
1041 .map_err(|_| MbusError::BufferLenMissmatch)?;
1042 data.extend_from_slice(app_payload)
1043 .map_err(|_| MbusError::BufferLenMissmatch)?;
1044 Ok(Pdu::new(FunctionCode::ReadFifoQueue, data, data_len as u8))
1045 }
1046
1047 #[inline]
1052 fn read_address_quantity_pair(&self) -> Result<(u16, u16), MbusError> {
1053 Ok((
1054 u16::from_be_bytes([
1055 self.data[PDU_ADDRESS_OFFSET_1B],
1056 self.data[PDU_ADDRESS_OFFSET_2B],
1057 ]),
1058 u16::from_be_bytes([
1059 self.data[PDU_QUANTITY_OFFSET_1B],
1060 self.data[PDU_QUANTITY_OFFSET_2B],
1061 ]),
1062 ))
1063 }
1064
1065 pub fn read_window(&self) -> Result<ReadWindow, MbusError> {
1069 if self.data_len != 4 {
1070 return Err(MbusError::InvalidPduLength);
1071 }
1072
1073 let (address, quantity) = self.read_address_quantity_pair()?;
1074 Ok(ReadWindow { address, quantity })
1075 }
1076
1077 pub fn write_single_u16_fields(&self) -> Result<WriteSingleU16Fields, MbusError> {
1079 if self.data_len != 4 {
1080 return Err(MbusError::InvalidPduLength);
1081 }
1082
1083 Ok(WriteSingleU16Fields {
1084 address: u16::from_be_bytes([
1085 self.data[PDU_ADDRESS_OFFSET_1B],
1086 self.data[PDU_ADDRESS_OFFSET_2B],
1087 ]),
1088 value: u16::from_be_bytes([
1089 self.data[PDU_QUANTITY_OFFSET_1B],
1090 self.data[PDU_QUANTITY_OFFSET_2B],
1091 ]),
1092 })
1093 }
1094
1095 pub fn write_multiple_fields(&self) -> Result<WriteMultipleFields<'_>, MbusError> {
1097 if self.data_len < 5 {
1098 return Err(MbusError::InvalidPduLength);
1099 }
1100
1101 let (address, quantity) = self.read_address_quantity_pair()?;
1102 let byte_count = self.data[PDU_BYTE_COUNT_OFFSET];
1103 let expected_len = 5usize
1104 .checked_add(byte_count as usize)
1105 .ok_or(MbusError::InvalidByteCount)?;
1106
1107 if self.data_len as usize != expected_len {
1108 return Err(MbusError::InvalidByteCount);
1109 }
1110
1111 Ok(WriteMultipleFields {
1112 address,
1113 quantity,
1114 byte_count,
1115 values: &self.data[5..expected_len],
1116 })
1117 }
1118
1119 pub fn mask_write_register_fields(&self) -> Result<MaskWriteRegisterFields, MbusError> {
1121 if self.data_len != 6 {
1122 return Err(MbusError::InvalidPduLength);
1123 }
1124
1125 Ok(MaskWriteRegisterFields {
1126 address: u16::from_be_bytes([
1127 self.data[PDU_ADDRESS_OFFSET_1B],
1128 self.data[PDU_ADDRESS_OFFSET_2B],
1129 ]),
1130 and_mask: u16::from_be_bytes([
1131 self.data[PDU_AND_MASK_OFFSET_1B],
1132 self.data[PDU_AND_MASK_OFFSET_2B],
1133 ]),
1134 or_mask: u16::from_be_bytes([
1135 self.data[PDU_OR_MASK_OFFSET_1B],
1136 self.data[PDU_OR_MASK_OFFSET_2B],
1137 ]),
1138 })
1139 }
1140
1141 pub fn byte_count_payload(&self) -> Result<ByteCountPayload<'_>, MbusError> {
1143 if self.data_len < 1 {
1144 return Err(MbusError::InvalidPduLength);
1145 }
1146
1147 let byte_count = self.data[0];
1148 let expected_len = 1usize
1149 .checked_add(byte_count as usize)
1150 .ok_or(MbusError::InvalidByteCount)?;
1151 if self.data_len as usize != expected_len {
1152 return Err(MbusError::InvalidByteCount);
1153 }
1154
1155 Ok(ByteCountPayload {
1156 byte_count,
1157 payload: &self.data[1..expected_len],
1158 })
1159 }
1160
1161 pub fn read_write_multiple_fields(&self) -> Result<ReadWriteMultipleFields<'_>, MbusError> {
1163 if (self.data_len as usize) < PDU_FC17_WRITE_VALUES_OFFSET {
1164 return Err(MbusError::InvalidPduLength);
1165 }
1166
1167 let (read_address, read_quantity) = self.read_address_quantity_pair()?;
1168 let write_address = u16::from_be_bytes([
1169 self.data[PDU_FC17_WRITE_ADDRESS_OFFSET_1B],
1170 self.data[PDU_FC17_WRITE_ADDRESS_OFFSET_2B],
1171 ]);
1172 let write_quantity = u16::from_be_bytes([
1173 self.data[PDU_FC17_WRITE_QUANTITY_OFFSET_1B],
1174 self.data[PDU_FC17_WRITE_QUANTITY_OFFSET_2B],
1175 ]);
1176 let write_byte_count = self.data[PDU_FC17_WRITE_BYTE_COUNT_OFFSET];
1177 let expected_len = PDU_FC17_WRITE_VALUES_OFFSET
1178 .checked_add(write_byte_count as usize)
1179 .ok_or(MbusError::InvalidByteCount)?;
1180
1181 if self.data_len as usize != expected_len {
1182 return Err(MbusError::InvalidByteCount);
1183 }
1184
1185 Ok(ReadWriteMultipleFields {
1186 read_address,
1187 read_quantity,
1188 write_address,
1189 write_quantity,
1190 write_byte_count,
1191 write_values: &self.data[PDU_FC17_WRITE_VALUES_OFFSET..expected_len],
1192 })
1193 }
1194
1195 pub fn single_byte_payload(&self) -> Result<u8, MbusError> {
1197 if self.data_len != 1 {
1198 return Err(MbusError::InvalidPduLength);
1199 }
1200 Ok(self.data[0])
1201 }
1202
1203 pub fn sub_function_payload(&self) -> Result<SubFunctionPayload<'_>, MbusError> {
1206 if self.data_len < 2 {
1207 return Err(MbusError::InvalidPduLength);
1208 }
1209 if !self.data_len.is_multiple_of(2) {
1210 return Err(MbusError::InvalidPduLength);
1211 }
1212 Ok(SubFunctionPayload {
1213 sub_function: u16::from_be_bytes([
1214 self.data[PDU_SUB_FUNCTION_OFFSET_1B],
1215 self.data[PDU_SUB_FUNCTION_OFFSET_2B],
1216 ]),
1217 payload: &self.data[2..self.data_len as usize],
1218 })
1219 }
1220
1221 pub fn u16_pair_fields(&self) -> Result<U16PairFields, MbusError> {
1223 if self.data_len != 4 {
1224 return Err(MbusError::InvalidPduLength);
1225 }
1226 Ok(U16PairFields {
1227 first: u16::from_be_bytes([
1228 self.data[PDU_ADDRESS_OFFSET_1B],
1229 self.data[PDU_ADDRESS_OFFSET_2B],
1230 ]),
1231 second: u16::from_be_bytes([
1232 self.data[PDU_QUANTITY_OFFSET_1B],
1233 self.data[PDU_QUANTITY_OFFSET_2B],
1234 ]),
1235 })
1236 }
1237
1238 pub fn mei_type_payload(&self) -> Result<MeiTypePayload<'_>, MbusError> {
1241 if self.data_len < 1 {
1242 return Err(MbusError::InvalidPduLength);
1243 }
1244 Ok(MeiTypePayload {
1245 mei_type_byte: self.data[PDU_MEI_TYPE_OFFSET],
1246 payload: &self.data[1..self.data_len as usize],
1247 })
1248 }
1249
1250 pub fn fifo_payload(&self) -> Result<FifoPayload<'_>, MbusError> {
1254 if self.data_len < 4 {
1255 return Err(MbusError::InvalidPduLength);
1256 }
1257 Ok(FifoPayload {
1258 fifo_byte_count: u16::from_be_bytes([
1259 self.data[PDU_FIFO_BYTE_COUNT_OFFSET_1B],
1260 self.data[PDU_FIFO_BYTE_COUNT_OFFSET_2B],
1261 ]),
1262 fifo_count: u16::from_be_bytes([
1263 self.data[PDU_FIFO_COUNT_OFFSET_1B],
1264 self.data[PDU_FIFO_COUNT_OFFSET_2B],
1265 ]),
1266 values: &self.data[PDU_FIFO_VALUES_OFFSET..self.data_len as usize],
1267 })
1268 }
1269
1270 #[cfg(feature = "fifo")]
1273 pub fn fifo_pointer(&self) -> Result<u16, MbusError> {
1274 if self.data_len != 2 {
1275 return Err(MbusError::InvalidPduLength);
1276 }
1277 Ok(u16::from_be_bytes([
1278 self.data[PDU_ADDRESS_OFFSET_1B],
1279 self.data[PDU_ADDRESS_OFFSET_2B],
1280 ]))
1281 }
1282
1283 #[cfg(feature = "diagnostics")]
1286 pub fn diagnostics_fields(&self) -> Result<(u16, u16), MbusError> {
1287 if self.data_len < 4 {
1288 return Err(MbusError::InvalidPduLength);
1289 }
1290 let sub_function = u16::from_be_bytes([
1291 self.data[PDU_SUB_FUNCTION_OFFSET_1B],
1292 self.data[PDU_SUB_FUNCTION_OFFSET_2B],
1293 ]);
1294 let data_word = u16::from_be_bytes([self.data[2], self.data[3]]);
1295 Ok((sub_function, data_word))
1296 }
1297
1298 #[cfg(feature = "file-record")]
1308 pub fn file_record_read_sub_requests(
1309 &self,
1310 ) -> Result<
1311 heapless::Vec<
1312 crate::models::file_record::FileRecordReadSubRequest,
1313 { crate::models::file_record::MAX_SUB_REQUESTS_PER_PDU },
1314 >,
1315 MbusError,
1316 > {
1317 use crate::models::file_record::{FILE_RECORD_REF_TYPE, FileRecordReadSubRequest};
1318
1319 const FILE_RECORD_READ_SUB_REQUEST_LEN: usize = 7;
1320 const FILE_RECORD_MAX_REQUEST_BYTE_COUNT: usize = 245;
1321
1322 let data_len = self.data_len as usize;
1323 if data_len < 1 + FILE_RECORD_READ_SUB_REQUEST_LEN {
1324 return Err(MbusError::InvalidPduLength);
1325 }
1326
1327 let data = self.data.as_slice();
1328 let byte_count = data[0] as usize;
1329 if byte_count != data_len - 1 {
1330 return Err(MbusError::InvalidByteCount);
1331 }
1332 if byte_count > FILE_RECORD_MAX_REQUEST_BYTE_COUNT {
1333 return Err(MbusError::InvalidByteCount);
1334 }
1335 if !byte_count.is_multiple_of(FILE_RECORD_READ_SUB_REQUEST_LEN) {
1336 return Err(MbusError::InvalidByteCount);
1337 }
1338
1339 let mut out: heapless::Vec<
1340 FileRecordReadSubRequest,
1341 { crate::models::file_record::MAX_SUB_REQUESTS_PER_PDU },
1342 > = heapless::Vec::new();
1343 let mut index = 1usize;
1344 while index < data_len {
1345 if data[index] != FILE_RECORD_REF_TYPE {
1346 return Err(MbusError::InvalidValue);
1347 }
1348 let file_number = u16::from_be_bytes([data[index + 1], data[index + 2]]);
1349 let record_number = u16::from_be_bytes([data[index + 3], data[index + 4]]);
1350 let record_length = u16::from_be_bytes([data[index + 5], data[index + 6]]);
1351 if record_length == 0 {
1352 return Err(MbusError::InvalidQuantity);
1353 }
1354
1355 out.push(FileRecordReadSubRequest {
1356 file_number,
1357 record_number,
1358 record_length,
1359 })
1360 .map_err(|_| MbusError::TooManyFileReadSubRequests)?;
1361
1362 index += FILE_RECORD_READ_SUB_REQUEST_LEN;
1363 }
1364
1365 Ok(out)
1366 }
1367
1368 #[cfg(feature = "file-record")]
1379 pub fn file_record_write_sub_requests<'a>(
1380 &'a self,
1381 ) -> Result<
1382 heapless::Vec<
1383 crate::models::file_record::FileRecordWriteSubRequest<'a>,
1384 { crate::models::file_record::MAX_SUB_REQUESTS_PER_PDU },
1385 >,
1386 MbusError,
1387 > {
1388 use crate::models::file_record::{FILE_RECORD_REF_TYPE, FileRecordWriteSubRequest};
1389
1390 const FILE_RECORD_WRITE_SUB_REQUEST_HEADER_LEN: usize = 7;
1391 const FILE_RECORD_MAX_REQUEST_BYTE_COUNT: usize = 245;
1392
1393 let data_len = self.data_len as usize;
1394 if data_len < 1 + FILE_RECORD_WRITE_SUB_REQUEST_HEADER_LEN + 2 {
1395 return Err(MbusError::InvalidPduLength);
1396 }
1397
1398 let data = self.data.as_slice();
1399 let byte_count = data[0] as usize;
1400 if byte_count != data_len - 1 {
1401 return Err(MbusError::InvalidByteCount);
1402 }
1403 if byte_count > FILE_RECORD_MAX_REQUEST_BYTE_COUNT {
1404 return Err(MbusError::InvalidByteCount);
1405 }
1406
1407 let mut out: heapless::Vec<
1408 FileRecordWriteSubRequest,
1409 { crate::models::file_record::MAX_SUB_REQUESTS_PER_PDU },
1410 > = heapless::Vec::new();
1411 let mut index = 1usize;
1412 while index < data_len {
1413 if index + FILE_RECORD_WRITE_SUB_REQUEST_HEADER_LEN > data_len {
1414 return Err(MbusError::InvalidPduLength);
1415 }
1416 if data[index] != FILE_RECORD_REF_TYPE {
1417 return Err(MbusError::InvalidValue);
1418 }
1419
1420 let file_number = u16::from_be_bytes([data[index + 1], data[index + 2]]);
1421 let record_number = u16::from_be_bytes([data[index + 3], data[index + 4]]);
1422 let record_length = u16::from_be_bytes([data[index + 5], data[index + 6]]);
1423 if record_length == 0 {
1424 return Err(MbusError::InvalidQuantity);
1425 }
1426
1427 let data_bytes_len = record_length as usize * 2;
1428 let end_index = index
1429 .checked_add(FILE_RECORD_WRITE_SUB_REQUEST_HEADER_LEN + data_bytes_len)
1430 .ok_or(MbusError::InvalidByteCount)?;
1431 if end_index > data_len {
1432 return Err(MbusError::InvalidByteCount);
1433 }
1434
1435 out.push(FileRecordWriteSubRequest {
1436 file_number,
1437 record_number,
1438 record_length,
1439 record_data_bytes: &data
1440 [index + FILE_RECORD_WRITE_SUB_REQUEST_HEADER_LEN..end_index],
1441 })
1442 .map_err(|_| MbusError::TooManyFileReadSubRequests)?;
1443
1444 index = end_index;
1445 }
1446
1447 if out.is_empty() {
1448 return Err(MbusError::InvalidPduLength);
1449 }
1450
1451 Ok(out)
1452 }
1453
1454 pub fn read_device_id_fields(&self) -> Result<ReadDeviceIdPduFields, MbusError> {
1462 if (self.data_len as usize) < PDU_MEI_OBJECTS_DATA_OFFSET {
1464 return Err(MbusError::InvalidPduLength);
1465 }
1466 let data = self.data.as_slice();
1467 let number_of_objects = data[PDU_MEI_NUM_OBJECTS_OFFSET];
1468
1469 let mut offset = PDU_MEI_OBJECTS_DATA_OFFSET;
1471 for _ in 0..number_of_objects as usize {
1472 if offset + 2 > data.len() {
1473 return Err(MbusError::InvalidPduLength);
1474 }
1475 let obj_len = data[offset + 1] as usize;
1476 offset += 2;
1477 if offset + obj_len > data.len() {
1478 return Err(MbusError::InvalidPduLength);
1479 }
1480 offset += obj_len;
1481 }
1482
1483 let payload_len = (self.data_len as usize) - PDU_MEI_OBJECTS_DATA_OFFSET;
1484 if payload_len > MAX_PDU_DATA_LEN {
1485 return Err(MbusError::BufferTooSmall);
1486 }
1487 let mut objects_data = [0u8; MAX_PDU_DATA_LEN];
1488 if payload_len > 0 {
1489 objects_data[..payload_len].copy_from_slice(&data[PDU_MEI_OBJECTS_DATA_OFFSET..]);
1490 }
1491
1492 Ok(ReadDeviceIdPduFields {
1493 mei_type_byte: data[PDU_MEI_TYPE_OFFSET],
1494 read_device_id_code_byte: data[PDU_MEI_READ_CODE_OFFSET],
1495 conformity_level_byte: data[PDU_MEI_CONFORMITY_LEVEL_OFFSET],
1496 more_follows: data[PDU_MEI_MORE_FOLLOWS_OFFSET] == 0xFF,
1497 next_object_id_byte: data[PDU_MEI_NEXT_OBJECT_ID_OFFSET],
1498 number_of_objects,
1499 objects_data,
1500 payload_len,
1501 })
1502 }
1503
1504 pub fn to_bytes(&self) -> Result<Vec<u8, 253>, MbusError> {
1516 let mut pdu_bytes = Vec::new(); pdu_bytes
1518 .push(self.function_code as u8)
1519 .map_err(|_| MbusError::Unexpected)?; pdu_bytes
1522 .extend_from_slice(&self.data.as_slice()[..self.data_len as usize])
1523 .map_err(|_| MbusError::BufferLenMissmatch)?; Ok(pdu_bytes)
1526 }
1527
1528 pub fn from_bytes(bytes: &[u8]) -> Result<Self, MbusError> {
1538 if bytes.is_empty() {
1539 return Err(MbusError::InvalidPduLength);
1540 }
1541
1542 let error_code = if is_exception_code(bytes[0]) {
1543 if bytes.len() < 2 {
1544 return Err(MbusError::InvalidPduLength);
1545 }
1546 Some(bytes[1]) } else {
1548 None
1549 };
1550 let function_code = clear_exception_bit(bytes[0]); let function_code = FunctionCode::try_from(function_code)?;
1553
1554 let data_slice = &bytes[1..];
1555 let data_len = data_slice.len();
1556
1557 if data_len > MAX_PDU_DATA_LEN {
1558 return Err(MbusError::InvalidPduLength);
1559 }
1560
1561 let mut data = heapless::Vec::new();
1562 data.extend_from_slice(data_slice)
1563 .map_err(|_| MbusError::BufferLenMissmatch)?;
1564
1565 Ok(Pdu {
1566 function_code,
1567 error_code,
1568 data,
1569 data_len: data_len as u8,
1570 })
1571 }
1572}
1573
1574pub fn compile_adu_frame(
1576 txn_id: u16,
1577 unit_id: u8,
1578 pdu: Pdu,
1579 transport_type: TransportType,
1580) -> Result<Vec<u8, MAX_ADU_FRAME_LEN>, MbusError> {
1581 match transport_type {
1582 TransportType::StdTcp | TransportType::CustomTcp => {
1583 let pdu_bytes_len = pdu.to_bytes()?.len() as u16;
1584 let mbap_header = MbapHeader::new(txn_id, pdu_bytes_len + 1, unit_id);
1585 ModbusMessage::new(AdditionalAddress::MbapHeader(mbap_header), pdu).to_bytes()
1586 }
1587 TransportType::StdSerial(serial_mode) | TransportType::CustomSerial(serial_mode) => {
1588 let slave_address = SlaveAddress(unit_id);
1589 let adu_bytes = match serial_mode {
1590 SerialMode::Rtu => {
1591 let mut adu_bytes =
1592 ModbusMessage::new(AdditionalAddress::SlaveAddress(slave_address), pdu)
1593 .to_bytes()?;
1594 let crc16 = checksum::crc16(adu_bytes.as_slice());
1596 let crc_bytes = crc16.to_le_bytes();
1598 adu_bytes
1599 .extend_from_slice(&crc_bytes)
1600 .map_err(|_| MbusError::Unexpected)?;
1601
1602 adu_bytes
1603 }
1604 SerialMode::Ascii => {
1605 ModbusMessage::new(AdditionalAddress::SlaveAddress(slave_address), pdu)
1606 .to_ascii_bytes()?
1607 }
1608 };
1609
1610 Ok(adu_bytes)
1611 }
1612 }
1613}
1614
1615pub fn decompile_adu_frame(
1617 frame: &[u8],
1618 transport_type: TransportType,
1619) -> Result<ModbusMessage, MbusError> {
1620 match transport_type {
1621 TransportType::StdTcp | TransportType::CustomTcp => {
1622 ModbusMessage::from_bytes(frame)
1624 }
1625 TransportType::StdSerial(serial_mode) | TransportType::CustomSerial(serial_mode) => {
1626 match serial_mode {
1627 SerialMode::Rtu => ModbusMessage::from_rtu_bytes(frame),
1628 SerialMode::Ascii => ModbusMessage::from_ascii_bytes(frame),
1629 }
1630 }
1631 }
1632}
1633
1634pub fn derive_length_from_bytes(frame: &[u8], transport_type: TransportType) -> Option<usize> {
1647 match transport_type {
1648 TransportType::StdTcp | TransportType::CustomTcp => {
1649 if frame.len() < 6 {
1652 return None;
1653 }
1654
1655 let protocol_id = u16::from_be_bytes([frame[2], frame[3]]);
1659 if protocol_id != MODBUS_PROTOCOL_ID {
1660 return Some(usize::MAX);
1661 }
1662
1663 let length_field = u16::from_be_bytes([frame[4], frame[5]]) as usize;
1666 Some(6 + length_field)
1667 }
1668 TransportType::StdSerial(SerialMode::Rtu)
1669 | TransportType::CustomSerial(SerialMode::Rtu) => {
1670 if frame.len() < 2 {
1671 return None;
1672 }
1673
1674 let fc = frame[1];
1675
1676 if is_exception_code(fc) {
1678 return Some(5);
1679 }
1680
1681 let check_crc = |len: usize| -> bool {
1684 if frame.len() >= len && len >= MIN_RTU_ADU_LEN {
1685 let data_len = len - RTU_CRC_SIZE;
1686 let received_crc = u16::from_le_bytes([frame[data_len], frame[data_len + 1]]);
1687 checksum::crc16(&frame[..data_len]) == received_crc
1688 } else {
1689 false
1690 }
1691 };
1692
1693 get_byte_count_from_frame(frame, fc, check_crc)
1694 }
1695 TransportType::StdSerial(SerialMode::Ascii)
1696 | TransportType::CustomSerial(SerialMode::Ascii) => {
1697 if frame.len() < MIN_ASCII_ADU_LEN {
1700 return None;
1701 }
1702
1703 frame.iter().position(|&b| b == b'\n').map(|pos| pos + 1)
1705 }
1706 }
1707}
1708
1709fn get_byte_count_from_frame(
1710 frame: &[u8],
1711 fc: u8,
1712 check_crc: impl Fn(usize) -> bool,
1713) -> Option<usize> {
1714 let mut candidates = heapless::Vec::<usize, 4>::new();
1715 let mut min_needed = usize::MAX;
1716
1717 let mut add_dyn = |cands: &mut heapless::Vec<usize, 4>, offset: usize, base: usize| {
1721 if frame.len() > offset {
1722 let _ = cands.push(base + frame[offset] as usize);
1723 } else {
1724 min_needed = core::cmp::min(min_needed, offset + 1);
1725 }
1726 };
1727
1728 match fc {
1730 1..=4 => {
1731 let _ = candidates.push(8);
1732 add_dyn(&mut candidates, 2, 5);
1733 }
1734 5 | 6 | 8 => {
1735 let _ = candidates.push(8);
1736 }
1737 7 => {
1738 let _ = candidates.push(4);
1739 let _ = candidates.push(5);
1740 }
1741 11 => {
1742 let _ = candidates.push(4);
1743 let _ = candidates.push(8);
1744 }
1745 12 | 17 => {
1746 let _ = candidates.push(4);
1747 add_dyn(&mut candidates, 2, 5);
1748 }
1749 15 | 16 => {
1750 let _ = candidates.push(8);
1751 add_dyn(&mut candidates, 6, 9);
1752 }
1753 20 | 21 => add_dyn(&mut candidates, 2, 5),
1754 22 => {
1755 let _ = candidates.push(10);
1756 }
1757 23 => {
1758 add_dyn(&mut candidates, 2, 5);
1759 add_dyn(&mut candidates, 10, 13);
1760 }
1761 24 => {
1762 let _ = candidates.push(6);
1763 if frame.len() >= 4 {
1764 let byte_count = u16::from_be_bytes([frame[2], frame[3]]) as usize;
1765 let _ = candidates.push(6 + byte_count);
1766 } else {
1767 min_needed = core::cmp::min(min_needed, 4);
1768 }
1769 }
1770 43 => {
1771 if check_crc(7) {
1772 return Some(7);
1773 }
1774 for len in MIN_RTU_ADU_LEN..=frame.len() {
1776 if check_crc(len) {
1777 return Some(len);
1778 }
1779 }
1780 return None;
1781 }
1782 _ => {
1783 for len in MIN_RTU_ADU_LEN..=frame.len() {
1784 if check_crc(len) {
1785 return Some(len);
1786 }
1787 }
1788 return None;
1789 }
1790 }
1791
1792 for &len in &candidates {
1794 if check_crc(len) {
1795 return Some(len);
1796 }
1797 }
1798
1799 let max_candidate = candidates.iter().copied().max().unwrap_or(0);
1801 let target = if min_needed != usize::MAX {
1802 core::cmp::max(min_needed, max_candidate)
1803 } else {
1804 max_candidate
1805 };
1806
1807 if target > 0 { Some(target) } else { None }
1808}
1809
1810fn nibble_to_hex(nibble: u8) -> u8 {
1812 match nibble {
1813 0..=9 => b'0' + nibble,
1814 10..=15 => b'A' + (nibble - 10),
1815 _ => b'?', }
1817}
1818
1819fn hex_char_to_nibble(c: u8) -> Result<u8, MbusError> {
1821 match c {
1822 b'0'..=b'9' => Ok(c - b'0'),
1823 b'A'..=b'F' => Ok(c - b'A' + 10),
1824 b'a'..=b'f' => Ok(c - b'a' + 10),
1825 _ => Err(MbusError::BasicParseError),
1826 }
1827}
1828
1829fn hex_pair_to_byte(high: u8, low: u8) -> Result<u8, MbusError> {
1831 let h = hex_char_to_nibble(high)?;
1832 let l = hex_char_to_nibble(low)?;
1833 Ok((h << 4) | l)
1834}
1835
1836#[cfg(test)]
1837mod tests {
1838 use super::*;
1839 use crate::function_codes::public::FunctionCode;
1840 use heapless::Vec;
1841
1842 #[test]
1849 fn test_pdu_from_bytes_valid_no_data() {
1850 let bytes = [0x11];
1851 let pdu = Pdu::from_bytes(&bytes).expect("Should parse a function-code-only PDU");
1852
1853 assert_eq!(pdu.function_code, FunctionCode::ReportServerId);
1854 assert_eq!(pdu.data_len, 0);
1855 assert!(pdu.data.is_empty());
1856 assert_eq!(pdu.error_code, None);
1857 }
1858
1859 #[test]
1865 fn test_pdu_from_bytes_valid_read_coils_request() {
1866 let bytes = [0x01, 0x00, 0x00, 0x00, 0x0A]; let pdu = Pdu::from_bytes(&bytes).expect("Should successfully parse Read Coils request");
1870
1871 assert_eq!(pdu.function_code, FunctionCode::ReadCoils);
1872 assert_eq!(pdu.data_len, 4);
1873 assert_eq!(pdu.data.as_slice(), &[0x00, 0x00, 0x00, 0x0A]);
1874 }
1875
1876 #[test]
1882 fn test_pdu_from_bytes_valid_read_holding_registers_response() {
1883 let bytes = [0x03, 0x04, 0x12, 0x34, 0x56, 0x78]; let pdu = Pdu::from_bytes(&bytes)
1887 .expect("Should successfully parse Read Holding Registers response");
1888
1889 assert_eq!(pdu.function_code, FunctionCode::ReadHoldingRegisters);
1890 assert_eq!(pdu.data_len, 5); assert_eq!(pdu.data.as_slice(), &[0x04, 0x12, 0x34, 0x56, 0x78]);
1892 }
1893
1894 #[test]
1900 fn test_pdu_from_bytes_valid_max_data_length() {
1901 let mut bytes_vec: Vec<u8, 253> = Vec::new();
1903 let _ = bytes_vec.push(0x03); for i in 0..252 {
1905 let _ = bytes_vec.push(i as u8);
1906 }
1907 let bytes = bytes_vec.as_slice();
1908 let pdu = Pdu::from_bytes(bytes).expect("Should parse valid PDU with max data");
1909
1910 assert_eq!(pdu.function_code, FunctionCode::ReadHoldingRegisters);
1911 assert_eq!(pdu.data_len, 252);
1912 assert_eq!(pdu.data.as_slice(), &bytes[1..]);
1913 }
1914
1915 #[test]
1919 fn test_pdu_from_bytes_empty_slice_error() {
1920 let bytes = [];
1921 let err = Pdu::from_bytes(&bytes).expect_err("Should return error for empty slice");
1922 assert_eq!(err, MbusError::InvalidPduLength);
1923 }
1924
1925 #[test]
1930 fn test_pdu_from_bytes_invalid_function_code_error() {
1931 let bytes = [0x00, 0x01, 0x02];
1933 let err = Pdu::from_bytes(&bytes).expect_err("Should return error for invalid FC 0x00");
1934 assert_eq!(err, MbusError::UnsupportedFunction(0x00));
1935
1936 let bytes = [0xFF, 0x01, 0x02];
1938 let err = Pdu::from_bytes(&bytes).expect_err("Should return error for invalid FC 0xFF");
1939 assert_eq!(err, MbusError::UnsupportedFunction(0x7F));
1940 }
1941
1942 #[test]
1948 fn test_pdu_from_bytes_data_too_long_error() {
1949 let mut bytes_vec: Vec<u8, 254> = Vec::new();
1951 let _ = bytes_vec.push(0x03); for i in 0..253 {
1953 let _ = bytes_vec.push(i as u8);
1955 }
1956 let bytes = bytes_vec.as_slice();
1957 let err = Pdu::from_bytes(bytes).expect_err("Should return error for too much data");
1958 assert_eq!(err, MbusError::InvalidPduLength);
1959 }
1960
1961 #[test]
1962 fn test_pdu_read_window_parses_expected_fields() {
1963 let pdu = Pdu::from_bytes(&[0x03, 0x12, 0x34, 0x00, 0x02]).expect("valid pdu");
1964 let parsed = pdu.read_window().expect("read window should parse");
1965
1966 assert_eq!(
1967 parsed,
1968 ReadWindow {
1969 address: 0x1234,
1970 quantity: 0x0002
1971 }
1972 );
1973 }
1974
1975 #[test]
1976 fn test_pdu_write_single_u16_fields_parses_expected_fields() {
1977 let pdu = Pdu::from_bytes(&[0x06, 0x01, 0x02, 0xAB, 0xCD]).expect("valid pdu");
1978 let parsed = pdu
1979 .write_single_u16_fields()
1980 .expect("write single fields should parse");
1981
1982 assert_eq!(
1983 parsed,
1984 WriteSingleU16Fields {
1985 address: 0x0102,
1986 value: 0xABCD
1987 }
1988 );
1989 }
1990
1991 #[test]
1992 fn test_pdu_write_multiple_fields_parses_expected_fields() {
1993 let pdu = Pdu::from_bytes(&[0x10, 0x00, 0x20, 0x00, 0x02, 0x04, 0x12, 0x34, 0x56, 0x78])
1994 .expect("valid pdu");
1995 let parsed = pdu
1996 .write_multiple_fields()
1997 .expect("write multiple fields should parse");
1998
1999 assert_eq!(parsed.address, 0x0020);
2000 assert_eq!(parsed.quantity, 0x0002);
2001 assert_eq!(parsed.byte_count, 0x04);
2002 assert_eq!(parsed.values, &[0x12, 0x34, 0x56, 0x78]);
2003 }
2004
2005 #[test]
2006 fn test_pdu_mask_write_register_fields_parses_expected_fields() {
2007 let pdu = Pdu::from_bytes(&[0x16, 0x00, 0x10, 0xFF, 0x00, 0x00, 0x0F]).expect("valid pdu");
2008 let parsed = pdu
2009 .mask_write_register_fields()
2010 .expect("mask write fields should parse");
2011
2012 assert_eq!(
2013 parsed,
2014 MaskWriteRegisterFields {
2015 address: 0x0010,
2016 and_mask: 0xFF00,
2017 or_mask: 0x000F
2018 }
2019 );
2020 }
2021
2022 #[test]
2023 fn test_pdu_byte_count_payload_parses_expected_fields() {
2024 let pdu = Pdu::from_bytes(&[0x03, 0x04, 0x12, 0x34, 0x56, 0x78]).expect("valid pdu");
2025 let parsed = pdu
2026 .byte_count_payload()
2027 .expect("byte-count payload should parse");
2028
2029 assert_eq!(parsed.byte_count, 0x04);
2030 assert_eq!(parsed.payload, &[0x12, 0x34, 0x56, 0x78]);
2031 }
2032
2033 #[test]
2034 fn test_pdu_write_multiple_fields_rejects_mismatched_byte_count() {
2035 let pdu =
2036 Pdu::from_bytes(&[0x10, 0x00, 0x20, 0x00, 0x02, 0x03, 0x12, 0x34]).expect("valid pdu");
2037 let err = pdu
2038 .write_multiple_fields()
2039 .expect_err("byte count mismatch should error");
2040
2041 assert_eq!(err, MbusError::InvalidByteCount);
2042 }
2043
2044 #[test]
2045 fn test_pdu_byte_count_payload_rejects_mismatched_byte_count() {
2046 let pdu = Pdu::from_bytes(&[0x03, 0x03, 0x12, 0x34]).expect("valid pdu");
2047 let err = pdu
2048 .byte_count_payload()
2049 .expect_err("byte count mismatch should error");
2050
2051 assert_eq!(err, MbusError::InvalidByteCount);
2052 }
2053
2054 #[test]
2055 fn test_pdu_read_write_multiple_fields_parses_expected_fields() {
2056 let pdu = Pdu::from_bytes(&[
2057 0x17, 0x00, 0x10, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02, 0x04, 0x12, 0x34, 0x56, 0x78,
2058 ])
2059 .expect("valid pdu");
2060 let parsed = pdu
2061 .read_write_multiple_fields()
2062 .expect("read/write multiple fields should parse");
2063
2064 assert_eq!(parsed.read_address, 0x0010);
2065 assert_eq!(parsed.read_quantity, 0x0002);
2066 assert_eq!(parsed.write_address, 0x0020);
2067 assert_eq!(parsed.write_quantity, 0x0002);
2068 assert_eq!(parsed.write_byte_count, 0x04);
2069 assert_eq!(parsed.write_values, &[0x12, 0x34, 0x56, 0x78]);
2070 }
2071
2072 #[test]
2073 fn test_pdu_read_write_multiple_fields_rejects_short_pdu() {
2074 let pdu = Pdu::from_bytes(&[0x17, 0x00, 0x10, 0x00, 0x02, 0x00]).expect("valid pdu");
2075 let err = pdu
2076 .read_write_multiple_fields()
2077 .expect_err("PDU too short should error");
2078
2079 assert_eq!(err, MbusError::InvalidPduLength);
2080 }
2081
2082 #[test]
2083 fn test_pdu_read_write_multiple_fields_rejects_mismatched_byte_count() {
2084 let pdu = Pdu::from_bytes(&[
2085 0x17, 0x00, 0x10, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02, 0x05, 0x12, 0x34, 0x56, 0x78,
2086 ])
2087 .expect("valid pdu");
2088 let err = pdu
2089 .read_write_multiple_fields()
2090 .expect_err("byte count mismatch should error");
2091
2092 assert_eq!(err, MbusError::InvalidByteCount);
2093 }
2094
2095 #[test]
2103 fn test_pdu_to_bytes_no_data() {
2104 let pdu = Pdu::new(FunctionCode::ReportServerId, Vec::new(), 0);
2105 let bytes = pdu.to_bytes().expect("Should convert PDU to bytes");
2106 assert_eq!(bytes.as_slice(), &[0x11]);
2107 }
2108
2109 #[test]
2115 fn test_pdu_to_bytes_with_data() {
2116 let mut data_vec = Vec::new();
2117 data_vec
2118 .extend_from_slice(&[0x00, 0x00, 0x00, 0x0A])
2119 .unwrap(); let pdu = Pdu::new(FunctionCode::ReadCoils, data_vec, 4);
2122 let bytes = pdu.to_bytes().expect("Should convert PDU to bytes");
2123 assert_eq!(bytes.as_slice(), &[0x01, 0x00, 0x00, 0x00, 0x0A]);
2124 }
2125
2126 #[test]
2132 fn test_pdu_to_bytes_max_data() {
2133 let mut data_vec = Vec::new();
2134 for i in 0..252 {
2135 data_vec.push(i as u8).unwrap();
2136 }
2137
2138 let pdu = Pdu::new(FunctionCode::ReadHoldingRegisters, data_vec, 252);
2139 let bytes = pdu
2140 .to_bytes()
2141 .expect("Should convert PDU to bytes with max data");
2142 let mut expected_bytes_vec: Vec<u8, 253> = Vec::new();
2143 let _ = expected_bytes_vec.push(0x03);
2144 for i in 0..252 {
2145 let _ = expected_bytes_vec.push(i as u8);
2146 }
2147 assert_eq!(bytes.as_slice(), expected_bytes_vec.as_slice());
2148 }
2149
2150 #[test]
2155 fn test_modbus_message_to_bytes_tcp() {
2156 let mbap_header = MbapHeader {
2157 transaction_id: 0x1234,
2158 protocol_id: 0x0000,
2159 length: 0x0005, unit_id: 0x01,
2161 };
2162
2163 let mut pdu_data_vec: Vec<u8, MAX_PDU_DATA_LEN> = Vec::new();
2164 pdu_data_vec.extend_from_slice(&[0x00, 0x00, 0x00]).unwrap();
2165
2166 let pdu = Pdu::new(
2167 FunctionCode::ReadHoldingRegisters,
2168 pdu_data_vec,
2169 3, );
2171
2172 let modbus_message = ModbusMessage::new(AdditionalAddress::MbapHeader(mbap_header), pdu);
2173 let adu_bytes = modbus_message
2174 .to_bytes()
2175 .expect("Failed to serialize ModbusMessage");
2176
2177 #[rustfmt::skip]
2178 let expected_adu: [u8; 11] = [
2179 0x12, 0x34, 0x00, 0x00, 0x00, 0x05, 0x01, 0x03, 0x00, 0x00, 0x00, ];
2186
2187 assert_eq!(adu_bytes.as_slice(), &expected_adu);
2188 }
2189
2190 #[test]
2196 fn test_function_code_try_from_valid() {
2197 assert_eq!(
2198 FunctionCode::try_from(0x01).unwrap(),
2199 FunctionCode::ReadCoils
2200 );
2201 assert_eq!(
2202 FunctionCode::try_from(0x08).unwrap(),
2203 FunctionCode::Diagnostics
2204 );
2205 assert_eq!(
2206 FunctionCode::try_from(0x2B).unwrap(),
2207 FunctionCode::EncapsulatedInterfaceTransport
2208 );
2209 assert_eq!(
2210 FunctionCode::try_from(0x18).unwrap(),
2211 FunctionCode::ReadFifoQueue
2212 );
2213 assert_eq!(
2214 FunctionCode::try_from(0x11).unwrap(),
2215 FunctionCode::ReportServerId
2216 );
2217 }
2218
2219 #[test]
2224 fn test_function_code_try_from_invalid() {
2225 let err = FunctionCode::try_from(0x00).expect_err("Should error for invalid FC 0x00");
2226 assert_eq!(err, MbusError::UnsupportedFunction(0x00));
2227
2228 let err =
2229 FunctionCode::try_from(0x09).expect_err("Should error for invalid FC 0x09 (reserved)");
2230 assert_eq!(err, MbusError::UnsupportedFunction(0x09));
2231
2232 let err = FunctionCode::try_from(0x64)
2233 .expect_err("Should error for invalid FC 0x64 (private range, not public)");
2234 assert_eq!(err, MbusError::UnsupportedFunction(0x64));
2235 }
2236
2237 #[test]
2244 fn test_pdu_round_trip_with_data() {
2245 let original_bytes = [0x01, 0x00, 0x00, 0x00, 0x0A]; let pdu = Pdu::from_bytes(&original_bytes).expect("from_bytes failed");
2247 let new_bytes = pdu.to_bytes().expect("to_bytes failed");
2248 assert_eq!(original_bytes.as_slice(), new_bytes.as_slice());
2249 }
2250
2251 #[test]
2256 fn test_pdu_round_trip_max_data() {
2257 let mut original_bytes_vec: Vec<u8, 253> = Vec::new();
2258 let _ = original_bytes_vec.push(0x03); for i in 0..252 {
2260 let _ = original_bytes_vec.push(i as u8);
2261 }
2262 let original_bytes = original_bytes_vec.as_slice();
2263
2264 let pdu = Pdu::from_bytes(original_bytes).expect("from_bytes failed");
2265 let new_bytes = pdu.to_bytes().expect("to_bytes failed");
2266 assert_eq!(original_bytes, new_bytes.as_slice());
2267 }
2268
2269 #[test]
2279 fn test_modbus_message_to_ascii_bytes_valid() {
2280 let slave_addr = SlaveAddress::new(1).unwrap();
2281 let mut data = Vec::new();
2282 data.extend_from_slice(&[0x00, 0x00, 0x00, 0x0A]).unwrap();
2283 let pdu = Pdu::new(FunctionCode::ReadCoils, data, 4);
2284 let message = ModbusMessage::new(AdditionalAddress::SlaveAddress(slave_addr), pdu);
2285
2286 let ascii_bytes = message
2287 .to_ascii_bytes()
2288 .expect("Failed to convert to ASCII");
2289
2290 let expected = b":01010000000AF4\r\n";
2291 assert_eq!(ascii_bytes.as_slice(), expected);
2292 }
2293
2294 #[test]
2299 fn test_modbus_message_to_ascii_bytes_max_capacity() {
2300 let slave_addr = SlaveAddress::new(1).unwrap();
2301 let mut data = Vec::new();
2302 for _ in 0..125 {
2303 data.push(0xAA).unwrap();
2304 }
2305 let pdu = Pdu::new(FunctionCode::ReadHoldingRegisters, data, 125);
2306 let message = ModbusMessage::new(AdditionalAddress::SlaveAddress(slave_addr), pdu);
2307
2308 let ascii_bytes = message.to_ascii_bytes().expect("Should fit in buffer");
2309 assert_eq!(ascii_bytes.len(), 259);
2310 }
2311
2312 #[test]
2317 fn test_modbus_message_to_ascii_bytes_large_payload() {
2318 let slave_addr = SlaveAddress::new(1).unwrap();
2319 let mut data = Vec::new();
2320 for _ in 0..126 {
2321 data.push(0xAA).unwrap();
2322 }
2323 let pdu = Pdu::new(FunctionCode::ReadHoldingRegisters, data, 126);
2324 let message = ModbusMessage::new(AdditionalAddress::SlaveAddress(slave_addr), pdu);
2325
2326 let ascii_bytes = message.to_ascii_bytes().expect("Should fit in buffer");
2327 assert_eq!(ascii_bytes.len(), 261);
2328 }
2329
2330 #[test]
2333 fn test_decompile_adu_frame_tcp_valid() {
2334 let frame = [
2335 0x12, 0x34, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x01, 0x00, 0x02, ];
2342 let msg = decompile_adu_frame(&frame, TransportType::StdTcp)
2343 .expect("Should decode valid TCP frame");
2344 assert_eq!(msg.function_code(), FunctionCode::ReadHoldingRegisters);
2345 if let AdditionalAddress::MbapHeader(header) = msg.additional_address {
2346 assert_eq!(header.transaction_id, 0x1234);
2347 } else {
2348 panic!("Expected MbapHeader");
2349 }
2350 }
2351
2352 #[test]
2353 fn test_decompile_adu_frame_tcp_invalid() {
2354 let frame = [0x00]; let err = decompile_adu_frame(&frame, TransportType::StdTcp).expect_err("Should fail");
2356 assert_eq!(err, MbusError::InvalidAduLength);
2357 }
2358
2359 #[test]
2360 fn test_decompile_adu_frame_rtu_valid() {
2361 let frame = [0x01, 0x03, 0x00, 0x6B, 0x00, 0x03, 0x74, 0x17];
2363 let msg = decompile_adu_frame(&frame, TransportType::StdSerial(SerialMode::Rtu))
2364 .expect("Valid RTU");
2365 assert_eq!(msg.function_code(), FunctionCode::ReadHoldingRegisters);
2366 }
2367
2368 #[test]
2369 fn test_decompile_adu_frame_rtu_too_short() {
2370 let frame = [0x01, 0x02, 0x03];
2371 let err = decompile_adu_frame(&frame, TransportType::StdSerial(SerialMode::Rtu))
2372 .expect_err("Too short");
2373 assert_eq!(err, MbusError::InvalidAduLength);
2374 }
2375
2376 #[test]
2377 fn test_decompile_adu_frame_rtu_crc_mismatch() {
2378 let frame = [0x01, 0x03, 0x00, 0x6B, 0x00, 0x03, 0x00, 0x00]; let err = decompile_adu_frame(&frame, TransportType::StdSerial(SerialMode::Rtu))
2380 .expect_err("CRC mismatch");
2381 assert_eq!(err, MbusError::ChecksumError);
2382 }
2383
2384 #[test]
2385 fn test_decompile_adu_frame_ascii_valid() {
2386 let frame = b":010300000001FB\r\n";
2388 let msg = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2389 .expect("Valid ASCII");
2390 assert_eq!(msg.function_code(), FunctionCode::ReadHoldingRegisters);
2391 }
2392
2393 #[test]
2394 fn test_decompile_adu_frame_ascii_too_short() {
2395 let frame = b":123\r\n";
2396 let err = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2397 .expect_err("Too short");
2398 assert_eq!(err, MbusError::InvalidAduLength);
2399 }
2400
2401 #[test]
2402 fn test_decompile_adu_frame_ascii_missing_start() {
2403 let frame = b"010300000001FB\r\n";
2404 let err = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2405 .expect_err("Missing start");
2406 assert_eq!(err, MbusError::BasicParseError);
2407 }
2408
2409 #[test]
2410 fn test_decompile_adu_frame_ascii_missing_end() {
2411 let frame = b":010300000001FB\r"; let err = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2413 .expect_err("Missing end");
2414 assert_eq!(err, MbusError::BasicParseError);
2415 }
2416
2417 #[test]
2418 fn test_decompile_adu_frame_ascii_odd_hex() {
2419 let frame = b":010300000001F\r\n"; let err = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2421 .expect_err("Odd hex");
2422 assert_eq!(err, MbusError::BasicParseError);
2423 }
2424
2425 #[test]
2426 fn test_decompile_adu_frame_ascii_lrc_mismatch() {
2427 let frame = b":01030000000100\r\n"; let err = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2429 .expect_err("LRC mismatch");
2430 assert_eq!(err, MbusError::ChecksumError);
2431 }
2432
2433 #[test]
2434 fn test_decompile_adu_frame_ascii_buffer_overflow() {
2435 let mut frame = Vec::<u8, 600>::new();
2437 frame.push(b':').unwrap();
2438 for _ in 0..261 {
2439 frame.extend_from_slice(b"00").unwrap();
2440 }
2441 frame.extend_from_slice(b"\r\n").unwrap();
2442 let err = decompile_adu_frame(&frame, TransportType::StdSerial(SerialMode::Ascii))
2443 .expect_err("Buffer overflow");
2444 assert_eq!(err, MbusError::BufferTooSmall);
2445 }
2446
2447 #[test]
2450 fn test_derive_length_tcp() {
2451 let short_frame = [0x00, 0x01, 0x00, 0x00, 0x00];
2453 assert_eq!(
2454 derive_length_from_bytes(&short_frame, TransportType::StdTcp),
2455 None
2456 );
2457
2458 let full_frame = [
2460 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01,
2461 ];
2462 assert_eq!(
2463 derive_length_from_bytes(&full_frame, TransportType::StdTcp),
2464 Some(12)
2465 );
2466
2467 let garbage_frame = [
2469 0x00, 0x01, 0xAA, 0xBB, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01,
2470 ];
2471 assert_eq!(
2472 derive_length_from_bytes(&garbage_frame, TransportType::StdTcp),
2473 Some(usize::MAX)
2474 );
2475 }
2476
2477 #[test]
2478 fn test_derive_length_rtu_fixed() {
2479 let request = [0x01, 0x05, 0x00, 0x0A, 0xFF, 0x00, 0x00, 0x00];
2481 assert_eq!(
2482 derive_length_from_bytes(&request, TransportType::StdSerial(SerialMode::Rtu)),
2483 Some(8)
2484 );
2485 }
2486
2487 #[test]
2488 fn test_derive_length_rtu_dynamic() {
2489 let mut resp = [0x01, 0x03, 0x02, 0x12, 0x34, 0x00, 0x00];
2492 let crc = checksum::crc16(&resp[..5]);
2493 let crc_bytes = crc.to_le_bytes();
2494 resp[5] = crc_bytes[0];
2495 resp[6] = crc_bytes[1];
2496
2497 assert_eq!(
2499 derive_length_from_bytes(&resp, TransportType::StdSerial(SerialMode::Rtu)),
2500 Some(7)
2501 );
2502
2503 assert_eq!(
2505 derive_length_from_bytes(&resp[..4], TransportType::StdSerial(SerialMode::Rtu)),
2506 Some(8)
2507 );
2508 }
2509
2510 #[test]
2511 fn test_derive_length_rtu_exception() {
2512 let exception = [0x01, 0x81, 0x02, 0x00, 0x00];
2514 assert_eq!(
2515 derive_length_from_bytes(&exception, TransportType::StdSerial(SerialMode::Rtu)),
2516 Some(5)
2517 );
2518 }
2519
2520 #[test]
2521 fn test_derive_length_rtu_forward_scan() {
2522 let mut custom_frame = [0x01, 0x44, 0xAA, 0xBB, 0x00, 0x00];
2524 let crc = checksum::crc16(&custom_frame[..4]);
2525 let crc_bytes = crc.to_le_bytes();
2526 custom_frame[4] = crc_bytes[0];
2527 custom_frame[5] = crc_bytes[1];
2528
2529 assert_eq!(
2530 derive_length_from_bytes(&custom_frame, TransportType::StdSerial(SerialMode::Rtu)),
2531 Some(6)
2532 );
2533
2534 assert_eq!(
2536 derive_length_from_bytes(
2537 &custom_frame[..4],
2538 TransportType::StdSerial(SerialMode::Rtu)
2539 ),
2540 None
2541 );
2542 }
2543
2544 #[test]
2545 fn test_derive_length_ascii() {
2546 let frame = b":010300000001FB\r\n";
2547 assert_eq!(
2548 derive_length_from_bytes(frame, TransportType::StdSerial(SerialMode::Ascii)),
2549 Some(17)
2550 );
2551
2552 let partial = b":010300000001F";
2553 assert_eq!(
2554 derive_length_from_bytes(partial, TransportType::StdSerial(SerialMode::Ascii)),
2555 None
2556 );
2557 }
2558}