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
18pub(crate) const MEI_TYPE_READ_DEVICE_IDENTIFICATION: u8 = 0x0E;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum FunctionCode {
26 ReadCoils,
28
29 ReadDiscreteInputs,
31
32 ReadHoldingRegisters,
34
35 ReadInputRegisters,
37
38 WriteSingleCoil,
40
41 WriteSingleRegister,
43
44 ReadExceptionStatus,
46
47 Diagnostics,
49
50 GetCommEventCounter,
52
53 GetCommEventLog,
55
56 WriteMultipleCoils,
58
59 WriteMultipleRegisters,
61
62 ReportServerId,
64
65 ReadFileRecord,
67
68 WriteFileRecord,
70
71 MaskWriteRegister,
73
74 ReadWriteMultipleRegisters,
76
77 ReadFifoQueue,
79
80 EncapsulatedInterfaceTransport,
82
83 Custom(u8),
85}
86
87impl FunctionCode {
88 #[must_use]
90 pub const fn new(value: u8) -> Self {
91 match value {
92 0x01 => Self::ReadCoils,
93 0x02 => Self::ReadDiscreteInputs,
94 0x03 => Self::ReadHoldingRegisters,
95 0x04 => Self::ReadInputRegisters,
96 0x05 => Self::WriteSingleCoil,
97 0x06 => Self::WriteSingleRegister,
98 0x07 => Self::ReadExceptionStatus,
99 0x08 => Self::Diagnostics,
100 0x0B => Self::GetCommEventCounter,
101 0x0C => Self::GetCommEventLog,
102 0x0F => Self::WriteMultipleCoils,
103 0x10 => Self::WriteMultipleRegisters,
104 0x11 => Self::ReportServerId,
105 0x14 => Self::ReadFileRecord,
106 0x15 => Self::WriteFileRecord,
107 0x16 => Self::MaskWriteRegister,
108 0x17 => Self::ReadWriteMultipleRegisters,
109 0x18 => Self::ReadFifoQueue,
110 0x2B => Self::EncapsulatedInterfaceTransport,
111 code => Self::Custom(code),
112 }
113 }
114
115 #[must_use]
117 pub const fn value(self) -> u8 {
118 match self {
119 Self::ReadCoils => 0x01,
120 Self::ReadDiscreteInputs => 0x02,
121 Self::ReadHoldingRegisters => 0x03,
122 Self::ReadInputRegisters => 0x04,
123 Self::WriteSingleCoil => 0x05,
124 Self::WriteSingleRegister => 0x06,
125 Self::ReadExceptionStatus => 0x07,
126 Self::Diagnostics => 0x08,
127 Self::GetCommEventCounter => 0x0B,
128 Self::GetCommEventLog => 0x0C,
129 Self::WriteMultipleCoils => 0x0F,
130 Self::WriteMultipleRegisters => 0x10,
131 Self::ReportServerId => 0x11,
132 Self::ReadFileRecord => 0x14,
133 Self::WriteFileRecord => 0x15,
134 Self::MaskWriteRegister => 0x16,
135 Self::ReadWriteMultipleRegisters => 0x17,
136 Self::ReadFifoQueue => 0x18,
137 Self::EncapsulatedInterfaceTransport => 0x2B,
138 Self::Custom(code) => code,
139 }
140 }
141}
142
143impl Display for FunctionCode {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 self.value().fmt(f)
146 }
147}
148
149pub type Address = u16;
156
157pub(crate) type Coil = bool;
162
163pub(crate) type Word = u16;
167
168pub type Quantity = u16;
170
171#[derive(Debug, Clone, PartialEq, Eq)]
173pub enum Request<'a> {
174 ReadCoils(Address, Quantity),
178
179 ReadDiscreteInputs(Address, Quantity),
183
184 WriteSingleCoil(Address, Coil),
188
189 WriteMultipleCoils(Address, Cow<'a, [Coil]>),
193
194 ReadInputRegisters(Address, Quantity),
198
199 ReadHoldingRegisters(Address, Quantity),
203
204 WriteSingleRegister(Address, Word),
208
209 WriteMultipleRegisters(Address, Cow<'a, [Word]>),
213
214 ReportServerId,
216
217 MaskWriteRegister(Address, Word, Word),
222
223 ReadWriteMultipleRegisters(Address, Quantity, Address, Cow<'a, [Word]>),
229
230 ReadDeviceIdentification(ReadCode, ObjectId),
235
236 Custom(u8, Cow<'a, [u8]>),
240}
241
242impl Request<'_> {
243 #[must_use]
245 pub fn into_owned(self) -> Request<'static> {
246 use Request::*;
247
248 match self {
249 ReadCoils(addr, qty) => ReadCoils(addr, qty),
250 ReadDiscreteInputs(addr, qty) => ReadDiscreteInputs(addr, qty),
251 WriteSingleCoil(addr, coil) => WriteSingleCoil(addr, coil),
252 WriteMultipleCoils(addr, coils) => {
253 WriteMultipleCoils(addr, Cow::Owned(coils.into_owned()))
254 }
255 ReadInputRegisters(addr, qty) => ReadInputRegisters(addr, qty),
256 ReadHoldingRegisters(addr, qty) => ReadHoldingRegisters(addr, qty),
257 WriteSingleRegister(addr, word) => WriteSingleRegister(addr, word),
258 WriteMultipleRegisters(addr, words) => {
259 WriteMultipleRegisters(addr, Cow::Owned(words.into_owned()))
260 }
261 ReportServerId => ReportServerId,
262 MaskWriteRegister(addr, and_mask, or_mask) => {
263 MaskWriteRegister(addr, and_mask, or_mask)
264 }
265 ReadWriteMultipleRegisters(addr, qty, write_addr, words) => {
266 ReadWriteMultipleRegisters(addr, qty, write_addr, Cow::Owned(words.into_owned()))
267 }
268 ReadDeviceIdentification(read_code, object_id) => {
269 ReadDeviceIdentification(read_code, object_id)
270 }
271
272 Custom(func, bytes) => Custom(func, Cow::Owned(bytes.into_owned())),
273 }
274 }
275
276 #[must_use]
278 pub const fn function_code(&self) -> FunctionCode {
279 use Request::*;
280
281 match self {
282 ReadCoils(_, _) => FunctionCode::ReadCoils,
283 ReadDiscreteInputs(_, _) => FunctionCode::ReadDiscreteInputs,
284
285 WriteSingleCoil(_, _) => FunctionCode::WriteSingleCoil,
286 WriteMultipleCoils(_, _) => FunctionCode::WriteMultipleCoils,
287
288 ReadInputRegisters(_, _) => FunctionCode::ReadInputRegisters,
289 ReadHoldingRegisters(_, _) => FunctionCode::ReadHoldingRegisters,
290
291 WriteSingleRegister(_, _) => FunctionCode::WriteSingleRegister,
292 WriteMultipleRegisters(_, _) => FunctionCode::WriteMultipleRegisters,
293
294 ReportServerId => FunctionCode::ReportServerId,
295
296 MaskWriteRegister(_, _, _) => FunctionCode::MaskWriteRegister,
297
298 ReadWriteMultipleRegisters(_, _, _, _) => FunctionCode::ReadWriteMultipleRegisters,
299
300 ReadDeviceIdentification(_, _) => FunctionCode::EncapsulatedInterfaceTransport,
301
302 Custom(code, _) => FunctionCode::Custom(*code),
303 }
304 }
305}
306
307#[cfg(feature = "server")]
309#[derive(Debug, Clone, PartialEq, Eq)]
310pub struct SlaveRequest<'a> {
311 pub slave: crate::slave::SlaveId,
313 pub request: Request<'a>,
315}
316
317#[cfg(feature = "server")]
318impl SlaveRequest<'_> {
319 #[must_use]
321 pub fn into_owned(self) -> SlaveRequest<'static> {
322 let Self { slave, request } = self;
323 SlaveRequest {
324 slave,
325 request: request.into_owned(),
326 }
327 }
328}
329
330#[derive(Debug, Clone, PartialEq, Eq)]
337pub enum Response {
338 ReadCoils(Vec<Coil>),
342
343 ReadDiscreteInputs(Vec<Coil>),
347
348 WriteSingleCoil(Address, Coil),
352
353 WriteMultipleCoils(Address, Quantity),
357
358 ReadInputRegisters(Vec<Word>),
361
362 ReadHoldingRegisters(Vec<Word>),
365
366 WriteSingleRegister(Address, Word),
370
371 WriteMultipleRegisters(Address, Quantity),
375
376 ReportServerId(u8, bool, Vec<u8>),
381
382 MaskWriteRegister(Address, Word, Word),
387
388 ReadWriteMultipleRegisters(Vec<Word>),
391
392 ReadDeviceIdentification(ReadDeviceIdentificationResponse),
394
395 Custom(u8, Bytes),
399}
400
401impl Response {
402 #[must_use]
404 pub const fn function_code(&self) -> FunctionCode {
405 use Response::*;
406
407 match self {
408 ReadCoils(_) => FunctionCode::ReadCoils,
409 ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs,
410
411 WriteSingleCoil(_, _) => FunctionCode::WriteSingleCoil,
412 WriteMultipleCoils(_, _) => FunctionCode::WriteMultipleCoils,
413
414 ReadInputRegisters(_) => FunctionCode::ReadInputRegisters,
415 ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters,
416
417 WriteSingleRegister(_, _) => FunctionCode::WriteSingleRegister,
418 WriteMultipleRegisters(_, _) => FunctionCode::WriteMultipleRegisters,
419
420 ReportServerId(_, _, _) => FunctionCode::ReportServerId,
421
422 MaskWriteRegister(_, _, _) => FunctionCode::MaskWriteRegister,
423
424 ReadWriteMultipleRegisters(_) => FunctionCode::ReadWriteMultipleRegisters,
425
426 ReadDeviceIdentification(_) => FunctionCode::EncapsulatedInterfaceTransport,
427
428 Custom(code, _) => FunctionCode::Custom(*code),
429 }
430 }
431}
432
433#[derive(Debug, Clone, Copy, PartialEq, Eq)]
435pub enum ExceptionCode {
436 IllegalFunction,
438 IllegalDataAddress,
440 IllegalDataValue,
442 ServerDeviceFailure,
444 Acknowledge,
446 ServerDeviceBusy,
448 MemoryParityError,
450 GatewayPathUnavailable,
452 GatewayTargetDevice,
454 Custom(u8),
459}
460
461impl From<ExceptionCode> for u8 {
462 fn from(from: ExceptionCode) -> Self {
463 use crate::frame::ExceptionCode::*;
464 match from {
465 IllegalFunction => 0x01,
466 IllegalDataAddress => 0x02,
467 IllegalDataValue => 0x03,
468 ServerDeviceFailure => 0x04,
469 Acknowledge => 0x05,
470 ServerDeviceBusy => 0x06,
471 MemoryParityError => 0x08,
472 GatewayPathUnavailable => 0x0A,
473 GatewayTargetDevice => 0x0B,
474 Custom(code) => code,
475 }
476 }
477}
478
479impl ExceptionCode {
480 #[must_use]
482 pub const fn new(value: u8) -> Self {
483 use crate::frame::ExceptionCode::*;
484
485 match value {
486 0x01 => IllegalFunction,
487 0x02 => IllegalDataAddress,
488 0x03 => IllegalDataValue,
489 0x04 => ServerDeviceFailure,
490 0x05 => Acknowledge,
491 0x06 => ServerDeviceBusy,
492 0x08 => MemoryParityError,
493 0x0A => GatewayPathUnavailable,
494 0x0B => GatewayTargetDevice,
495 other => Custom(other),
496 }
497 }
498
499 pub(crate) fn description(&self) -> &str {
500 use crate::frame::ExceptionCode::*;
501
502 match *self {
503 IllegalFunction => "Illegal function",
504 IllegalDataAddress => "Illegal data address",
505 IllegalDataValue => "Illegal data value",
506 ServerDeviceFailure => "Server device failure",
507 Acknowledge => "Acknowledge",
508 ServerDeviceBusy => "Server device busy",
509 MemoryParityError => "Memory parity error",
510 GatewayPathUnavailable => "Gateway path unavailable",
511 GatewayTargetDevice => "Gateway target device failed to respond",
512 Custom(_) => "Custom",
513 }
514 }
515}
516
517#[derive(Debug, Clone, Copy, PartialEq, Eq)]
522pub enum ReadCode {
523 Basic,
526 Regular,
529 Extended,
532 Specific,
535}
536
537impl ReadCode {
538 #[must_use]
547 pub const fn try_from_value(value: u8) -> Option<Self> {
548 Some(match value {
549 0x01 => ReadCode::Basic,
550 0x02 => ReadCode::Regular,
551 0x03 => ReadCode::Extended,
552 0x04 => ReadCode::Specific,
553 _ => return None,
554 })
555 }
556
557 #[must_use]
562 pub const fn value(self) -> u8 {
563 match self {
564 ReadCode::Basic => 0x01,
565 ReadCode::Regular => 0x02,
566 ReadCode::Extended => 0x03,
567 ReadCode::Specific => 0x04,
568 }
569 }
570}
571
572#[derive(Debug, Clone, Copy, PartialEq, Eq)]
577pub enum ConformityLevel {
578 BasicIdentificationStreamOnly,
580
581 RegularIdentificationStreamOnly,
583
584 ExtendedIdentificationStreamOnly,
586
587 BasicIdentification,
589
590 RegularIdentification,
592
593 ExtendedIdentification,
595}
596
597impl ConformityLevel {
598 #[must_use]
607 pub const fn try_from_value(value: u8) -> Option<Self> {
608 Some(match value {
609 0x01 => ConformityLevel::BasicIdentificationStreamOnly,
610 0x02 => ConformityLevel::RegularIdentificationStreamOnly,
611 0x03 => ConformityLevel::ExtendedIdentificationStreamOnly,
612 0x81 => ConformityLevel::BasicIdentification,
613 0x82 => ConformityLevel::RegularIdentification,
614 0x83 => ConformityLevel::ExtendedIdentification,
615 _ => return None,
616 })
617 }
618
619 #[must_use]
624 pub const fn value(self) -> u8 {
625 match self {
626 ConformityLevel::BasicIdentificationStreamOnly => 0x01,
627 ConformityLevel::RegularIdentificationStreamOnly => 0x02,
628 ConformityLevel::ExtendedIdentificationStreamOnly => 0x03,
629 ConformityLevel::BasicIdentification => 0x81,
630 ConformityLevel::RegularIdentification => 0x82,
631 ConformityLevel::ExtendedIdentification => 0x83,
632 }
633 }
634}
635
636pub type ObjectId = u8;
640
641pub type MoreFollows = bool;
645
646pub type NextObjectId = u8;
650
651pub type DeviceIdObjects = Vec<DeviceIdObject>;
662
663#[derive(Debug, Clone, PartialEq, Eq)]
671pub struct DeviceIdObject {
672 pub id: u8,
679
680 pub value: Bytes,
684}
685
686impl DeviceIdObject {
687 pub fn value_as_str(&self) -> Option<&str> {
696 std::str::from_utf8(&self.value).ok()
697 }
698}
699
700#[derive(Debug, Clone, PartialEq, Eq)]
702pub struct ReadDeviceIdentificationResponse {
703 pub read_code: ReadCode,
705 pub conformity_level: ConformityLevel,
707 pub more_follows: MoreFollows,
709 pub next_object_id: NextObjectId,
711 pub device_id_objects: DeviceIdObjects,
713}
714
715#[derive(Debug, Clone, Copy, PartialEq, Eq)]
717pub struct ExceptionResponse {
718 pub function: FunctionCode,
719 pub exception: ExceptionCode,
720}
721
722#[derive(Debug, Clone)]
724pub(crate) struct RequestPdu<'a>(pub(crate) Request<'a>);
725
726impl<'a> From<Request<'a>> for RequestPdu<'a> {
727 fn from(from: Request<'a>) -> Self {
728 RequestPdu(from)
729 }
730}
731
732impl<'a> From<RequestPdu<'a>> for Request<'a> {
733 fn from(from: RequestPdu<'a>) -> Self {
734 from.0
735 }
736}
737
738#[derive(Debug, Clone, PartialEq, Eq)]
740pub(crate) struct ResponsePdu(pub(crate) Result<Response, ExceptionResponse>);
741
742impl From<Response> for ResponsePdu {
743 fn from(from: Response) -> Self {
744 ResponsePdu(Ok(from))
745 }
746}
747
748impl From<ExceptionResponse> for ResponsePdu {
749 fn from(from: ExceptionResponse) -> Self {
750 ResponsePdu(Err(from))
751 }
752}
753
754#[cfg(any(
755 feature = "rtu-over-tcp-server",
756 feature = "rtu-server",
757 feature = "tcp-server"
758))]
759#[derive(Debug, Clone, PartialEq, Eq)]
760pub(crate) struct OptionalResponsePdu(pub(crate) Option<ResponsePdu>);
761
762#[cfg(any(
763 feature = "rtu-over-tcp-server",
764 feature = "rtu-server",
765 feature = "tcp-server"
766))]
767impl From<Result<Option<Response>, ExceptionResponse>> for OptionalResponsePdu {
768 fn from(from: Result<Option<Response>, ExceptionResponse>) -> Self {
769 match from {
770 Ok(None) => Self(None),
771 Ok(Some(response)) => Self(Some(response.into())),
772 Err(exception) => Self(Some(exception.into())),
773 }
774 }
775}
776
777impl From<ResponsePdu> for Result<Response, ExceptionResponse> {
778 fn from(from: ResponsePdu) -> Self {
779 from.0
780 }
781}
782
783impl fmt::Display for ExceptionCode {
784 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
785 write!(f, "{}", self.description())
786 }
787}
788
789impl error::Error for ExceptionCode {
790 fn description(&self) -> &str {
791 self.description()
792 }
793}
794
795impl fmt::Display for ExceptionResponse {
796 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
797 write!(f, "Modbus function {}: {}", self.function, self.exception)
798 }
799}
800
801impl error::Error for ExceptionResponse {
802 fn description(&self) -> &str {
803 self.exception.description()
804 }
805}
806
807#[cfg(test)]
808mod tests {
809 use super::*;
810
811 #[test]
812 fn new_function_code() {
813 assert_eq!(FunctionCode::ReadCoils, FunctionCode::new(0x01));
814 assert_eq!(FunctionCode::ReadDiscreteInputs, FunctionCode::new(0x02));
815
816 assert_eq!(FunctionCode::WriteSingleCoil, FunctionCode::new(0x05));
817 assert_eq!(FunctionCode::WriteSingleRegister, FunctionCode::new(0x06));
818
819 assert_eq!(FunctionCode::ReadHoldingRegisters, FunctionCode::new(0x03));
820 assert_eq!(FunctionCode::ReadInputRegisters, FunctionCode::new(0x04));
821
822 assert_eq!(FunctionCode::WriteMultipleCoils, FunctionCode::new(0x0F));
823 assert_eq!(
824 FunctionCode::WriteMultipleRegisters,
825 FunctionCode::new(0x10)
826 );
827
828 assert_eq!(FunctionCode::MaskWriteRegister, FunctionCode::new(0x16));
829
830 assert_eq!(
831 FunctionCode::ReadWriteMultipleRegisters,
832 FunctionCode::new(0x17)
833 );
834
835 assert_eq!(FunctionCode::Custom(70), FunctionCode::new(70));
836 }
837
838 #[test]
839 fn function_code_values() {
840 assert_eq!(FunctionCode::ReadCoils.value(), 0x01);
841 assert_eq!(FunctionCode::ReadDiscreteInputs.value(), 0x02);
842
843 assert_eq!(FunctionCode::WriteSingleCoil.value(), 0x05);
844 assert_eq!(FunctionCode::WriteSingleRegister.value(), 0x06);
845
846 assert_eq!(FunctionCode::ReadHoldingRegisters.value(), 0x03);
847 assert_eq!(FunctionCode::ReadInputRegisters.value(), 0x04);
848
849 assert_eq!(FunctionCode::WriteMultipleCoils.value(), 0x0F);
850 assert_eq!(FunctionCode::WriteMultipleRegisters.value(), 0x10);
851
852 assert_eq!(FunctionCode::MaskWriteRegister.value(), 0x16);
853
854 assert_eq!(FunctionCode::ReadWriteMultipleRegisters.value(), 0x17);
855
856 assert_eq!(FunctionCode::Custom(70).value(), 70);
857 }
858
859 #[test]
860 fn function_code_from_request() {
861 use Request::*;
862
863 assert_eq!(ReadCoils(0, 0).function_code(), FunctionCode::ReadCoils);
864 assert_eq!(
865 ReadDiscreteInputs(0, 0).function_code(),
866 FunctionCode::ReadDiscreteInputs
867 );
868
869 assert_eq!(
870 WriteSingleCoil(0, true).function_code(),
871 FunctionCode::WriteSingleCoil
872 );
873 assert_eq!(
874 WriteMultipleCoils(0, Cow::Borrowed(&[])).function_code(),
875 FunctionCode::WriteMultipleCoils
876 );
877
878 assert_eq!(
879 ReadInputRegisters(0, 0).function_code(),
880 FunctionCode::ReadInputRegisters
881 );
882 assert_eq!(
883 ReadHoldingRegisters(0, 0).function_code(),
884 FunctionCode::ReadHoldingRegisters
885 );
886
887 assert_eq!(
888 WriteSingleRegister(0, 0).function_code(),
889 FunctionCode::WriteSingleRegister
890 );
891 assert_eq!(
892 WriteMultipleRegisters(0, Cow::Borrowed(&[])).function_code(),
893 FunctionCode::WriteMultipleRegisters
894 );
895
896 assert_eq!(
897 MaskWriteRegister(0, 0, 0).function_code(),
898 FunctionCode::MaskWriteRegister
899 );
900
901 assert_eq!(
902 ReadWriteMultipleRegisters(0, 0, 0, Cow::Borrowed(&[])).function_code(),
903 FunctionCode::ReadWriteMultipleRegisters
904 );
905
906 assert_eq!(Custom(88, Cow::Borrowed(&[])).function_code().value(), 88);
907 }
908
909 #[test]
910 fn function_code_from_response() {
911 use Response::*;
912
913 assert_eq!(ReadCoils(vec![]).function_code(), FunctionCode::ReadCoils);
914 assert_eq!(
915 ReadDiscreteInputs(vec![]).function_code(),
916 FunctionCode::ReadDiscreteInputs
917 );
918
919 assert_eq!(
920 WriteSingleCoil(0x0, false).function_code(),
921 FunctionCode::WriteSingleCoil
922 );
923 assert_eq!(
924 WriteMultipleCoils(0x0, 0x0).function_code(),
925 FunctionCode::WriteMultipleCoils
926 );
927
928 assert_eq!(
929 ReadInputRegisters(vec![]).function_code(),
930 FunctionCode::ReadInputRegisters
931 );
932 assert_eq!(
933 ReadHoldingRegisters(vec![]).function_code(),
934 FunctionCode::ReadHoldingRegisters
935 );
936
937 assert_eq!(
938 WriteSingleRegister(0, 0).function_code(),
939 FunctionCode::WriteSingleRegister
940 );
941 assert_eq!(
942 WriteMultipleRegisters(0, 0).function_code(),
943 FunctionCode::WriteMultipleRegisters
944 );
945
946 assert_eq!(
947 MaskWriteRegister(0, 0, 0).function_code(),
948 FunctionCode::MaskWriteRegister
949 );
950
951 assert_eq!(
952 ReadWriteMultipleRegisters(vec![]).function_code(),
953 FunctionCode::ReadWriteMultipleRegisters
954 );
955
956 assert_eq!(
957 Custom(99, Bytes::from_static(&[])).function_code().value(),
958 99
959 );
960 }
961}