1#[cfg(feature = "rtu")]
5pub(crate) mod rtu;
6
7#[cfg(feature = "tcp")]
8pub(crate) mod tcp;
9
10use std::{
11 borrow::Cow,
12 error,
13 fmt::{self, Display},
14};
15
16use crate::bytes::Bytes;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum FunctionCode {
23 ReadCoils,
25
26 ReadDiscreteInputs,
28
29 ReadHoldingRegisters,
31
32 ReadInputRegisters,
34
35 WriteSingleCoil,
37
38 WriteSingleRegister,
40
41 ReadExceptionStatus,
43
44 Diagnostics,
46
47 GetCommEventCounter,
49
50 GetCommEventLog,
52
53 WriteMultipleCoils,
55
56 WriteMultipleRegisters,
58
59 ReportServerId,
61
62 ReadFileRecord,
64
65 WriteFileRecord,
67
68 MaskWriteRegister,
70
71 ReadWriteMultipleRegisters,
73
74 ReadFifoQueue,
76
77 EncapsulatedInterfaceTransport,
79
80 Custom(u8),
82}
83
84impl FunctionCode {
85 #[must_use]
87 pub const fn new(value: u8) -> Self {
88 match value {
89 0x01 => Self::ReadCoils,
90 0x02 => Self::ReadDiscreteInputs,
91 0x03 => Self::ReadHoldingRegisters,
92 0x04 => Self::ReadInputRegisters,
93 0x05 => Self::WriteSingleCoil,
94 0x06 => Self::WriteSingleRegister,
95 0x07 => Self::ReadExceptionStatus,
96 0x08 => Self::Diagnostics,
97 0x0B => Self::GetCommEventCounter,
98 0x0C => Self::GetCommEventLog,
99 0x0F => Self::WriteMultipleCoils,
100 0x10 => Self::WriteMultipleRegisters,
101 0x11 => Self::ReportServerId,
102 0x14 => Self::ReadFileRecord,
103 0x15 => Self::WriteFileRecord,
104 0x16 => Self::MaskWriteRegister,
105 0x17 => Self::ReadWriteMultipleRegisters,
106 0x18 => Self::ReadFifoQueue,
107 0x2B => Self::EncapsulatedInterfaceTransport,
108 code => Self::Custom(code),
109 }
110 }
111
112 #[must_use]
114 pub const fn value(self) -> u8 {
115 match self {
116 Self::ReadCoils => 0x01,
117 Self::ReadDiscreteInputs => 0x02,
118 Self::ReadHoldingRegisters => 0x03,
119 Self::ReadInputRegisters => 0x04,
120 Self::WriteSingleCoil => 0x05,
121 Self::WriteSingleRegister => 0x06,
122 Self::ReadExceptionStatus => 0x07,
123 Self::Diagnostics => 0x08,
124 Self::GetCommEventCounter => 0x0B,
125 Self::GetCommEventLog => 0x0C,
126 Self::WriteMultipleCoils => 0x0F,
127 Self::WriteMultipleRegisters => 0x10,
128 Self::ReportServerId => 0x11,
129 Self::ReadFileRecord => 0x14,
130 Self::WriteFileRecord => 0x15,
131 Self::MaskWriteRegister => 0x16,
132 Self::ReadWriteMultipleRegisters => 0x17,
133 Self::ReadFifoQueue => 0x18,
134 Self::EncapsulatedInterfaceTransport => 0x2B,
135 Self::Custom(code) => code,
136 }
137 }
138}
139
140impl Display for FunctionCode {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 self.value().fmt(f)
143 }
144}
145
146pub type Address = u16;
153
154pub(crate) type Coil = bool;
159
160pub(crate) type Word = u16;
164
165pub type Quantity = u16;
167
168#[derive(Debug, Clone, PartialEq, Eq)]
170pub enum Request<'a> {
171 ReadCoils(Address, Quantity),
175
176 ReadDiscreteInputs(Address, Quantity),
180
181 WriteSingleCoil(Address, Coil),
185
186 WriteMultipleCoils(Address, Cow<'a, [Coil]>),
190
191 ReadInputRegisters(Address, Quantity),
195
196 ReadHoldingRegisters(Address, Quantity),
200
201 WriteSingleRegister(Address, Word),
205
206 WriteMultipleRegisters(Address, Cow<'a, [Word]>),
210
211 ReportServerId,
213
214 MaskWriteRegister(Address, Word, Word),
219
220 ReadWriteMultipleRegisters(Address, Quantity, Address, Cow<'a, [Word]>),
226
227 Custom(u8, Cow<'a, [u8]>),
231}
232
233impl Request<'_> {
234 #[must_use]
236 pub fn into_owned(self) -> Request<'static> {
237 use Request::*;
238
239 match self {
240 ReadCoils(addr, qty) => ReadCoils(addr, qty),
241 ReadDiscreteInputs(addr, qty) => ReadDiscreteInputs(addr, qty),
242 WriteSingleCoil(addr, coil) => WriteSingleCoil(addr, coil),
243 WriteMultipleCoils(addr, coils) => {
244 WriteMultipleCoils(addr, Cow::Owned(coils.into_owned()))
245 }
246 ReadInputRegisters(addr, qty) => ReadInputRegisters(addr, qty),
247 ReadHoldingRegisters(addr, qty) => ReadHoldingRegisters(addr, qty),
248 WriteSingleRegister(addr, word) => WriteSingleRegister(addr, word),
249 WriteMultipleRegisters(addr, words) => {
250 WriteMultipleRegisters(addr, Cow::Owned(words.into_owned()))
251 }
252 ReportServerId => ReportServerId,
253 MaskWriteRegister(addr, and_mask, or_mask) => {
254 MaskWriteRegister(addr, and_mask, or_mask)
255 }
256 ReadWriteMultipleRegisters(addr, qty, write_addr, words) => {
257 ReadWriteMultipleRegisters(addr, qty, write_addr, Cow::Owned(words.into_owned()))
258 }
259 Custom(func, bytes) => Custom(func, Cow::Owned(bytes.into_owned())),
260 }
261 }
262
263 #[must_use]
265 pub const fn function_code(&self) -> FunctionCode {
266 use Request::*;
267
268 match self {
269 ReadCoils(_, _) => FunctionCode::ReadCoils,
270 ReadDiscreteInputs(_, _) => FunctionCode::ReadDiscreteInputs,
271
272 WriteSingleCoil(_, _) => FunctionCode::WriteSingleCoil,
273 WriteMultipleCoils(_, _) => FunctionCode::WriteMultipleCoils,
274
275 ReadInputRegisters(_, _) => FunctionCode::ReadInputRegisters,
276 ReadHoldingRegisters(_, _) => FunctionCode::ReadHoldingRegisters,
277
278 WriteSingleRegister(_, _) => FunctionCode::WriteSingleRegister,
279 WriteMultipleRegisters(_, _) => FunctionCode::WriteMultipleRegisters,
280
281 ReportServerId => FunctionCode::ReportServerId,
282
283 MaskWriteRegister(_, _, _) => FunctionCode::MaskWriteRegister,
284
285 ReadWriteMultipleRegisters(_, _, _, _) => FunctionCode::ReadWriteMultipleRegisters,
286
287 Custom(code, _) => FunctionCode::Custom(*code),
288 }
289 }
290}
291
292#[cfg(feature = "server")]
294#[derive(Debug, Clone, PartialEq, Eq)]
295pub struct SlaveRequest<'a> {
296 pub slave: crate::slave::SlaveId,
298 pub request: Request<'a>,
300}
301
302#[cfg(feature = "server")]
303impl SlaveRequest<'_> {
304 #[must_use]
306 pub fn into_owned(self) -> SlaveRequest<'static> {
307 let Self { slave, request } = self;
308 SlaveRequest {
309 slave,
310 request: request.into_owned(),
311 }
312 }
313}
314
315#[derive(Debug, Clone, PartialEq, Eq)]
322pub enum Response {
323 ReadCoils(Vec<Coil>),
327
328 ReadDiscreteInputs(Vec<Coil>),
332
333 WriteSingleCoil(Address, Coil),
337
338 WriteMultipleCoils(Address, Quantity),
342
343 ReadInputRegisters(Vec<Word>),
346
347 ReadHoldingRegisters(Vec<Word>),
350
351 WriteSingleRegister(Address, Word),
355
356 WriteMultipleRegisters(Address, Quantity),
360
361 ReportServerId(u8, bool, Vec<u8>),
366
367 MaskWriteRegister(Address, Word, Word),
372
373 ReadWriteMultipleRegisters(Vec<Word>),
376
377 Custom(u8, Bytes),
381}
382
383impl Response {
384 #[must_use]
386 pub const fn function_code(&self) -> FunctionCode {
387 use Response::*;
388
389 match self {
390 ReadCoils(_) => FunctionCode::ReadCoils,
391 ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs,
392
393 WriteSingleCoil(_, _) => FunctionCode::WriteSingleCoil,
394 WriteMultipleCoils(_, _) => FunctionCode::WriteMultipleCoils,
395
396 ReadInputRegisters(_) => FunctionCode::ReadInputRegisters,
397 ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters,
398
399 WriteSingleRegister(_, _) => FunctionCode::WriteSingleRegister,
400 WriteMultipleRegisters(_, _) => FunctionCode::WriteMultipleRegisters,
401
402 ReportServerId(_, _, _) => FunctionCode::ReportServerId,
403
404 MaskWriteRegister(_, _, _) => FunctionCode::MaskWriteRegister,
405
406 ReadWriteMultipleRegisters(_) => FunctionCode::ReadWriteMultipleRegisters,
407
408 Custom(code, _) => FunctionCode::Custom(*code),
409 }
410 }
411}
412
413#[derive(Debug, Clone, Copy, PartialEq, Eq)]
415pub enum ExceptionCode {
416 IllegalFunction,
418 IllegalDataAddress,
420 IllegalDataValue,
422 ServerDeviceFailure,
424 Acknowledge,
426 ServerDeviceBusy,
428 MemoryParityError,
430 GatewayPathUnavailable,
432 GatewayTargetDevice,
434 Custom(u8),
439}
440
441impl From<ExceptionCode> for u8 {
442 fn from(from: ExceptionCode) -> Self {
443 use crate::frame::ExceptionCode::*;
444 match from {
445 IllegalFunction => 0x01,
446 IllegalDataAddress => 0x02,
447 IllegalDataValue => 0x03,
448 ServerDeviceFailure => 0x04,
449 Acknowledge => 0x05,
450 ServerDeviceBusy => 0x06,
451 MemoryParityError => 0x08,
452 GatewayPathUnavailable => 0x0A,
453 GatewayTargetDevice => 0x0B,
454 Custom(code) => code,
455 }
456 }
457}
458
459impl ExceptionCode {
460 #[must_use]
462 pub const fn new(value: u8) -> Self {
463 use crate::frame::ExceptionCode::*;
464
465 match value {
466 0x01 => IllegalFunction,
467 0x02 => IllegalDataAddress,
468 0x03 => IllegalDataValue,
469 0x04 => ServerDeviceFailure,
470 0x05 => Acknowledge,
471 0x06 => ServerDeviceBusy,
472 0x08 => MemoryParityError,
473 0x0A => GatewayPathUnavailable,
474 0x0B => GatewayTargetDevice,
475 other => Custom(other),
476 }
477 }
478
479 pub(crate) fn description(&self) -> &str {
480 use crate::frame::ExceptionCode::*;
481
482 match *self {
483 IllegalFunction => "Illegal function",
484 IllegalDataAddress => "Illegal data address",
485 IllegalDataValue => "Illegal data value",
486 ServerDeviceFailure => "Server device failure",
487 Acknowledge => "Acknowledge",
488 ServerDeviceBusy => "Server device busy",
489 MemoryParityError => "Memory parity error",
490 GatewayPathUnavailable => "Gateway path unavailable",
491 GatewayTargetDevice => "Gateway target device failed to respond",
492 Custom(_) => "Custom",
493 }
494 }
495}
496
497#[derive(Debug, Clone, Copy, PartialEq, Eq)]
499pub struct ExceptionResponse {
500 pub function: FunctionCode,
501 pub exception: ExceptionCode,
502}
503
504#[derive(Debug, Clone)]
506pub(crate) struct RequestPdu<'a>(pub(crate) Request<'a>);
507
508impl<'a> From<Request<'a>> for RequestPdu<'a> {
509 fn from(from: Request<'a>) -> Self {
510 RequestPdu(from)
511 }
512}
513
514impl<'a> From<RequestPdu<'a>> for Request<'a> {
515 fn from(from: RequestPdu<'a>) -> Self {
516 from.0
517 }
518}
519
520#[derive(Debug, Clone, PartialEq, Eq)]
522pub(crate) struct ResponsePdu(pub(crate) Result<Response, ExceptionResponse>);
523
524impl From<Response> for ResponsePdu {
525 fn from(from: Response) -> Self {
526 ResponsePdu(Ok(from))
527 }
528}
529
530impl From<ExceptionResponse> for ResponsePdu {
531 fn from(from: ExceptionResponse) -> Self {
532 ResponsePdu(Err(from))
533 }
534}
535
536#[cfg(any(
537 feature = "rtu-over-tcp-server",
538 feature = "rtu-server",
539 feature = "tcp-server"
540))]
541#[derive(Debug, Clone, PartialEq, Eq)]
542pub(crate) struct OptionalResponsePdu(pub(crate) Option<ResponsePdu>);
543
544#[cfg(any(
545 feature = "rtu-over-tcp-server",
546 feature = "rtu-server",
547 feature = "tcp-server"
548))]
549impl From<Result<Option<Response>, ExceptionResponse>> for OptionalResponsePdu {
550 fn from(from: Result<Option<Response>, ExceptionResponse>) -> Self {
551 match from {
552 Ok(None) => Self(None),
553 Ok(Some(response)) => Self(Some(response.into())),
554 Err(exception) => Self(Some(exception.into())),
555 }
556 }
557}
558
559impl From<ResponsePdu> for Result<Response, ExceptionResponse> {
560 fn from(from: ResponsePdu) -> Self {
561 from.0
562 }
563}
564
565impl fmt::Display for ExceptionCode {
566 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
567 write!(f, "{}", self.description())
568 }
569}
570
571impl error::Error for ExceptionCode {
572 fn description(&self) -> &str {
573 self.description()
574 }
575}
576
577impl fmt::Display for ExceptionResponse {
578 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
579 write!(f, "Modbus function {}: {}", self.function, self.exception)
580 }
581}
582
583impl error::Error for ExceptionResponse {
584 fn description(&self) -> &str {
585 self.exception.description()
586 }
587}
588
589#[cfg(test)]
590mod tests {
591 use super::*;
592
593 #[test]
594 fn new_function_code() {
595 assert_eq!(FunctionCode::ReadCoils, FunctionCode::new(0x01));
596 assert_eq!(FunctionCode::ReadDiscreteInputs, FunctionCode::new(0x02));
597
598 assert_eq!(FunctionCode::WriteSingleCoil, FunctionCode::new(0x05));
599 assert_eq!(FunctionCode::WriteSingleRegister, FunctionCode::new(0x06));
600
601 assert_eq!(FunctionCode::ReadHoldingRegisters, FunctionCode::new(0x03));
602 assert_eq!(FunctionCode::ReadInputRegisters, FunctionCode::new(0x04));
603
604 assert_eq!(FunctionCode::WriteMultipleCoils, FunctionCode::new(0x0F));
605 assert_eq!(
606 FunctionCode::WriteMultipleRegisters,
607 FunctionCode::new(0x10)
608 );
609
610 assert_eq!(FunctionCode::MaskWriteRegister, FunctionCode::new(0x16));
611
612 assert_eq!(
613 FunctionCode::ReadWriteMultipleRegisters,
614 FunctionCode::new(0x17)
615 );
616
617 assert_eq!(FunctionCode::Custom(70), FunctionCode::new(70));
618 }
619
620 #[test]
621 fn function_code_values() {
622 assert_eq!(FunctionCode::ReadCoils.value(), 0x01);
623 assert_eq!(FunctionCode::ReadDiscreteInputs.value(), 0x02);
624
625 assert_eq!(FunctionCode::WriteSingleCoil.value(), 0x05);
626 assert_eq!(FunctionCode::WriteSingleRegister.value(), 0x06);
627
628 assert_eq!(FunctionCode::ReadHoldingRegisters.value(), 0x03);
629 assert_eq!(FunctionCode::ReadInputRegisters.value(), 0x04);
630
631 assert_eq!(FunctionCode::WriteMultipleCoils.value(), 0x0F);
632 assert_eq!(FunctionCode::WriteMultipleRegisters.value(), 0x10);
633
634 assert_eq!(FunctionCode::MaskWriteRegister.value(), 0x16);
635
636 assert_eq!(FunctionCode::ReadWriteMultipleRegisters.value(), 0x17);
637
638 assert_eq!(FunctionCode::Custom(70).value(), 70);
639 }
640
641 #[test]
642 fn function_code_from_request() {
643 use Request::*;
644
645 assert_eq!(ReadCoils(0, 0).function_code(), FunctionCode::ReadCoils);
646 assert_eq!(
647 ReadDiscreteInputs(0, 0).function_code(),
648 FunctionCode::ReadDiscreteInputs
649 );
650
651 assert_eq!(
652 WriteSingleCoil(0, true).function_code(),
653 FunctionCode::WriteSingleCoil
654 );
655 assert_eq!(
656 WriteMultipleCoils(0, Cow::Borrowed(&[])).function_code(),
657 FunctionCode::WriteMultipleCoils
658 );
659
660 assert_eq!(
661 ReadInputRegisters(0, 0).function_code(),
662 FunctionCode::ReadInputRegisters
663 );
664 assert_eq!(
665 ReadHoldingRegisters(0, 0).function_code(),
666 FunctionCode::ReadHoldingRegisters
667 );
668
669 assert_eq!(
670 WriteSingleRegister(0, 0).function_code(),
671 FunctionCode::WriteSingleRegister
672 );
673 assert_eq!(
674 WriteMultipleRegisters(0, Cow::Borrowed(&[])).function_code(),
675 FunctionCode::WriteMultipleRegisters
676 );
677
678 assert_eq!(
679 MaskWriteRegister(0, 0, 0).function_code(),
680 FunctionCode::MaskWriteRegister
681 );
682
683 assert_eq!(
684 ReadWriteMultipleRegisters(0, 0, 0, Cow::Borrowed(&[])).function_code(),
685 FunctionCode::ReadWriteMultipleRegisters
686 );
687
688 assert_eq!(Custom(88, Cow::Borrowed(&[])).function_code().value(), 88);
689 }
690
691 #[test]
692 fn function_code_from_response() {
693 use Response::*;
694
695 assert_eq!(ReadCoils(vec![]).function_code(), FunctionCode::ReadCoils);
696 assert_eq!(
697 ReadDiscreteInputs(vec![]).function_code(),
698 FunctionCode::ReadDiscreteInputs
699 );
700
701 assert_eq!(
702 WriteSingleCoil(0x0, false).function_code(),
703 FunctionCode::WriteSingleCoil
704 );
705 assert_eq!(
706 WriteMultipleCoils(0x0, 0x0).function_code(),
707 FunctionCode::WriteMultipleCoils
708 );
709
710 assert_eq!(
711 ReadInputRegisters(vec![]).function_code(),
712 FunctionCode::ReadInputRegisters
713 );
714 assert_eq!(
715 ReadHoldingRegisters(vec![]).function_code(),
716 FunctionCode::ReadHoldingRegisters
717 );
718
719 assert_eq!(
720 WriteSingleRegister(0, 0).function_code(),
721 FunctionCode::WriteSingleRegister
722 );
723 assert_eq!(
724 WriteMultipleRegisters(0, 0).function_code(),
725 FunctionCode::WriteMultipleRegisters
726 );
727
728 assert_eq!(
729 MaskWriteRegister(0, 0, 0).function_code(),
730 FunctionCode::MaskWriteRegister
731 );
732
733 assert_eq!(
734 ReadWriteMultipleRegisters(vec![]).function_code(),
735 FunctionCode::ReadWriteMultipleRegisters
736 );
737
738 assert_eq!(
739 Custom(99, Bytes::from_static(&[])).function_code().value(),
740 99
741 );
742 }
743}