1#![allow(clippy::needless_pass_by_value)]
10
11use bytes::Bytes;
12use rusty_modbus_codec::error::DecodeError;
13use rusty_modbus_codec::response::{
14 EncapsulatedInterfaceResponse, ExceptionResponse, GetCommEventCounterResponse,
15 GetCommEventLogResponse, MaskWriteRegisterResponse, ReadExceptionStatusResponse,
16 ReadFifoQueueResponse, ReadFileRecordResponse, WriteFileRecordResponse,
17 WriteMultipleCoilsResponse, WriteMultipleRegistersResponse, WriteSingleCoilResponse,
18 WriteSingleRegisterResponse,
19};
20use rusty_modbus_types::{DiagnosticSubFunction, FunctionCode, MeiType};
21
22fn pdu_data(pdu: &Bytes) -> Result<&[u8], DecodeError> {
23 if pdu.is_empty() {
24 return Err(DecodeError::Truncated {
25 expected: 1,
26 actual: 0,
27 });
28 }
29 Ok(&pdu[1..])
30}
31
32#[derive(Debug, Clone)]
38pub struct OwnedReadCoilsResponse {
39 pub byte_count: u8,
41 pub coil_status: Bytes,
43}
44
45impl OwnedReadCoilsResponse {
46 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
52 let data = pdu_data(&pdu)?;
53 if data.is_empty() {
54 return Err(DecodeError::Truncated {
55 expected: 1,
56 actual: 0,
57 });
58 }
59 let byte_count = data[0];
60 let payload = &data[1..];
61 if payload.len() != usize::from(byte_count) {
62 return Err(DecodeError::ByteCountMismatch {
63 declared: usize::from(byte_count),
64 actual: payload.len(),
65 });
66 }
67 let coil_status = pdu.slice(2..2 + usize::from(byte_count));
68 Ok(Self {
69 byte_count,
70 coil_status,
71 })
72 }
73
74 #[must_use]
80 pub fn coil(&self, index: usize) -> bool {
81 let byte_idx = index / 8;
82 let bit_idx = index % 8;
83 (self.coil_status[byte_idx] >> bit_idx) & 1 == 1
84 }
85}
86
87#[derive(Debug, Clone)]
89pub struct OwnedReadDiscreteInputsResponse {
90 pub byte_count: u8,
92 pub input_status: Bytes,
94}
95
96impl OwnedReadDiscreteInputsResponse {
97 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
103 let data = pdu_data(&pdu)?;
104 if data.is_empty() {
105 return Err(DecodeError::Truncated {
106 expected: 1,
107 actual: 0,
108 });
109 }
110 let byte_count = data[0];
111 let payload = &data[1..];
112 if payload.len() != usize::from(byte_count) {
113 return Err(DecodeError::ByteCountMismatch {
114 declared: usize::from(byte_count),
115 actual: payload.len(),
116 });
117 }
118 let input_status = pdu.slice(2..2 + usize::from(byte_count));
119 Ok(Self {
120 byte_count,
121 input_status,
122 })
123 }
124
125 #[must_use]
131 pub fn coil(&self, index: usize) -> bool {
132 let byte_idx = index / 8;
133 let bit_idx = index % 8;
134 (self.input_status[byte_idx] >> bit_idx) & 1 == 1
135 }
136}
137
138#[derive(Debug, Clone)]
144pub struct OwnedReadHoldingRegistersResponse {
145 pub byte_count: u8,
147 pub register_data: Bytes,
149}
150
151impl OwnedReadHoldingRegistersResponse {
152 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
158 let data = pdu_data(&pdu)?;
159 if data.is_empty() {
160 return Err(DecodeError::Truncated {
161 expected: 1,
162 actual: 0,
163 });
164 }
165 let byte_count = data[0];
166 let payload = &data[1..];
167 if payload.len() != usize::from(byte_count) {
168 return Err(DecodeError::ByteCountMismatch {
169 declared: usize::from(byte_count),
170 actual: payload.len(),
171 });
172 }
173 let register_data = pdu.slice(2..2 + usize::from(byte_count));
174 Ok(Self {
175 byte_count,
176 register_data,
177 })
178 }
179
180 #[must_use]
182 pub fn count(&self) -> usize {
183 self.register_data.len() / 2
184 }
185
186 #[must_use]
192 pub fn register(&self, index: usize) -> u16 {
193 let off = index * 2;
194 u16::from_be_bytes([self.register_data[off], self.register_data[off + 1]])
195 }
196
197 pub fn registers(&self) -> impl Iterator<Item = u16> + '_ {
199 self.register_data
200 .chunks_exact(2)
201 .map(|c| u16::from_be_bytes([c[0], c[1]]))
202 }
203
204 #[must_use]
206 pub fn raw(&self) -> &[u8] {
207 &self.register_data
208 }
209}
210
211#[derive(Debug, Clone)]
213pub struct OwnedReadInputRegistersResponse {
214 pub byte_count: u8,
216 pub register_data: Bytes,
218}
219
220impl OwnedReadInputRegistersResponse {
221 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
227 let data = pdu_data(&pdu)?;
228 if data.is_empty() {
229 return Err(DecodeError::Truncated {
230 expected: 1,
231 actual: 0,
232 });
233 }
234 let byte_count = data[0];
235 let payload = &data[1..];
236 if payload.len() != usize::from(byte_count) {
237 return Err(DecodeError::ByteCountMismatch {
238 declared: usize::from(byte_count),
239 actual: payload.len(),
240 });
241 }
242 let register_data = pdu.slice(2..2 + usize::from(byte_count));
243 Ok(Self {
244 byte_count,
245 register_data,
246 })
247 }
248
249 #[must_use]
251 pub fn count(&self) -> usize {
252 self.register_data.len() / 2
253 }
254
255 #[must_use]
261 pub fn register(&self, index: usize) -> u16 {
262 let off = index * 2;
263 u16::from_be_bytes([self.register_data[off], self.register_data[off + 1]])
264 }
265
266 pub fn registers(&self) -> impl Iterator<Item = u16> + '_ {
268 self.register_data
269 .chunks_exact(2)
270 .map(|c| u16::from_be_bytes([c[0], c[1]]))
271 }
272
273 #[must_use]
275 pub fn raw(&self) -> &[u8] {
276 &self.register_data
277 }
278}
279
280#[derive(Debug, Clone)]
286pub struct OwnedReadWriteMultipleRegistersResponse {
287 pub byte_count: u8,
289 pub register_data: Bytes,
291}
292
293impl OwnedReadWriteMultipleRegistersResponse {
294 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
300 let data = pdu_data(&pdu)?;
301 if data.is_empty() {
302 return Err(DecodeError::Truncated {
303 expected: 1,
304 actual: 0,
305 });
306 }
307 let byte_count = data[0];
308 let payload = &data[1..];
309 if payload.len() != usize::from(byte_count) {
310 return Err(DecodeError::ByteCountMismatch {
311 declared: usize::from(byte_count),
312 actual: payload.len(),
313 });
314 }
315 let register_data = pdu.slice(2..2 + usize::from(byte_count));
316 Ok(Self {
317 byte_count,
318 register_data,
319 })
320 }
321
322 #[must_use]
324 pub fn count(&self) -> usize {
325 self.register_data.len() / 2
326 }
327
328 #[must_use]
334 pub fn register(&self, index: usize) -> u16 {
335 let off = index * 2;
336 u16::from_be_bytes([self.register_data[off], self.register_data[off + 1]])
337 }
338
339 pub fn registers(&self) -> impl Iterator<Item = u16> + '_ {
341 self.register_data
342 .chunks_exact(2)
343 .map(|c| u16::from_be_bytes([c[0], c[1]]))
344 }
345
346 #[must_use]
348 pub fn raw(&self) -> &[u8] {
349 &self.register_data
350 }
351}
352
353#[derive(Debug, Clone)]
359pub struct OwnedReadFifoQueueResponse {
360 pub byte_count: u16,
362 pub fifo_count: u16,
364 pub fifo_values: Bytes,
366}
367
368impl OwnedReadFifoQueueResponse {
369 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
375 let data = pdu_data(&pdu)?;
376 let decoded = ReadFifoQueueResponse::decode(data)?;
377 let fifo_values = pdu.slice(5..5 + decoded.fifo_values.len());
378 Ok(Self {
379 byte_count: decoded.byte_count,
380 fifo_count: decoded.fifo_count,
381 fifo_values,
382 })
383 }
384}
385
386#[derive(Debug, Clone)]
392pub struct OwnedReadFileRecordResponse {
393 pub byte_count: u8,
395 pub data: Bytes,
397}
398
399impl OwnedReadFileRecordResponse {
400 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
406 let data = pdu_data(&pdu)?;
407 let decoded = ReadFileRecordResponse::decode(data)?;
408 let owned_data = pdu.slice(2..2 + decoded.data.len());
409 Ok(Self {
410 byte_count: decoded.byte_count,
411 data: owned_data,
412 })
413 }
414}
415
416#[derive(Debug, Clone)]
418pub struct OwnedWriteFileRecordResponse {
419 pub byte_count: u8,
421 pub data: Bytes,
423}
424
425impl OwnedWriteFileRecordResponse {
426 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
432 let data = pdu_data(&pdu)?;
433 let decoded = WriteFileRecordResponse::decode(data)?;
434 let owned_data = pdu.slice(2..2 + decoded.data.len());
435 Ok(Self {
436 byte_count: decoded.byte_count,
437 data: owned_data,
438 })
439 }
440}
441
442#[derive(Debug, Clone)]
448pub struct OwnedDiagnosticsResponse {
449 pub sub_function: DiagnosticSubFunction,
451 pub data: Bytes,
453}
454
455impl OwnedDiagnosticsResponse {
456 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
462 let data = pdu_data(&pdu)?;
463 if data.len() < 2 {
464 return Err(DecodeError::Truncated {
465 expected: 2,
466 actual: data.len(),
467 });
468 }
469 let raw_sub = u16::from_be_bytes([data[0], data[1]]);
470 let sub_function = DiagnosticSubFunction::from_raw(raw_sub)
471 .ok_or(DecodeError::UnknownDiagnosticSubFunction(raw_sub))?;
472 let payload = &data[2..];
473 if !payload.len().is_multiple_of(2) {
474 return Err(DecodeError::InvalidDiagnosticDataLength {
475 length: payload.len(),
476 });
477 }
478 let owned_data = pdu.slice(3..);
479 Ok(Self {
480 sub_function,
481 data: owned_data,
482 })
483 }
484}
485
486#[derive(Debug, Clone)]
488pub struct OwnedGetCommEventLogResponse {
489 pub byte_count: u8,
491 pub status: u16,
493 pub event_count: u16,
495 pub message_count: u16,
497 pub events: Bytes,
499}
500
501impl OwnedGetCommEventLogResponse {
502 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
508 let data = pdu_data(&pdu)?;
509 let decoded = GetCommEventLogResponse::decode(data)?;
510 let events = pdu.slice(8..8 + decoded.events.len());
511 Ok(Self {
512 byte_count: decoded.byte_count,
513 status: decoded.status,
514 event_count: decoded.event_count,
515 message_count: decoded.message_count,
516 events,
517 })
518 }
519}
520
521#[derive(Debug, Clone)]
523pub struct OwnedReportServerIdResponse {
524 pub byte_count: u8,
526 pub data: Bytes,
528}
529
530impl OwnedReportServerIdResponse {
531 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
537 let data = pdu_data(&pdu)?;
538 if data.is_empty() {
539 return Err(DecodeError::Truncated {
540 expected: 1,
541 actual: 0,
542 });
543 }
544 let byte_count = data[0];
545 let payload = &data[1..];
546 if payload.len() != usize::from(byte_count) {
547 return Err(DecodeError::ByteCountMismatch {
548 declared: usize::from(byte_count),
549 actual: payload.len(),
550 });
551 }
552 let owned_data = pdu.slice(2..2 + usize::from(byte_count));
553 Ok(Self {
554 byte_count,
555 data: owned_data,
556 })
557 }
558}
559
560#[derive(Debug, Clone)]
566pub struct OwnedEncapsulatedInterfaceResponse {
567 pub mei_type: MeiType,
569 pub data: Bytes,
571}
572
573impl OwnedEncapsulatedInterfaceResponse {
574 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
580 let data = pdu_data(&pdu)?;
581 let decoded = EncapsulatedInterfaceResponse::decode(data)?;
582 let owned_data = pdu.slice(2..);
583 Ok(Self {
584 mei_type: decoded.mei_type,
585 data: owned_data,
586 })
587 }
588}
589
590#[derive(Debug, Clone, Default)]
599pub struct OwnedDeviceIdentification {
600 pub vendor_name: Option<String>,
602 pub product_code: Option<String>,
604 pub major_minor_revision: Option<String>,
606}
607
608#[derive(Debug)]
617pub enum OwnedResponsePdu {
618 ReadCoils(OwnedReadCoilsResponse),
620 ReadDiscreteInputs(OwnedReadDiscreteInputsResponse),
622 ReadHoldingRegisters(OwnedReadHoldingRegistersResponse),
624 ReadInputRegisters(OwnedReadInputRegistersResponse),
626 WriteSingleCoil(WriteSingleCoilResponse),
628 WriteSingleRegister(WriteSingleRegisterResponse),
630 ReadExceptionStatus(ReadExceptionStatusResponse),
632 Diagnostics(OwnedDiagnosticsResponse),
634 GetCommEventCounter(GetCommEventCounterResponse),
636 GetCommEventLog(OwnedGetCommEventLogResponse),
638 WriteMultipleCoils(WriteMultipleCoilsResponse),
640 WriteMultipleRegisters(WriteMultipleRegistersResponse),
642 ReportServerId(OwnedReportServerIdResponse),
644 ReadFileRecord(OwnedReadFileRecordResponse),
646 WriteFileRecord(OwnedWriteFileRecordResponse),
648 MaskWriteRegister(MaskWriteRegisterResponse),
650 ReadWriteMultipleRegisters(OwnedReadWriteMultipleRegistersResponse),
652 ReadFifoQueue(OwnedReadFifoQueueResponse),
654 EncapsulatedInterface(OwnedEncapsulatedInterfaceResponse),
656 Custom(u8, Bytes),
658 Exception(ExceptionResponse),
660}
661
662impl OwnedResponsePdu {
663 pub fn from_pdu(pdu: Bytes) -> Result<Self, DecodeError> {
672 if pdu.is_empty() {
673 return Err(DecodeError::Truncated {
674 expected: 1,
675 actual: 0,
676 });
677 }
678
679 let fc_byte = pdu[0];
680
681 if FunctionCode::is_exception_response(fc_byte) {
683 let resp = ExceptionResponse::decode(fc_byte, &pdu[1..])?;
684 return Ok(Self::Exception(resp));
685 }
686
687 let fc = FunctionCode::from_raw(fc_byte).unwrap_or(FunctionCode::Custom(fc_byte));
690
691 let data = &pdu[1..];
692
693 match fc {
694 FunctionCode::ReadCoils => OwnedReadCoilsResponse::from_pdu(pdu).map(Self::ReadCoils),
695 FunctionCode::ReadDiscreteInputs => {
696 OwnedReadDiscreteInputsResponse::from_pdu(pdu).map(Self::ReadDiscreteInputs)
697 }
698 FunctionCode::ReadHoldingRegisters => {
699 OwnedReadHoldingRegistersResponse::from_pdu(pdu).map(Self::ReadHoldingRegisters)
700 }
701 FunctionCode::ReadInputRegisters => {
702 OwnedReadInputRegistersResponse::from_pdu(pdu).map(Self::ReadInputRegisters)
703 }
704 FunctionCode::WriteSingleCoil => {
705 WriteSingleCoilResponse::decode(data).map(Self::WriteSingleCoil)
706 }
707 FunctionCode::WriteSingleRegister => {
708 WriteSingleRegisterResponse::decode(data).map(Self::WriteSingleRegister)
709 }
710 FunctionCode::ReadExceptionStatus => {
711 ReadExceptionStatusResponse::decode(data).map(Self::ReadExceptionStatus)
712 }
713 FunctionCode::Diagnostics => {
714 OwnedDiagnosticsResponse::from_pdu(pdu).map(Self::Diagnostics)
715 }
716 FunctionCode::GetCommEventCounter => {
717 GetCommEventCounterResponse::decode(data).map(Self::GetCommEventCounter)
718 }
719 FunctionCode::GetCommEventLog => {
720 OwnedGetCommEventLogResponse::from_pdu(pdu).map(Self::GetCommEventLog)
721 }
722 FunctionCode::WriteMultipleCoils => {
723 WriteMultipleCoilsResponse::decode(data).map(Self::WriteMultipleCoils)
724 }
725 FunctionCode::WriteMultipleRegisters => {
726 WriteMultipleRegistersResponse::decode(data).map(Self::WriteMultipleRegisters)
727 }
728 FunctionCode::ReportServerId => {
729 OwnedReportServerIdResponse::from_pdu(pdu).map(Self::ReportServerId)
730 }
731 FunctionCode::ReadFileRecord => {
732 OwnedReadFileRecordResponse::from_pdu(pdu).map(Self::ReadFileRecord)
733 }
734 FunctionCode::WriteFileRecord => {
735 OwnedWriteFileRecordResponse::from_pdu(pdu).map(Self::WriteFileRecord)
736 }
737 FunctionCode::MaskWriteRegister => {
738 MaskWriteRegisterResponse::decode(data).map(Self::MaskWriteRegister)
739 }
740 FunctionCode::ReadWriteMultipleRegisters => {
741 OwnedReadWriteMultipleRegistersResponse::from_pdu(pdu)
742 .map(Self::ReadWriteMultipleRegisters)
743 }
744 FunctionCode::ReadFifoQueue => {
745 OwnedReadFifoQueueResponse::from_pdu(pdu).map(Self::ReadFifoQueue)
746 }
747 FunctionCode::EncapsulatedInterfaceTransport => {
748 OwnedEncapsulatedInterfaceResponse::from_pdu(pdu).map(Self::EncapsulatedInterface)
749 }
750 FunctionCode::Custom(fc) => Ok(Self::Custom(fc, pdu.slice(1..))),
751 }
752 }
753
754 #[must_use]
760 pub fn function_code(&self) -> u8 {
761 match self {
762 Self::ReadCoils(_) => FunctionCode::ReadCoils.code(),
763 Self::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs.code(),
764 Self::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters.code(),
765 Self::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters.code(),
766 Self::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil.code(),
767 Self::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister.code(),
768 Self::ReadExceptionStatus(_) => FunctionCode::ReadExceptionStatus.code(),
769 Self::Diagnostics(_) => FunctionCode::Diagnostics.code(),
770 Self::GetCommEventCounter(_) => FunctionCode::GetCommEventCounter.code(),
771 Self::GetCommEventLog(_) => FunctionCode::GetCommEventLog.code(),
772 Self::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils.code(),
773 Self::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters.code(),
774 Self::ReportServerId(_) => FunctionCode::ReportServerId.code(),
775 Self::ReadFileRecord(_) => FunctionCode::ReadFileRecord.code(),
776 Self::WriteFileRecord(_) => FunctionCode::WriteFileRecord.code(),
777 Self::MaskWriteRegister(_) => FunctionCode::MaskWriteRegister.code(),
778 Self::ReadWriteMultipleRegisters(_) => FunctionCode::ReadWriteMultipleRegisters.code(),
779 Self::ReadFifoQueue(_) => FunctionCode::ReadFifoQueue.code(),
780 Self::EncapsulatedInterface(_) => FunctionCode::EncapsulatedInterfaceTransport.code(),
781 Self::Custom(fc, _) => *fc,
782 Self::Exception(e) => e.function_code.exception_code(),
783 }
784 }
785}