rusty_modbus_codec/
decode.rs1use rusty_modbus_types::{FunctionCode, MAX_PDU_SIZE};
4
5use crate::error::DecodeError;
6use crate::pdu::{PduRef, RequestPdu, ResponsePdu};
7use crate::request::{
8 DiagnosticsRequest, EncapsulatedInterfaceRequest, MaskWriteRegisterRequest, ReadCoilsRequest,
9 ReadDiscreteInputsRequest, ReadFifoQueueRequest, ReadFileRecordRequest,
10 ReadHoldingRegistersRequest, ReadInputRegistersRequest, ReadWriteMultipleRegistersRequest,
11 WriteFileRecordRequest, WriteMultipleCoilsRequest, WriteMultipleRegistersRequest,
12 WriteSingleCoilRequest, WriteSingleRegisterRequest,
13};
14use crate::response::{
15 DiagnosticsResponse, EncapsulatedInterfaceResponse, ExceptionResponse,
16 GetCommEventCounterResponse, GetCommEventLogResponse, MaskWriteRegisterResponse,
17 ReadCoilsResponse, ReadDiscreteInputsResponse, ReadExceptionStatusResponse,
18 ReadFifoQueueResponse, ReadFileRecordResponse, ReadHoldingRegistersResponse,
19 ReadInputRegistersResponse, ReadWriteMultipleRegistersResponse, ReportServerIdResponse,
20 WriteFileRecordResponse, WriteMultipleCoilsResponse, WriteMultipleRegistersResponse,
21 WriteSingleCoilResponse, WriteSingleRegisterResponse,
22};
23
24pub fn decode_pdu_ref(pdu: &[u8]) -> Result<PduRef<'_>, DecodeError> {
33 if pdu.len() > MAX_PDU_SIZE {
34 return Err(DecodeError::PduTooLarge {
35 length: pdu.len(),
36 maximum: MAX_PDU_SIZE,
37 });
38 }
39 if pdu.is_empty() {
40 return Err(DecodeError::Truncated {
41 expected: 1,
42 actual: 0,
43 });
44 }
45 Ok(PduRef {
46 function_code: pdu[0],
47 data: &pdu[1..],
48 })
49}
50
51pub fn decode_request(pdu: &[u8]) -> Result<RequestPdu<'_>, DecodeError> {
59 let pdu_ref = decode_pdu_ref(pdu)?;
60 let fc = pdu_ref.function_code;
61 let data = pdu_ref.data;
62
63 let fc_enum = FunctionCode::from_raw(fc).ok_or(DecodeError::UnknownFunctionCode(fc))?;
64
65 match fc_enum {
66 FunctionCode::ReadCoils => ReadCoilsRequest::decode(data).map(RequestPdu::ReadCoils),
67 FunctionCode::ReadDiscreteInputs => {
68 ReadDiscreteInputsRequest::decode(data).map(RequestPdu::ReadDiscreteInputs)
69 }
70 FunctionCode::ReadHoldingRegisters => {
71 ReadHoldingRegistersRequest::decode(data).map(RequestPdu::ReadHoldingRegisters)
72 }
73 FunctionCode::ReadInputRegisters => {
74 ReadInputRegistersRequest::decode(data).map(RequestPdu::ReadInputRegisters)
75 }
76 FunctionCode::WriteSingleCoil => {
77 WriteSingleCoilRequest::decode(data).map(RequestPdu::WriteSingleCoil)
78 }
79 FunctionCode::WriteSingleRegister => {
80 WriteSingleRegisterRequest::decode(data).map(RequestPdu::WriteSingleRegister)
81 }
82 FunctionCode::ReadExceptionStatus => {
83 DecodeError::check_exact_len(data, 0)?;
84 Ok(RequestPdu::ReadExceptionStatus)
85 }
86 FunctionCode::Diagnostics => DiagnosticsRequest::decode(data).map(RequestPdu::Diagnostics),
87 FunctionCode::GetCommEventCounter => {
88 DecodeError::check_exact_len(data, 0)?;
89 Ok(RequestPdu::GetCommEventCounter)
90 }
91 FunctionCode::GetCommEventLog => {
92 DecodeError::check_exact_len(data, 0)?;
93 Ok(RequestPdu::GetCommEventLog)
94 }
95 FunctionCode::WriteMultipleCoils => {
96 WriteMultipleCoilsRequest::decode(data).map(RequestPdu::WriteMultipleCoils)
97 }
98 FunctionCode::WriteMultipleRegisters => {
99 WriteMultipleRegistersRequest::decode(data).map(RequestPdu::WriteMultipleRegisters)
100 }
101 FunctionCode::ReportServerId => {
102 DecodeError::check_exact_len(data, 0)?;
103 Ok(RequestPdu::ReportServerId)
104 }
105 FunctionCode::ReadFileRecord => {
106 ReadFileRecordRequest::decode(data).map(RequestPdu::ReadFileRecord)
107 }
108 FunctionCode::WriteFileRecord => {
109 WriteFileRecordRequest::decode(data).map(RequestPdu::WriteFileRecord)
110 }
111 FunctionCode::MaskWriteRegister => {
112 MaskWriteRegisterRequest::decode(data).map(RequestPdu::MaskWriteRegister)
113 }
114 FunctionCode::ReadWriteMultipleRegisters => ReadWriteMultipleRegistersRequest::decode(data)
115 .map(RequestPdu::ReadWriteMultipleRegisters),
116 FunctionCode::ReadFifoQueue => {
117 ReadFifoQueueRequest::decode(data).map(RequestPdu::ReadFifoQueue)
118 }
119 FunctionCode::EncapsulatedInterfaceTransport => {
120 EncapsulatedInterfaceRequest::decode(data).map(RequestPdu::EncapsulatedInterface)
121 }
122 FunctionCode::Custom(fc) => Ok(RequestPdu::Custom(fc, data)),
123 }
124}
125
126pub fn decode_response(pdu: &[u8]) -> Result<ResponsePdu<'_>, DecodeError> {
134 let pdu_ref = decode_pdu_ref(pdu)?;
135 let fc = pdu_ref.function_code;
136 let data = pdu_ref.data;
137
138 if FunctionCode::is_exception_response(fc) {
140 return ExceptionResponse::decode(fc, data).map(ResponsePdu::Exception);
141 }
142
143 let fc_enum = FunctionCode::from_raw(fc).ok_or(DecodeError::UnknownFunctionCode(fc))?;
146
147 match fc_enum {
148 FunctionCode::ReadCoils => ReadCoilsResponse::decode(data).map(ResponsePdu::ReadCoils),
149 FunctionCode::ReadDiscreteInputs => {
150 ReadDiscreteInputsResponse::decode(data).map(ResponsePdu::ReadDiscreteInputs)
151 }
152 FunctionCode::ReadHoldingRegisters => {
153 ReadHoldingRegistersResponse::decode(data).map(ResponsePdu::ReadHoldingRegisters)
154 }
155 FunctionCode::ReadInputRegisters => {
156 ReadInputRegistersResponse::decode(data).map(ResponsePdu::ReadInputRegisters)
157 }
158 FunctionCode::WriteSingleCoil => {
159 WriteSingleCoilResponse::decode(data).map(ResponsePdu::WriteSingleCoil)
160 }
161 FunctionCode::WriteSingleRegister => {
162 WriteSingleRegisterResponse::decode(data).map(ResponsePdu::WriteSingleRegister)
163 }
164 FunctionCode::ReadExceptionStatus => {
165 ReadExceptionStatusResponse::decode(data).map(ResponsePdu::ReadExceptionStatus)
166 }
167 FunctionCode::Diagnostics => {
168 DiagnosticsResponse::decode(data).map(ResponsePdu::Diagnostics)
169 }
170 FunctionCode::GetCommEventCounter => {
171 GetCommEventCounterResponse::decode(data).map(ResponsePdu::GetCommEventCounter)
172 }
173 FunctionCode::GetCommEventLog => {
174 GetCommEventLogResponse::decode(data).map(ResponsePdu::GetCommEventLog)
175 }
176 FunctionCode::WriteMultipleCoils => {
177 WriteMultipleCoilsResponse::decode(data).map(ResponsePdu::WriteMultipleCoils)
178 }
179 FunctionCode::WriteMultipleRegisters => {
180 WriteMultipleRegistersResponse::decode(data).map(ResponsePdu::WriteMultipleRegisters)
181 }
182 FunctionCode::ReportServerId => {
183 ReportServerIdResponse::decode(data).map(ResponsePdu::ReportServerId)
184 }
185 FunctionCode::ReadFileRecord => {
186 ReadFileRecordResponse::decode(data).map(ResponsePdu::ReadFileRecord)
187 }
188 FunctionCode::WriteFileRecord => {
189 WriteFileRecordResponse::decode(data).map(ResponsePdu::WriteFileRecord)
190 }
191 FunctionCode::MaskWriteRegister => {
192 MaskWriteRegisterResponse::decode(data).map(ResponsePdu::MaskWriteRegister)
193 }
194 FunctionCode::ReadWriteMultipleRegisters => {
195 ReadWriteMultipleRegistersResponse::decode(data)
196 .map(ResponsePdu::ReadWriteMultipleRegisters)
197 }
198 FunctionCode::ReadFifoQueue => {
199 ReadFifoQueueResponse::decode(data).map(ResponsePdu::ReadFifoQueue)
200 }
201 FunctionCode::EncapsulatedInterfaceTransport => {
202 EncapsulatedInterfaceResponse::decode(data).map(ResponsePdu::EncapsulatedInterface)
203 }
204 FunctionCode::Custom(fc) => Ok(ResponsePdu::Custom(fc, data)),
205 }
206}