1use 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
26const 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 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 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())); for dio in device_id_objects {
171 buf.put_u8(dio.id);
172 buf.put_u8(u8_len(dio.value.len())); 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
205fn 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)] fn 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 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 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
338fn 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)] fn 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 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 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 let mut bytes = bytes;
522 return Ok(Custom(fn_code, bytes.split_off(1)));
523 }
524 };
525 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); 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 + 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 assert_eq!(bytes[0], 0x10);
923
924 assert_eq!(bytes[1], 0x00);
926 assert_eq!(bytes[2], 0x06);
927
928 assert_eq!(bytes[3], 0x00);
930 assert_eq!(bytes[4], 0x02);
931
932 assert_eq!(bytes[5], 0x04);
934
935 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 assert_eq!(bytes[0], 0x16);
955
956 assert_eq!(bytes[1], 0xAB);
958 assert_eq!(bytes[2], 0xCD);
959
960 assert_eq!(bytes[3], 0xEF);
962 assert_eq!(bytes[4], 0x12);
963
964 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 assert_eq!(bytes[0], 0x17);
981
982 assert_eq!(bytes[1], 0x00);
984 assert_eq!(bytes[2], 0x05);
985
986 assert_eq!(bytes[3], 0x00);
988 assert_eq!(bytes[4], 0x33);
989
990 assert_eq!(bytes[5], 0x00);
992 assert_eq!(bytes[6], 0x03);
993
994 assert_eq!(bytes[7], 0x00);
996 assert_eq!(bytes[8], 0x02);
997
998 assert_eq!(bytes[9], 0x04);
1000
1001 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}