1use rusty_modbus_codec::request::{ReadFileRecordRequest, WriteFileRecordRequest};
4use rusty_modbus_codec::response::{
5 GetCommEventCounterResponse, MaskWriteRegisterResponse, ReadExceptionStatusResponse,
6 WriteFileRecordResponse, WriteMultipleCoilsResponse, WriteMultipleRegistersResponse,
7 WriteSingleCoilResponse, WriteSingleRegisterResponse,
8};
9use rusty_modbus_codec::{DecodeError, RequestPdu, decode_request, validate};
10use rusty_modbus_types::{
11 Address, DiagnosticSubFunction, ExceptionCode, FunctionCode, MAX_FIFO_VALUES, MAX_PDU_SIZE,
12 MeiType, Quantity, UnitId,
13};
14use tracing::debug;
15
16use crate::config::DeviceIdentification;
17use crate::device_id::build_device_id_response;
18use crate::file_record;
19use crate::response_encode::encode_response;
20use crate::store::{
21 DataStore, MAX_COMM_EVENT_LOG_EVENTS, MAX_DIAGNOSTIC_RESPONSE_DATA_LEN,
22 MAX_FILE_RECORD_REGISTERS, MAX_SERVER_ID_BYTES,
23};
24
25#[allow(clippy::too_many_lines)]
29#[tracing::instrument(
30 level = "trace",
31 skip(pdu, store, device_id),
32 fields(
33 unit_id = unit_id.0,
34 pdu_len = pdu.len(),
35 function_code = pdu.first().copied().unwrap_or_default()
36 )
37)]
38pub async fn process_request<S: DataStore>(
39 pdu: &[u8],
40 unit_id: UnitId,
41 store: &S,
42 device_id: &DeviceIdentification,
43) -> Option<Vec<u8>> {
44 let is_broadcast = unit_id.is_broadcast();
45
46 let request = match decode_request(pdu) {
47 Ok(req) => req,
48 Err(e) => {
49 if is_broadcast {
50 return None;
51 }
52 let fc = pdu.first().copied().unwrap_or(0);
53 let exc = match e {
57 DecodeError::UnknownFunctionCode(_)
61 | DecodeError::UnknownDiagnosticSubFunction(_) => ExceptionCode::IllegalFunction,
62 DecodeError::InvalidReferenceType(_) | DecodeError::FileRecordOutOfRange { .. } => {
63 ExceptionCode::IllegalDataAddress
64 }
65 _ => {
66 ExceptionCode::IllegalDataValue
70 }
71 };
72 debug!(
73 function_code = fc,
74 exception_code = exc.code(),
75 error = %e,
76 "request decode failed; returning Modbus exception"
77 );
78 return Some(encode_exception(fc | 0x80, exc));
79 }
80 };
81
82 dispatch_request(request, pdu, is_broadcast, store, device_id).await
83}
84
85#[allow(clippy::too_many_lines)]
86async fn dispatch_request<S: DataStore>(
87 request: RequestPdu<'_>,
88 pdu: &[u8],
89 is_broadcast: bool,
90 store: &S,
91 device_id: &DeviceIdentification,
92) -> Option<Vec<u8>> {
93 match request {
94 RequestPdu::ReadHoldingRegisters(req) => {
95 if is_broadcast {
96 return None;
97 }
98 Some(
99 handle_read_registers(
100 FunctionCode::ReadHoldingRegisters,
101 req.address,
102 req.quantity,
103 store,
104 true,
105 )
106 .await,
107 )
108 }
109 RequestPdu::ReadInputRegisters(req) => {
110 if is_broadcast {
111 return None;
112 }
113 Some(
114 handle_read_registers(
115 FunctionCode::ReadInputRegisters,
116 req.address,
117 req.quantity,
118 store,
119 false,
120 )
121 .await,
122 )
123 }
124 RequestPdu::ReadCoils(req) => {
125 if is_broadcast {
126 return None;
127 }
128 Some(
129 handle_read_bits(
130 FunctionCode::ReadCoils,
131 req.address,
132 req.quantity,
133 store,
134 true,
135 )
136 .await,
137 )
138 }
139 RequestPdu::ReadDiscreteInputs(req) => {
140 if is_broadcast {
141 return None;
142 }
143 Some(
144 handle_read_bits(
145 FunctionCode::ReadDiscreteInputs,
146 req.address,
147 req.quantity,
148 store,
149 false,
150 )
151 .await,
152 )
153 }
154 RequestPdu::WriteSingleRegister(req) => {
155 let result = store.write_register(req.address.0, req.value).await;
156 if is_broadcast {
157 return None;
158 }
159 Some(match result {
160 Ok(()) => encode_response(&WriteSingleRegisterResponse {
161 address: req.address,
162 value: req.value,
163 }),
164 Err(ec) => encode_exception(FunctionCode::WriteSingleRegister.exception_code(), ec),
165 })
166 }
167 RequestPdu::WriteMultipleRegisters(req) => {
168 if let Err(ec) =
169 validate::validate_write_registers(req.address.0, req.quantity.0, req.byte_count)
170 {
171 if is_broadcast {
172 return None;
173 }
174 return Some(encode_exception(
175 FunctionCode::WriteMultipleRegisters.exception_code(),
176 ec,
177 ));
178 }
179 let result = store
180 .write_registers_be(req.address.0, req.quantity.0, req.register_values)
181 .await;
182 if is_broadcast {
183 return None;
184 }
185 Some(match result {
186 Ok(()) => encode_response(&WriteMultipleRegistersResponse {
187 address: req.address,
188 quantity: req.quantity,
189 }),
190 Err(ec) => {
191 encode_exception(FunctionCode::WriteMultipleRegisters.exception_code(), ec)
192 }
193 })
194 }
195 RequestPdu::WriteSingleCoil(req) => {
196 let result = store.write_coil(req.address.0, req.value.as_bool()).await;
197 if is_broadcast {
198 return None;
199 }
200 Some(match result {
201 Ok(()) => encode_response(&WriteSingleCoilResponse {
202 address: req.address,
203 value: req.value,
204 }),
205 Err(ec) => encode_exception(FunctionCode::WriteSingleCoil.exception_code(), ec),
206 })
207 }
208 RequestPdu::WriteMultipleCoils(req) => {
209 if let Err(ec) =
210 validate::validate_write_coils(req.address.0, req.quantity.0, req.byte_count)
211 {
212 if is_broadcast {
213 return None;
214 }
215 return Some(encode_exception(
216 FunctionCode::WriteMultipleCoils.exception_code(),
217 ec,
218 ));
219 }
220 let result = store
221 .write_coils_packed(req.address.0, req.quantity.0, req.coil_values)
222 .await;
223 if is_broadcast {
224 return None;
225 }
226 Some(match result {
227 Ok(()) => encode_response(&WriteMultipleCoilsResponse {
228 address: req.address,
229 quantity: req.quantity,
230 }),
231 Err(ec) => encode_exception(FunctionCode::WriteMultipleCoils.exception_code(), ec),
232 })
233 }
234 RequestPdu::MaskWriteRegister(req) => {
235 let result = handle_mask_write(req.address, req.and_mask, req.or_mask, store).await;
236 if is_broadcast {
237 return None;
238 }
239 Some(match result {
240 Ok(()) => encode_response(&MaskWriteRegisterResponse {
241 address: req.address,
242 and_mask: req.and_mask,
243 or_mask: req.or_mask,
244 }),
245 Err(ec) => encode_exception(FunctionCode::MaskWriteRegister.exception_code(), ec),
246 })
247 }
248 RequestPdu::ReadWriteMultipleRegisters(req) => {
249 if is_broadcast {
250 return None;
251 }
252 Some(handle_read_write_multiple(req, store).await)
253 }
254 RequestPdu::ReadFileRecord(req) => {
255 if is_broadcast {
256 return None;
257 }
258 Some(handle_read_file_record(req, store).await)
259 }
260 RequestPdu::WriteFileRecord(req) => {
261 let result = apply_write_file_record(&req, store).await;
262 if is_broadcast {
263 return None;
264 }
265 Some(match result {
266 Ok(()) => encode_response(&WriteFileRecordResponse {
268 byte_count: req.byte_count,
269 data: req.sub_requests,
270 }),
271 Err(ec) => encode_exception(FunctionCode::WriteFileRecord.exception_code(), ec),
272 })
273 }
274 RequestPdu::ReadFifoQueue(req) => {
275 if is_broadcast {
276 return None;
277 }
278 Some(handle_read_fifo_queue(req.fifo_pointer_address, store).await)
279 }
280 RequestPdu::ReadExceptionStatus => {
281 if is_broadcast {
282 return None;
283 }
284 Some(match store.read_exception_status().await {
285 Ok(status) => encode_response(&ReadExceptionStatusResponse { status }),
286 Err(ec) => encode_exception(FunctionCode::ReadExceptionStatus.exception_code(), ec),
287 })
288 }
289 RequestPdu::Diagnostics(req) => {
290 let result = handle_diagnostics(req.sub_function, req.data, store).await;
294 if is_broadcast {
295 return None;
296 }
297 result
298 }
299 RequestPdu::GetCommEventCounter => {
300 if is_broadcast {
301 return None;
302 }
303 Some(match store.get_comm_event_counter().await {
304 Ok((status, event_count)) => encode_response(&GetCommEventCounterResponse {
305 status,
306 event_count,
307 }),
308 Err(ec) => encode_exception(FunctionCode::GetCommEventCounter.exception_code(), ec),
309 })
310 }
311 RequestPdu::GetCommEventLog => {
312 if is_broadcast {
313 return None;
314 }
315 Some(handle_comm_event_log(store).await)
316 }
317 RequestPdu::ReportServerId => {
318 if is_broadcast {
319 return None;
320 }
321 Some(handle_report_server_id(store).await)
322 }
323 RequestPdu::EncapsulatedInterface(req) => {
324 if is_broadcast {
325 return None;
326 }
327 if req.mei_type == MeiType::ReadDeviceIdentification {
328 Some(build_device_id_response(req.data, device_id))
329 } else {
330 let fc = pdu.first().copied().unwrap_or(0);
331 Some(encode_exception(fc | 0x80, ExceptionCode::IllegalFunction))
332 }
333 }
334 RequestPdu::Custom(..) => {
335 if is_broadcast {
336 return None;
337 }
338 let fc = pdu.first().copied().unwrap_or(0);
339 Some(encode_exception(fc | 0x80, ExceptionCode::IllegalFunction))
340 }
341 }
342}
343
344async fn handle_comm_event_log<S: DataStore>(store: &S) -> Vec<u8> {
345 let mut response = Vec::with_capacity(8 + MAX_COMM_EVENT_LOG_EVENTS);
346 response.push(FunctionCode::GetCommEventLog.code());
347 response.push(0);
348 response.extend_from_slice(&[0; 6]);
349
350 let events_start = response.len();
351 let result = store.append_comm_event_log(&mut response).await;
352 match result {
353 Ok(meta) => {
354 let events_len = response.len().saturating_sub(events_start);
355 if events_len > MAX_COMM_EVENT_LOG_EVENTS {
356 return encode_exception(
357 FunctionCode::GetCommEventLog.exception_code(),
358 ExceptionCode::ServerDeviceFailure,
359 );
360 }
361 let byte_count =
362 match checked_response_u8(events_len + 6, FunctionCode::GetCommEventLog) {
363 Ok(byte_count) => byte_count,
364 Err(resp) => return resp,
365 };
366 response[1] = byte_count;
367 response[2..4].copy_from_slice(&meta.status.to_be_bytes());
368 response[4..6].copy_from_slice(&meta.event_count.to_be_bytes());
369 response[6..8].copy_from_slice(&meta.message_count.to_be_bytes());
370 response
371 }
372 Err(ec) => encode_exception(FunctionCode::GetCommEventLog.exception_code(), ec),
373 }
374}
375
376async fn handle_diagnostics<S: DataStore>(
377 sub_function: DiagnosticSubFunction,
378 data: &[u8],
379 store: &S,
380) -> Option<Vec<u8>> {
381 let mut response = Vec::with_capacity(3 + MAX_DIAGNOSTIC_RESPONSE_DATA_LEN);
382 response.push(FunctionCode::Diagnostics.code());
383 response.extend_from_slice(&sub_function.code().to_be_bytes());
384
385 let data_start = response.len();
386 let result = store
387 .append_diagnostic_response(sub_function, data, &mut response)
388 .await;
389 match result {
390 Ok(Some(count)) => {
391 let actual_count = response.len().saturating_sub(data_start);
392 if count != actual_count
393 || actual_count > MAX_DIAGNOSTIC_RESPONSE_DATA_LEN
394 || !actual_count.is_multiple_of(2)
395 {
396 return Some(encode_exception(
397 FunctionCode::Diagnostics.exception_code(),
398 ExceptionCode::ServerDeviceFailure,
399 ));
400 }
401 Some(response)
402 }
403 Ok(None) => None,
404 Err(ec) => Some(encode_exception(
405 FunctionCode::Diagnostics.exception_code(),
406 ec,
407 )),
408 }
409}
410
411async fn handle_report_server_id<S: DataStore>(store: &S) -> Vec<u8> {
412 let mut response = Vec::with_capacity(2 + MAX_SERVER_ID_BYTES);
413 response.push(FunctionCode::ReportServerId.code());
414 response.push(0);
415
416 let data_start = response.len();
417 let result = store.append_server_id(&mut response).await;
418 match result {
419 Ok(count) => {
420 let actual_count = response.len().saturating_sub(data_start);
421 if count != actual_count || actual_count > MAX_SERVER_ID_BYTES {
422 return encode_exception(
423 FunctionCode::ReportServerId.exception_code(),
424 ExceptionCode::ServerDeviceFailure,
425 );
426 }
427 let byte_count = match checked_response_u8(actual_count, FunctionCode::ReportServerId) {
428 Ok(byte_count) => byte_count,
429 Err(resp) => return resp,
430 };
431 response[1] = byte_count;
432 response
433 }
434 Err(ec) => encode_exception(FunctionCode::ReportServerId.exception_code(), ec),
435 }
436}
437
438async fn handle_read_registers<S: DataStore>(
439 fc: FunctionCode,
440 address: Address,
441 quantity: Quantity,
442 store: &S,
443 is_holding: bool,
444) -> Vec<u8> {
445 if let Err(ec) = validate::validate_read_registers(address.0, quantity.0) {
446 return encode_exception(fc.exception_code(), ec);
447 }
448
449 let byte_count = match checked_response_u8(usize::from(quantity.0) * 2, fc) {
450 Ok(byte_count) => byte_count,
451 Err(resp) => return resp,
452 };
453 let response_fc = if is_holding {
454 FunctionCode::ReadHoldingRegisters
455 } else {
456 FunctionCode::ReadInputRegisters
457 };
458 let mut response = vec![0u8; 2 + usize::from(byte_count)];
459 response[0] = response_fc.code();
460 response[1] = byte_count;
461
462 let result = if is_holding {
463 store
464 .read_holding_registers_be(address.0, quantity.0, &mut response[2..])
465 .await
466 } else {
467 store
468 .read_input_registers_be(address.0, quantity.0, &mut response[2..])
469 .await
470 };
471
472 match result {
473 Ok(count) => {
474 if let Err(ec) = validate_store_count(count, quantity.0, usize::from(quantity.0)) {
475 return encode_exception(fc.exception_code(), ec);
476 }
477 response
478 }
479 Err(ec) => encode_exception(fc.exception_code(), ec),
480 }
481}
482
483async fn handle_read_bits<S: DataStore>(
484 fc: FunctionCode,
485 address: Address,
486 quantity: Quantity,
487 store: &S,
488 is_coils: bool,
489) -> Vec<u8> {
490 let validation = if is_coils {
491 validate::validate_read_coils(address.0, quantity.0)
492 } else {
493 validate::validate_read_discrete_inputs(address.0, quantity.0)
494 };
495 if let Err(ec) = validation {
496 return encode_exception(fc.exception_code(), ec);
497 }
498
499 let byte_count = match checked_response_u8(usize::from(quantity.0).div_ceil(8), fc) {
500 Ok(byte_count) => byte_count,
501 Err(resp) => return resp,
502 };
503 let response_fc = if is_coils {
504 FunctionCode::ReadCoils
505 } else {
506 FunctionCode::ReadDiscreteInputs
507 };
508 let mut response = vec![0u8; 2 + usize::from(byte_count)];
509 response[0] = response_fc.code();
510 response[1] = byte_count;
511
512 let result = if is_coils {
513 store
514 .read_coils_packed(address.0, quantity.0, &mut response[2..])
515 .await
516 } else {
517 store
518 .read_discrete_inputs_packed(address.0, quantity.0, &mut response[2..])
519 .await
520 };
521
522 match result {
523 Ok(count) => {
524 if let Err(ec) = validate_store_count(count, quantity.0, usize::from(quantity.0)) {
525 return encode_exception(fc.exception_code(), ec);
526 }
527 response
528 }
529 Err(ec) => encode_exception(fc.exception_code(), ec),
530 }
531}
532
533async fn handle_mask_write<S: DataStore>(
534 address: Address,
535 and_mask: u16,
536 or_mask: u16,
537 store: &S,
538) -> Result<(), ExceptionCode> {
539 validate::validate_mask_write_address(address.0)?;
540
541 let mut buf = [0u16; 1];
542 store.read_holding_registers(address.0, 1, &mut buf).await?;
543 let result = (buf[0] & and_mask) | (or_mask & !and_mask);
544 store.write_register(address.0, result).await
545}
546
547async fn handle_read_write_multiple<S: DataStore>(
548 req: rusty_modbus_codec::request::ReadWriteMultipleRegistersRequest<'_>,
549 store: &S,
550) -> Vec<u8> {
551 if let Err(ec) = validate::validate_read_write_registers(
552 req.read_address.0,
553 req.read_quantity.0,
554 req.write_address.0,
555 req.write_quantity.0,
556 req.write_byte_count,
557 ) {
558 return encode_exception(
559 FunctionCode::ReadWriteMultipleRegisters.exception_code(),
560 ec,
561 );
562 }
563
564 if let Err(ec) = store
566 .write_registers_be(
567 req.write_address.0,
568 req.write_quantity.0,
569 req.write_register_values,
570 )
571 .await
572 {
573 return encode_exception(
574 FunctionCode::ReadWriteMultipleRegisters.exception_code(),
575 ec,
576 );
577 }
578
579 let byte_count = match checked_response_u8(
580 usize::from(req.read_quantity.0) * 2,
581 FunctionCode::ReadWriteMultipleRegisters,
582 ) {
583 Ok(byte_count) => byte_count,
584 Err(resp) => return resp,
585 };
586 let mut response = vec![0u8; 2 + usize::from(byte_count)];
587 response[0] = FunctionCode::ReadWriteMultipleRegisters.code();
588 response[1] = byte_count;
589
590 match store
591 .read_holding_registers_be(req.read_address.0, req.read_quantity.0, &mut response[2..])
592 .await
593 {
594 Ok(count) => {
595 if let Err(ec) =
596 validate_store_count(count, req.read_quantity.0, usize::from(req.read_quantity.0))
597 {
598 return encode_exception(
599 FunctionCode::ReadWriteMultipleRegisters.exception_code(),
600 ec,
601 );
602 }
603 response
604 }
605 Err(ec) => encode_exception(
606 FunctionCode::ReadWriteMultipleRegisters.exception_code(),
607 ec,
608 ),
609 }
610}
611
612async fn handle_read_file_record<S: DataStore>(
613 req: ReadFileRecordRequest<'_>,
614 store: &S,
615) -> Vec<u8> {
616 let subs = req.sub_requests;
617 if subs.is_empty() || !subs.len().is_multiple_of(7) || subs.len() > 0xF5 {
620 return encode_exception(
621 FunctionCode::ReadFileRecord.exception_code(),
622 ExceptionCode::IllegalDataValue,
623 );
624 }
625 let mut response = Vec::with_capacity(MAX_PDU_SIZE);
626 response.push(FunctionCode::ReadFileRecord.code());
627 response.push(0);
628 for chunk in subs.chunks_exact(7) {
629 if chunk[0] != 6 {
630 return encode_exception(
632 FunctionCode::ReadFileRecord.exception_code(),
633 ExceptionCode::IllegalDataAddress,
634 );
635 }
636 let file = u16::from_be_bytes([chunk[1], chunk[2]]);
637 let record = u16::from_be_bytes([chunk[3], chunk[4]]);
638 let length = u16::from_be_bytes([chunk[5], chunk[6]]);
639 if let Err(ec) = file_record::validate_range(file, record, usize::from(length)) {
640 return encode_exception(FunctionCode::ReadFileRecord.exception_code(), ec);
641 }
642 let requested_count = usize::from(length);
643 if requested_count > MAX_FILE_RECORD_REGISTERS {
644 return encode_exception(
645 FunctionCode::ReadFileRecord.exception_code(),
646 ExceptionCode::IllegalDataAddress,
647 );
648 }
649 let group_start = response.len();
650 let value_byte_count = requested_count * 2;
651 response.resize(group_start + 2 + value_byte_count, 0);
652 match store
653 .read_file_record_be(
654 file,
655 record,
656 length,
657 &mut response[group_start + 2..group_start + 2 + value_byte_count],
658 )
659 .await
660 {
661 Ok(n) => {
662 let n = match validate_store_count(n, length, requested_count) {
663 Ok(n) => n,
664 Err(ec) => {
665 return encode_exception(FunctionCode::ReadFileRecord.exception_code(), ec);
666 }
667 };
668 let resp_len = 1 + 2 * n;
671 let resp_len = match checked_response_u8(resp_len, FunctionCode::ReadFileRecord) {
672 Ok(resp_len) => resp_len,
673 Err(resp) => return resp,
674 };
675 response[group_start] = resp_len;
676 response[group_start + 1] = 0x06;
677 }
678 Err(ec) => return encode_exception(FunctionCode::ReadFileRecord.exception_code(), ec),
679 }
680 if response.len() - 2 > 250 {
684 return encode_exception(
685 FunctionCode::ReadFileRecord.exception_code(),
686 ExceptionCode::IllegalDataValue,
687 );
688 }
689 }
690 let byte_count = match checked_response_u8(response.len() - 2, FunctionCode::ReadFileRecord) {
691 Ok(byte_count) => byte_count,
692 Err(resp) => return resp,
693 };
694 response[1] = byte_count;
695 response
696}
697
698async fn apply_write_file_record<S: DataStore>(
699 req: &WriteFileRecordRequest<'_>,
700 store: &S,
701) -> Result<(), ExceptionCode> {
702 const MAX_WRITE_FILE_RECORD_REQUEST_BYTES: usize = 0xFB;
703 const MIN_WRITE_FILE_RECORD_SUB_REQUEST_BYTES: usize = 9;
704 const MAX_WRITE_FILE_RECORD_GROUPS: usize =
705 MAX_WRITE_FILE_RECORD_REQUEST_BYTES / MIN_WRITE_FILE_RECORD_SUB_REQUEST_BYTES;
706
707 #[derive(Clone, Copy)]
708 struct WriteFileRecordGroup<'a> {
709 file: u16,
710 record: u16,
711 length: u16,
712 value_bytes: &'a [u8],
713 }
714
715 if !(0x09..=0xFB).contains(&req.byte_count) {
717 return Err(ExceptionCode::IllegalDataValue);
718 }
719 let mut subs = req.sub_requests;
723 let mut groups = [WriteFileRecordGroup {
724 file: 0,
725 record: 0,
726 length: 0,
727 value_bytes: &[],
728 }; MAX_WRITE_FILE_RECORD_GROUPS];
729 let mut group_count = 0;
730 while !subs.is_empty() {
731 if subs.len() < 7 {
734 return Err(ExceptionCode::IllegalDataValue);
735 }
736 if subs[0] != 6 {
737 return Err(ExceptionCode::IllegalDataAddress);
738 }
739 let file = u16::from_be_bytes([subs[1], subs[2]]);
740 let record = u16::from_be_bytes([subs[3], subs[4]]);
741 let length = u16::from_be_bytes([subs[5], subs[6]]);
742 let data_end = 7 + 2 * usize::from(length);
743 if subs.len() < data_end {
744 return Err(ExceptionCode::IllegalDataValue);
745 }
746 file_record::validate_range(file, record, usize::from(length))?;
747 if group_count == groups.len() {
748 return Err(ExceptionCode::IllegalDataValue);
749 }
750 groups[group_count] = WriteFileRecordGroup {
751 file,
752 record,
753 length,
754 value_bytes: &subs[7..data_end],
755 };
756 group_count += 1;
757 subs = &subs[data_end..];
758 }
759 for group in &groups[..group_count] {
761 store
762 .write_file_record_be(group.file, group.record, group.length, group.value_bytes)
763 .await?;
764 }
765 Ok(())
766}
767
768async fn handle_read_fifo_queue<S: DataStore>(address: Address, store: &S) -> Vec<u8> {
769 const MAX_FIFO_VALUE_BYTES: usize = MAX_FIFO_VALUES as usize * 2;
770
771 let mut response = vec![0u8; 5 + MAX_FIFO_VALUE_BYTES];
772 response[0] = FunctionCode::ReadFifoQueue.code();
773
774 match store
775 .read_fifo_queue_be(address.0, &mut response[5..])
776 .await
777 {
778 Ok(count) => {
779 if count > usize::from(MAX_FIFO_VALUES) {
782 return encode_exception(
783 FunctionCode::ReadFifoQueue.exception_code(),
784 ExceptionCode::IllegalDataValue,
785 );
786 }
787 let byte_count = match checked_response_u16(2 + count * 2, FunctionCode::ReadFifoQueue)
788 {
789 Ok(byte_count) => byte_count,
790 Err(resp) => return resp,
791 };
792 let fifo_count = match checked_response_u16(count, FunctionCode::ReadFifoQueue) {
793 Ok(fifo_count) => fifo_count,
794 Err(resp) => return resp,
795 };
796 response[1..3].copy_from_slice(&byte_count.to_be_bytes());
797 response[3..5].copy_from_slice(&fifo_count.to_be_bytes());
798 response.truncate(5 + count * 2);
799 response
800 }
801 Err(ec) => encode_exception(FunctionCode::ReadFifoQueue.exception_code(), ec),
802 }
803}
804
805fn checked_response_u8(value: usize, fc: FunctionCode) -> Result<u8, Vec<u8>> {
806 u8::try_from(value)
807 .map_err(|_| encode_exception(fc.exception_code(), ExceptionCode::ServerDeviceFailure))
808}
809
810fn checked_response_u16(value: usize, fc: FunctionCode) -> Result<u16, Vec<u8>> {
811 u16::try_from(value)
812 .map_err(|_| encode_exception(fc.exception_code(), ExceptionCode::ServerDeviceFailure))
813}
814
815fn validate_store_count(
816 count: usize,
817 requested: u16,
818 capacity: usize,
819) -> Result<usize, ExceptionCode> {
820 if count == usize::from(requested) && count <= capacity {
821 Ok(count)
822 } else {
823 Err(ExceptionCode::ServerDeviceFailure)
824 }
825}
826
827fn encode_exception(fc_with_flag: u8, ec: ExceptionCode) -> Vec<u8> {
828 debug!(
829 function_code = fc_with_flag & 0x7F,
830 exception_function_code = fc_with_flag,
831 exception_code = ec.code(),
832 exception = ?ec,
833 "encoding Modbus exception response"
834 );
835 vec![fc_with_flag, ec.code()]
836}