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 #[cfg(feature = "holding-registers")]
871 pub fn build_mask_write_register(
872 address: u16,
873 and_mask: u16,
874 or_mask: u16,
875 ) -> Result<Self, MbusError> {
876 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
877 data.extend_from_slice(&address.to_be_bytes())
878 .map_err(|_| MbusError::BufferLenMissmatch)?;
879 data.extend_from_slice(&and_mask.to_be_bytes())
880 .map_err(|_| MbusError::BufferLenMissmatch)?;
881 data.extend_from_slice(&or_mask.to_be_bytes())
882 .map_err(|_| MbusError::BufferLenMissmatch)?;
883 Ok(Pdu::new(FunctionCode::MaskWriteRegister, data, 6))
884 }
885
886 #[cfg(feature = "holding-registers")]
891 pub fn build_read_write_multiple(
892 read_address: u16,
893 read_quantity: u16,
894 write_address: u16,
895 write_quantity: u16,
896 write_values: &[u8],
897 ) -> Result<Self, MbusError> {
898 let byte_count = write_values.len();
899 if byte_count > u8::MAX as usize {
900 return Err(MbusError::InvalidByteCount);
901 }
902 let data_len = 9 + byte_count;
903 if data_len > MAX_PDU_DATA_LEN {
904 return Err(MbusError::BufferTooSmall);
905 }
906 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
907 data.extend_from_slice(&read_address.to_be_bytes())
908 .map_err(|_| MbusError::BufferLenMissmatch)?;
909 data.extend_from_slice(&read_quantity.to_be_bytes())
910 .map_err(|_| MbusError::BufferLenMissmatch)?;
911 data.extend_from_slice(&write_address.to_be_bytes())
912 .map_err(|_| MbusError::BufferLenMissmatch)?;
913 data.extend_from_slice(&write_quantity.to_be_bytes())
914 .map_err(|_| MbusError::BufferLenMissmatch)?;
915 data.push(byte_count as u8)
916 .map_err(|_| MbusError::BufferLenMissmatch)?;
917 data.extend_from_slice(write_values)
918 .map_err(|_| MbusError::BufferLenMissmatch)?;
919 Ok(Pdu::new(
920 FunctionCode::ReadWriteMultipleRegisters,
921 data,
922 data_len as u8,
923 ))
924 }
925
926 pub fn build_sub_function(
930 fc: FunctionCode,
931 sub_function: u16,
932 words: &[u16],
933 ) -> Result<Self, MbusError> {
934 let data_len = 2 + words.len() * 2;
935 if data_len > MAX_PDU_DATA_LEN {
936 return Err(MbusError::BufferTooSmall);
937 }
938 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
939 data.extend_from_slice(&sub_function.to_be_bytes())
940 .map_err(|_| MbusError::BufferLenMissmatch)?;
941 for &word in words {
942 data.extend_from_slice(&word.to_be_bytes())
943 .map_err(|_| MbusError::BufferLenMissmatch)?;
944 }
945 Ok(Pdu::new(fc, data, data_len as u8))
946 }
947
948 pub fn build_mei_type(
952 fc: FunctionCode,
953 mei_type: u8,
954 payload: &[u8],
955 ) -> Result<Self, MbusError> {
956 let data_len = 1 + payload.len();
957 if data_len > MAX_PDU_DATA_LEN {
958 return Err(MbusError::BufferTooSmall);
959 }
960 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
961 data.push(mei_type)
962 .map_err(|_| MbusError::BufferLenMissmatch)?;
963 data.extend_from_slice(payload)
964 .map_err(|_| MbusError::BufferLenMissmatch)?;
965 Ok(Pdu::new(fc, data, data_len as u8))
966 }
967
968 pub fn build_u16_payload(fc: FunctionCode, value: u16) -> Result<Self, MbusError> {
972 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
973 data.extend_from_slice(&value.to_be_bytes())
974 .map_err(|_| MbusError::BufferLenMissmatch)?;
975 Ok(Pdu::new(fc, data, 2))
976 }
977
978 pub fn build_byte_payload(fc: FunctionCode, byte: u8) -> Result<Self, MbusError> {
982 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
983 data.push(byte).map_err(|_| MbusError::BufferLenMissmatch)?;
984 Ok(Pdu::new(fc, data, 1))
985 }
986
987 pub fn build_empty(fc: FunctionCode) -> Self {
991 Pdu::new(fc, heapless::Vec::new(), 0)
992 }
993
994 pub fn build_byte_count_payload(fc: FunctionCode, payload: &[u8]) -> Result<Self, MbusError> {
1001 let byte_count = u8::try_from(payload.len()).map_err(|_| MbusError::InvalidByteCount)?;
1002 let data_len = 1 + payload.len();
1003 if data_len > MAX_PDU_DATA_LEN {
1004 return Err(MbusError::BufferTooSmall);
1005 }
1006 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
1007 data.push(byte_count)
1008 .map_err(|_| MbusError::BufferLenMissmatch)?;
1009 data.extend_from_slice(payload)
1010 .map_err(|_| MbusError::BufferLenMissmatch)?;
1011 Ok(Pdu::new(fc, data, data_len as u8))
1012 }
1013
1014 #[cfg(feature = "diagnostics")]
1019 pub fn build_diagnostics(sub_function: u16, result: u16) -> Result<Self, MbusError> {
1020 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
1021 data.extend_from_slice(&sub_function.to_be_bytes())
1022 .map_err(|_| MbusError::BufferLenMissmatch)?;
1023 data.extend_from_slice(&result.to_be_bytes())
1024 .map_err(|_| MbusError::BufferLenMissmatch)?;
1025 Ok(Pdu::new(FunctionCode::Diagnostics, data, 4))
1026 }
1027
1028 #[cfg(feature = "fifo")]
1034 pub fn build_fifo_payload(app_payload: &[u8]) -> Result<Self, MbusError> {
1035 let byte_count =
1036 u16::try_from(app_payload.len()).map_err(|_| MbusError::InvalidByteCount)?;
1037 let data_len = 2 + app_payload.len();
1038 if data_len > MAX_PDU_DATA_LEN {
1039 return Err(MbusError::BufferTooSmall);
1040 }
1041 let mut data: heapless::Vec<u8, MAX_PDU_DATA_LEN> = heapless::Vec::new();
1042 data.extend_from_slice(&byte_count.to_be_bytes())
1043 .map_err(|_| MbusError::BufferLenMissmatch)?;
1044 data.extend_from_slice(app_payload)
1045 .map_err(|_| MbusError::BufferLenMissmatch)?;
1046 Ok(Pdu::new(FunctionCode::ReadFifoQueue, data, data_len as u8))
1047 }
1048
1049 #[inline]
1054 fn read_address_quantity_pair(&self) -> Result<(u16, u16), MbusError> {
1055 Ok((
1056 u16::from_be_bytes([
1057 self.data[PDU_ADDRESS_OFFSET_1B],
1058 self.data[PDU_ADDRESS_OFFSET_2B],
1059 ]),
1060 u16::from_be_bytes([
1061 self.data[PDU_QUANTITY_OFFSET_1B],
1062 self.data[PDU_QUANTITY_OFFSET_2B],
1063 ]),
1064 ))
1065 }
1066
1067 pub fn read_window(&self) -> Result<ReadWindow, MbusError> {
1071 if self.data_len != 4 {
1072 return Err(MbusError::InvalidPduLength);
1073 }
1074
1075 let (address, quantity) = self.read_address_quantity_pair()?;
1076 Ok(ReadWindow { address, quantity })
1077 }
1078
1079 pub fn write_single_u16_fields(&self) -> Result<WriteSingleU16Fields, MbusError> {
1081 if self.data_len != 4 {
1082 return Err(MbusError::InvalidPduLength);
1083 }
1084
1085 Ok(WriteSingleU16Fields {
1086 address: u16::from_be_bytes([
1087 self.data[PDU_ADDRESS_OFFSET_1B],
1088 self.data[PDU_ADDRESS_OFFSET_2B],
1089 ]),
1090 value: u16::from_be_bytes([
1091 self.data[PDU_QUANTITY_OFFSET_1B],
1092 self.data[PDU_QUANTITY_OFFSET_2B],
1093 ]),
1094 })
1095 }
1096
1097 pub fn write_multiple_fields(&self) -> Result<WriteMultipleFields<'_>, MbusError> {
1099 if self.data_len < 5 {
1100 return Err(MbusError::InvalidPduLength);
1101 }
1102
1103 let (address, quantity) = self.read_address_quantity_pair()?;
1104 let byte_count = self.data[PDU_BYTE_COUNT_OFFSET];
1105 let expected_len = 5usize
1106 .checked_add(byte_count as usize)
1107 .ok_or(MbusError::InvalidByteCount)?;
1108
1109 if self.data_len as usize != expected_len {
1110 return Err(MbusError::InvalidByteCount);
1111 }
1112
1113 Ok(WriteMultipleFields {
1114 address,
1115 quantity,
1116 byte_count,
1117 values: &self.data[5..expected_len],
1118 })
1119 }
1120
1121 pub fn mask_write_register_fields(&self) -> Result<MaskWriteRegisterFields, MbusError> {
1123 if self.data_len != 6 {
1124 return Err(MbusError::InvalidPduLength);
1125 }
1126
1127 Ok(MaskWriteRegisterFields {
1128 address: u16::from_be_bytes([
1129 self.data[PDU_ADDRESS_OFFSET_1B],
1130 self.data[PDU_ADDRESS_OFFSET_2B],
1131 ]),
1132 and_mask: u16::from_be_bytes([
1133 self.data[PDU_AND_MASK_OFFSET_1B],
1134 self.data[PDU_AND_MASK_OFFSET_2B],
1135 ]),
1136 or_mask: u16::from_be_bytes([
1137 self.data[PDU_OR_MASK_OFFSET_1B],
1138 self.data[PDU_OR_MASK_OFFSET_2B],
1139 ]),
1140 })
1141 }
1142
1143 pub fn byte_count_payload(&self) -> Result<ByteCountPayload<'_>, MbusError> {
1145 if self.data_len < 1 {
1146 return Err(MbusError::InvalidPduLength);
1147 }
1148
1149 let byte_count = self.data[0];
1150 let expected_len = 1usize
1151 .checked_add(byte_count as usize)
1152 .ok_or(MbusError::InvalidByteCount)?;
1153 if self.data_len as usize != expected_len {
1154 return Err(MbusError::InvalidByteCount);
1155 }
1156
1157 Ok(ByteCountPayload {
1158 byte_count,
1159 payload: &self.data[1..expected_len],
1160 })
1161 }
1162
1163 pub fn read_write_multiple_fields(&self) -> Result<ReadWriteMultipleFields<'_>, MbusError> {
1165 if (self.data_len as usize) < PDU_FC17_WRITE_VALUES_OFFSET {
1166 return Err(MbusError::InvalidPduLength);
1167 }
1168
1169 let (read_address, read_quantity) = self.read_address_quantity_pair()?;
1170 let write_address = u16::from_be_bytes([
1171 self.data[PDU_FC17_WRITE_ADDRESS_OFFSET_1B],
1172 self.data[PDU_FC17_WRITE_ADDRESS_OFFSET_2B],
1173 ]);
1174 let write_quantity = u16::from_be_bytes([
1175 self.data[PDU_FC17_WRITE_QUANTITY_OFFSET_1B],
1176 self.data[PDU_FC17_WRITE_QUANTITY_OFFSET_2B],
1177 ]);
1178 let write_byte_count = self.data[PDU_FC17_WRITE_BYTE_COUNT_OFFSET];
1179 let expected_len = PDU_FC17_WRITE_VALUES_OFFSET
1180 .checked_add(write_byte_count as usize)
1181 .ok_or(MbusError::InvalidByteCount)?;
1182
1183 if self.data_len as usize != expected_len {
1184 return Err(MbusError::InvalidByteCount);
1185 }
1186
1187 Ok(ReadWriteMultipleFields {
1188 read_address,
1189 read_quantity,
1190 write_address,
1191 write_quantity,
1192 write_byte_count,
1193 write_values: &self.data[PDU_FC17_WRITE_VALUES_OFFSET..expected_len],
1194 })
1195 }
1196
1197 pub fn single_byte_payload(&self) -> Result<u8, MbusError> {
1199 if self.data_len != 1 {
1200 return Err(MbusError::InvalidPduLength);
1201 }
1202 Ok(self.data[0])
1203 }
1204
1205 pub fn sub_function_payload(&self) -> Result<SubFunctionPayload<'_>, MbusError> {
1208 if self.data_len < 2 {
1209 return Err(MbusError::InvalidPduLength);
1210 }
1211 if !self.data_len.is_multiple_of(2) {
1212 return Err(MbusError::InvalidPduLength);
1213 }
1214 Ok(SubFunctionPayload {
1215 sub_function: u16::from_be_bytes([
1216 self.data[PDU_SUB_FUNCTION_OFFSET_1B],
1217 self.data[PDU_SUB_FUNCTION_OFFSET_2B],
1218 ]),
1219 payload: &self.data[2..self.data_len as usize],
1220 })
1221 }
1222
1223 pub fn u16_pair_fields(&self) -> Result<U16PairFields, MbusError> {
1225 if self.data_len != 4 {
1226 return Err(MbusError::InvalidPduLength);
1227 }
1228 Ok(U16PairFields {
1229 first: u16::from_be_bytes([
1230 self.data[PDU_ADDRESS_OFFSET_1B],
1231 self.data[PDU_ADDRESS_OFFSET_2B],
1232 ]),
1233 second: u16::from_be_bytes([
1234 self.data[PDU_QUANTITY_OFFSET_1B],
1235 self.data[PDU_QUANTITY_OFFSET_2B],
1236 ]),
1237 })
1238 }
1239
1240 pub fn mei_type_payload(&self) -> Result<MeiTypePayload<'_>, MbusError> {
1243 if self.data_len < 1 {
1244 return Err(MbusError::InvalidPduLength);
1245 }
1246 Ok(MeiTypePayload {
1247 mei_type_byte: self.data[PDU_MEI_TYPE_OFFSET],
1248 payload: &self.data[1..self.data_len as usize],
1249 })
1250 }
1251
1252 pub fn fifo_payload(&self) -> Result<FifoPayload<'_>, MbusError> {
1256 if self.data_len < 4 {
1257 return Err(MbusError::InvalidPduLength);
1258 }
1259 Ok(FifoPayload {
1260 fifo_byte_count: u16::from_be_bytes([
1261 self.data[PDU_FIFO_BYTE_COUNT_OFFSET_1B],
1262 self.data[PDU_FIFO_BYTE_COUNT_OFFSET_2B],
1263 ]),
1264 fifo_count: u16::from_be_bytes([
1265 self.data[PDU_FIFO_COUNT_OFFSET_1B],
1266 self.data[PDU_FIFO_COUNT_OFFSET_2B],
1267 ]),
1268 values: &self.data[PDU_FIFO_VALUES_OFFSET..self.data_len as usize],
1269 })
1270 }
1271
1272 #[cfg(feature = "fifo")]
1275 pub fn fifo_pointer(&self) -> Result<u16, MbusError> {
1276 if self.data_len != 2 {
1277 return Err(MbusError::InvalidPduLength);
1278 }
1279 Ok(u16::from_be_bytes([
1280 self.data[PDU_ADDRESS_OFFSET_1B],
1281 self.data[PDU_ADDRESS_OFFSET_2B],
1282 ]))
1283 }
1284
1285 #[cfg(feature = "diagnostics")]
1288 pub fn diagnostics_fields(&self) -> Result<(u16, u16), MbusError> {
1289 if self.data_len < 4 {
1290 return Err(MbusError::InvalidPduLength);
1291 }
1292 let sub_function = u16::from_be_bytes([
1293 self.data[PDU_SUB_FUNCTION_OFFSET_1B],
1294 self.data[PDU_SUB_FUNCTION_OFFSET_2B],
1295 ]);
1296 let data_word = u16::from_be_bytes([self.data[2], self.data[3]]);
1297 Ok((sub_function, data_word))
1298 }
1299
1300 #[cfg(feature = "file-record")]
1310 pub fn file_record_read_sub_requests(
1311 &self,
1312 ) -> Result<
1313 heapless::Vec<
1314 crate::models::file_record::FileRecordReadSubRequest,
1315 { crate::models::file_record::MAX_SUB_REQUESTS_PER_PDU },
1316 >,
1317 MbusError,
1318 > {
1319 use crate::models::file_record::{FILE_RECORD_REF_TYPE, FileRecordReadSubRequest};
1320
1321 const FILE_RECORD_READ_SUB_REQUEST_LEN: usize = 7;
1322 const FILE_RECORD_MAX_REQUEST_BYTE_COUNT: usize = 245;
1323
1324 let data_len = self.data_len as usize;
1325 if data_len < 1 + FILE_RECORD_READ_SUB_REQUEST_LEN {
1326 return Err(MbusError::InvalidPduLength);
1327 }
1328
1329 let data = self.data.as_slice();
1330 let byte_count = data[0] as usize;
1331 if byte_count != data_len - 1 {
1332 return Err(MbusError::InvalidByteCount);
1333 }
1334 if byte_count > FILE_RECORD_MAX_REQUEST_BYTE_COUNT {
1335 return Err(MbusError::InvalidByteCount);
1336 }
1337 if !byte_count.is_multiple_of(FILE_RECORD_READ_SUB_REQUEST_LEN) {
1338 return Err(MbusError::InvalidByteCount);
1339 }
1340
1341 let mut out: heapless::Vec<
1342 FileRecordReadSubRequest,
1343 { crate::models::file_record::MAX_SUB_REQUESTS_PER_PDU },
1344 > = heapless::Vec::new();
1345 let mut index = 1usize;
1346 while index < data_len {
1347 if data[index] != FILE_RECORD_REF_TYPE {
1348 return Err(MbusError::InvalidValue);
1349 }
1350 let file_number = u16::from_be_bytes([data[index + 1], data[index + 2]]);
1351 let record_number = u16::from_be_bytes([data[index + 3], data[index + 4]]);
1352 let record_length = u16::from_be_bytes([data[index + 5], data[index + 6]]);
1353 if record_length == 0 {
1354 return Err(MbusError::InvalidQuantity);
1355 }
1356
1357 out.push(FileRecordReadSubRequest {
1358 file_number,
1359 record_number,
1360 record_length,
1361 })
1362 .map_err(|_| MbusError::TooManyFileReadSubRequests)?;
1363
1364 index += FILE_RECORD_READ_SUB_REQUEST_LEN;
1365 }
1366
1367 Ok(out)
1368 }
1369
1370 #[cfg(feature = "file-record")]
1381 pub fn file_record_write_sub_requests<'a>(
1382 &'a self,
1383 ) -> Result<
1384 heapless::Vec<
1385 crate::models::file_record::FileRecordWriteSubRequest<'a>,
1386 { crate::models::file_record::MAX_SUB_REQUESTS_PER_PDU },
1387 >,
1388 MbusError,
1389 > {
1390 use crate::models::file_record::{FILE_RECORD_REF_TYPE, FileRecordWriteSubRequest};
1391
1392 const FILE_RECORD_WRITE_SUB_REQUEST_HEADER_LEN: usize = 7;
1393 const FILE_RECORD_MAX_REQUEST_BYTE_COUNT: usize = 245;
1394
1395 let data_len = self.data_len as usize;
1396 if data_len < 1 + FILE_RECORD_WRITE_SUB_REQUEST_HEADER_LEN + 2 {
1397 return Err(MbusError::InvalidPduLength);
1398 }
1399
1400 let data = self.data.as_slice();
1401 let byte_count = data[0] as usize;
1402 if byte_count != data_len - 1 {
1403 return Err(MbusError::InvalidByteCount);
1404 }
1405 if byte_count > FILE_RECORD_MAX_REQUEST_BYTE_COUNT {
1406 return Err(MbusError::InvalidByteCount);
1407 }
1408
1409 let mut out: heapless::Vec<
1410 FileRecordWriteSubRequest,
1411 { crate::models::file_record::MAX_SUB_REQUESTS_PER_PDU },
1412 > = heapless::Vec::new();
1413 let mut index = 1usize;
1414 while index < data_len {
1415 if index + FILE_RECORD_WRITE_SUB_REQUEST_HEADER_LEN > data_len {
1416 return Err(MbusError::InvalidPduLength);
1417 }
1418 if data[index] != FILE_RECORD_REF_TYPE {
1419 return Err(MbusError::InvalidValue);
1420 }
1421
1422 let file_number = u16::from_be_bytes([data[index + 1], data[index + 2]]);
1423 let record_number = u16::from_be_bytes([data[index + 3], data[index + 4]]);
1424 let record_length = u16::from_be_bytes([data[index + 5], data[index + 6]]);
1425 if record_length == 0 {
1426 return Err(MbusError::InvalidQuantity);
1427 }
1428
1429 let data_bytes_len = record_length as usize * 2;
1430 let end_index = index
1431 .checked_add(FILE_RECORD_WRITE_SUB_REQUEST_HEADER_LEN + data_bytes_len)
1432 .ok_or(MbusError::InvalidByteCount)?;
1433 if end_index > data_len {
1434 return Err(MbusError::InvalidByteCount);
1435 }
1436
1437 out.push(FileRecordWriteSubRequest {
1438 file_number,
1439 record_number,
1440 record_length,
1441 record_data_bytes: &data
1442 [index + FILE_RECORD_WRITE_SUB_REQUEST_HEADER_LEN..end_index],
1443 })
1444 .map_err(|_| MbusError::TooManyFileReadSubRequests)?;
1445
1446 index = end_index;
1447 }
1448
1449 if out.is_empty() {
1450 return Err(MbusError::InvalidPduLength);
1451 }
1452
1453 Ok(out)
1454 }
1455
1456 pub fn read_device_id_fields(&self) -> Result<ReadDeviceIdPduFields, MbusError> {
1464 if (self.data_len as usize) < PDU_MEI_OBJECTS_DATA_OFFSET {
1466 return Err(MbusError::InvalidPduLength);
1467 }
1468 let data = self.data.as_slice();
1469 let number_of_objects = data[PDU_MEI_NUM_OBJECTS_OFFSET];
1470
1471 let mut offset = PDU_MEI_OBJECTS_DATA_OFFSET;
1473 for _ in 0..number_of_objects as usize {
1474 if offset + 2 > data.len() {
1475 return Err(MbusError::InvalidPduLength);
1476 }
1477 let obj_len = data[offset + 1] as usize;
1478 offset += 2;
1479 if offset + obj_len > data.len() {
1480 return Err(MbusError::InvalidPduLength);
1481 }
1482 offset += obj_len;
1483 }
1484
1485 let payload_len = (self.data_len as usize) - PDU_MEI_OBJECTS_DATA_OFFSET;
1486 if payload_len > MAX_PDU_DATA_LEN {
1487 return Err(MbusError::BufferTooSmall);
1488 }
1489 let mut objects_data = [0u8; MAX_PDU_DATA_LEN];
1490 if payload_len > 0 {
1491 objects_data[..payload_len].copy_from_slice(&data[PDU_MEI_OBJECTS_DATA_OFFSET..]);
1492 }
1493
1494 Ok(ReadDeviceIdPduFields {
1495 mei_type_byte: data[PDU_MEI_TYPE_OFFSET],
1496 read_device_id_code_byte: data[PDU_MEI_READ_CODE_OFFSET],
1497 conformity_level_byte: data[PDU_MEI_CONFORMITY_LEVEL_OFFSET],
1498 more_follows: data[PDU_MEI_MORE_FOLLOWS_OFFSET] == 0xFF,
1499 next_object_id_byte: data[PDU_MEI_NEXT_OBJECT_ID_OFFSET],
1500 number_of_objects,
1501 objects_data,
1502 payload_len,
1503 })
1504 }
1505
1506 pub fn to_bytes(&self) -> Result<Vec<u8, 253>, MbusError> {
1518 let mut pdu_bytes = Vec::new(); pdu_bytes
1520 .push(self.function_code as u8)
1521 .map_err(|_| MbusError::Unexpected)?; pdu_bytes
1524 .extend_from_slice(&self.data.as_slice()[..self.data_len as usize])
1525 .map_err(|_| MbusError::BufferLenMissmatch)?; Ok(pdu_bytes)
1528 }
1529
1530 pub fn from_bytes(bytes: &[u8]) -> Result<Self, MbusError> {
1540 if bytes.is_empty() {
1541 return Err(MbusError::InvalidPduLength);
1542 }
1543
1544 let error_code = if is_exception_code(bytes[0]) {
1545 if bytes.len() < 2 {
1546 return Err(MbusError::InvalidPduLength);
1547 }
1548 Some(bytes[1]) } else {
1550 None
1551 };
1552 let function_code = clear_exception_bit(bytes[0]); let function_code = FunctionCode::try_from(function_code)?;
1555
1556 let data_slice = &bytes[1..];
1557 let data_len = data_slice.len();
1558
1559 if data_len > MAX_PDU_DATA_LEN {
1560 return Err(MbusError::InvalidPduLength);
1561 }
1562
1563 let mut data = heapless::Vec::new();
1564 data.extend_from_slice(data_slice)
1565 .map_err(|_| MbusError::BufferLenMissmatch)?;
1566
1567 Ok(Pdu {
1568 function_code,
1569 error_code,
1570 data,
1571 data_len: data_len as u8,
1572 })
1573 }
1574}
1575
1576pub fn compile_adu_frame(
1578 txn_id: u16,
1579 unit_id: u8,
1580 pdu: Pdu,
1581 transport_type: TransportType,
1582) -> Result<Vec<u8, MAX_ADU_FRAME_LEN>, MbusError> {
1583 match transport_type {
1584 TransportType::StdTcp | TransportType::CustomTcp => {
1585 let pdu_bytes_len = pdu.to_bytes()?.len() as u16;
1586 let mbap_header = MbapHeader::new(txn_id, pdu_bytes_len + 1, unit_id);
1587 ModbusMessage::new(AdditionalAddress::MbapHeader(mbap_header), pdu).to_bytes()
1588 }
1589 TransportType::StdSerial(serial_mode) | TransportType::CustomSerial(serial_mode) => {
1590 let slave_address = SlaveAddress(unit_id);
1591 let adu_bytes = match serial_mode {
1592 SerialMode::Rtu => {
1593 let mut adu_bytes =
1594 ModbusMessage::new(AdditionalAddress::SlaveAddress(slave_address), pdu)
1595 .to_bytes()?;
1596 let crc16 = checksum::crc16(adu_bytes.as_slice());
1598 let crc_bytes = crc16.to_le_bytes();
1600 adu_bytes
1601 .extend_from_slice(&crc_bytes)
1602 .map_err(|_| MbusError::Unexpected)?;
1603
1604 adu_bytes
1605 }
1606 SerialMode::Ascii => {
1607 ModbusMessage::new(AdditionalAddress::SlaveAddress(slave_address), pdu)
1608 .to_ascii_bytes()?
1609 }
1610 };
1611
1612 Ok(adu_bytes)
1613 }
1614 }
1615}
1616
1617pub fn decompile_adu_frame(
1619 frame: &[u8],
1620 transport_type: TransportType,
1621) -> Result<ModbusMessage, MbusError> {
1622 match transport_type {
1623 TransportType::StdTcp | TransportType::CustomTcp => {
1624 ModbusMessage::from_bytes(frame)
1626 }
1627 TransportType::StdSerial(serial_mode) | TransportType::CustomSerial(serial_mode) => {
1628 match serial_mode {
1629 SerialMode::Rtu => ModbusMessage::from_rtu_bytes(frame),
1630 SerialMode::Ascii => ModbusMessage::from_ascii_bytes(frame),
1631 }
1632 }
1633 }
1634}
1635
1636pub fn derive_length_from_bytes(frame: &[u8], transport_type: TransportType) -> Option<usize> {
1649 match transport_type {
1650 TransportType::StdTcp | TransportType::CustomTcp => {
1651 if frame.len() < 6 {
1654 return None;
1655 }
1656
1657 let protocol_id = u16::from_be_bytes([frame[2], frame[3]]);
1661 if protocol_id != MODBUS_PROTOCOL_ID {
1662 return Some(usize::MAX);
1663 }
1664
1665 let length_field = u16::from_be_bytes([frame[4], frame[5]]) as usize;
1668 Some(6 + length_field)
1669 }
1670 TransportType::StdSerial(SerialMode::Rtu)
1671 | TransportType::CustomSerial(SerialMode::Rtu) => {
1672 if frame.len() < 2 {
1673 return None;
1674 }
1675
1676 let fc = frame[1];
1677
1678 if is_exception_code(fc) {
1680 return Some(5);
1681 }
1682
1683 let check_crc = |len: usize| -> bool {
1686 if frame.len() >= len && len >= MIN_RTU_ADU_LEN {
1687 let data_len = len - RTU_CRC_SIZE;
1688 let received_crc = u16::from_le_bytes([frame[data_len], frame[data_len + 1]]);
1689 checksum::crc16(&frame[..data_len]) == received_crc
1690 } else {
1691 false
1692 }
1693 };
1694
1695 get_byte_count_from_frame(frame, fc, check_crc)
1696 }
1697 TransportType::StdSerial(SerialMode::Ascii)
1698 | TransportType::CustomSerial(SerialMode::Ascii) => {
1699 if frame.len() < MIN_ASCII_ADU_LEN {
1702 return None;
1703 }
1704
1705 frame.iter().position(|&b| b == b'\n').map(|pos| pos + 1)
1707 }
1708 }
1709}
1710
1711fn get_byte_count_from_frame(
1712 frame: &[u8],
1713 fc: u8,
1714 check_crc: impl Fn(usize) -> bool,
1715) -> Option<usize> {
1716 let mut candidates = heapless::Vec::<usize, 4>::new();
1717 let mut min_needed = usize::MAX;
1718
1719 let mut add_dyn = |cands: &mut heapless::Vec<usize, 4>, offset: usize, base: usize| {
1723 if frame.len() > offset {
1724 let _ = cands.push(base + frame[offset] as usize);
1725 } else {
1726 min_needed = core::cmp::min(min_needed, offset + 1);
1727 }
1728 };
1729
1730 match fc {
1732 1..=4 => {
1733 let _ = candidates.push(8);
1734 add_dyn(&mut candidates, 2, 5);
1735 }
1736 5 | 6 | 8 => {
1737 let _ = candidates.push(8);
1738 }
1739 7 => {
1740 let _ = candidates.push(4);
1741 let _ = candidates.push(5);
1742 }
1743 11 => {
1744 let _ = candidates.push(4);
1745 let _ = candidates.push(8);
1746 }
1747 12 | 17 => {
1748 let _ = candidates.push(4);
1749 add_dyn(&mut candidates, 2, 5);
1750 }
1751 15 | 16 => {
1752 let _ = candidates.push(8);
1753 add_dyn(&mut candidates, 6, 9);
1754 }
1755 20 | 21 => add_dyn(&mut candidates, 2, 5),
1756 22 => {
1757 let _ = candidates.push(10);
1758 }
1759 23 => {
1760 add_dyn(&mut candidates, 2, 5);
1761 add_dyn(&mut candidates, 10, 13);
1762 }
1763 24 => {
1764 let _ = candidates.push(6);
1765 if frame.len() >= 4 {
1766 let byte_count = u16::from_be_bytes([frame[2], frame[3]]) as usize;
1767 let _ = candidates.push(6 + byte_count);
1768 } else {
1769 min_needed = core::cmp::min(min_needed, 4);
1770 }
1771 }
1772 43 => {
1773 if check_crc(7) {
1774 return Some(7);
1775 }
1776 for len in MIN_RTU_ADU_LEN..=frame.len() {
1778 if check_crc(len) {
1779 return Some(len);
1780 }
1781 }
1782 return None;
1783 }
1784 _ => {
1785 for len in MIN_RTU_ADU_LEN..=frame.len() {
1786 if check_crc(len) {
1787 return Some(len);
1788 }
1789 }
1790 return None;
1791 }
1792 }
1793
1794 for &len in &candidates {
1796 if check_crc(len) {
1797 return Some(len);
1798 }
1799 }
1800
1801 let max_candidate = candidates.iter().copied().max().unwrap_or(0);
1803 let target = if min_needed != usize::MAX {
1804 core::cmp::max(min_needed, max_candidate)
1805 } else {
1806 max_candidate
1807 };
1808
1809 if target > 0 { Some(target) } else { None }
1810}
1811
1812fn nibble_to_hex(nibble: u8) -> u8 {
1814 match nibble {
1815 0..=9 => b'0' + nibble,
1816 10..=15 => b'A' + (nibble - 10),
1817 _ => b'?', }
1819}
1820
1821fn hex_char_to_nibble(c: u8) -> Result<u8, MbusError> {
1823 match c {
1824 b'0'..=b'9' => Ok(c - b'0'),
1825 b'A'..=b'F' => Ok(c - b'A' + 10),
1826 b'a'..=b'f' => Ok(c - b'a' + 10),
1827 _ => Err(MbusError::BasicParseError),
1828 }
1829}
1830
1831fn hex_pair_to_byte(high: u8, low: u8) -> Result<u8, MbusError> {
1833 let h = hex_char_to_nibble(high)?;
1834 let l = hex_char_to_nibble(low)?;
1835 Ok((h << 4) | l)
1836}
1837
1838#[cfg(test)]
1839mod tests {
1840 use super::*;
1841 use crate::function_codes::public::FunctionCode;
1842 use heapless::Vec;
1843
1844 #[test]
1851 fn test_pdu_from_bytes_valid_no_data() {
1852 let bytes = [0x11];
1853 let pdu = Pdu::from_bytes(&bytes).expect("Should parse a function-code-only PDU");
1854
1855 assert_eq!(pdu.function_code, FunctionCode::ReportServerId);
1856 assert_eq!(pdu.data_len, 0);
1857 assert!(pdu.data.is_empty());
1858 assert_eq!(pdu.error_code, None);
1859 }
1860
1861 #[test]
1867 fn test_pdu_from_bytes_valid_read_coils_request() {
1868 let bytes = [0x01, 0x00, 0x00, 0x00, 0x0A]; let pdu = Pdu::from_bytes(&bytes).expect("Should successfully parse Read Coils request");
1872
1873 assert_eq!(pdu.function_code, FunctionCode::ReadCoils);
1874 assert_eq!(pdu.data_len, 4);
1875 assert_eq!(pdu.data.as_slice(), &[0x00, 0x00, 0x00, 0x0A]);
1876 }
1877
1878 #[test]
1884 fn test_pdu_from_bytes_valid_read_holding_registers_response() {
1885 let bytes = [0x03, 0x04, 0x12, 0x34, 0x56, 0x78]; let pdu = Pdu::from_bytes(&bytes)
1889 .expect("Should successfully parse Read Holding Registers response");
1890
1891 assert_eq!(pdu.function_code, FunctionCode::ReadHoldingRegisters);
1892 assert_eq!(pdu.data_len, 5); assert_eq!(pdu.data.as_slice(), &[0x04, 0x12, 0x34, 0x56, 0x78]);
1894 }
1895
1896 #[test]
1902 fn test_pdu_from_bytes_valid_max_data_length() {
1903 let mut bytes_vec: Vec<u8, 253> = Vec::new();
1905 let _ = bytes_vec.push(0x03); for i in 0..252 {
1907 let _ = bytes_vec.push(i as u8);
1908 }
1909 let bytes = bytes_vec.as_slice();
1910 let pdu = Pdu::from_bytes(bytes).expect("Should parse valid PDU with max data");
1911
1912 assert_eq!(pdu.function_code, FunctionCode::ReadHoldingRegisters);
1913 assert_eq!(pdu.data_len, 252);
1914 assert_eq!(pdu.data.as_slice(), &bytes[1..]);
1915 }
1916
1917 #[test]
1921 fn test_pdu_from_bytes_empty_slice_error() {
1922 let bytes = [];
1923 let err = Pdu::from_bytes(&bytes).expect_err("Should return error for empty slice");
1924 assert_eq!(err, MbusError::InvalidPduLength);
1925 }
1926
1927 #[test]
1932 fn test_pdu_from_bytes_invalid_function_code_error() {
1933 let bytes = [0x00, 0x01, 0x02];
1935 let err = Pdu::from_bytes(&bytes).expect_err("Should return error for invalid FC 0x00");
1936 assert_eq!(err, MbusError::UnsupportedFunction(0x00));
1937
1938 let bytes = [0xFF, 0x01, 0x02];
1940 let err = Pdu::from_bytes(&bytes).expect_err("Should return error for invalid FC 0xFF");
1941 assert_eq!(err, MbusError::UnsupportedFunction(0x7F));
1942 }
1943
1944 #[test]
1950 fn test_pdu_from_bytes_data_too_long_error() {
1951 let mut bytes_vec: Vec<u8, 254> = Vec::new();
1953 let _ = bytes_vec.push(0x03); for i in 0..253 {
1955 let _ = bytes_vec.push(i as u8);
1957 }
1958 let bytes = bytes_vec.as_slice();
1959 let err = Pdu::from_bytes(bytes).expect_err("Should return error for too much data");
1960 assert_eq!(err, MbusError::InvalidPduLength);
1961 }
1962
1963 #[test]
1964 fn test_pdu_read_window_parses_expected_fields() {
1965 let pdu = Pdu::from_bytes(&[0x03, 0x12, 0x34, 0x00, 0x02]).expect("valid pdu");
1966 let parsed = pdu.read_window().expect("read window should parse");
1967
1968 assert_eq!(
1969 parsed,
1970 ReadWindow {
1971 address: 0x1234,
1972 quantity: 0x0002
1973 }
1974 );
1975 }
1976
1977 #[test]
1978 fn test_pdu_write_single_u16_fields_parses_expected_fields() {
1979 let pdu = Pdu::from_bytes(&[0x06, 0x01, 0x02, 0xAB, 0xCD]).expect("valid pdu");
1980 let parsed = pdu
1981 .write_single_u16_fields()
1982 .expect("write single fields should parse");
1983
1984 assert_eq!(
1985 parsed,
1986 WriteSingleU16Fields {
1987 address: 0x0102,
1988 value: 0xABCD
1989 }
1990 );
1991 }
1992
1993 #[test]
1994 fn test_pdu_write_multiple_fields_parses_expected_fields() {
1995 let pdu = Pdu::from_bytes(&[0x10, 0x00, 0x20, 0x00, 0x02, 0x04, 0x12, 0x34, 0x56, 0x78])
1996 .expect("valid pdu");
1997 let parsed = pdu
1998 .write_multiple_fields()
1999 .expect("write multiple fields should parse");
2000
2001 assert_eq!(parsed.address, 0x0020);
2002 assert_eq!(parsed.quantity, 0x0002);
2003 assert_eq!(parsed.byte_count, 0x04);
2004 assert_eq!(parsed.values, &[0x12, 0x34, 0x56, 0x78]);
2005 }
2006
2007 #[test]
2008 fn test_pdu_mask_write_register_fields_parses_expected_fields() {
2009 let pdu = Pdu::from_bytes(&[0x16, 0x00, 0x10, 0xFF, 0x00, 0x00, 0x0F]).expect("valid pdu");
2010 let parsed = pdu
2011 .mask_write_register_fields()
2012 .expect("mask write fields should parse");
2013
2014 assert_eq!(
2015 parsed,
2016 MaskWriteRegisterFields {
2017 address: 0x0010,
2018 and_mask: 0xFF00,
2019 or_mask: 0x000F
2020 }
2021 );
2022 }
2023
2024 #[test]
2025 fn test_pdu_byte_count_payload_parses_expected_fields() {
2026 let pdu = Pdu::from_bytes(&[0x03, 0x04, 0x12, 0x34, 0x56, 0x78]).expect("valid pdu");
2027 let parsed = pdu
2028 .byte_count_payload()
2029 .expect("byte-count payload should parse");
2030
2031 assert_eq!(parsed.byte_count, 0x04);
2032 assert_eq!(parsed.payload, &[0x12, 0x34, 0x56, 0x78]);
2033 }
2034
2035 #[test]
2036 fn test_pdu_write_multiple_fields_rejects_mismatched_byte_count() {
2037 let pdu =
2038 Pdu::from_bytes(&[0x10, 0x00, 0x20, 0x00, 0x02, 0x03, 0x12, 0x34]).expect("valid pdu");
2039 let err = pdu
2040 .write_multiple_fields()
2041 .expect_err("byte count mismatch should error");
2042
2043 assert_eq!(err, MbusError::InvalidByteCount);
2044 }
2045
2046 #[test]
2047 fn test_pdu_byte_count_payload_rejects_mismatched_byte_count() {
2048 let pdu = Pdu::from_bytes(&[0x03, 0x03, 0x12, 0x34]).expect("valid pdu");
2049 let err = pdu
2050 .byte_count_payload()
2051 .expect_err("byte count mismatch should error");
2052
2053 assert_eq!(err, MbusError::InvalidByteCount);
2054 }
2055
2056 #[test]
2057 fn test_pdu_read_write_multiple_fields_parses_expected_fields() {
2058 let pdu = Pdu::from_bytes(&[
2059 0x17, 0x00, 0x10, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02, 0x04, 0x12, 0x34, 0x56, 0x78,
2060 ])
2061 .expect("valid pdu");
2062 let parsed = pdu
2063 .read_write_multiple_fields()
2064 .expect("read/write multiple fields should parse");
2065
2066 assert_eq!(parsed.read_address, 0x0010);
2067 assert_eq!(parsed.read_quantity, 0x0002);
2068 assert_eq!(parsed.write_address, 0x0020);
2069 assert_eq!(parsed.write_quantity, 0x0002);
2070 assert_eq!(parsed.write_byte_count, 0x04);
2071 assert_eq!(parsed.write_values, &[0x12, 0x34, 0x56, 0x78]);
2072 }
2073
2074 #[test]
2075 fn test_pdu_read_write_multiple_fields_rejects_short_pdu() {
2076 let pdu = Pdu::from_bytes(&[0x17, 0x00, 0x10, 0x00, 0x02, 0x00]).expect("valid pdu");
2077 let err = pdu
2078 .read_write_multiple_fields()
2079 .expect_err("PDU too short should error");
2080
2081 assert_eq!(err, MbusError::InvalidPduLength);
2082 }
2083
2084 #[test]
2085 fn test_pdu_read_write_multiple_fields_rejects_mismatched_byte_count() {
2086 let pdu = Pdu::from_bytes(&[
2087 0x17, 0x00, 0x10, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02, 0x05, 0x12, 0x34, 0x56, 0x78,
2088 ])
2089 .expect("valid pdu");
2090 let err = pdu
2091 .read_write_multiple_fields()
2092 .expect_err("byte count mismatch should error");
2093
2094 assert_eq!(err, MbusError::InvalidByteCount);
2095 }
2096
2097 #[test]
2105 fn test_pdu_to_bytes_no_data() {
2106 let pdu = Pdu::new(FunctionCode::ReportServerId, Vec::new(), 0);
2107 let bytes = pdu.to_bytes().expect("Should convert PDU to bytes");
2108 assert_eq!(bytes.as_slice(), &[0x11]);
2109 }
2110
2111 #[test]
2117 fn test_pdu_to_bytes_with_data() {
2118 let mut data_vec = Vec::new();
2119 data_vec
2120 .extend_from_slice(&[0x00, 0x00, 0x00, 0x0A])
2121 .unwrap(); let pdu = Pdu::new(FunctionCode::ReadCoils, data_vec, 4);
2124 let bytes = pdu.to_bytes().expect("Should convert PDU to bytes");
2125 assert_eq!(bytes.as_slice(), &[0x01, 0x00, 0x00, 0x00, 0x0A]);
2126 }
2127
2128 #[test]
2134 fn test_pdu_to_bytes_max_data() {
2135 let mut data_vec = Vec::new();
2136 for i in 0..252 {
2137 data_vec.push(i as u8).unwrap();
2138 }
2139
2140 let pdu = Pdu::new(FunctionCode::ReadHoldingRegisters, data_vec, 252);
2141 let bytes = pdu
2142 .to_bytes()
2143 .expect("Should convert PDU to bytes with max data");
2144 let mut expected_bytes_vec: Vec<u8, 253> = Vec::new();
2145 let _ = expected_bytes_vec.push(0x03);
2146 for i in 0..252 {
2147 let _ = expected_bytes_vec.push(i as u8);
2148 }
2149 assert_eq!(bytes.as_slice(), expected_bytes_vec.as_slice());
2150 }
2151
2152 #[test]
2157 fn test_modbus_message_to_bytes_tcp() {
2158 let mbap_header = MbapHeader {
2159 transaction_id: 0x1234,
2160 protocol_id: 0x0000,
2161 length: 0x0005, unit_id: 0x01,
2163 };
2164
2165 let mut pdu_data_vec: Vec<u8, MAX_PDU_DATA_LEN> = Vec::new();
2166 pdu_data_vec.extend_from_slice(&[0x00, 0x00, 0x00]).unwrap();
2167
2168 let pdu = Pdu::new(
2169 FunctionCode::ReadHoldingRegisters,
2170 pdu_data_vec,
2171 3, );
2173
2174 let modbus_message = ModbusMessage::new(AdditionalAddress::MbapHeader(mbap_header), pdu);
2175 let adu_bytes = modbus_message
2176 .to_bytes()
2177 .expect("Failed to serialize ModbusMessage");
2178
2179 #[rustfmt::skip]
2180 let expected_adu: [u8; 11] = [
2181 0x12, 0x34, 0x00, 0x00, 0x00, 0x05, 0x01, 0x03, 0x00, 0x00, 0x00, ];
2188
2189 assert_eq!(adu_bytes.as_slice(), &expected_adu);
2190 }
2191
2192 #[test]
2198 fn test_function_code_try_from_valid() {
2199 assert_eq!(
2200 FunctionCode::try_from(0x01).unwrap(),
2201 FunctionCode::ReadCoils
2202 );
2203 assert_eq!(
2204 FunctionCode::try_from(0x08).unwrap(),
2205 FunctionCode::Diagnostics
2206 );
2207 assert_eq!(
2208 FunctionCode::try_from(0x2B).unwrap(),
2209 FunctionCode::EncapsulatedInterfaceTransport
2210 );
2211 assert_eq!(
2212 FunctionCode::try_from(0x18).unwrap(),
2213 FunctionCode::ReadFifoQueue
2214 );
2215 assert_eq!(
2216 FunctionCode::try_from(0x11).unwrap(),
2217 FunctionCode::ReportServerId
2218 );
2219 }
2220
2221 #[test]
2226 fn test_function_code_try_from_invalid() {
2227 let err = FunctionCode::try_from(0x00).expect_err("Should error for invalid FC 0x00");
2228 assert_eq!(err, MbusError::UnsupportedFunction(0x00));
2229
2230 let err =
2231 FunctionCode::try_from(0x09).expect_err("Should error for invalid FC 0x09 (reserved)");
2232 assert_eq!(err, MbusError::UnsupportedFunction(0x09));
2233
2234 let err = FunctionCode::try_from(0x64)
2235 .expect_err("Should error for invalid FC 0x64 (private range, not public)");
2236 assert_eq!(err, MbusError::UnsupportedFunction(0x64));
2237 }
2238
2239 #[test]
2246 fn test_pdu_round_trip_with_data() {
2247 let original_bytes = [0x01, 0x00, 0x00, 0x00, 0x0A]; let pdu = Pdu::from_bytes(&original_bytes).expect("from_bytes failed");
2249 let new_bytes = pdu.to_bytes().expect("to_bytes failed");
2250 assert_eq!(original_bytes.as_slice(), new_bytes.as_slice());
2251 }
2252
2253 #[test]
2258 fn test_pdu_round_trip_max_data() {
2259 let mut original_bytes_vec: Vec<u8, 253> = Vec::new();
2260 let _ = original_bytes_vec.push(0x03); for i in 0..252 {
2262 let _ = original_bytes_vec.push(i as u8);
2263 }
2264 let original_bytes = original_bytes_vec.as_slice();
2265
2266 let pdu = Pdu::from_bytes(original_bytes).expect("from_bytes failed");
2267 let new_bytes = pdu.to_bytes().expect("to_bytes failed");
2268 assert_eq!(original_bytes, new_bytes.as_slice());
2269 }
2270
2271 #[test]
2281 fn test_modbus_message_to_ascii_bytes_valid() {
2282 let slave_addr = SlaveAddress::new(1).unwrap();
2283 let mut data = Vec::new();
2284 data.extend_from_slice(&[0x00, 0x00, 0x00, 0x0A]).unwrap();
2285 let pdu = Pdu::new(FunctionCode::ReadCoils, data, 4);
2286 let message = ModbusMessage::new(AdditionalAddress::SlaveAddress(slave_addr), pdu);
2287
2288 let ascii_bytes = message
2289 .to_ascii_bytes()
2290 .expect("Failed to convert to ASCII");
2291
2292 let expected = b":01010000000AF4\r\n";
2293 assert_eq!(ascii_bytes.as_slice(), expected);
2294 }
2295
2296 #[test]
2301 fn test_modbus_message_to_ascii_bytes_max_capacity() {
2302 let slave_addr = SlaveAddress::new(1).unwrap();
2303 let mut data = Vec::new();
2304 for _ in 0..125 {
2305 data.push(0xAA).unwrap();
2306 }
2307 let pdu = Pdu::new(FunctionCode::ReadHoldingRegisters, data, 125);
2308 let message = ModbusMessage::new(AdditionalAddress::SlaveAddress(slave_addr), pdu);
2309
2310 let ascii_bytes = message.to_ascii_bytes().expect("Should fit in buffer");
2311 assert_eq!(ascii_bytes.len(), 259);
2312 }
2313
2314 #[test]
2319 fn test_modbus_message_to_ascii_bytes_large_payload() {
2320 let slave_addr = SlaveAddress::new(1).unwrap();
2321 let mut data = Vec::new();
2322 for _ in 0..126 {
2323 data.push(0xAA).unwrap();
2324 }
2325 let pdu = Pdu::new(FunctionCode::ReadHoldingRegisters, data, 126);
2326 let message = ModbusMessage::new(AdditionalAddress::SlaveAddress(slave_addr), pdu);
2327
2328 let ascii_bytes = message.to_ascii_bytes().expect("Should fit in buffer");
2329 assert_eq!(ascii_bytes.len(), 261);
2330 }
2331
2332 #[test]
2335 fn test_decompile_adu_frame_tcp_valid() {
2336 let frame = [
2337 0x12, 0x34, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x01, 0x00, 0x02, ];
2344 let msg = decompile_adu_frame(&frame, TransportType::StdTcp)
2345 .expect("Should decode valid TCP frame");
2346 assert_eq!(msg.function_code(), FunctionCode::ReadHoldingRegisters);
2347 if let AdditionalAddress::MbapHeader(header) = msg.additional_address {
2348 assert_eq!(header.transaction_id, 0x1234);
2349 } else {
2350 panic!("Expected MbapHeader");
2351 }
2352 }
2353
2354 #[test]
2355 fn test_decompile_adu_frame_tcp_invalid() {
2356 let frame = [0x00]; let err = decompile_adu_frame(&frame, TransportType::StdTcp).expect_err("Should fail");
2358 assert_eq!(err, MbusError::InvalidAduLength);
2359 }
2360
2361 #[test]
2362 fn test_decompile_adu_frame_rtu_valid() {
2363 let frame = [0x01, 0x03, 0x00, 0x6B, 0x00, 0x03, 0x74, 0x17];
2365 let msg = decompile_adu_frame(&frame, TransportType::StdSerial(SerialMode::Rtu))
2366 .expect("Valid RTU");
2367 assert_eq!(msg.function_code(), FunctionCode::ReadHoldingRegisters);
2368 }
2369
2370 #[test]
2371 fn test_decompile_adu_frame_rtu_too_short() {
2372 let frame = [0x01, 0x02, 0x03];
2373 let err = decompile_adu_frame(&frame, TransportType::StdSerial(SerialMode::Rtu))
2374 .expect_err("Too short");
2375 assert_eq!(err, MbusError::InvalidAduLength);
2376 }
2377
2378 #[test]
2379 fn test_decompile_adu_frame_rtu_crc_mismatch() {
2380 let frame = [0x01, 0x03, 0x00, 0x6B, 0x00, 0x03, 0x00, 0x00]; let err = decompile_adu_frame(&frame, TransportType::StdSerial(SerialMode::Rtu))
2382 .expect_err("CRC mismatch");
2383 assert_eq!(err, MbusError::ChecksumError);
2384 }
2385
2386 #[test]
2387 fn test_decompile_adu_frame_ascii_valid() {
2388 let frame = b":010300000001FB\r\n";
2390 let msg = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2391 .expect("Valid ASCII");
2392 assert_eq!(msg.function_code(), FunctionCode::ReadHoldingRegisters);
2393 }
2394
2395 #[test]
2396 fn test_decompile_adu_frame_ascii_too_short() {
2397 let frame = b":123\r\n";
2398 let err = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2399 .expect_err("Too short");
2400 assert_eq!(err, MbusError::InvalidAduLength);
2401 }
2402
2403 #[test]
2404 fn test_decompile_adu_frame_ascii_missing_start() {
2405 let frame = b"010300000001FB\r\n";
2406 let err = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2407 .expect_err("Missing start");
2408 assert_eq!(err, MbusError::BasicParseError);
2409 }
2410
2411 #[test]
2412 fn test_decompile_adu_frame_ascii_missing_end() {
2413 let frame = b":010300000001FB\r"; let err = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2415 .expect_err("Missing end");
2416 assert_eq!(err, MbusError::BasicParseError);
2417 }
2418
2419 #[test]
2420 fn test_decompile_adu_frame_ascii_odd_hex() {
2421 let frame = b":010300000001F\r\n"; let err = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2423 .expect_err("Odd hex");
2424 assert_eq!(err, MbusError::BasicParseError);
2425 }
2426
2427 #[test]
2428 fn test_decompile_adu_frame_ascii_lrc_mismatch() {
2429 let frame = b":01030000000100\r\n"; let err = decompile_adu_frame(frame, TransportType::StdSerial(SerialMode::Ascii))
2431 .expect_err("LRC mismatch");
2432 assert_eq!(err, MbusError::ChecksumError);
2433 }
2434
2435 #[test]
2436 fn test_decompile_adu_frame_ascii_buffer_overflow() {
2437 let mut frame = Vec::<u8, 600>::new();
2439 frame.push(b':').unwrap();
2440 for _ in 0..261 {
2441 frame.extend_from_slice(b"00").unwrap();
2442 }
2443 frame.extend_from_slice(b"\r\n").unwrap();
2444 let err = decompile_adu_frame(&frame, TransportType::StdSerial(SerialMode::Ascii))
2445 .expect_err("Buffer overflow");
2446 assert_eq!(err, MbusError::BufferTooSmall);
2447 }
2448
2449 #[test]
2452 fn test_derive_length_tcp() {
2453 let short_frame = [0x00, 0x01, 0x00, 0x00, 0x00];
2455 assert_eq!(
2456 derive_length_from_bytes(&short_frame, TransportType::StdTcp),
2457 None
2458 );
2459
2460 let full_frame = [
2462 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01,
2463 ];
2464 assert_eq!(
2465 derive_length_from_bytes(&full_frame, TransportType::StdTcp),
2466 Some(12)
2467 );
2468
2469 let garbage_frame = [
2471 0x00, 0x01, 0xAA, 0xBB, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01,
2472 ];
2473 assert_eq!(
2474 derive_length_from_bytes(&garbage_frame, TransportType::StdTcp),
2475 Some(usize::MAX)
2476 );
2477 }
2478
2479 #[test]
2480 fn test_derive_length_rtu_fixed() {
2481 let request = [0x01, 0x05, 0x00, 0x0A, 0xFF, 0x00, 0x00, 0x00];
2483 assert_eq!(
2484 derive_length_from_bytes(&request, TransportType::StdSerial(SerialMode::Rtu)),
2485 Some(8)
2486 );
2487 }
2488
2489 #[test]
2490 fn test_derive_length_rtu_dynamic() {
2491 let mut resp = [0x01, 0x03, 0x02, 0x12, 0x34, 0x00, 0x00];
2494 let crc = checksum::crc16(&resp[..5]);
2495 let crc_bytes = crc.to_le_bytes();
2496 resp[5] = crc_bytes[0];
2497 resp[6] = crc_bytes[1];
2498
2499 assert_eq!(
2501 derive_length_from_bytes(&resp, TransportType::StdSerial(SerialMode::Rtu)),
2502 Some(7)
2503 );
2504
2505 assert_eq!(
2507 derive_length_from_bytes(&resp[..4], TransportType::StdSerial(SerialMode::Rtu)),
2508 Some(8)
2509 );
2510 }
2511
2512 #[test]
2513 fn test_derive_length_rtu_exception() {
2514 let exception = [0x01, 0x81, 0x02, 0x00, 0x00];
2516 assert_eq!(
2517 derive_length_from_bytes(&exception, TransportType::StdSerial(SerialMode::Rtu)),
2518 Some(5)
2519 );
2520 }
2521
2522 #[test]
2523 fn test_derive_length_rtu_forward_scan() {
2524 let mut custom_frame = [0x01, 0x44, 0xAA, 0xBB, 0x00, 0x00];
2526 let crc = checksum::crc16(&custom_frame[..4]);
2527 let crc_bytes = crc.to_le_bytes();
2528 custom_frame[4] = crc_bytes[0];
2529 custom_frame[5] = crc_bytes[1];
2530
2531 assert_eq!(
2532 derive_length_from_bytes(&custom_frame, TransportType::StdSerial(SerialMode::Rtu)),
2533 Some(6)
2534 );
2535
2536 assert_eq!(
2538 derive_length_from_bytes(
2539 &custom_frame[..4],
2540 TransportType::StdSerial(SerialMode::Rtu)
2541 ),
2542 None
2543 );
2544 }
2545
2546 #[test]
2547 fn test_derive_length_ascii() {
2548 let frame = b":010300000001FB\r\n";
2549 assert_eq!(
2550 derive_length_from_bytes(frame, TransportType::StdSerial(SerialMode::Ascii)),
2551 Some(17)
2552 );
2553
2554 let partial = b":010300000001F";
2555 assert_eq!(
2556 derive_length_from_bytes(partial, TransportType::StdSerial(SerialMode::Ascii)),
2557 None
2558 );
2559 }
2560}