tokio_modbus/codec/
mod.rs

1// SPDX-FileCopyrightText: Copyright (c) 2017-2025 slowtec GmbH <post@slowtec.de>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use std::{
5    convert::TryFrom,
6    io::{self, BufRead as _, Cursor, Error, ErrorKind},
7};
8
9use byteorder::{BigEndian, ReadBytesExt as _};
10
11use crate::{
12    bytes::{Buf as _, Bytes},
13    frame::{
14        Coil, ConformityLevel, DeviceIdObject, ReadCode, ReadDeviceIdentificationResponse,
15        RequestPdu, ResponsePdu, MEI_TYPE_READ_DEVICE_IDENTIFICATION,
16    },
17    ExceptionCode, ExceptionResponse, FunctionCode, Request, Response,
18};
19
20#[cfg(feature = "rtu")]
21pub(crate) mod rtu;
22
23#[cfg(feature = "tcp")]
24pub(crate) mod tcp;
25
26/// Maximum request/response PDU size.
27///
28/// As defined by the spec for both RTU and TCP.
29const MAX_PDU_SIZE: usize = 253;
30
31#[cfg(any(test, feature = "rtu", feature = "tcp"))]
32#[allow(clippy::cast_possible_truncation)]
33fn u16_len(len: usize) -> u16 {
34    // This type conversion should always be safe, because either
35    // the caller is responsible to pass a valid usize or the
36    // possible values are limited by the protocol.
37    debug_assert!(len <= u16::MAX.into());
38    len as u16
39}
40
41#[cfg(any(test, feature = "rtu", feature = "tcp"))]
42#[allow(clippy::cast_possible_truncation)]
43fn u8_len(len: usize) -> u8 {
44    // This type conversion should always be safe, because either
45    // the caller is responsible to pass a valid usize or the
46    // possible values are limited by the protocol.
47    debug_assert!(len <= u8::MAX.into());
48    len as u8
49}
50
51#[cfg(any(test, feature = "rtu", feature = "tcp"))]
52fn encode_request_pdu(buf: &mut crate::bytes::BytesMut, request: &Request<'_>) {
53    use crate::{bytes::BufMut as _, frame::Request::*};
54    buf.put_u8(request.function_code().value());
55    match request {
56        ReadCoils(address, quantity)
57        | ReadDiscreteInputs(address, quantity)
58        | ReadInputRegisters(address, quantity)
59        | ReadHoldingRegisters(address, quantity) => {
60            buf.put_u16(*address);
61            buf.put_u16(*quantity);
62        }
63        WriteSingleCoil(address, state) => {
64            buf.put_u16(*address);
65            buf.put_u16(bool_to_coil(*state));
66        }
67        WriteMultipleCoils(address, coils) => {
68            buf.put_u16(*address);
69            buf.put_u16(u16_len(coils.len()));
70            buf.put_u8(u8_len(packed_coils_size(coils)));
71            encode_packed_coils(buf, coils);
72        }
73        WriteSingleRegister(address, word) => {
74            buf.put_u16(*address);
75            buf.put_u16(*word);
76        }
77        WriteMultipleRegisters(address, words) => {
78            buf.put_u16(*address);
79            let len = words.len();
80            buf.put_u16(u16_len(len));
81            buf.put_u8(u8_len(len * 2));
82            for w in words.as_ref() {
83                buf.put_u16(*w);
84            }
85        }
86        ReportServerId => {}
87        MaskWriteRegister(address, and_mask, or_mask) => {
88            buf.put_u16(*address);
89            buf.put_u16(*and_mask);
90            buf.put_u16(*or_mask);
91        }
92        ReadWriteMultipleRegisters(read_address, quantity, write_address, words) => {
93            buf.put_u16(*read_address);
94            buf.put_u16(*quantity);
95            buf.put_u16(*write_address);
96            let len = words.len();
97            buf.put_u16(u16_len(len));
98            buf.put_u8(u8_len(len * 2));
99            for w in words.as_ref() {
100                buf.put_u16(*w);
101            }
102        }
103        ReadDeviceIdentification(read_code, object_id) => {
104            buf.put_u8(MEI_TYPE_READ_DEVICE_IDENTIFICATION);
105            buf.put_u8(read_code.value());
106            buf.put_u8(*object_id);
107        }
108        Custom(_, custom_data) => {
109            buf.put_slice(custom_data.as_ref());
110        }
111    }
112}
113
114#[cfg(any(test, feature = "server"))]
115fn encode_response_pdu(buf: &mut crate::bytes::BytesMut, response: &Response) {
116    use crate::{
117        bytes::BufMut as _,
118        frame::{ReadDeviceIdentificationResponse, Response::*},
119    };
120    buf.put_u8(response.function_code().value());
121    match response {
122        ReadCoils(coils) | ReadDiscreteInputs(coils) => {
123            buf.put_u8(u8_len(packed_coils_size(coils)));
124            encode_packed_coils(buf, coils);
125        }
126        ReadInputRegisters(registers)
127        | ReadHoldingRegisters(registers)
128        | ReadWriteMultipleRegisters(registers) => {
129            buf.put_u8(u8_len(registers.len() * 2));
130            for r in registers {
131                buf.put_u16(*r);
132            }
133        }
134        WriteSingleCoil(address, state) => {
135            buf.put_u16(*address);
136            buf.put_u16(bool_to_coil(*state));
137        }
138        WriteMultipleCoils(address, quantity) | WriteMultipleRegisters(address, quantity) => {
139            buf.put_u16(*address);
140            buf.put_u16(*quantity);
141        }
142        ReportServerId(server_id, run_indication, additional_data) => {
143            buf.put_u8(2 + u8_len(additional_data.len()));
144            buf.put_u8(*server_id);
145            buf.put_u8(if *run_indication { 0xFF } else { 0x00 });
146            buf.put_slice(additional_data);
147        }
148        WriteSingleRegister(address, word) => {
149            buf.put_u16(*address);
150            buf.put_u16(*word);
151        }
152        MaskWriteRegister(address, and_mask, or_mask) => {
153            buf.put_u16(*address);
154            buf.put_u16(*and_mask);
155            buf.put_u16(*or_mask);
156        }
157        ReadDeviceIdentification(ReadDeviceIdentificationResponse {
158            read_code,
159            conformity_level,
160            more_follows,
161            next_object_id,
162            device_id_objects,
163        }) => {
164            buf.put_u8(MEI_TYPE_READ_DEVICE_IDENTIFICATION);
165            buf.put_u8(read_code.value());
166            buf.put_u8(conformity_level.value());
167            buf.put_u8(if *more_follows { 0xff } else { 0x00 });
168            buf.put_u8(*next_object_id);
169            buf.put_u8(u8_len(device_id_objects.len())); // response_pdu_size validates the length
170            for dio in device_id_objects {
171                buf.put_u8(dio.id);
172                buf.put_u8(u8_len(dio.value.len())); // response_pdu_size validates the length
173                buf.put_slice(&dio.value);
174            }
175        }
176        Custom(_, custom_data) => {
177            buf.put_slice(custom_data);
178        }
179    }
180}
181
182#[cfg(any(test, feature = "server"))]
183fn encode_exception_response_pdu(buf: &mut crate::bytes::BytesMut, response: ExceptionResponse) {
184    use crate::bytes::BufMut as _;
185    debug_assert!(response.function.value() < 0x80);
186    buf.put_u8(response.function.value() + 0x80);
187    buf.put_u8(response.exception.into());
188}
189
190#[cfg(feature = "server")]
191fn encode_response_result_pdu(
192    buf: &mut crate::bytes::BytesMut,
193    res: &Result<Response, ExceptionResponse>,
194) {
195    match res {
196        Ok(response) => encode_response_pdu(buf, response),
197        Err(response) => encode_exception_response_pdu(buf, *response),
198    }
199}
200
201fn read_u16_be(reader: &mut impl io::Read) -> io::Result<u16> {
202    reader.read_u16::<BigEndian>()
203}
204
205// Only needed for requests with a dynamic payload size.
206fn check_request_pdu_size(pdu_size: usize) -> io::Result<()> {
207    if pdu_size > MAX_PDU_SIZE {
208        return Err(io::Error::new(
209            ErrorKind::InvalidData,
210            "request PDU size exceeded",
211        ));
212    }
213    Ok(())
214}
215
216#[allow(clippy::too_many_lines)] // TODO
217fn decode_request_pdu_bytes(bytes: &Bytes) -> io::Result<Request<'static>> {
218    use crate::frame::Request::*;
219    let pdu_size = bytes.len();
220    let rdr = &mut Cursor::new(&bytes);
221    let fn_code = rdr.read_u8()?;
222    let req = match fn_code {
223        0x01 => ReadCoils(read_u16_be(rdr)?, read_u16_be(rdr)?),
224        0x02 => ReadDiscreteInputs(read_u16_be(rdr)?, read_u16_be(rdr)?),
225        0x05 => WriteSingleCoil(read_u16_be(rdr)?, coil_to_bool(read_u16_be(rdr)?)?),
226        0x0F => {
227            check_request_pdu_size(pdu_size)?;
228            let address = read_u16_be(rdr)?;
229            let quantity = read_u16_be(rdr)?;
230            let byte_count = usize::from(rdr.read_u8()?);
231            if bytes.len() < 6 + byte_count {
232                return Err(io::Error::new(ErrorKind::InvalidData, "too short"));
233            }
234            rdr.consume(byte_count);
235            let packed_coils = &bytes[6..6 + byte_count];
236            WriteMultipleCoils(address, decode_packed_coils(packed_coils, quantity).into())
237        }
238        0x04 => ReadInputRegisters(read_u16_be(rdr)?, read_u16_be(rdr)?),
239        0x03 => ReadHoldingRegisters(read_u16_be(rdr)?, read_u16_be(rdr)?),
240        0x06 => WriteSingleRegister(read_u16_be(rdr)?, read_u16_be(rdr)?),
241        0x10 => {
242            check_request_pdu_size(pdu_size)?;
243            let address = read_u16_be(rdr)?;
244            let quantity = read_u16_be(rdr)?;
245            let byte_count = rdr.read_u8()?;
246            if u16::from(byte_count) != quantity * 2 {
247                return Err(io::Error::new(ErrorKind::InvalidData, "invalid quantity"));
248            }
249            let mut data = Vec::with_capacity(quantity.into());
250            for _ in 0..quantity {
251                data.push(read_u16_be(rdr)?);
252            }
253            WriteMultipleRegisters(address, data.into())
254        }
255        0x11 => ReportServerId,
256        0x16 => {
257            let address = read_u16_be(rdr)?;
258            let and_mask = read_u16_be(rdr)?;
259            let or_mask = read_u16_be(rdr)?;
260            MaskWriteRegister(address, and_mask, or_mask)
261        }
262        0x17 => {
263            check_request_pdu_size(pdu_size)?;
264            let read_address = read_u16_be(rdr)?;
265            let read_quantity = read_u16_be(rdr)?;
266            let write_address = read_u16_be(rdr)?;
267            let write_quantity = read_u16_be(rdr)?;
268            let write_count = rdr.read_u8()?;
269            if u16::from(write_count) != write_quantity * 2 {
270                return Err(io::Error::new(
271                    ErrorKind::InvalidData,
272                    "invalid write quantity",
273                ));
274            }
275            let mut data = Vec::with_capacity(write_quantity.into());
276            for _ in 0..write_quantity {
277                data.push(read_u16_be(rdr)?);
278            }
279            ReadWriteMultipleRegisters(read_address, read_quantity, write_address, data.into())
280        }
281        0x2b => {
282            let mei_type = rdr.read_u8()?;
283            if mei_type != MEI_TYPE_READ_DEVICE_IDENTIFICATION {
284                return Err(Error::new(
285                    ErrorKind::InvalidData,
286                    format!("unsupported MEI type: {mei_type:#04X}"),
287                ));
288            }
289
290            check_request_pdu_size(pdu_size)?;
291            let Some(read_device_id_code) = ReadCode::try_from_value(rdr.read_u8()?) else {
292                return Err(Error::new(
293                    ErrorKind::InvalidData,
294                    "Invalid Read device ID code",
295                ));
296            };
297            let object_id = rdr.read_u8()?;
298            ReadDeviceIdentification(read_device_id_code, object_id)
299        }
300        fn_code if fn_code < 0x80 => {
301            // Consume all remaining bytes as custom data.
302            return Ok(Custom(fn_code, bytes[1..].to_vec().into()));
303        }
304        fn_code => {
305            return Err(Error::new(
306                ErrorKind::InvalidData,
307                format!("invalid function code: 0x{fn_code:02X}"),
308            ));
309        }
310    };
311    // Verify that all data has been consumed and decoded.
312    if rdr.has_remaining() {
313        return Err(io::Error::new(
314            io::ErrorKind::InvalidData,
315            "undecoded request data",
316        ));
317    }
318    Ok(req)
319}
320
321impl TryFrom<Bytes> for Request<'static> {
322    type Error = Error;
323
324    fn try_from(pdu_bytes: Bytes) -> Result<Self, Self::Error> {
325        decode_request_pdu_bytes(&pdu_bytes)
326    }
327}
328
329impl TryFrom<Bytes> for RequestPdu<'static> {
330    type Error = Error;
331
332    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
333        let pdu = Request::try_from(bytes)?.into();
334        Ok(pdu)
335    }
336}
337
338// Only needed for responses with a dynamic payload size.
339fn check_response_pdu_size(pdu_size: usize) -> io::Result<()> {
340    if pdu_size > MAX_PDU_SIZE {
341        return Err(io::Error::new(
342            ErrorKind::InvalidInput,
343            "response PDU size exceeded",
344        ));
345    }
346    Ok(())
347}
348
349#[allow(clippy::too_many_lines)] // TODO
350fn decode_response_pdu_bytes(bytes: Bytes) -> io::Result<Response> {
351    use crate::frame::Response::*;
352    let pdu_size = bytes.len();
353    let rdr = &mut Cursor::new(&bytes);
354    let fn_code = rdr.read_u8()?;
355    let response = match fn_code {
356        0x01 => {
357            check_response_pdu_size(pdu_size)?;
358            let byte_count = rdr.read_u8()?;
359            if bytes.len() < 2 + usize::from(byte_count) {
360                return Err(io::Error::new(ErrorKind::InvalidData, "too short"));
361            }
362            let packed_coils = &bytes[2..2 + usize::from(byte_count)];
363            rdr.consume(byte_count.into());
364            // Here we have no information about the exact requested quantity so we just
365            // unpack the whole byte.
366            let quantity = u16::from(byte_count) * 8;
367            ReadCoils(decode_packed_coils(packed_coils, quantity))
368        }
369        0x02 => {
370            check_response_pdu_size(pdu_size)?;
371            let byte_count = rdr.read_u8()?;
372            if bytes.len() < 2 + usize::from(byte_count) {
373                return Err(io::Error::new(ErrorKind::InvalidData, "too short"));
374            }
375            let packed_coils = &bytes[2..2 + usize::from(byte_count)];
376            rdr.consume(byte_count.into());
377            // Here we have no information about the exact requested quantity so we just
378            // unpack the whole byte.
379            let quantity = u16::from(byte_count) * 8;
380            ReadDiscreteInputs(decode_packed_coils(packed_coils, quantity))
381        }
382        0x05 => WriteSingleCoil(read_u16_be(rdr)?, coil_to_bool(read_u16_be(rdr)?)?),
383        0x0F => WriteMultipleCoils(read_u16_be(rdr)?, read_u16_be(rdr)?),
384        0x04 => {
385            check_response_pdu_size(pdu_size)?;
386            let byte_count = rdr.read_u8()?;
387            if byte_count % 2 != 0 {
388                return Err(io::Error::new(
389                    io::ErrorKind::InvalidData,
390                    "invalid quantity",
391                ));
392            }
393            let quantity = byte_count / 2;
394            let mut data = Vec::with_capacity(quantity.into());
395            for _ in 0..quantity {
396                data.push(read_u16_be(rdr)?);
397            }
398            ReadInputRegisters(data)
399        }
400        0x03 => {
401            check_response_pdu_size(pdu_size)?;
402            let byte_count = rdr.read_u8()?;
403            if byte_count % 2 != 0 {
404                return Err(io::Error::new(
405                    io::ErrorKind::InvalidData,
406                    "invalid quantity",
407                ));
408            }
409            let quantity = byte_count / 2;
410            let mut data = Vec::with_capacity(quantity.into());
411            for _ in 0..quantity {
412                data.push(read_u16_be(rdr)?);
413            }
414            ReadHoldingRegisters(data)
415        }
416        0x06 => WriteSingleRegister(read_u16_be(rdr)?, read_u16_be(rdr)?),
417        0x10 => WriteMultipleRegisters(read_u16_be(rdr)?, read_u16_be(rdr)?),
418        0x11 => {
419            check_response_pdu_size(pdu_size)?;
420            let byte_count = rdr.read_u8()?;
421            if byte_count < 2 {
422                return Err(io::Error::new(io::ErrorKind::InvalidData, "too short"));
423            }
424            let data_len = (byte_count - 2).into();
425            let server_id = rdr.read_u8()?;
426            let run_indication_status = match rdr.read_u8()? {
427                0x00 => false,
428                0xFF => true,
429                status => {
430                    return Err(Error::new(
431                        ErrorKind::InvalidData,
432                        format!("invalid run indication status: 0x{status:02X}"),
433                    ));
434                }
435            };
436            let mut data = Vec::with_capacity(data_len);
437            for _ in 0..data_len {
438                data.push(rdr.read_u8()?);
439            }
440            ReportServerId(server_id, run_indication_status, data)
441        }
442        0x16 => {
443            let address = read_u16_be(rdr)?;
444            let and_mask = read_u16_be(rdr)?;
445            let or_mask = read_u16_be(rdr)?;
446            MaskWriteRegister(address, and_mask, or_mask)
447        }
448        0x17 => {
449            check_response_pdu_size(pdu_size)?;
450            let byte_count = rdr.read_u8()?;
451            if byte_count % 2 != 0 {
452                return Err(io::Error::new(
453                    io::ErrorKind::InvalidData,
454                    "invalid quantity",
455                ));
456            }
457            let quantity = byte_count / 2;
458            let mut data = Vec::with_capacity(quantity.into());
459            for _ in 0..quantity {
460                data.push(read_u16_be(rdr)?);
461            }
462            ReadWriteMultipleRegisters(data)
463        }
464        0x2b => {
465            let mei_type = rdr.read_u8()?;
466            if mei_type != MEI_TYPE_READ_DEVICE_IDENTIFICATION {
467                return Err(Error::new(
468                    ErrorKind::InvalidData,
469                    format!("unsupported MEI type: {mei_type:#04X}"),
470                ));
471            }
472
473            check_response_pdu_size(pdu_size)?;
474            let Some(read_device_id_code) = ReadCode::try_from_value(rdr.read_u8()?) else {
475                return Err(Error::new(
476                    ErrorKind::InvalidData,
477                    "Invalid Read device ID code",
478                ));
479            };
480            let Some(conformity_level) = ConformityLevel::try_from_value(rdr.read_u8()?) else {
481                return Err(Error::new(
482                    ErrorKind::InvalidData,
483                    "Invalid conformity level",
484                ));
485            };
486            let more_follows = rdr.read_u8()? == 0xff;
487            let next_object_id = rdr.read_u8()?;
488            let count = rdr.read_u8()?;
489            let mut objects = Vec::with_capacity(count.into());
490            for _ in 0..count {
491                let id = rdr.read_u8()?;
492                let len: usize = rdr.read_u8()?.into();
493
494                let position = usize::try_from(rdr.position()).map_err(|_| {
495                    Error::new(
496                        ErrorKind::Unsupported,
497                        "Position exceeds usize limits on this platform",
498                    )
499                })?;
500                let bytes = rdr.get_ref();
501
502                if position + len > bytes.len() {
503                    return Err(Error::new(ErrorKind::InvalidData, "Invalid object length"));
504                }
505
506                let value = bytes.slice(position..position + len);
507                rdr.set_position((position + len) as u64);
508
509                objects.push(DeviceIdObject { id, value });
510            }
511            ReadDeviceIdentification(ReadDeviceIdentificationResponse {
512                read_code: read_device_id_code,
513                conformity_level,
514                more_follows,
515                next_object_id,
516                device_id_objects: objects,
517            })
518        }
519        _ => {
520            // Consume all remaining bytes as custom data.
521            let mut bytes = bytes;
522            return Ok(Custom(fn_code, bytes.split_off(1)));
523        }
524    };
525    // Verify that all data has been consumed and decoded.
526    if rdr.has_remaining() {
527        return Err(io::Error::new(
528            io::ErrorKind::InvalidData,
529            "undecoded response data",
530        ));
531    }
532    Ok(response)
533}
534
535impl TryFrom<Bytes> for Response {
536    type Error = Error;
537
538    fn try_from(pdu_bytes: Bytes) -> Result<Self, Self::Error> {
539        decode_response_pdu_bytes(pdu_bytes)
540    }
541}
542
543impl TryFrom<Bytes> for ExceptionResponse {
544    type Error = Error;
545
546    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
547        let mut rdr = Cursor::new(&bytes);
548        let fn_err_code = rdr.read_u8()?;
549        if fn_err_code < 0x80 {
550            return Err(Error::new(
551                ErrorKind::InvalidData,
552                "Invalid exception function code",
553            ));
554        }
555        let function = fn_err_code - 0x80;
556        let exception = ExceptionCode::new(rdr.read_u8()?);
557        Ok(ExceptionResponse {
558            function: FunctionCode::new(function),
559            exception,
560        })
561    }
562}
563
564impl TryFrom<Bytes> for ResponsePdu {
565    type Error = Error;
566
567    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
568        let fn_code = Cursor::new(&bytes).read_u8()?;
569        let pdu = if fn_code < 0x80 {
570            Response::try_from(bytes)?.into()
571        } else {
572            ExceptionResponse::try_from(bytes)?.into()
573        };
574        Ok(pdu)
575    }
576}
577
578#[cfg(any(test, feature = "rtu", feature = "tcp"))]
579fn bool_to_coil(state: bool) -> u16 {
580    if state {
581        0xFF00
582    } else {
583        0x0000
584    }
585}
586
587fn coil_to_bool(coil: u16) -> io::Result<bool> {
588    match coil {
589        0xFF00 => Ok(true),
590        0x0000 => Ok(false),
591        _ => Err(Error::new(ErrorKind::InvalidData, "Invalid coil value: {}")),
592    }
593}
594
595#[cfg(any(test, feature = "rtu", feature = "tcp"))]
596fn packed_coils_size(coils: &[Coil]) -> usize {
597    coils.len().div_ceil(8)
598}
599
600#[cfg(any(test, feature = "rtu", feature = "tcp"))]
601fn encode_packed_coils(buf: &mut crate::bytes::BytesMut, coils: &[Coil]) -> usize {
602    let packed_coils_size = packed_coils_size(coils);
603    let offset = buf.len();
604    buf.resize(offset + packed_coils_size, 0);
605    let buf = &mut buf[offset..];
606    for (i, b) in coils.iter().enumerate() {
607        let v = u8::from(*b); // 0 or 1
608        buf[i / 8] |= v << (i % 8);
609    }
610    packed_coils_size
611}
612
613fn decode_packed_coils(bytes: &[u8], count: u16) -> Vec<Coil> {
614    let mut res = Vec::with_capacity(count.into());
615    for i in 0usize..count.into() {
616        res.push((bytes[i / 8] >> (i % 8)) & 0b1 > 0);
617    }
618    res
619}
620
621#[cfg(any(feature = "rtu", feature = "tcp"))]
622fn request_pdu_size(request: &Request<'_>) -> io::Result<usize> {
623    use crate::frame::Request::*;
624    let size = match request {
625        ReadCoils(_, _)
626        | ReadDiscreteInputs(_, _)
627        | ReadInputRegisters(_, _)
628        | ReadHoldingRegisters(_, _)
629        | WriteSingleRegister(_, _)
630        | WriteSingleCoil(_, _) => 5,
631        WriteMultipleCoils(_, coils) => 6 + packed_coils_size(coils),
632        WriteMultipleRegisters(_, data) => 6 + data.len() * 2,
633        ReportServerId => 1,
634        MaskWriteRegister(_, _, _) => 7,
635        ReadWriteMultipleRegisters(_, _, _, data) => 10 + data.len() * 2,
636        ReadDeviceIdentification(_, _) => 4,
637        Custom(_, data) => 1 + data.len(),
638    };
639    if size > MAX_PDU_SIZE {
640        return Err(io::Error::new(
641            ErrorKind::InvalidInput,
642            "request PDU size exceeded",
643        ));
644    }
645    Ok(size)
646}
647
648#[cfg(feature = "server")]
649fn response_pdu_size(response: &Response) -> io::Result<usize> {
650    use crate::frame::Response::*;
651    let size = match response {
652        ReadCoils(coils) | ReadDiscreteInputs(coils) => 2 + packed_coils_size(coils),
653        WriteSingleCoil(_, _)
654        | WriteMultipleCoils(_, _)
655        | WriteMultipleRegisters(_, _)
656        | WriteSingleRegister(_, _) => 5,
657        ReadInputRegisters(data)
658        | ReadHoldingRegisters(data)
659        | ReadWriteMultipleRegisters(data) => 2 + data.len() * 2,
660        ReportServerId(_, _, data) => 3 + data.len(),
661        MaskWriteRegister(_, _, _) => 7,
662        ReadDeviceIdentification(response) => {
663            // 7-byte fixed header: function code, MEI type, device ID code,
664            // conformity level, more follows flag, next object ID, and object count.
665            //
666            // Each object adds 2 bytes overhead (object ID + length) plus its value length.
667            //
668            // This calculation and the subsequent size check ensure the total length
669            // fits within the maximum PDU size, implicitly limiting the number of objects
670            // so it always fits in a u8.
671            7 + response
672                .device_id_objects
673                .iter()
674                .map(|o| 2 + o.value.len())
675                .sum::<usize>()
676        }
677        Custom(_, data) => 1 + data.len(),
678    };
679    if size > MAX_PDU_SIZE {
680        return Err(io::Error::new(
681            ErrorKind::InvalidInput,
682            "response PDU size exceeded",
683        ));
684    }
685    Ok(size)
686}
687
688#[cfg(feature = "server")]
689fn response_result_pdu_size(res: &Result<Response, ExceptionResponse>) -> io::Result<usize> {
690    match res {
691        Ok(response) => response_pdu_size(response),
692        Err(_) => Ok(2),
693    }
694}
695
696#[cfg(test)]
697mod tests {
698
699    use std::borrow::Cow;
700
701    use crate::bytes::BytesMut;
702
703    use super::*;
704
705    fn encode_request_pdu_to_bytes(request: &Request<'_>) -> Bytes {
706        let mut buf = BytesMut::new();
707        encode_request_pdu(&mut buf, request);
708        buf.freeze()
709    }
710
711    fn encode_response_pdu_to_bytes(response: &Response) -> Bytes {
712        let mut buf = BytesMut::new();
713        encode_response_pdu(&mut buf, response);
714        buf.freeze()
715    }
716
717    fn encode_exception_response_pdu_to_bytes(response: ExceptionResponse) -> Bytes {
718        let mut buf = BytesMut::new();
719        encode_exception_response_pdu(&mut buf, response);
720        buf.freeze()
721    }
722
723    fn encode_packed_coils_to_bytes(coils: &[Coil]) -> Bytes {
724        let mut buf = BytesMut::new();
725        encode_packed_coils(&mut buf, coils);
726        buf.freeze()
727    }
728
729    #[test]
730    fn convert_bool_to_coil() {
731        assert_eq!(bool_to_coil(true), 0xFF00);
732        assert_eq!(bool_to_coil(false), 0x0000);
733    }
734
735    #[test]
736    fn convert_coil_to_bool() {
737        assert!(coil_to_bool(0xFF00).unwrap());
738        assert!(!coil_to_bool(0x0000).unwrap());
739    }
740
741    #[test]
742    fn convert_booleans_to_bytes() {
743        assert_eq!(&encode_packed_coils_to_bytes(&[])[..], &[]);
744        assert_eq!(&encode_packed_coils_to_bytes(&[true])[..], &[0b1]);
745        assert_eq!(&encode_packed_coils_to_bytes(&[false])[..], &[0b0]);
746        assert_eq!(&encode_packed_coils_to_bytes(&[true, false])[..], &[0b_01]);
747        assert_eq!(&encode_packed_coils_to_bytes(&[false, true])[..], &[0b_10]);
748        assert_eq!(&encode_packed_coils_to_bytes(&[true, true])[..], &[0b_11]);
749        assert_eq!(
750            &encode_packed_coils_to_bytes(&[true; 8])[..],
751            &[0b_1111_1111]
752        );
753        assert_eq!(&encode_packed_coils_to_bytes(&[true; 9])[..], &[255, 1]);
754        assert_eq!(&encode_packed_coils_to_bytes(&[false; 8])[..], &[0]);
755        assert_eq!(&encode_packed_coils_to_bytes(&[false; 9])[..], &[0, 0]);
756    }
757
758    #[test]
759    fn test_unpack_bits() {
760        assert_eq!(decode_packed_coils(&[], 0), &[]);
761        assert_eq!(decode_packed_coils(&[0, 0], 0), &[]);
762        assert_eq!(decode_packed_coils(&[0b1], 1), &[true]);
763        assert_eq!(decode_packed_coils(&[0b01], 2), &[true, false]);
764        assert_eq!(decode_packed_coils(&[0b10], 2), &[false, true]);
765        assert_eq!(decode_packed_coils(&[0b101], 3), &[true, false, true]);
766        assert_eq!(decode_packed_coils(&[0xff, 0b11], 10), &[true; 10]);
767    }
768
769    #[test]
770    fn exception_response_into_bytes() {
771        let bytes = encode_exception_response_pdu_to_bytes(ExceptionResponse {
772            function: FunctionCode::ReadHoldingRegisters,
773            exception: ExceptionCode::IllegalDataAddress,
774        });
775        assert_eq!(bytes[0], 0x83);
776        assert_eq!(bytes[1], 0x02);
777    }
778
779    #[test]
780    fn exception_response_from_bytes() {
781        assert!(ExceptionResponse::try_from(Bytes::from(vec![0x79, 0x02])).is_err());
782
783        let bytes = Bytes::from(vec![0x83, 0x02]);
784        let response = ExceptionResponse::try_from(bytes).unwrap();
785        assert_eq!(
786            response,
787            ExceptionResponse {
788                function: FunctionCode::ReadHoldingRegisters,
789                exception: ExceptionCode::IllegalDataAddress,
790            }
791        );
792    }
793
794    #[test]
795    fn pdu_into_bytes() {
796        let req_pdu = encode_request_pdu_to_bytes(&Request::ReadCoils(0x01, 5));
797        let response_pdu = encode_response_pdu_to_bytes(&Response::ReadCoils(vec![]));
798        let ex_pdu = encode_exception_response_pdu_to_bytes(ExceptionResponse {
799            function: FunctionCode::ReadHoldingRegisters,
800            exception: ExceptionCode::ServerDeviceFailure,
801        });
802
803        assert_eq!(req_pdu[0], 0x01);
804        assert_eq!(req_pdu[1], 0x00);
805        assert_eq!(req_pdu[2], 0x01);
806        assert_eq!(req_pdu[3], 0x00);
807        assert_eq!(req_pdu[4], 0x05);
808
809        assert_eq!(response_pdu[0], 0x01);
810        assert_eq!(response_pdu[1], 0x00);
811
812        assert_eq!(ex_pdu[0], 0x83);
813        assert_eq!(ex_pdu[1], 0x04);
814
815        let req_pdu = encode_request_pdu_to_bytes(&Request::ReadHoldingRegisters(0x082B, 2));
816        assert_eq!(req_pdu.len(), 5);
817        assert_eq!(req_pdu[0], 0x03);
818        assert_eq!(req_pdu[1], 0x08);
819        assert_eq!(req_pdu[2], 0x2B);
820        assert_eq!(req_pdu[3], 0x00);
821        assert_eq!(req_pdu[4], 0x02);
822    }
823
824    #[test]
825    fn pdu_with_a_lot_of_data_into_bytes() {
826        let _req_pdu = encode_request_pdu_to_bytes(&Request::WriteMultipleRegisters(
827            0x01,
828            Cow::Borrowed(&[0; 80]),
829        ));
830        let _response_pdu =
831            encode_response_pdu_to_bytes(&Response::ReadInputRegisters(vec![0; 80]));
832    }
833
834    mod serialize_requests {
835
836        use super::*;
837
838        #[test]
839        fn read_coils() {
840            let bytes = encode_request_pdu_to_bytes(&Request::ReadCoils(0x12, 4));
841            assert_eq!(bytes[0], 1);
842            assert_eq!(bytes[1], 0x00);
843            assert_eq!(bytes[2], 0x12);
844            assert_eq!(bytes[3], 0x00);
845            assert_eq!(bytes[4], 0x04);
846        }
847
848        #[test]
849        fn read_discrete_inputs() {
850            let bytes = encode_request_pdu_to_bytes(&Request::ReadDiscreteInputs(0x03, 19));
851            assert_eq!(bytes[0], 2);
852            assert_eq!(bytes[1], 0x00);
853            assert_eq!(bytes[2], 0x03);
854            assert_eq!(bytes[3], 0x00);
855            assert_eq!(bytes[4], 19);
856        }
857
858        #[test]
859        fn write_single_coil() {
860            let bytes = encode_request_pdu_to_bytes(&Request::WriteSingleCoil(0x1234, true));
861            assert_eq!(bytes[0], 5);
862            assert_eq!(bytes[1], 0x12);
863            assert_eq!(bytes[2], 0x34);
864            assert_eq!(bytes[3], 0xFF);
865            assert_eq!(bytes[4], 0x00);
866        }
867
868        #[test]
869        fn write_multiple_coils() {
870            let states = [true, false, true, true];
871            let bytes = encode_request_pdu_to_bytes(&Request::WriteMultipleCoils(
872                0x3311,
873                Cow::Borrowed(&states),
874            ));
875            assert_eq!(bytes[0], 0x0F);
876            assert_eq!(bytes[1], 0x33);
877            assert_eq!(bytes[2], 0x11);
878            assert_eq!(bytes[3], 0x00);
879            assert_eq!(bytes[4], 0x04);
880            assert_eq!(bytes[5], 0x01);
881            assert_eq!(bytes[6], 0b_0000_1101);
882        }
883
884        #[test]
885        fn read_input_registers() {
886            let bytes = encode_request_pdu_to_bytes(&Request::ReadInputRegisters(0x09, 77));
887            assert_eq!(bytes[0], 4);
888            assert_eq!(bytes[1], 0x00);
889            assert_eq!(bytes[2], 0x09);
890            assert_eq!(bytes[3], 0x00);
891            assert_eq!(bytes[4], 0x4D);
892        }
893
894        #[test]
895        fn read_holding_registers() {
896            let bytes = encode_request_pdu_to_bytes(&Request::ReadHoldingRegisters(0x09, 77));
897            assert_eq!(bytes[0], 3);
898            assert_eq!(bytes[1], 0x00);
899            assert_eq!(bytes[2], 0x09);
900            assert_eq!(bytes[3], 0x00);
901            assert_eq!(bytes[4], 0x4D);
902        }
903
904        #[test]
905        fn write_single_register() {
906            let bytes = encode_request_pdu_to_bytes(&Request::WriteSingleRegister(0x07, 0xABCD));
907            assert_eq!(bytes[0], 6);
908            assert_eq!(bytes[1], 0x00);
909            assert_eq!(bytes[2], 0x07);
910            assert_eq!(bytes[3], 0xAB);
911            assert_eq!(bytes[4], 0xCD);
912        }
913
914        #[test]
915        fn write_multiple_registers() {
916            let bytes = encode_request_pdu_to_bytes(&Request::WriteMultipleRegisters(
917                0x06,
918                Cow::Borrowed(&[0xABCD, 0xEF12]),
919            ));
920
921            // function code
922            assert_eq!(bytes[0], 0x10);
923
924            // write starting address
925            assert_eq!(bytes[1], 0x00);
926            assert_eq!(bytes[2], 0x06);
927
928            // quantity to write
929            assert_eq!(bytes[3], 0x00);
930            assert_eq!(bytes[4], 0x02);
931
932            // write byte count
933            assert_eq!(bytes[5], 0x04);
934
935            // values
936            assert_eq!(bytes[6], 0xAB);
937            assert_eq!(bytes[7], 0xCD);
938            assert_eq!(bytes[8], 0xEF);
939            assert_eq!(bytes[9], 0x12);
940        }
941
942        #[test]
943        fn report_server_id() {
944            let bytes = encode_request_pdu_to_bytes(&Request::ReportServerId);
945            assert_eq!(bytes[0], 0x11);
946        }
947
948        #[test]
949        fn masked_write_register() {
950            let bytes =
951                encode_request_pdu_to_bytes(&Request::MaskWriteRegister(0xABCD, 0xEF12, 0x2345));
952
953            // function code
954            assert_eq!(bytes[0], 0x16);
955
956            // address
957            assert_eq!(bytes[1], 0xAB);
958            assert_eq!(bytes[2], 0xCD);
959
960            // and mask
961            assert_eq!(bytes[3], 0xEF);
962            assert_eq!(bytes[4], 0x12);
963
964            // or mask
965            assert_eq!(bytes[5], 0x23);
966            assert_eq!(bytes[6], 0x45);
967        }
968
969        #[test]
970        fn read_write_multiple_registers() {
971            let data = [0xABCD, 0xEF12];
972            let bytes = encode_request_pdu_to_bytes(&Request::ReadWriteMultipleRegisters(
973                0x05,
974                51,
975                0x03,
976                Cow::Borrowed(&data),
977            ));
978
979            // function code
980            assert_eq!(bytes[0], 0x17);
981
982            // read starting address
983            assert_eq!(bytes[1], 0x00);
984            assert_eq!(bytes[2], 0x05);
985
986            // quantity to read
987            assert_eq!(bytes[3], 0x00);
988            assert_eq!(bytes[4], 0x33);
989
990            // write starting address
991            assert_eq!(bytes[5], 0x00);
992            assert_eq!(bytes[6], 0x03);
993
994            // quantity to write
995            assert_eq!(bytes[7], 0x00);
996            assert_eq!(bytes[8], 0x02);
997
998            // write byte count
999            assert_eq!(bytes[9], 0x04);
1000
1001            // values
1002            assert_eq!(bytes[10], 0xAB);
1003            assert_eq!(bytes[11], 0xCD);
1004            assert_eq!(bytes[12], 0xEF);
1005            assert_eq!(bytes[13], 0x12);
1006        }
1007
1008        #[test]
1009        fn custom() {
1010            let bytes = encode_request_pdu_to_bytes(&Request::Custom(
1011                0x55,
1012                Cow::Borrowed(&[0xCC, 0x88, 0xAA, 0xFF]),
1013            ));
1014            assert_eq!(bytes[0], 0x55);
1015            assert_eq!(bytes[1], 0xCC);
1016            assert_eq!(bytes[2], 0x88);
1017            assert_eq!(bytes[3], 0xAA);
1018            assert_eq!(bytes[4], 0xFF);
1019        }
1020    }
1021
1022    mod deserialize_requests {
1023
1024        use super::*;
1025
1026        #[test]
1027        fn empty_request() {
1028            assert!(Request::try_from(Bytes::from(vec![])).is_err());
1029        }
1030
1031        #[test]
1032        fn read_coils() {
1033            assert!(Request::try_from(Bytes::from(vec![0x01])).is_err());
1034            assert!(Request::try_from(Bytes::from(vec![0x01, 0x0, 0x0, 0x22])).is_err());
1035
1036            let bytes = Bytes::from(vec![0x01, 0x00, 0x12, 0x0, 0x4]);
1037            let req = Request::try_from(bytes).unwrap();
1038            assert_eq!(req, Request::ReadCoils(0x12, 4));
1039        }
1040
1041        #[test]
1042        fn read_discrete_inputs() {
1043            let bytes = Bytes::from(vec![2, 0x00, 0x03, 0x00, 19]);
1044            let req = Request::try_from(bytes).unwrap();
1045            assert_eq!(req, Request::ReadDiscreteInputs(0x03, 19));
1046        }
1047
1048        #[test]
1049        fn write_single_coil() {
1050            let bytes = Bytes::from(vec![5, 0x12, 0x34, 0xFF, 0x00]);
1051            let req = Request::try_from(bytes).unwrap();
1052            assert_eq!(req, Request::WriteSingleCoil(0x1234, true));
1053        }
1054
1055        #[test]
1056        fn write_multiple_coils() {
1057            assert!(Request::try_from(Bytes::from(vec![
1058                0x0F,
1059                0x33,
1060                0x11,
1061                0x00,
1062                0x04,
1063                0x02,
1064                0b_0000_1101,
1065            ]))
1066            .is_err());
1067
1068            let bytes = Bytes::from(vec![0x0F, 0x33, 0x11, 0x00, 0x04, 0x01, 0b_0000_1101]);
1069            let req = Request::try_from(bytes).unwrap();
1070            assert_eq!(
1071                req,
1072                Request::WriteMultipleCoils(0x3311, Cow::Borrowed(&[true, false, true, true]))
1073            );
1074        }
1075
1076        #[test]
1077        fn read_input_registers() {
1078            let bytes = Bytes::from(vec![4, 0x00, 0x09, 0x00, 0x4D]);
1079            let req = Request::try_from(bytes).unwrap();
1080            assert_eq!(req, Request::ReadInputRegisters(0x09, 77));
1081        }
1082
1083        #[test]
1084        fn read_holding_registers() {
1085            let bytes = Bytes::from(vec![3, 0x00, 0x09, 0x00, 0x4D]);
1086            let req = Request::try_from(bytes).unwrap();
1087            assert_eq!(req, Request::ReadHoldingRegisters(0x09, 77));
1088        }
1089
1090        #[test]
1091        fn write_single_register() {
1092            let bytes = Bytes::from(vec![6, 0x00, 0x07, 0xAB, 0xCD]);
1093            let req = Request::try_from(bytes).unwrap();
1094            assert_eq!(req, Request::WriteSingleRegister(0x07, 0xABCD));
1095        }
1096
1097        #[test]
1098        fn write_multiple_registers() {
1099            assert!(Request::try_from(Bytes::from(vec![
1100                0x10, 0x00, 0x06, 0x00, 0x02, 0x05, 0xAB, 0xCD, 0xEF, 0x12,
1101            ]))
1102            .is_err());
1103
1104            let bytes = Bytes::from(vec![
1105                0x10, 0x00, 0x06, 0x00, 0x02, 0x04, 0xAB, 0xCD, 0xEF, 0x12,
1106            ]);
1107            let req = Request::try_from(bytes).unwrap();
1108            assert_eq!(
1109                req,
1110                Request::WriteMultipleRegisters(0x06, Cow::Borrowed(&[0xABCD, 0xEF12]))
1111            );
1112        }
1113
1114        #[test]
1115        fn report_server_id() {
1116            let bytes = Bytes::from(vec![0x11]);
1117            let req = Request::try_from(bytes).unwrap();
1118            assert_eq!(req, Request::ReportServerId);
1119        }
1120
1121        #[test]
1122        fn masked_write_register() {
1123            let bytes = Bytes::from(vec![0x16, 0xAB, 0xCD, 0xEF, 0x12, 0x23, 0x45]);
1124            let req = Request::try_from(bytes).unwrap();
1125            assert_eq!(req, Request::MaskWriteRegister(0xABCD, 0xEF12, 0x2345));
1126        }
1127
1128        #[test]
1129        fn read_write_multiple_registers() {
1130            assert!(Request::try_from(Bytes::from(vec![
1131                0x17, 0x00, 0x05, 0x00, 0x33, 0x00, 0x03, 0x00, 0x02, 0x05, 0xAB, 0xCD, 0xEF, 0x12,
1132            ]))
1133            .is_err());
1134            let bytes = Bytes::from(vec![
1135                0x17, 0x00, 0x05, 0x00, 0x33, 0x00, 0x03, 0x00, 0x02, 0x04, 0xAB, 0xCD, 0xEF, 0x12,
1136            ]);
1137            let req = Request::try_from(bytes).unwrap();
1138            let data = [0xABCD, 0xEF12];
1139            assert_eq!(
1140                req,
1141                Request::ReadWriteMultipleRegisters(0x05, 51, 0x03, Cow::Borrowed(&data))
1142            );
1143        }
1144
1145        #[test]
1146        fn read_device_identification() {
1147            let bytes = Bytes::from(vec![0x2B, 0x0E, 0x01, 0x01]);
1148            let req = Request::try_from(bytes).unwrap();
1149            assert_eq!(req, Request::ReadDeviceIdentification(ReadCode::Basic, 1));
1150        }
1151
1152        #[test]
1153        fn custom() {
1154            let bytes = Bytes::from(vec![0x55, 0xCC, 0x88, 0xAA, 0xFF]);
1155            let req = Request::try_from(bytes).unwrap();
1156            assert_eq!(
1157                req,
1158                Request::Custom(0x55, Cow::Borrowed(&[0xCC, 0x88, 0xAA, 0xFF]))
1159            );
1160        }
1161    }
1162
1163    mod serialize_responses {
1164
1165        use super::*;
1166
1167        #[test]
1168        fn read_coils() {
1169            let bytes = encode_response_pdu_to_bytes(&Response::ReadCoils(vec![
1170                true, false, false, true, false,
1171            ]));
1172            assert_eq!(bytes[0], 1);
1173            assert_eq!(bytes[1], 1);
1174            assert_eq!(bytes[2], 0b_0000_1001);
1175        }
1176
1177        #[test]
1178        fn read_discrete_inputs() {
1179            let bytes = encode_response_pdu_to_bytes(&Response::ReadDiscreteInputs(vec![
1180                true, false, true, true,
1181            ]));
1182            assert_eq!(bytes[0], 2);
1183            assert_eq!(bytes[1], 1);
1184            assert_eq!(bytes[2], 0b_0000_1101);
1185        }
1186
1187        #[test]
1188        fn write_single_coil() {
1189            let bytes = encode_response_pdu_to_bytes(&Response::WriteSingleCoil(0x33, true));
1190            assert_eq!(bytes[0], 5);
1191            assert_eq!(bytes[1], 0x00);
1192            assert_eq!(bytes[2], 0x33);
1193            assert_eq!(bytes[3], 0xFF);
1194            assert_eq!(bytes[4], 0x00);
1195        }
1196
1197        #[test]
1198        fn write_multiple_coils() {
1199            let bytes = encode_response_pdu_to_bytes(&Response::WriteMultipleCoils(0x3311, 5));
1200            assert_eq!(bytes[0], 0x0F);
1201            assert_eq!(bytes[1], 0x33);
1202            assert_eq!(bytes[2], 0x11);
1203            assert_eq!(bytes[3], 0x00);
1204            assert_eq!(bytes[4], 0x05);
1205        }
1206
1207        #[test]
1208        fn read_input_registers() {
1209            let bytes = encode_response_pdu_to_bytes(&Response::ReadInputRegisters(vec![
1210                0xAA00, 0xCCBB, 0xEEDD,
1211            ]));
1212            assert_eq!(bytes[0], 4);
1213            assert_eq!(bytes[1], 0x06);
1214            assert_eq!(bytes[2], 0xAA);
1215            assert_eq!(bytes[3], 0x00);
1216            assert_eq!(bytes[4], 0xCC);
1217            assert_eq!(bytes[5], 0xBB);
1218            assert_eq!(bytes[6], 0xEE);
1219            assert_eq!(bytes[7], 0xDD);
1220        }
1221
1222        #[test]
1223        fn read_holding_registers() {
1224            let bytes =
1225                encode_response_pdu_to_bytes(&Response::ReadHoldingRegisters(vec![0xAA00, 0x1111]));
1226            assert_eq!(bytes[0], 3);
1227            assert_eq!(bytes[1], 0x04);
1228            assert_eq!(bytes[2], 0xAA);
1229            assert_eq!(bytes[3], 0x00);
1230            assert_eq!(bytes[4], 0x11);
1231            assert_eq!(bytes[5], 0x11);
1232        }
1233
1234        #[test]
1235        fn read_device_identification() {
1236            let bytes = encode_response_pdu_to_bytes(&Response::ReadDeviceIdentification(
1237                ReadDeviceIdentificationResponse {
1238                    read_code: ReadCode::Basic,
1239                    conformity_level: ConformityLevel::RegularIdentificationStreamOnly,
1240                    more_follows: false,
1241                    next_object_id: 0,
1242                    device_id_objects: vec![
1243                        DeviceIdObject {
1244                            id: 1,
1245                            value: Bytes::from("ProductCode"),
1246                        },
1247                        DeviceIdObject {
1248                            id: 2,
1249                            value: Bytes::from("2.1.3"),
1250                        },
1251                    ],
1252                },
1253            ));
1254            assert_eq!(bytes[0], 0x2B);
1255            assert_eq!(bytes[1], 0x0E);
1256            assert_eq!(bytes[2], 0x01);
1257            assert_eq!(bytes[3], 0x02);
1258            assert_eq!(bytes[4], 0x00);
1259            assert_eq!(bytes[5], 0x00);
1260            assert_eq!(bytes[6], 0x02);
1261            assert_eq!(bytes[7], 0x01);
1262            assert_eq!(bytes[8], 11);
1263            assert_eq!(std::str::from_utf8(&bytes[9..20]).unwrap(), "ProductCode");
1264            assert_eq!(bytes[20], 0x02);
1265            assert_eq!(bytes[21], 5);
1266            assert_eq!(std::str::from_utf8(&bytes[22..27]).unwrap(), "2.1.3");
1267        }
1268
1269        #[test]
1270        fn write_single_register() {
1271            let bytes = encode_response_pdu_to_bytes(&Response::WriteSingleRegister(0x07, 0xABCD));
1272            assert_eq!(bytes[0], 6);
1273            assert_eq!(bytes[1], 0x00);
1274            assert_eq!(bytes[2], 0x07);
1275            assert_eq!(bytes[3], 0xAB);
1276            assert_eq!(bytes[4], 0xCD);
1277        }
1278
1279        #[test]
1280        fn write_multiple_registers() {
1281            let bytes = encode_response_pdu_to_bytes(&Response::WriteMultipleRegisters(0x06, 2));
1282            assert_eq!(bytes[0], 0x10);
1283            assert_eq!(bytes[1], 0x00);
1284            assert_eq!(bytes[2], 0x06);
1285            assert_eq!(bytes[3], 0x00);
1286            assert_eq!(bytes[4], 0x02);
1287        }
1288
1289        #[test]
1290        fn report_server_id() {
1291            let bytes = encode_response_pdu_to_bytes(&Response::ReportServerId(
1292                0x42,
1293                true,
1294                vec![0x10, 0x20],
1295            ));
1296            assert_eq!(bytes[0], 0x11);
1297            assert_eq!(bytes[1], 0x04);
1298            assert_eq!(bytes[2], 0x42);
1299            assert_eq!(bytes[3], 0xFF);
1300            assert_eq!(bytes[4], 0x10);
1301            assert_eq!(bytes[5], 0x20);
1302        }
1303
1304        #[test]
1305        fn masked_write_register() {
1306            let bytes =
1307                encode_response_pdu_to_bytes(&Response::MaskWriteRegister(0x06, 0x8001, 0x4002));
1308            assert_eq!(bytes[0], 0x16);
1309            assert_eq!(bytes[1], 0x00);
1310            assert_eq!(bytes[2], 0x06);
1311            assert_eq!(bytes[3], 0x80);
1312            assert_eq!(bytes[4], 0x01);
1313            assert_eq!(bytes[5], 0x40);
1314            assert_eq!(bytes[6], 0x02);
1315        }
1316
1317        #[test]
1318        fn read_write_multiple_registers() {
1319            let bytes =
1320                encode_response_pdu_to_bytes(&Response::ReadWriteMultipleRegisters(vec![0x1234]));
1321            assert_eq!(bytes[0], 0x17);
1322            assert_eq!(bytes[1], 0x02);
1323            assert_eq!(bytes[2], 0x12);
1324            assert_eq!(bytes[3], 0x34);
1325        }
1326
1327        #[test]
1328        fn custom() {
1329            let bytes = encode_response_pdu_to_bytes(&Response::Custom(
1330                0x55,
1331                Bytes::from_static(&[0xCC, 0x88, 0xAA, 0xFF]),
1332            ));
1333            assert_eq!(bytes[0], 0x55);
1334            assert_eq!(bytes[1], 0xCC);
1335            assert_eq!(bytes[2], 0x88);
1336            assert_eq!(bytes[3], 0xAA);
1337            assert_eq!(bytes[4], 0xFF);
1338        }
1339    }
1340
1341    mod deserialize_responses {
1342
1343        use super::*;
1344
1345        #[test]
1346        fn read_coils() {
1347            let bytes = Bytes::from(vec![1, 1, 0b_0000_1001]);
1348            let response = Response::try_from(bytes).unwrap();
1349            assert_eq!(
1350                response,
1351                Response::ReadCoils(vec![true, false, false, true, false, false, false, false])
1352            );
1353        }
1354
1355        #[test]
1356        fn read_coils_max_quantity() {
1357            let quantity = 2000;
1358            let byte_count = quantity / 8;
1359            let mut raw: Vec<u8> = vec![1, u8_len(byte_count)];
1360            let mut values: Vec<u8> = (0..byte_count).map(|_| 0b_1111_1111).collect();
1361            raw.append(&mut values);
1362            let bytes = Bytes::from(raw);
1363            let response = Response::try_from(bytes).unwrap();
1364            assert_eq!(response, Response::ReadCoils(vec![true; quantity]));
1365        }
1366
1367        #[test]
1368        fn read_discrete_inputs() {
1369            let bytes = Bytes::from(vec![2, 1, 0b_0000_1001]);
1370            let response = Response::try_from(bytes).unwrap();
1371            assert_eq!(
1372                response,
1373                Response::ReadDiscreteInputs(vec![
1374                    true, false, false, true, false, false, false, false,
1375                ],)
1376            );
1377        }
1378
1379        #[test]
1380        fn read_discrete_inputs_max_quantity() {
1381            let quantity = 2000;
1382            let byte_count = quantity / 8;
1383            let mut raw: Vec<u8> = vec![2, u8_len(byte_count)];
1384            let mut values: Vec<u8> = (0..byte_count).map(|_| 0b_1111_1111).collect();
1385            raw.append(&mut values);
1386            let bytes = Bytes::from(raw);
1387            let response = Response::try_from(bytes).unwrap();
1388            assert_eq!(response, Response::ReadDiscreteInputs(vec![true; quantity]));
1389        }
1390
1391        #[test]
1392        fn read_device_identification() {
1393            let bytes = Bytes::from(vec![
1394                0x2B, 0x0E, 0x01, 0x02, 0x00, 0x00, 0x02, 0x01, 11, b'P', b'r', b'o', b'd', b'u',
1395                b'c', b't', b'C', b'o', b'd', b'e', 0x02, 5, b'2', b'.', b'1', b'.', b'3',
1396            ]);
1397            let response = Response::try_from(bytes).unwrap();
1398            assert_eq!(
1399                response,
1400                Response::ReadDeviceIdentification(ReadDeviceIdentificationResponse {
1401                    read_code: ReadCode::Basic,
1402                    conformity_level: ConformityLevel::RegularIdentificationStreamOnly,
1403                    more_follows: false,
1404                    next_object_id: 0,
1405                    device_id_objects: vec![
1406                        DeviceIdObject {
1407                            id: 1,
1408                            value: Bytes::from("ProductCode"),
1409                        },
1410                        DeviceIdObject {
1411                            id: 2,
1412                            value: Bytes::from("2.1.3"),
1413                        },
1414                    ],
1415                })
1416            );
1417        }
1418
1419        #[test]
1420        fn write_single_coil() {
1421            let bytes = Bytes::from(vec![5, 0x00, 0x33, 0xFF, 0x00]);
1422            let response = Response::try_from(bytes).unwrap();
1423            assert_eq!(response, Response::WriteSingleCoil(0x33, true));
1424        }
1425
1426        #[test]
1427        fn write_multiple_coils() {
1428            let bytes = Bytes::from(vec![0x0F, 0x33, 0x11, 0x00, 0x05]);
1429            let response = Response::try_from(bytes).unwrap();
1430            assert_eq!(response, Response::WriteMultipleCoils(0x3311, 5));
1431        }
1432
1433        #[test]
1434        fn read_input_registers() {
1435            let bytes = Bytes::from(vec![4, 0x06, 0xAA, 0x00, 0xCC, 0xBB, 0xEE, 0xDD]);
1436            let response = Response::try_from(bytes).unwrap();
1437            assert_eq!(
1438                response,
1439                Response::ReadInputRegisters(vec![0xAA00, 0xCCBB, 0xEEDD])
1440            );
1441        }
1442
1443        #[test]
1444        fn read_holding_registers() {
1445            let bytes = Bytes::from(vec![3, 0x04, 0xAA, 0x00, 0x11, 0x11]);
1446            let response = Response::try_from(bytes).unwrap();
1447            assert_eq!(
1448                response,
1449                Response::ReadHoldingRegisters(vec![0xAA00, 0x1111])
1450            );
1451        }
1452
1453        #[test]
1454        fn write_single_register() {
1455            let bytes = Bytes::from(vec![6, 0x00, 0x07, 0xAB, 0xCD]);
1456            let response = Response::try_from(bytes).unwrap();
1457            assert_eq!(response, Response::WriteSingleRegister(0x07, 0xABCD));
1458        }
1459
1460        #[test]
1461        fn write_multiple_registers() {
1462            let bytes = Bytes::from(vec![0x10, 0x00, 0x06, 0x00, 0x02]);
1463            let response = Response::try_from(bytes).unwrap();
1464            assert_eq!(response, Response::WriteMultipleRegisters(0x06, 2));
1465        }
1466
1467        #[test]
1468        fn report_server_id() {
1469            let bytes = Bytes::from(vec![0x11, 0x04, 0x042, 0xFF, 0x10, 0x20]);
1470            let response = Response::try_from(bytes).unwrap();
1471            assert_eq!(
1472                response,
1473                Response::ReportServerId(0x42, true, vec![0x10, 0x20])
1474            );
1475        }
1476
1477        #[test]
1478        fn masked_write_register() {
1479            let bytes = Bytes::from(vec![0x16, 0x00, 0x06, 0x80, 0x01, 0x40, 0x02]);
1480            let response = Response::try_from(bytes).unwrap();
1481            assert_eq!(response, Response::MaskWriteRegister(6, 0x8001, 0x4002));
1482        }
1483
1484        #[test]
1485        fn read_write_multiple_registers() {
1486            let bytes = Bytes::from(vec![0x17, 0x02, 0x12, 0x34]);
1487            let response = Response::try_from(bytes).unwrap();
1488            assert_eq!(response, Response::ReadWriteMultipleRegisters(vec![0x1234]));
1489        }
1490
1491        #[test]
1492        fn custom() {
1493            let bytes = Bytes::from(vec![0x55, 0xCC, 0x88, 0xAA, 0xFF]);
1494            let response = Response::try_from(bytes).unwrap();
1495            assert_eq!(
1496                response,
1497                Response::Custom(0x55, Bytes::from_static(&[0xCC, 0x88, 0xAA, 0xFF]))
1498            );
1499        }
1500    }
1501}