From 2723204e204d04c9fcf1dce7109387099711c263 Mon Sep 17 00:00:00 2001 From: thr2240 Date: Wed, 6 Dec 2023 21:46:30 +0300 Subject: [PATCH 001/106] feat: implement send/receive custom buffer --- rodbus/examples/client.rs | 14 ++++ rodbus/examples/server.rs | 14 ++++ rodbus/src/client/channel.rs | 16 ++++ rodbus/src/client/message.rs | 9 +++ rodbus/src/client/requests/mod.rs | 1 + rodbus/src/client/requests/send_buffer.rs | 94 +++++++++++++++++++++++ rodbus/src/common/function.rs | 6 ++ rodbus/src/serial/frame.rs | 25 ++++++ rodbus/src/server/handler.rs | 13 ++++ rodbus/src/server/request.rs | 16 ++++ rodbus/src/server/task.rs | 1 + 11 files changed, 209 insertions(+) create mode 100644 rodbus/src/client/requests/send_buffer.rs diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 9403fb87..94c58fd5 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -267,6 +267,20 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { + // ANCHOR: send_custom_buffer + let result = channel + .send_custom_buffer( + params, + Indexed { + index: (0x1), + value: (0xAB), + }, + ) + .await; + print_write_result(result); + // ANCHOR_END: write_multiple_registers + } _ => println!("unknown command"), } } diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index 4fc490cb..913961ad 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -78,6 +78,20 @@ impl RequestHandler for SimpleHandler { } } + fn receive_custom_buffer(&self, value: Indexed) -> Result { + tracing::info!( + "receive custom buffer, index: {} value: {}", + value.index, + value.value + ); + + if value.value == 0xAB { + Ok(0xCD) + } else { + Err(ExceptionCode::IllegalFunction) + } + } + fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { tracing::info!( "write single register, index: {} value: {}", diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index 3b58340e..58f35bfa 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -5,6 +5,7 @@ use crate::client::requests::read_bits::ReadBits; use crate::client::requests::read_registers::ReadRegisters; use crate::client::requests::write_multiple::{MultipleWriteRequest, WriteMultiple}; use crate::client::requests::write_single::SingleWrite; +use crate::client::requests::send_buffer::SendBuffer; use crate::error::*; use crate::types::{AddressRange, BitIterator, Indexed, RegisterIterator, UnitId}; use crate::DecodeLevel; @@ -163,6 +164,21 @@ impl Channel { rx.await? } + /// Send buffer to the server + pub async fn send_custom_buffer( + &mut self, + param: RequestParam, + request: Indexed, + ) -> Result, RequestError> { + let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); + let request = wrap( + param, + RequestDetails::SendCustomBuffers(SendBuffer::new(request, Promise::channel(tx))), + ); + self.tx.send(request).await?; + rx.await? + } + /// Write a single coil on the server pub async fn write_single_coil( &mut self, diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index c18abb7e..dc2c39f9 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -10,6 +10,7 @@ use crate::client::requests::read_bits::ReadBits; use crate::client::requests::read_registers::ReadRegisters; use crate::client::requests::write_multiple::MultipleWriteRequest; use crate::client::requests::write_single::SingleWrite; +use crate::client::requests::send_buffer::SendBuffer; use crate::common::traits::Serialize; use crate::types::{Indexed, UnitId}; @@ -41,6 +42,7 @@ pub(crate) enum RequestDetails { ReadDiscreteInputs(ReadBits), ReadHoldingRegisters(ReadRegisters), ReadInputRegisters(ReadRegisters), + SendCustomBuffers(SendBuffer>), WriteSingleCoil(SingleWrite>), WriteSingleRegister(SingleWrite>), WriteMultipleCoils(MultipleWriteRequest), @@ -125,6 +127,7 @@ impl RequestDetails { RequestDetails::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs, RequestDetails::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters, RequestDetails::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters, + RequestDetails::SendCustomBuffers(_) => FunctionCode::SendCustomBuffers, RequestDetails::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil, RequestDetails::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, RequestDetails::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, @@ -138,6 +141,7 @@ impl RequestDetails { RequestDetails::ReadDiscreteInputs(x) => x.failure(err), RequestDetails::ReadHoldingRegisters(x) => x.failure(err), RequestDetails::ReadInputRegisters(x) => x.failure(err), + RequestDetails::SendCustomBuffers(x) => x.failure(err), RequestDetails::WriteSingleCoil(x) => x.failure(err), RequestDetails::WriteSingleRegister(x) => x.failure(err), RequestDetails::WriteMultipleCoils(x) => x.failure(err), @@ -156,6 +160,7 @@ impl RequestDetails { RequestDetails::ReadDiscreteInputs(x) => x.handle_response(cursor, function, decode), RequestDetails::ReadHoldingRegisters(x) => x.handle_response(cursor, function, decode), RequestDetails::ReadInputRegisters(x) => x.handle_response(cursor, function, decode), + RequestDetails::SendCustomBuffers(x) => x.handle_response(cursor, function, decode), RequestDetails::WriteSingleCoil(x) => x.handle_response(cursor, function, decode), RequestDetails::WriteSingleRegister(x) => x.handle_response(cursor, function, decode), RequestDetails::WriteMultipleCoils(x) => x.handle_response(cursor, function, decode), @@ -173,6 +178,7 @@ impl Serialize for RequestDetails { RequestDetails::ReadDiscreteInputs(x) => x.serialize(cursor), RequestDetails::ReadHoldingRegisters(x) => x.serialize(cursor), RequestDetails::ReadInputRegisters(x) => x.serialize(cursor), + RequestDetails::SendCustomBuffers(x) => x.serialize(cursor), RequestDetails::WriteSingleCoil(x) => x.serialize(cursor), RequestDetails::WriteSingleRegister(x) => x.serialize(cursor), RequestDetails::WriteMultipleCoils(x) => x.serialize(cursor), @@ -219,6 +225,9 @@ impl std::fmt::Display for RequestDetailsDisplay<'_> { RequestDetails::ReadInputRegisters(details) => { write!(f, "{}", details.request.get())?; } + RequestDetails::SendCustomBuffers(details) => { + write!(f, "{}", details.request)?; + } RequestDetails::WriteSingleCoil(details) => { write!(f, "{}", details.request)?; } diff --git a/rodbus/src/client/requests/mod.rs b/rodbus/src/client/requests/mod.rs index bfebd0a4..b3e56fb2 100644 --- a/rodbus/src/client/requests/mod.rs +++ b/rodbus/src/client/requests/mod.rs @@ -2,3 +2,4 @@ pub(crate) mod read_bits; pub(crate) mod read_registers; pub(crate) mod write_multiple; pub(crate) mod write_single; +pub(crate) mod send_buffer; diff --git a/rodbus/src/client/requests/send_buffer.rs b/rodbus/src/client/requests/send_buffer.rs new file mode 100644 index 00000000..2e19d731 --- /dev/null +++ b/rodbus/src/client/requests/send_buffer.rs @@ -0,0 +1,94 @@ +use std::fmt::Display; + +use crate::client::message::Promise; +use crate::common::function::FunctionCode; +use crate::decode::AppDecodeLevel; +use crate::error::AduParseError; +use crate::error::RequestError; +use crate::types::{coil_from_u16, coil_to_u16, Indexed}; + +use scursor::{ReadCursor, WriteCursor}; + +pub(crate) trait SendBufferOperation: Sized + PartialEq { + fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError>; + fn parse(cursor: &mut ReadCursor) -> Result; +} + +pub(crate) struct SendBuffer +where + T: SendBufferOperation + Display + Send + 'static, +{ + pub(crate) request: T, + promise: Promise, +} + +impl SendBuffer +where + T: SendBufferOperation + Display + Send + 'static, +{ + pub(crate) fn new(request: T, promise: Promise) -> Self { + Self { request, promise } + } + + pub(crate) fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { + self.request.serialize(cursor) + } + + pub(crate) fn failure(&mut self, err: RequestError) { + self.promise.failure(err) + } + + pub(crate) fn handle_response( + &mut self, + cursor: ReadCursor, + function: FunctionCode, + decode: AppDecodeLevel, + ) -> Result<(), RequestError> { + let response = self.parse_all(cursor)?; + + if decode.data_headers() { + tracing::info!("PDU RX - {} {}", function, response); + } else if decode.header() { + tracing::info!("PDU RX - {}", function); + } + + self.promise.success(response); + Ok(()) + } + + fn parse_all(&self, mut cursor: ReadCursor) -> Result { + let response = T::parse(&mut cursor)?; + cursor.expect_empty()?; + if self.request != response { + return Err(AduParseError::ReplyEchoMismatch.into()); + } + Ok(response) + } +} + +impl SendBufferOperation for Indexed { + fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { + cursor.write_u16_be(self.index)?; + cursor.write_u16_be(coil_to_u16(self.value))?; + Ok(()) + } + + fn parse(cursor: &mut ReadCursor) -> Result { + Ok(Indexed::new( + cursor.read_u16_be()?, + coil_from_u16(cursor.read_u16_be()?)?, + )) + } +} + +impl SendBufferOperation for Indexed { + fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { + cursor.write_u16_be(self.index)?; + cursor.write_u16_be(self.value)?; + Ok(()) + } + + fn parse(cursor: &mut ReadCursor) -> Result { + Ok(Indexed::new(cursor.read_u16_be()?, cursor.read_u16_be()?)) + } +} diff --git a/rodbus/src/common/function.rs b/rodbus/src/common/function.rs index e85bf14a..4052f630 100644 --- a/rodbus/src/common/function.rs +++ b/rodbus/src/common/function.rs @@ -9,6 +9,7 @@ mod constants { pub(crate) const WRITE_SINGLE_REGISTER: u8 = 6; pub(crate) const WRITE_MULTIPLE_COILS: u8 = 15; pub(crate) const WRITE_MULTIPLE_REGISTERS: u8 = 16; + pub(crate) const SEND_CUSTOM_BUFFERS: u8 = 68; } #[derive(Debug, Copy, Clone, PartialEq)] @@ -22,6 +23,7 @@ pub(crate) enum FunctionCode { WriteSingleRegister = constants::WRITE_SINGLE_REGISTER, WriteMultipleCoils = constants::WRITE_MULTIPLE_COILS, WriteMultipleRegisters = constants::WRITE_MULTIPLE_REGISTERS, + SendCustomBuffers = constants::SEND_CUSTOM_BUFFERS, } impl Display for FunctionCode { @@ -49,6 +51,9 @@ impl Display for FunctionCode { FunctionCode::WriteMultipleRegisters => { write!(f, "WRITE MULTIPLE REGISTERS ({:#04X})", self.get_value()) } + FunctionCode::SendCustomBuffers => { + write!(f, "SEND CUSTOM BUFFER ({:#04X})", self.get_value()) + } } } } @@ -72,6 +77,7 @@ impl FunctionCode { constants::WRITE_SINGLE_REGISTER => Some(FunctionCode::WriteSingleRegister), constants::WRITE_MULTIPLE_COILS => Some(FunctionCode::WriteMultipleCoils), constants::WRITE_MULTIPLE_REGISTERS => Some(FunctionCode::WriteMultipleRegisters), + constants::SEND_CUSTOM_BUFFERS => Some(FunctionCode::SendCustomBuffers), _ => None, } } diff --git a/rodbus/src/serial/frame.rs b/rodbus/src/serial/frame.rs index 642f7a8a..586c0ac3 100644 --- a/rodbus/src/serial/frame.rs +++ b/rodbus/src/serial/frame.rs @@ -83,6 +83,7 @@ impl RtuParser { FunctionCode::ReadDiscreteInputs => LengthMode::Fixed(4), FunctionCode::ReadHoldingRegisters => LengthMode::Fixed(4), FunctionCode::ReadInputRegisters => LengthMode::Fixed(4), + FunctionCode::SendCustomBuffers => LengthMode::Offset(1), FunctionCode::WriteSingleCoil => LengthMode::Fixed(4), FunctionCode::WriteSingleRegister => LengthMode::Fixed(4), FunctionCode::WriteMultipleCoils => LengthMode::Offset(5), @@ -93,6 +94,7 @@ impl RtuParser { FunctionCode::ReadDiscreteInputs => LengthMode::Offset(1), FunctionCode::ReadHoldingRegisters => LengthMode::Offset(1), FunctionCode::ReadInputRegisters => LengthMode::Offset(1), + FunctionCode::SendCustomBuffers => LengthMode::Offset(1), FunctionCode::WriteSingleCoil => LengthMode::Fixed(4), FunctionCode::WriteSingleRegister => LengthMode::Fixed(4), FunctionCode::WriteMultipleCoils => LengthMode::Fixed(4), @@ -345,6 +347,21 @@ mod tests { 0x71, 0x86, // crc ]; + const SEND_CUSTOM_BUFFER_REQUEST: &[u8] = &[ + UNIT_ID, // unit id + 0x44, // function code + 0x02, // byte count + 0x01, 0xAB, // additonal data + 0xC8, 0xD9, // crc + ]; + + const SEND_CUSTOM_BUFFER_RESPONSE: &[u8] = &[ + UNIT_ID, // unit id + 0x44, // function code + 0x01, // byte count + 0xCD, // return value + 0x88, 0x2C, // crc + ]; const WRITE_SINGLE_COIL_REQUEST: &[u8] = &[ UNIT_ID, // unit id 0x05, // function code @@ -427,6 +444,10 @@ mod tests { FunctionCode::ReadInputRegisters, READ_INPUT_REGISTERS_REQUEST, ), + ( + FunctionCode::SendCustomBuffers, + SEND_CUSTOM_BUFFER_REQUEST, + ), (FunctionCode::WriteSingleCoil, WRITE_SINGLE_COIL_REQUEST), ( FunctionCode::WriteSingleRegister, @@ -456,6 +477,10 @@ mod tests { FunctionCode::ReadInputRegisters, READ_INPUT_REGISTERS_RESPONSE, ), + ( + FunctionCode::SendCustomBuffers, + SEND_CUSTOM_BUFFER_RESPONSE, + ), (FunctionCode::WriteSingleCoil, WRITE_SINGLE_COIL_RESPONSE), ( FunctionCode::WriteSingleRegister, diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 43658927..db81863e 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -43,6 +43,11 @@ pub trait RequestHandler: Send + 'static { Err(ExceptionCode::IllegalFunction) } + /// Read single custom buffer or return an ExceptionCode + fn receive_custom_buffer(&self, _value: Indexed) -> Result { + Err(ExceptionCode::IllegalFunction) + } + /// Write a single coil value fn write_single_coil(&mut self, _value: Indexed) -> Result<(), ExceptionCode> { Err(ExceptionCode::IllegalFunction) @@ -177,6 +182,14 @@ pub trait AuthorizationHandler: Send + Sync + 'static { Authorization::Deny } + /// Authorize a Read Custom Buffer request + fn receive_custom_buffer( + &self, + _value: Indexed, + _role: &str, + ) -> Authorization { + Authorization::Deny + } /// Authorize a Read Holding Registers request fn read_holding_registers( &self, diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 77843d67..ffd30db8 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -17,6 +17,7 @@ pub(crate) enum Request<'a> { ReadDiscreteInputs(ReadBitsRange), ReadHoldingRegisters(ReadRegistersRange), ReadInputRegisters(ReadRegistersRange), + SendCustomBuffers(Indexed), WriteSingleCoil(Indexed), WriteSingleRegister(Indexed), WriteMultipleCoils(WriteCoils<'a>), @@ -60,6 +61,7 @@ impl<'a> Request<'a> { Request::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs, Request::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters, Request::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters, + Request::SendCustomBuffers(_) => FunctionCode::SendCustomBuffers, Request::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil, Request::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, Request::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, @@ -73,6 +75,7 @@ impl<'a> Request<'a> { Request::ReadDiscreteInputs(_) => None, Request::ReadHoldingRegisters(_) => None, Request::ReadInputRegisters(_) => None, + Request::SendCustomBuffers(_) => None, Request::WriteSingleCoil(x) => Some(BroadcastRequest::WriteSingleCoil(x)), Request::WriteSingleRegister(x) => Some(BroadcastRequest::WriteSingleRegister(x)), Request::WriteMultipleCoils(x) => Some(BroadcastRequest::WriteMultipleCoils(x)), @@ -123,6 +126,10 @@ impl<'a> Request<'a> { let registers = RegisterWriter::new(*range, |i| handler.read_input_register(i)); writer.format_reply(header, function, ®isters, level) } + Request::SendCustomBuffers(request) => { + let result = handler.receive_custom_buffer(*request).map(|_| *request); + write_result(function, header, writer, result, level) + } Request::WriteSingleCoil(request) => { let result = handler.write_single_coil(*request).map(|_| *request); write_result(function, header, writer, result, level) @@ -172,6 +179,12 @@ impl<'a> Request<'a> { cursor.expect_empty()?; Ok(x) } + FunctionCode::SendCustomBuffers => { + let x = + Request::SendCustomBuffers(Indexed::::parse(cursor)?); + cursor.expect_empty()?; + Ok(x) + } FunctionCode::WriteSingleCoil => { let x = Request::WriteSingleCoil(Indexed::::parse(cursor)?); cursor.expect_empty()?; @@ -233,6 +246,9 @@ impl std::fmt::Display for RequestDisplay<'_, '_> { Request::ReadInputRegisters(range) => { write!(f, " {}", range.get())?; } + Request::SendCustomBuffers(request) => { + write!(f, " {request}")?; + } Request::WriteSingleCoil(request) => { write!(f, " {request}")?; } diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index fbb7bfca..28f39149 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -256,6 +256,7 @@ impl AuthorizationType { handler.read_holding_registers(unit_id, x.inner, role) } Request::ReadInputRegisters(x) => handler.read_input_registers(unit_id, x.inner, role), + Request::SendCustomBuffers(x) => handler.receive_custom_buffer(*x, role), Request::WriteSingleCoil(x) => handler.write_single_coil(unit_id, x.index, role), Request::WriteSingleRegister(x) => { handler.write_single_register(unit_id, x.index, role) From 3a07ec15d59fab031797c48242caca3b4010c4b2 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 17 Jan 2024 19:13:07 +0100 Subject: [PATCH 002/106] chore: fix client and server ports for Unix-based architectures with no root privileagues --- rodbus/examples/client.rs | 2 +- rodbus/examples/server.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 94c58fd5..46288208 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -61,7 +61,7 @@ where async fn run_tcp() -> Result<(), Box> { // ANCHOR: create_tcp_channel let channel = spawn_tcp_client_task( - HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 502), + HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 1502), 1, default_retry_strategy(), DecodeLevel::default(), diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index 913961ad..6c8815e8 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -181,7 +181,7 @@ async fn run_tcp() -> Result<(), Box> { // ANCHOR: tcp_server_create let server = rodbus::server::spawn_tcp_server_task( 1, - "127.0.0.1:502".parse()?, + "127.0.0.1:1502".parse()?, map, AddressFilter::Any, DecodeLevel::default(), From c9c7923e1d45a1206d4a52c0cb42d81200c8097a Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 18 Jan 2024 16:59:01 +0100 Subject: [PATCH 003/106] chore: Implement placeholder commands for read/write custom function code --- rodbus/examples/client.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 46288208..81d6880e 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -281,6 +281,16 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { + // ANCHOR: write_custom_function_code + println!("write success"); + // ANCHOR_END: write_custom_function_code + } + "rcfc" => { + // ANCHOR: read_custom_function_code + println!("success"); + // ANCHOR_END: read_custom_function_code + } _ => println!("unknown command"), } } From ad140d66e2b5b452d1fc0dd62d7d2ae40bf82d49 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 18 Jan 2024 19:13:12 +0100 Subject: [PATCH 004/106] feat: Implement request building for custom function code --- rodbus/src/client/requests/write_custom_fc.rs | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 rodbus/src/client/requests/write_custom_fc.rs diff --git a/rodbus/src/client/requests/write_custom_fc.rs b/rodbus/src/client/requests/write_custom_fc.rs new file mode 100644 index 00000000..4bcd0c22 --- /dev/null +++ b/rodbus/src/client/requests/write_custom_fc.rs @@ -0,0 +1,94 @@ +use std::fmt::Display; + +use crate::client::message::Promise; +use crate::common::function::FunctionCode; +use crate::decode::AppDecodeLevel; +use crate::error::AduParseError; +use crate::error::RequestError; +use crate::types::{coil_from_u16, coil_to_u16, Indexed}; + +use scursor::{ReadCursor, WriteCursor}; + +pub(crate) trait WriteCustomFCOperation: Sized + PartialEq { + fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError>; + fn parse(cursor: &mut ReadCursor) -> Result; +} + +pub(crate) struct WriteCustomFC +where + T: WriteCustomFCOperation + Display + Send + 'static, +{ + pub(crate) request: T, + promise: Promise, +} + +impl WriteCustomFC +where + T: WriteCustomFCOperation + Display + Send + 'static, +{ + pub(crate) fn new(request: T, promise: Promise) -> Self { + Self { request, promise } + } + + pub(crate) fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { + self.request.serialize(cursor) + } + + pub(crate) fn failure(&mut self, err: RequestError) { + self.promise.failure(err) + } + + pub(crate) fn handle_response( + &mut self, + cursor: ReadCursor, + function: FunctionCode, + decode: AppDecodeLevel, + ) -> Result<(), RequestError> { + let response = self.parse_all(cursor)?; + + if decode.data_headers() { + tracing::info!("PDU RX - {} {}", function, response); + } else if decode.header() { + tracing::info!("PDU RX - {}", function); + } + + self.promise.success(response); + Ok(()) + } + + fn parse_all(&self, mut cursor: ReadCursor) -> Result { + let response = T::parse(&mut cursor)?; + cursor.expect_empty()?; + if self.request != response { + return Err(AduParseError::ReplyEchoMismatch.into()); + } + Ok(response) + } +} + +impl WriteCustomFCOperation for Indexed { + fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { + cursor.write_u16_be(self.index)?; + cursor.write_u16_be(coil_to_u16(self.value))?; + Ok(()) + } + + fn parse(cursor: &mut ReadCursor) -> Result { + Ok(Indexed::new( + cursor.read_u16_be()?, + coil_from_u16(cursor.read_u16_be()?)?, + )) + } +} + +impl WriteCustomFCOperation for Indexed { + fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { + cursor.write_u16_be(self.index)?; + cursor.write_u16_be(self.value)?; + Ok(()) + } + + fn parse(cursor: &mut ReadCursor) -> Result { + Ok(Indexed::new(cursor.read_u16_be()?, cursor.read_u16_be()?)) + } +} From bc84c5ee8dd36a24e483916af38cfcef5fb65e3e Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 18 Jan 2024 19:14:59 +0100 Subject: [PATCH 005/106] feat: Declare custom function code module --- rodbus/src/client/requests/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rodbus/src/client/requests/mod.rs b/rodbus/src/client/requests/mod.rs index b3e56fb2..7a2bd345 100644 --- a/rodbus/src/client/requests/mod.rs +++ b/rodbus/src/client/requests/mod.rs @@ -3,3 +3,4 @@ pub(crate) mod read_registers; pub(crate) mod write_multiple; pub(crate) mod write_single; pub(crate) mod send_buffer; +pub(crate) mod write_custom_fc; \ No newline at end of file From e42d375b8a48aebcd54890b7a1c9aeda44f8239b Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 18 Jan 2024 19:26:25 +0100 Subject: [PATCH 006/106] feat: Add 'write custom function code' as function code --- rodbus/src/common/function.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rodbus/src/common/function.rs b/rodbus/src/common/function.rs index 4052f630..f30ba928 100644 --- a/rodbus/src/common/function.rs +++ b/rodbus/src/common/function.rs @@ -10,6 +10,7 @@ mod constants { pub(crate) const WRITE_MULTIPLE_COILS: u8 = 15; pub(crate) const WRITE_MULTIPLE_REGISTERS: u8 = 16; pub(crate) const SEND_CUSTOM_BUFFERS: u8 = 68; + pub(crate) const WRITE_CUSTOM_FUNCTION_CODE: u8 = 69; } #[derive(Debug, Copy, Clone, PartialEq)] @@ -24,6 +25,7 @@ pub(crate) enum FunctionCode { WriteMultipleCoils = constants::WRITE_MULTIPLE_COILS, WriteMultipleRegisters = constants::WRITE_MULTIPLE_REGISTERS, SendCustomBuffers = constants::SEND_CUSTOM_BUFFERS, + WriteCustomFunctionCode = constants::WRITE_CUSTOM_FUNCTION_CODE, } impl Display for FunctionCode { @@ -54,6 +56,9 @@ impl Display for FunctionCode { FunctionCode::SendCustomBuffers => { write!(f, "SEND CUSTOM BUFFER ({:#04X})", self.get_value()) } + FunctionCode::WriteCustomFunctionCode => { + write!(f, "WRITE CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } } } } @@ -78,6 +83,7 @@ impl FunctionCode { constants::WRITE_MULTIPLE_COILS => Some(FunctionCode::WriteMultipleCoils), constants::WRITE_MULTIPLE_REGISTERS => Some(FunctionCode::WriteMultipleRegisters), constants::SEND_CUSTOM_BUFFERS => Some(FunctionCode::SendCustomBuffers), + constants::WRITE_CUSTOM_FUNCTION_CODE => Some(FunctionCode::WriteCustomFunctionCode), _ => None, } } From 6288b90bfcdf051f300f00eb04bea6dbec654ee0 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 18 Jan 2024 19:30:04 +0100 Subject: [PATCH 007/106] feat: Implement 'write custom function code' functionality into the message handler --- rodbus/src/client/message.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index dc2c39f9..9d462eaf 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -11,6 +11,7 @@ use crate::client::requests::read_registers::ReadRegisters; use crate::client::requests::write_multiple::MultipleWriteRequest; use crate::client::requests::write_single::SingleWrite; use crate::client::requests::send_buffer::SendBuffer; +use crate::client::requests::write_custom_fc::WriteCustomFC; use crate::common::traits::Serialize; use crate::types::{Indexed, UnitId}; @@ -47,6 +48,7 @@ pub(crate) enum RequestDetails { WriteSingleRegister(SingleWrite>), WriteMultipleCoils(MultipleWriteRequest), WriteMultipleRegisters(MultipleWriteRequest), + WriteCustomFunctionCode(WriteCustomFC>), } impl Request { @@ -132,6 +134,7 @@ impl RequestDetails { RequestDetails::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, RequestDetails::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, RequestDetails::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, + RequestDetails::WriteCustomFunctionCode(_) => FunctionCode::WriteCustomFunctionCode, } } @@ -146,6 +149,7 @@ impl RequestDetails { RequestDetails::WriteSingleRegister(x) => x.failure(err), RequestDetails::WriteMultipleCoils(x) => x.failure(err), RequestDetails::WriteMultipleRegisters(x) => x.failure(err), + RequestDetails::WriteCustomFunctionCode(x) => x.failure(err), } } @@ -166,6 +170,9 @@ impl RequestDetails { RequestDetails::WriteMultipleCoils(x) => x.handle_response(cursor, function, decode), RequestDetails::WriteMultipleRegisters(x) => { x.handle_response(cursor, function, decode) + }, + RequestDetails::WriteCustomFunctionCode(x) => { + x.handle_response(cursor, function, decode) } } } @@ -183,6 +190,7 @@ impl Serialize for RequestDetails { RequestDetails::WriteSingleRegister(x) => x.serialize(cursor), RequestDetails::WriteMultipleCoils(x) => x.serialize(cursor), RequestDetails::WriteMultipleRegisters(x) => x.serialize(cursor), + RequestDetails::WriteCustomFunctionCode(x) => x.serialize(cursor), } } } @@ -250,6 +258,9 @@ impl std::fmt::Display for RequestDetailsDisplay<'_> { } } } + RequestDetails::WriteCustomFunctionCode(details) => { + write!(f, "{}", details.request)?; + } } } From 284e8d95fe2dab710939e3d39d5bbbac4d6a9e09 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 18 Jan 2024 19:38:14 +0100 Subject: [PATCH 008/106] feat: Implement 'write custom function code' in channel handler --- rodbus/src/client/channel.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index 58f35bfa..3e1db41c 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -10,6 +10,8 @@ use crate::error::*; use crate::types::{AddressRange, BitIterator, Indexed, RegisterIterator, UnitId}; use crate::DecodeLevel; +use super::requests::write_custom_fc::WriteCustomFC; + /// Async channel used to make requests #[derive(Debug, Clone)] pub struct Channel { @@ -179,6 +181,21 @@ impl Channel { rx.await? } + /// Write a Custom Function Code to the server + pub async fn write_custom_function_code( + &mut self, + param: RequestParam, + request: Indexed, + ) -> Result, RequestError> { + let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); + let request = wrap( + param, + RequestDetails::WriteCustomFunctionCode(WriteCustomFC::new(request, Promise::channel(tx))), + ); + self.tx.send(request).await?; + rx.await? + } + /// Write a single coil on the server pub async fn write_single_coil( &mut self, From d0b381f60c9740630f7719fd8deca3260a5bec90 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 18 Jan 2024 19:40:13 +0100 Subject: [PATCH 009/106] feat: Implement 'write custom function code' in client example --- rodbus/examples/client.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 81d6880e..d732816c 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -283,12 +283,20 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { // ANCHOR: write_custom_function_code - println!("write success"); + let result: Result, RequestError> = channel + .write_custom_function_code( + params, + Indexed { + index: (0x1), + value: (0x44), + }, + ) + .await; // ANCHOR_END: write_custom_function_code } "rcfc" => { // ANCHOR: read_custom_function_code - println!("success"); + println!("read success"); // ANCHOR_END: read_custom_function_code } _ => println!("unknown command"), From fc3d8b487fbc9e62e9d91577149a7b99370cf088 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 18 Jan 2024 19:51:23 +0100 Subject: [PATCH 010/106] feat: Implement 'write custom function code' in server request handler --- rodbus/src/server/handler.rs | 5 +++++ rodbus/src/server/request.rs | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index db81863e..40ee7831 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -67,6 +67,11 @@ pub trait RequestHandler: Send + 'static { fn write_multiple_registers(&mut self, _values: WriteRegisters) -> Result<(), ExceptionCode> { Err(ExceptionCode::IllegalFunction) } + + /// Write a custom function code + fn write_custom_function_code(&mut self, _value: Indexed) -> Result<(), ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } } /// Trait useful for converting None into IllegalDataAddress diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index ffd30db8..79854651 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -22,6 +22,7 @@ pub(crate) enum Request<'a> { WriteSingleRegister(Indexed), WriteMultipleCoils(WriteCoils<'a>), WriteMultipleRegisters(WriteRegisters<'a>), + WriteCustomFunctionCode(Indexed), } /// All requests that support broadcast @@ -66,6 +67,7 @@ impl<'a> Request<'a> { Request::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, Request::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, Request::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, + Request::WriteCustomFunctionCode(_) => FunctionCode::WriteCustomFunctionCode, } } @@ -80,6 +82,7 @@ impl<'a> Request<'a> { Request::WriteSingleRegister(x) => Some(BroadcastRequest::WriteSingleRegister(x)), Request::WriteMultipleCoils(x) => Some(BroadcastRequest::WriteMultipleCoils(x)), Request::WriteMultipleRegisters(x) => Some(BroadcastRequest::WriteMultipleRegisters(x)), + Request::WriteCustomFunctionCode(_) => None, } } @@ -148,6 +151,10 @@ impl<'a> Request<'a> { .map(|_| items.range); write_result(function, header, writer, result, level) } + Request::WriteCustomFunctionCode(request) => { + let result = handler.write_custom_function_code(*request).map(|_| *request); + write_result(function, header, writer, result, level) + } } } @@ -213,6 +220,12 @@ impl<'a> Request<'a> { RegisterIterator::parse_all(range, cursor)?, ))) } + FunctionCode::WriteCustomFunctionCode => { + let x = + Request::WriteCustomFunctionCode(Indexed::::parse(cursor)?); + cursor.expect_empty()?; + Ok(x) + } } } } @@ -269,6 +282,9 @@ impl std::fmt::Display for RequestDisplay<'_, '_> { RegisterIteratorDisplay::new(self.level, items.iterator) )?; } + Request::WriteCustomFunctionCode(request) => { + write!(f, " {request}")?; + } } } From 10c81d91c630f798ee3968094d2b5f7aa400890d Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 18 Jan 2024 19:55:02 +0100 Subject: [PATCH 011/106] feat: Implement 'write custom function code' in server handler & server task handler --- rodbus/src/server/handler.rs | 5 +++++ rodbus/src/server/task.rs | 1 + 2 files changed, 6 insertions(+) diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 40ee7831..0f460c05 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -244,6 +244,11 @@ pub trait AuthorizationHandler: Send + Sync + 'static { ) -> Authorization { Authorization::Deny } + + /// Authorize a Write Custom Function Code request + fn write_custom_function_code(&self, _value: Indexed, _role: &str) -> Authorization { + Authorization::Deny + } } /// Read-only authorization handler that blindly accepts diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index 28f39149..a96164e7 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -265,6 +265,7 @@ impl AuthorizationType { Request::WriteMultipleRegisters(x) => { handler.write_multiple_registers(unit_id, x.range, role) } + Request::WriteCustomFunctionCode(x) => handler.write_custom_function_code(*x, role), } } From a232ef314fbe94932081f00996ef378ab8654fc6 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 18 Jan 2024 20:05:10 +0100 Subject: [PATCH 012/106] feat: Implement 'write custom function code' in frame handler --- rodbus/examples/client.rs | 1 + rodbus/src/serial/frame.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index d732816c..3292cbe6 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -292,6 +292,7 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { diff --git a/rodbus/src/serial/frame.rs b/rodbus/src/serial/frame.rs index 586c0ac3..cbe54da7 100644 --- a/rodbus/src/serial/frame.rs +++ b/rodbus/src/serial/frame.rs @@ -88,6 +88,7 @@ impl RtuParser { FunctionCode::WriteSingleRegister => LengthMode::Fixed(4), FunctionCode::WriteMultipleCoils => LengthMode::Offset(5), FunctionCode::WriteMultipleRegisters => LengthMode::Offset(5), + FunctionCode::WriteCustomFunctionCode => LengthMode::Offset(1), }, ParserType::Response => match function_code { FunctionCode::ReadCoils => LengthMode::Offset(1), @@ -99,6 +100,7 @@ impl RtuParser { FunctionCode::WriteSingleRegister => LengthMode::Fixed(4), FunctionCode::WriteMultipleCoils => LengthMode::Fixed(4), FunctionCode::WriteMultipleRegisters => LengthMode::Fixed(4), + FunctionCode::WriteCustomFunctionCode => LengthMode::Offset(1), }, } } From d6c7a31d2aca9b454ce03b9833258d5bae1f7e52 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 19 Jan 2024 18:11:33 +0100 Subject: [PATCH 013/106] feat: Add CustomFC type; Implement client request for CustomFC type --- rodbus/src/client/requests/write_custom_fc.rs | 31 ++++++-------- rodbus/src/types.rs | 40 ++++++++++--------- 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/rodbus/src/client/requests/write_custom_fc.rs b/rodbus/src/client/requests/write_custom_fc.rs index 4bcd0c22..5a46e396 100644 --- a/rodbus/src/client/requests/write_custom_fc.rs +++ b/rodbus/src/client/requests/write_custom_fc.rs @@ -1,11 +1,11 @@ use std::fmt::Display; +use crate::CustomFunctionCode; use crate::client::message::Promise; use crate::common::function::FunctionCode; use crate::decode::AppDecodeLevel; use crate::error::AduParseError; use crate::error::RequestError; -use crate::types::{coil_from_u16, coil_to_u16, Indexed}; use scursor::{ReadCursor, WriteCursor}; @@ -66,29 +66,22 @@ where } } -impl WriteCustomFCOperation for Indexed { +impl WriteCustomFCOperation for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { - cursor.write_u16_be(self.index)?; - cursor.write_u16_be(coil_to_u16(self.value))?; - Ok(()) - } + cursor.write_u16_be(self.len() as u16)?; - fn parse(cursor: &mut ReadCursor) -> Result { - Ok(Indexed::new( - cursor.read_u16_be()?, - coil_from_u16(cursor.read_u16_be()?)?, - )) - } -} - -impl WriteCustomFCOperation for Indexed { - fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { - cursor.write_u16_be(self.index)?; - cursor.write_u16_be(self.value)?; + for &item in self.iter() { + cursor.write_u16_be(item)?; + } Ok(()) } fn parse(cursor: &mut ReadCursor) -> Result { - Ok(Indexed::new(cursor.read_u16_be()?, cursor.read_u16_be()?)) + let len = cursor.read_u16_be()? as usize; + let mut vec = Vec::with_capacity(len); + for _ in 0..len { + vec.push(cursor.read_u16_be()?); + } + Ok(CustomFunctionCode::new(vec)) } } diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 0f9d94eb..a538e6b2 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -85,25 +85,10 @@ pub(crate) struct RegisterIteratorDisplay<'a> { level: AppDecodeLevel, } -impl std::fmt::Display for UnitId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:#04X}", self.value) - } -} - -impl<'a> BitIterator<'a> { - pub(crate) fn parse_all( - range: AddressRange, - cursor: &'a mut ReadCursor, - ) -> Result { - let bytes = cursor.read_bytes(crate::common::bits::num_bytes_for_bits(range.count))?; - cursor.expect_empty()?; - Ok(Self { - bytes, - range, - pos: 0, - }) - } +/// Custom buffer +#[derive(Clone, Debug, PartialEq)] +pub struct CustomFunctionCode { + data: Vec, } impl<'a> BitIteratorDisplay<'a> { @@ -367,6 +352,23 @@ impl Default for UnitId { } } +impl CustomFunctionCode { + /// Create a new custom function code + pub fn new(data: Vec) -> Self { + Self { data } + } + + /// Get the length of the underlying vector + pub fn len(&self) -> usize { + self.data.len() + } + + // Iterate over the underlying vector + pub fn iter(&self) -> std::slice::Iter { + self.data.iter() + } +} + #[cfg(test)] mod tests { use crate::error::*; From 6d6d91657d781c5c9d84a1db1ad678ea8045dcbc Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 19 Jan 2024 18:28:13 +0100 Subject: [PATCH 014/106] feat: Implement CustomFC Display trait --- rodbus/src/types.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index a538e6b2..81f69321 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -369,6 +369,19 @@ impl CustomFunctionCode { } } +impl std::fmt::Display for CustomFunctionCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[")?; + for (i, val) in self.data.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "{}", val)?; + } + write!(f, "]") + } +} + #[cfg(test)] mod tests { use crate::error::*; From 90744c1763fcfdb1aaea32410a41ca15f25da58e Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 19 Jan 2024 18:31:18 +0100 Subject: [PATCH 015/106] feat: Implement CustomFC type in client-side request --- rodbus/examples/client.rs | 2 +- rodbus/src/client/channel.rs | 8 ++++---- rodbus/src/client/message.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 3292cbe6..98c1c9c0 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -283,7 +283,7 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { // ANCHOR: write_custom_function_code - let result: Result, RequestError> = channel + let result: Result = channel .write_custom_function_code( params, Indexed { diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index 3e1db41c..dc1d0a03 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -7,7 +7,7 @@ use crate::client::requests::write_multiple::{MultipleWriteRequest, WriteMultipl use crate::client::requests::write_single::SingleWrite; use crate::client::requests::send_buffer::SendBuffer; use crate::error::*; -use crate::types::{AddressRange, BitIterator, Indexed, RegisterIterator, UnitId}; +use crate::types::{AddressRange, BitIterator, Indexed, RegisterIterator, UnitId, CustomFunctionCode}; use crate::DecodeLevel; use super::requests::write_custom_fc::WriteCustomFC; @@ -185,9 +185,9 @@ impl Channel { pub async fn write_custom_function_code( &mut self, param: RequestParam, - request: Indexed, - ) -> Result, RequestError> { - let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); + request: CustomFunctionCode, + ) -> Result { + let (tx, rx) = tokio::sync::oneshot::channel::>(); let request = wrap( param, RequestDetails::WriteCustomFunctionCode(WriteCustomFC::new(request, Promise::channel(tx))), diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 9d462eaf..e581fecc 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -13,7 +13,7 @@ use crate::client::requests::write_single::SingleWrite; use crate::client::requests::send_buffer::SendBuffer; use crate::client::requests::write_custom_fc::WriteCustomFC; use crate::common::traits::Serialize; -use crate::types::{Indexed, UnitId}; +use crate::types::{Indexed, UnitId, CustomFunctionCode}; use scursor::{ReadCursor, WriteCursor}; use std::time::Duration; @@ -48,7 +48,7 @@ pub(crate) enum RequestDetails { WriteSingleRegister(SingleWrite>), WriteMultipleCoils(MultipleWriteRequest), WriteMultipleRegisters(MultipleWriteRequest), - WriteCustomFunctionCode(WriteCustomFC>), + WriteCustomFunctionCode(WriteCustomFC), } impl Request { From 38b3404dc72d2455dfaec3d82507564694436174 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 22 Jan 2024 16:57:20 +0100 Subject: [PATCH 016/106] fix: Implement UnitId Display trait --- rodbus/src/types.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 81f69321..17a59154 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -91,6 +91,12 @@ pub struct CustomFunctionCode { data: Vec, } +impl std::fmt::Display for UnitId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:#04X}", self.value) + } +} + impl<'a> BitIteratorDisplay<'a> { pub(crate) fn new(level: AppDecodeLevel, iterator: BitIterator<'a>) -> Self { Self { iterator, level } From fa71af4737293ab53ec55ab025478d4c8e662fb2 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 22 Jan 2024 16:59:15 +0100 Subject: [PATCH 017/106] chore: Replace write custom FC client request parameter --- rodbus/examples/client.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 98c1c9c0..ab80ef59 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -286,10 +286,7 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box = channel .write_custom_function_code( params, - Indexed { - index: (0x1), - value: (0x44), - }, + CustomFunctionCode::new(vec![0x01, 0x02, 0x03, 0x04]) ) .await; print_write_result(result); From 788be9ff939873f91dabbf410889021298aab029 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 22 Jan 2024 17:05:22 +0100 Subject: [PATCH 018/106] fix: Implement BitIterator parse_all fn --- rodbus/src/types.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 17a59154..fc70359f 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -97,6 +97,21 @@ impl std::fmt::Display for UnitId { } } +impl<'a> BitIterator<'a> { + pub(crate) fn parse_all( + range: AddressRange, + cursor: &'a mut ReadCursor, + ) -> Result { + let bytes = cursor.read_bytes(crate::common::bits::num_bytes_for_bits(range.count))?; + cursor.expect_empty()?; + Ok(Self { + bytes, + range, + pos: 0, + }) + } +} + impl<'a> BitIteratorDisplay<'a> { pub(crate) fn new(level: AppDecodeLevel, iterator: BitIterator<'a>) -> Self { Self { iterator, level } From 1934f3dace72b9a87d78605a9a86d0b6e1e10d81 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 22 Jan 2024 17:07:05 +0100 Subject: [PATCH 019/106] fix: Implement missing documentation for CustomFunctionCode fn --- rodbus/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index fc70359f..cd0b0bbe 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -384,7 +384,7 @@ impl CustomFunctionCode { self.data.len() } - // Iterate over the underlying vector + /// Iterate over the underlying vector pub fn iter(&self) -> std::slice::Iter { self.data.iter() } From 476e5ea9347eafbd0bf2d18f34f3c905ffd0ae25 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 22 Jan 2024 20:53:38 +0100 Subject: [PATCH 020/106] chore: Set tracing level to DEBUG --- rodbus/examples/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index ab80ef59..afcbe682 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -16,7 +16,7 @@ async fn main() -> Result<(), Box> { // ANCHOR: logging tracing_subscriber::fmt() - .with_max_level(tracing::Level::INFO) + .with_max_level(tracing::Level::DEBUG) .with_target(false) .init(); // ANCHOR_END: logging From ea4f641e363101f0ab3ab999553796d3c04f47fb Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 22 Jan 2024 20:55:10 +0100 Subject: [PATCH 021/106] fix: Implement customFC type in server-side request --- rodbus/src/server/handler.rs | 4 ++-- rodbus/src/server/request.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 0f460c05..813c5db0 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -69,7 +69,7 @@ pub trait RequestHandler: Send + 'static { } /// Write a custom function code - fn write_custom_function_code(&mut self, _value: Indexed) -> Result<(), ExceptionCode> { + fn write_custom_function_code(&mut self, _value: CustomFunctionCode) -> Result<(), ExceptionCode> { Err(ExceptionCode::IllegalFunction) } } @@ -246,7 +246,7 @@ pub trait AuthorizationHandler: Send + Sync + 'static { } /// Authorize a Write Custom Function Code request - fn write_custom_function_code(&self, _value: Indexed, _role: &str) -> Authorization { + fn write_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } } diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 79854651..0b34d196 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -22,7 +22,7 @@ pub(crate) enum Request<'a> { WriteSingleRegister(Indexed), WriteMultipleCoils(WriteCoils<'a>), WriteMultipleRegisters(WriteRegisters<'a>), - WriteCustomFunctionCode(Indexed), + WriteCustomFunctionCode(CustomFunctionCode), } /// All requests that support broadcast @@ -222,7 +222,7 @@ impl<'a> Request<'a> { } FunctionCode::WriteCustomFunctionCode => { let x = - Request::WriteCustomFunctionCode(Indexed::::parse(cursor)?); + Request::WriteCustomFunctionCode(CustomFunctionCode::parse(cursor)?); cursor.expect_empty()?; Ok(x) } From debeaa1f2efb6c4177b39a4cd603126e508f463c Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 22 Jan 2024 20:57:07 +0100 Subject: [PATCH 022/106] feat: Implement custom function code parser --- rodbus/src/common/parse.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 8b6dce04..8e705815 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -1,6 +1,6 @@ use crate::common::traits::Parse; use crate::error::*; -use crate::types::{coil_from_u16, AddressRange, Indexed}; +use crate::types::{coil_from_u16, AddressRange, Indexed, CustomFunctionCode}; use scursor::ReadCursor; @@ -28,6 +28,17 @@ impl Parse for Indexed { } } +impl Parse for CustomFunctionCode { + fn parse(cursor: &mut ReadCursor) -> Result { + let len = cursor.read_u16_be()? as usize; + let mut vec = Vec::with_capacity(len); + for _ in 0..len { + vec.push(cursor.read_u16_be()?); + } + Ok(CustomFunctionCode::new(vec)) // Construct a U16Vec from the parsed vector + } +} + #[cfg(test)] mod coils { use crate::common::traits::Parse; From 9a9eaf460aa3dd12fea5d52c6cc337acb82d53da Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 22 Jan 2024 21:15:45 +0100 Subject: [PATCH 023/106] feat: Implement Serialize & Loggable traits for CustomFunctionCode --- rodbus/src/common/serialize.rs | 45 +++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 905cfe01..db6a00f7 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -8,7 +8,7 @@ use crate::error::{InternalError, RequestError}; use crate::server::response::{BitWriter, RegisterWriter}; use crate::types::{ coil_from_u16, coil_to_u16, AddressRange, BitIterator, BitIteratorDisplay, Indexed, - RegisterIterator, RegisterIteratorDisplay, + RegisterIterator, RegisterIteratorDisplay, CustomFunctionCode, }; use scursor::{ReadCursor, WriteCursor}; @@ -290,6 +290,49 @@ impl Serialize for WriteMultiple { } } +impl Serialize for CustomFunctionCode { + fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { + cursor.write_u16_be(self.len() as u16)?; + + for &item in self.iter() { + cursor.write_u16_be(item)?; + } + Ok(()) + } +} + +impl Loggable for CustomFunctionCode { + fn log( + &self, + payload: &[u8], + level: crate::decode::AppDecodeLevel, + f: &mut std::fmt::Formatter, + ) -> std::fmt::Result { + if level.data_headers() { + let mut cursor = ReadCursor::new(payload); + + let len = match cursor.read_u16_be() { + Ok(len) => len, + Err(_) => return Ok(()), + }; + + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + match cursor.read_u16_be() { + Ok(value) => vec.push(value), + Err(_) => return Ok(()), + } + } + + let value = CustomFunctionCode::new(vec); + + write!(f, "{value}")?; + } + + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; From c1d5e68484c6d51d723b80a4b6b12f34c6ec2e3e Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Tue, 23 Jan 2024 19:54:36 +0100 Subject: [PATCH 024/106] fix: Replace Vec with a fixed-size array for now, to work around the server-side request error (missing Copy trait) --- rodbus/examples/client.rs | 4 ++-- rodbus/src/client/requests/write_custom_fc.rs | 16 ++++++++++------ rodbus/src/common/parse.rs | 13 +++++++++++-- rodbus/src/common/serialize.rs | 13 +++++++++++-- rodbus/src/types.rs | 8 ++++---- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index afcbe682..067557f4 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -283,10 +283,10 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { // ANCHOR: write_custom_function_code - let result: Result = channel + let result = channel .write_custom_function_code( params, - CustomFunctionCode::new(vec![0x01, 0x02, 0x03, 0x04]) + CustomFunctionCode::new([0x01, 0x02, 0x03, 0x04]) ) .await; print_write_result(result); diff --git a/rodbus/src/client/requests/write_custom_fc.rs b/rodbus/src/client/requests/write_custom_fc.rs index 5a46e396..3f359498 100644 --- a/rodbus/src/client/requests/write_custom_fc.rs +++ b/rodbus/src/client/requests/write_custom_fc.rs @@ -77,11 +77,15 @@ impl WriteCustomFCOperation for CustomFunctionCode { } fn parse(cursor: &mut ReadCursor) -> Result { - let len = cursor.read_u16_be()? as usize; - let mut vec = Vec::with_capacity(len); - for _ in 0..len { - vec.push(cursor.read_u16_be()?); - } - Ok(CustomFunctionCode::new(vec)) + //let len = cursor.read_u16_be()? as usize; + + let val1 = cursor.read_u16_be()?; + let val2 = cursor.read_u16_be()?; + let val3 = cursor.read_u16_be()?; + let val4 = cursor.read_u16_be()?; + + let values = [val1, val2, val3, val4]; + + Ok(CustomFunctionCode::new(values)) } } diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 8e705815..4bcbb790 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -30,12 +30,21 @@ impl Parse for Indexed { impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { - let len = cursor.read_u16_be()? as usize; + /*let len = cursor.read_u16_be()? as usize; let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(cursor.read_u16_be()?); } - Ok(CustomFunctionCode::new(vec)) // Construct a U16Vec from the parsed vector + Ok(CustomFunctionCode::new(vec)) // Construct a U16Vec from the parsed vector*/ + + let val1 = cursor.read_u16_be()?; + let val2 = cursor.read_u16_be()?; + let val3 = cursor.read_u16_be()?; + let val4 = cursor.read_u16_be()?; + + let values = [val1, val2, val3, val4]; + + Ok(CustomFunctionCode::new(values)) } } diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index db6a00f7..3c9e3c39 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -311,7 +311,7 @@ impl Loggable for CustomFunctionCode { if level.data_headers() { let mut cursor = ReadCursor::new(payload); - let len = match cursor.read_u16_be() { + /*let len = match cursor.read_u16_be() { Ok(len) => len, Err(_) => return Ok(()), }; @@ -322,9 +322,18 @@ impl Loggable for CustomFunctionCode { Ok(value) => vec.push(value), Err(_) => return Ok(()), } + }*/ + + let mut data = [0_u16; 4]; + + for i in 0..4 { + data[i] = match cursor.read_u16_be() { + Ok(value) => value, + Err(_) => return Ok(()), + }; } - let value = CustomFunctionCode::new(vec); + let value = CustomFunctionCode::new(data); write!(f, "{value}")?; } diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index cd0b0bbe..dfc832b1 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -86,9 +86,9 @@ pub(crate) struct RegisterIteratorDisplay<'a> { } /// Custom buffer -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Copy, PartialEq)] pub struct CustomFunctionCode { - data: Vec, + data: [u16; 4], } impl std::fmt::Display for UnitId { @@ -375,7 +375,7 @@ impl Default for UnitId { impl CustomFunctionCode { /// Create a new custom function code - pub fn new(data: Vec) -> Self { + pub fn new(data: [u16; 4]) -> Self { Self { data } } @@ -392,7 +392,7 @@ impl CustomFunctionCode { impl std::fmt::Display for CustomFunctionCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "[")?; + write!(f, "value: [")?; for (i, val) in self.data.iter().enumerate() { if i != 0 { write!(f, ", ")?; From d71bf865f11e22c27ea6b25d8a89b16ab28e9342 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Tue, 23 Jan 2024 20:34:57 +0100 Subject: [PATCH 025/106] fix: Implement missing customFC request in server-side request handling --- rodbus/examples/server.rs | 6 ++++++ rodbus/src/server/handler.rs | 2 +- rodbus/src/types.rs | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index 6c8815e8..6efee4a9 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -92,6 +92,12 @@ impl RequestHandler for SimpleHandler { } } + fn write_custom_function_code(&self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { + tracing::info!("processing custom function code, {:?}", values); + + Ok(()) + } + fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { tracing::info!( "write single register, index: {} value: {}", diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 813c5db0..36496cfa 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -69,7 +69,7 @@ pub trait RequestHandler: Send + 'static { } /// Write a custom function code - fn write_custom_function_code(&mut self, _value: CustomFunctionCode) -> Result<(), ExceptionCode> { + fn write_custom_function_code(&self, _values: CustomFunctionCode) -> Result<(), ExceptionCode> { Err(ExceptionCode::IllegalFunction) } } diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index dfc832b1..205d709e 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -392,7 +392,7 @@ impl CustomFunctionCode { impl std::fmt::Display for CustomFunctionCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "value: [")?; + write!(f, "values: [")?; for (i, val) in self.data.iter().enumerate() { if i != 0 { write!(f, ", ")?; From 5bd36f17c2a1f84710533a20936a59fdee96320a Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 24 Jan 2024 14:24:39 +0100 Subject: [PATCH 026/106] fix: Implement correct CustomFunctionCode value parsing --- rodbus/src/client/requests/write_custom_fc.rs | 1 + rodbus/src/common/parse.rs | 8 ++------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/rodbus/src/client/requests/write_custom_fc.rs b/rodbus/src/client/requests/write_custom_fc.rs index 3f359498..6247b0e4 100644 --- a/rodbus/src/client/requests/write_custom_fc.rs +++ b/rodbus/src/client/requests/write_custom_fc.rs @@ -78,6 +78,7 @@ impl WriteCustomFCOperation for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { //let len = cursor.read_u16_be()? as usize; + cursor.read_u16_be()?; // Skip the length field let val1 = cursor.read_u16_be()?; let val2 = cursor.read_u16_be()?; diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 4bcbb790..0c22d8ae 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -30,12 +30,8 @@ impl Parse for Indexed { impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { - /*let len = cursor.read_u16_be()? as usize; - let mut vec = Vec::with_capacity(len); - for _ in 0..len { - vec.push(cursor.read_u16_be()?); - } - Ok(CustomFunctionCode::new(vec)) // Construct a U16Vec from the parsed vector*/ + //let len = cursor.read_u16_be()? as usize; + cursor.read_u16_be()?; // Skip the length field let val1 = cursor.read_u16_be()?; let val2 = cursor.read_u16_be()?; From bff9685ddecdc0c3a74870bb09c16b91ce91deac Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 24 Jan 2024 17:45:27 +0100 Subject: [PATCH 027/106] fix: Implement customFC length attribute --- rodbus/src/client/requests/write_custom_fc.rs | 5 ++--- rodbus/src/common/parse.rs | 5 ++--- rodbus/src/common/serialize.rs | 2 +- rodbus/src/types.rs | 5 +++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/rodbus/src/client/requests/write_custom_fc.rs b/rodbus/src/client/requests/write_custom_fc.rs index 6247b0e4..c3b3abbd 100644 --- a/rodbus/src/client/requests/write_custom_fc.rs +++ b/rodbus/src/client/requests/write_custom_fc.rs @@ -77,8 +77,7 @@ impl WriteCustomFCOperation for CustomFunctionCode { } fn parse(cursor: &mut ReadCursor) -> Result { - //let len = cursor.read_u16_be()? as usize; - cursor.read_u16_be()?; // Skip the length field + let len = cursor.read_u16_be()? as usize; let val1 = cursor.read_u16_be()?; let val2 = cursor.read_u16_be()?; @@ -87,6 +86,6 @@ impl WriteCustomFCOperation for CustomFunctionCode { let values = [val1, val2, val3, val4]; - Ok(CustomFunctionCode::new(values)) + Ok(CustomFunctionCode::new(len, values)) } } diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 0c22d8ae..e9d767e1 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -30,8 +30,7 @@ impl Parse for Indexed { impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { - //let len = cursor.read_u16_be()? as usize; - cursor.read_u16_be()?; // Skip the length field + let len = cursor.read_u16_be()? as usize; let val1 = cursor.read_u16_be()?; let val2 = cursor.read_u16_be()?; @@ -40,7 +39,7 @@ impl Parse for CustomFunctionCode { let values = [val1, val2, val3, val4]; - Ok(CustomFunctionCode::new(values)) + Ok(CustomFunctionCode::new(len, values)) } } diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 3c9e3c39..077f5825 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -333,7 +333,7 @@ impl Loggable for CustomFunctionCode { }; } - let value = CustomFunctionCode::new(data); + let value = CustomFunctionCode::new(0x04, data); write!(f, "{value}")?; } diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 205d709e..1ec44898 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -88,6 +88,7 @@ pub(crate) struct RegisterIteratorDisplay<'a> { /// Custom buffer #[derive(Clone, Debug, Copy, PartialEq)] pub struct CustomFunctionCode { + len: usize, data: [u16; 4], } @@ -375,8 +376,8 @@ impl Default for UnitId { impl CustomFunctionCode { /// Create a new custom function code - pub fn new(data: [u16; 4]) -> Self { - Self { data } + pub fn new(len: usize, data: [u16; 4]) -> Self { + Self { len, data } } /// Get the length of the underlying vector From 8954e364fb38d0a3f1076ab27abe94123ca90d19 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 24 Jan 2024 18:11:33 +0100 Subject: [PATCH 028/106] feat: Improved server and client examples for custom function code feature --- rodbus/examples/client.rs | 6 ++++-- rodbus/examples/server.rs | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 067557f4..57817776 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -198,7 +198,7 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box Result<(), Box { // ANCHOR: write_custom_function_code + let length = 0x04 as usize; + let values = [0x2, 0x3, 0x4, 0x5]; let result = channel .write_custom_function_code( params, - CustomFunctionCode::new([0x01, 0x02, 0x03, 0x04]) + CustomFunctionCode::new(length, values) ) .await; print_write_result(result); diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index 6efee4a9..aed1da88 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -93,7 +93,11 @@ impl RequestHandler for SimpleHandler { } fn write_custom_function_code(&self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { - tracing::info!("processing custom function code, {:?}", values); + let mut custom_fc_args = [0_u16; 4]; + for (i, &value) in values.iter().enumerate() { + custom_fc_args[i] = value; + } + tracing::info!("processing custom function code values: {:?}", custom_fc_args); Ok(()) } From 2719e7499fe25add940c693682d52e5ca1417041 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 24 Jan 2024 18:13:33 +0100 Subject: [PATCH 029/106] chore: Improve type naming of the custom function code feature --- rodbus/src/client/channel.rs | 4 ++-- rodbus/src/client/message.rs | 4 ++-- rodbus/src/client/requests/write_custom_fc.rs | 13 ++++++------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index dc1d0a03..80737db0 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -6,11 +6,11 @@ use crate::client::requests::read_registers::ReadRegisters; use crate::client::requests::write_multiple::{MultipleWriteRequest, WriteMultiple}; use crate::client::requests::write_single::SingleWrite; use crate::client::requests::send_buffer::SendBuffer; +use crate::client::requests::write_custom_fc::WriteCustomFunctionCode; use crate::error::*; use crate::types::{AddressRange, BitIterator, Indexed, RegisterIterator, UnitId, CustomFunctionCode}; use crate::DecodeLevel; -use super::requests::write_custom_fc::WriteCustomFC; /// Async channel used to make requests #[derive(Debug, Clone)] @@ -190,7 +190,7 @@ impl Channel { let (tx, rx) = tokio::sync::oneshot::channel::>(); let request = wrap( param, - RequestDetails::WriteCustomFunctionCode(WriteCustomFC::new(request, Promise::channel(tx))), + RequestDetails::WriteCustomFunctionCode(WriteCustomFunctionCode::new(request, Promise::channel(tx))), ); self.tx.send(request).await?; rx.await? diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index e581fecc..e4238b31 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -11,7 +11,7 @@ use crate::client::requests::read_registers::ReadRegisters; use crate::client::requests::write_multiple::MultipleWriteRequest; use crate::client::requests::write_single::SingleWrite; use crate::client::requests::send_buffer::SendBuffer; -use crate::client::requests::write_custom_fc::WriteCustomFC; +use crate::client::requests::write_custom_fc::WriteCustomFunctionCode; use crate::common::traits::Serialize; use crate::types::{Indexed, UnitId, CustomFunctionCode}; @@ -48,7 +48,7 @@ pub(crate) enum RequestDetails { WriteSingleRegister(SingleWrite>), WriteMultipleCoils(MultipleWriteRequest), WriteMultipleRegisters(MultipleWriteRequest), - WriteCustomFunctionCode(WriteCustomFC), + WriteCustomFunctionCode(WriteCustomFunctionCode), } impl Request { diff --git a/rodbus/src/client/requests/write_custom_fc.rs b/rodbus/src/client/requests/write_custom_fc.rs index c3b3abbd..5bb7f9fa 100644 --- a/rodbus/src/client/requests/write_custom_fc.rs +++ b/rodbus/src/client/requests/write_custom_fc.rs @@ -9,22 +9,22 @@ use crate::error::RequestError; use scursor::{ReadCursor, WriteCursor}; -pub(crate) trait WriteCustomFCOperation: Sized + PartialEq { +pub(crate) trait WriteCustomFunctionCodeOperation: Sized + PartialEq { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError>; fn parse(cursor: &mut ReadCursor) -> Result; } -pub(crate) struct WriteCustomFC +pub(crate) struct WriteCustomFunctionCode where - T: WriteCustomFCOperation + Display + Send + 'static, + T: WriteCustomFunctionCodeOperation + Display + Send + 'static, { pub(crate) request: T, promise: Promise, } -impl WriteCustomFC +impl WriteCustomFunctionCode where - T: WriteCustomFCOperation + Display + Send + 'static, + T: WriteCustomFunctionCodeOperation + Display + Send + 'static, { pub(crate) fn new(request: T, promise: Promise) -> Self { Self { request, promise } @@ -66,7 +66,7 @@ where } } -impl WriteCustomFCOperation for CustomFunctionCode { +impl WriteCustomFunctionCodeOperation for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u16_be(self.len() as u16)?; @@ -78,7 +78,6 @@ impl WriteCustomFCOperation for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { let len = cursor.read_u16_be()? as usize; - let val1 = cursor.read_u16_be()?; let val2 = cursor.read_u16_be()?; let val3 = cursor.read_u16_be()?; From be8eba72e09f6ae6a9886eee4ebefb0fafbd6678 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 24 Jan 2024 18:59:26 +0100 Subject: [PATCH 030/106] chore: Improve server and client examples for write custom function code feature --- rodbus/examples/client.rs | 5 +++-- rodbus/examples/server.rs | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 57817776..3acdc0c9 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -199,7 +199,7 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box Result<(), Box { // ANCHOR: write_custom_function_code let length = 0x04 as usize; - let values = [0x2, 0x3, 0x4, 0x5]; + let values = [0x2, 0x3, 0x4, 0x5]; // i.e.: Voltage Hi = 0x02 / Voltage Lo = 0x03 / Current Hi = 0x04 / Current Lo = 0x05 + let result = channel .write_custom_function_code( params, diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index aed1da88..781c4157 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -93,11 +93,11 @@ impl RequestHandler for SimpleHandler { } fn write_custom_function_code(&self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { - let mut custom_fc_args = [0_u16; 4]; + let mut custom_fc_args = [0_u16; 4]; // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 for (i, &value) in values.iter().enumerate() { custom_fc_args[i] = value; } - tracing::info!("processing custom function code values: {:?}", custom_fc_args); + tracing::info!("processing custom function code arguments: {:?}", custom_fc_args); Ok(()) } @@ -155,7 +155,7 @@ impl RequestHandler for SimpleHandler { async fn main() -> Result<(), Box> { // initialize logging tracing_subscriber::fmt() - .with_max_level(tracing::Level::INFO) + .with_max_level(tracing::Level::DEBUG) .with_target(false) .init(); @@ -305,8 +305,8 @@ async fn run_server( server .set_decode_level(DecodeLevel::new( AppDecodeLevel::DataValues, - FrameDecodeLevel::Header, - PhysDecodeLevel::Length, + FrameDecodeLevel::Payload, + PhysDecodeLevel::Data, )) .await?; } From 6438e99fc01006bfe28d21aab051c092b097304d Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 25 Jan 2024 17:32:25 +0100 Subject: [PATCH 031/106] chore: Set server and client port in examples >1024 for non-root linux users --- rodbus/examples/client.rs | 4 ++-- rodbus/examples/server.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 3acdc0c9..65843496 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -96,7 +96,7 @@ async fn run_rtu() -> Result<(), Box> { async fn run_tls(tls_config: TlsClientConfig) -> Result<(), Box> { // ANCHOR: create_tls_channel let channel = spawn_tls_client_task( - HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 802), + HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 1802), 1, default_retry_strategy(), tls_config, @@ -178,7 +178,7 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box Result<(), Box Date: Thu, 25 Jan 2024 17:33:36 +0100 Subject: [PATCH 032/106] fix: Authorize Write Custom Function Code Request in server handler (required for TLS communication) --- rodbus/src/server/handler.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 36496cfa..f4a347a6 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -327,6 +327,11 @@ impl AuthorizationHandler for ReadOnlyAuthorizationHandler { ) -> Authorization { Authorization::Deny } + + /// Authorize a Write Custom Function Code request + fn write_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } } #[cfg(test)] From 3504b1d19b2cbb1969797e4698d4ae0cbe429fd9 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 25 Jan 2024 17:35:52 +0100 Subject: [PATCH 033/106] fix: Correct logged offset server PDU TX values for the Custom Function Code request --- rodbus/src/common/serialize.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 077f5825..3e751de7 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -311,21 +311,13 @@ impl Loggable for CustomFunctionCode { if level.data_headers() { let mut cursor = ReadCursor::new(payload); - /*let len = match cursor.read_u16_be() { - Ok(len) => len, + let len = match cursor.read_u16_be() { + Ok(value) => value as usize, Err(_) => return Ok(()), }; - let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len { - match cursor.read_u16_be() { - Ok(value) => vec.push(value), - Err(_) => return Ok(()), - } - }*/ - let mut data = [0_u16; 4]; - + for i in 0..4 { data[i] = match cursor.read_u16_be() { Ok(value) => value, @@ -333,9 +325,10 @@ impl Loggable for CustomFunctionCode { }; } - let value = CustomFunctionCode::new(0x04, data); + let custom_fc = CustomFunctionCode::new(len, data); + + write!(f, "{:?}", custom_fc)?; - write!(f, "{value}")?; } Ok(()) From 21413afd958e2e22f4bf06926e636679887fc0ac Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 25 Jan 2024 19:09:53 +0100 Subject: [PATCH 034/106] test: Add Custom Function Code feature parsing unit test --- rodbus/src/common/parse.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index e9d767e1..b5faa97d 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -78,4 +78,11 @@ mod coils { let result = Indexed::::parse(&mut cursor); assert_eq!(result, Ok(Indexed::new(1, 0xCAFE))); } + + #[test] + fn parse_succeeds_for_valid_custom_function_code() { + let mut cursor = ReadCursor::new(&[0x00, 0x04, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE, 0xC0, 0xDE]); + let result = crate::types::CustomFunctionCode::parse(&mut cursor); + assert_eq!(result, Ok(crate::types::CustomFunctionCode::new(4, [0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE]))); + } } From 570aefb856e6bd0952b32122fe02e357d4c409c6 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 25 Jan 2024 19:17:54 +0100 Subject: [PATCH 035/106] test: Add Custom Function Code parsing unit test for invalid values --- rodbus/src/common/parse.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index b5faa97d..272ca1e4 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -85,4 +85,11 @@ mod coils { let result = crate::types::CustomFunctionCode::parse(&mut cursor); assert_eq!(result, Ok(crate::types::CustomFunctionCode::new(4, [0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE]))); } + + #[test] + fn parse_fails_for_invalid_custom_function_code() { + let mut cursor = ReadCursor::new(&[0x00, 0x04, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE, 0xC0]); + let result = crate::types::CustomFunctionCode::parse(&mut cursor); + assert_eq!(result, Err(AduParseError::InsufficientBytes.into())); + } } From 74794615a8e47df01441c3bbc17cfedf225eb3b8 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 25 Jan 2024 19:23:55 +0100 Subject: [PATCH 036/106] test: Add Custom Function Code serializing unit test --- rodbus/src/common/serialize.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 3e751de7..9bf8b7c7 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -347,4 +347,13 @@ mod tests { range.serialize(&mut cursor).unwrap(); assert_eq!(buffer, [0x00, 0x03, 0x02, 0x00]); } + + #[test] + fn serializes_valid_custom_function_code() { + let custom_fc = CustomFunctionCode::new(4, [0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE]); + let mut buffer = [0u8; 10]; + let mut cursor = WriteCursor::new(&mut buffer); + custom_fc.serialize(&mut cursor).unwrap(); + assert_eq!(buffer, [0x00, 0x04, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE, 0xC0, 0xDE]); + } } From 7248688eb2baf0f3855ab6903d519dd40973d051 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 25 Jan 2024 20:05:31 +0100 Subject: [PATCH 037/106] test: Add Custom Function Code integration test --- rodbus/src/types.rs | 2 +- rodbus/tests/integration_test.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 1ec44898..a952384d 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -85,7 +85,7 @@ pub(crate) struct RegisterIteratorDisplay<'a> { level: AppDecodeLevel, } -/// Custom buffer +/// Custom Function Code #[derive(Clone, Debug, Copy, PartialEq)] pub struct CustomFunctionCode { len: usize, diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index d49b9ca5..40ff421d 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -222,6 +222,13 @@ async fn test_requests_and_responses() { Indexed::new(2, 0x0506) ] ); + assert_eq!( + channel + .write_custom_function_code(params, CustomFunctionCode::new(4, [0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])) + .await + .unwrap(), + CustomFunctionCode::new(4, [0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + ) } #[test] From 116c97b18e5f46d210c85fdcb90c385c38dc7924 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 26 Jan 2024 17:13:30 +0100 Subject: [PATCH 038/106] refactor: Improve client example script for the Custom Function Code feature --- rodbus/examples/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 65843496..3914090f 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -284,7 +284,7 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { // ANCHOR: write_custom_function_code let length = 0x04 as usize; - let values = [0x2, 0x3, 0x4, 0x5]; // i.e.: Voltage Hi = 0x02 / Voltage Lo = 0x03 / Current Hi = 0x04 / Current Lo = 0x05 + let values = [0xC0, 0xDE, 0xCA, 0xFE]; // i.e.: Voltage Hi = 0xC0 / Voltage Lo = 0xDE / Current Hi = 0xCA / Current Lo = 0xFE let result = channel .write_custom_function_code( From 51cd99908f7b852e5619c91923fe7c1c51d34f9a Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 26 Jan 2024 17:14:14 +0100 Subject: [PATCH 039/106] test: Add integration test for the Custom Function Code feature --- rodbus/tests/integration_test.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index 40ff421d..00b23664 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -13,6 +13,7 @@ struct Handler { pub discrete_inputs: [bool; 10], pub holding_registers: [u16; 10], pub input_registers: [u16; 10], + pub custom_function_code: [u16; 5], } impl Handler { @@ -22,6 +23,7 @@ impl Handler { discrete_inputs: [false; 10], holding_registers: [0; 10], input_registers: [0; 10], + custom_function_code: [0; 5], } } } @@ -94,7 +96,17 @@ impl RequestHandler for Handler { } Ok(()) } -} + + fn write_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { + for (i, &value) in values.iter().enumerate() { + match self.custom_function_code.get_mut(i) { + Some(c) => *c = value, + None => return Err(ExceptionCode::IllegalDataAddress), + } + } + Ok(()) + } + } async fn test_requests_and_responses() { let handler = Handler::new().wrap(); @@ -224,11 +236,11 @@ async fn test_requests_and_responses() { ); assert_eq!( channel - .write_custom_function_code(params, CustomFunctionCode::new(4, [0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])) + .write_custom_function_code(params, CustomFunctionCode::new(0x04, [0xC0, 0xDE, 0xCA, 0xFE])) .await .unwrap(), - CustomFunctionCode::new(4, [0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) - ) + CustomFunctionCode::new(4, [0xC0, 0xDE, 0xCA, 0xFE]) + ); } #[test] From e2c80ceaa57f6010cbaeb80897e6f2376c76b97f Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 26 Jan 2024 17:15:07 +0100 Subject: [PATCH 040/106] fix: Resolve immutable server handler value for the Custom Function Code request --- rodbus/src/server/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index f4a347a6..d189e8aa 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -69,7 +69,7 @@ pub trait RequestHandler: Send + 'static { } /// Write a custom function code - fn write_custom_function_code(&self, _values: CustomFunctionCode) -> Result<(), ExceptionCode> { + fn write_custom_function_code(&mut self, _values: CustomFunctionCode) -> Result<(), ExceptionCode> { Err(ExceptionCode::IllegalFunction) } } From 15dd7e612e51e1901c2971e1eedf61a3c0163594 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 26 Jan 2024 17:20:39 +0100 Subject: [PATCH 041/106] refactor(client): code cleanup --- rodbus/examples/client.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 3914090f..f0841854 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -16,7 +16,7 @@ async fn main() -> Result<(), Box> { // ANCHOR: logging tracing_subscriber::fmt() - .with_max_level(tracing::Level::DEBUG) + .with_max_level(tracing::Level::INFO) .with_target(false) .init(); // ANCHOR_END: logging @@ -61,7 +61,7 @@ where async fn run_tcp() -> Result<(), Box> { // ANCHOR: create_tcp_channel let channel = spawn_tcp_client_task( - HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 1502), + HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 502), 1, default_retry_strategy(), DecodeLevel::default(), @@ -96,7 +96,7 @@ async fn run_rtu() -> Result<(), Box> { async fn run_tls(tls_config: TlsClientConfig) -> Result<(), Box> { // ANCHOR: create_tls_channel let channel = spawn_tls_client_task( - HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 1802), + HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 802), 1, default_retry_strategy(), tls_config, @@ -178,7 +178,7 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box Result<(), Box Result<(), Box { - // ANCHOR: read_custom_function_code - println!("read success"); - // ANCHOR_END: read_custom_function_code - } _ => println!("unknown command"), } } From 6e4f06848a6e3248048ba1a763eb7665968addb0 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 26 Jan 2024 17:23:03 +0100 Subject: [PATCH 042/106] refactor(server): code cleanup --- rodbus/examples/server.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index 102f06e3..7fbaf4e2 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -92,7 +92,7 @@ impl RequestHandler for SimpleHandler { } } - fn write_custom_function_code(&self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { + fn write_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { let mut custom_fc_args = [0_u16; 4]; // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 for (i, &value) in values.iter().enumerate() { custom_fc_args[i] = value; @@ -155,7 +155,7 @@ impl RequestHandler for SimpleHandler { async fn main() -> Result<(), Box> { // initialize logging tracing_subscriber::fmt() - .with_max_level(tracing::Level::DEBUG) + .with_max_level(tracing::Level::INFO) .with_target(false) .init(); @@ -191,7 +191,7 @@ async fn run_tcp() -> Result<(), Box> { // ANCHOR: tcp_server_create let server = rodbus::server::spawn_tcp_server_task( 1, - "127.0.0.1:1502".parse()?, + "127.0.0.1:502".parse()?, map, AddressFilter::Any, DecodeLevel::default(), @@ -230,7 +230,7 @@ async fn run_tls(tls_config: TlsServerConfig) -> Result<(), Box Date: Fri, 26 Jan 2024 17:26:16 +0100 Subject: [PATCH 043/106] refactor(parse): code cleanup --- rodbus/src/common/parse.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 272ca1e4..73e7bcc3 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -30,14 +30,8 @@ impl Parse for Indexed { impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { - let len = cursor.read_u16_be()? as usize; - - let val1 = cursor.read_u16_be()?; - let val2 = cursor.read_u16_be()?; - let val3 = cursor.read_u16_be()?; - let val4 = cursor.read_u16_be()?; - - let values = [val1, val2, val3, val4]; + let len = cursor.read_u16_be()? as usize; + let values = [cursor.read_u16_be()?, cursor.read_u16_be()?, cursor.read_u16_be()?, cursor.read_u16_be()?]; Ok(CustomFunctionCode::new(len, values)) } From 81f6188fe6dbd6da1400c228f537f7525c313a2d Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 26 Jan 2024 17:44:21 +0100 Subject: [PATCH 044/106] docs(write_custom_function_code): Add documentation for write custom function code feature --- rodbus/WCFC_README.md | 64 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 rodbus/WCFC_README.md diff --git a/rodbus/WCFC_README.md b/rodbus/WCFC_README.md new file mode 100644 index 00000000..1867d359 --- /dev/null +++ b/rodbus/WCFC_README.md @@ -0,0 +1,64 @@ +# 69 (0x45) Write Custom Function Code + +This document provides a detailed overview of the custom function code (0x45) used in the MODBUS Application Protocol. This function code is user-defined and falls within the range of 65 to 72, as specified in the MODBUS Application Protocol Specification V1.1b3 (Page 10, Section 5: Function Code Categories). + + +## Introduction +The 0x45 function code enables the implementation of user-defined logic on a remote server device. It facilitates the transmission, reception, and processing of a custom function code with a fixed-size data buffer. This buffer currently supports 4 arguments, each 2 bytes (u16) in size, allowing for the execution of custom logic remotely. + +**Note:** To increase flexibility, support for a variable-length data buffer will be included in a future update. + + +## Request Structure +| Parameter | Size | Range / Value | +|--------------------|----------|-----------------------| +| Function code | 1 Byte | 0x45 | +| Length | 2 Bytes | 0x0004 | +| Data | 8 Bytes | 0x0000 to 0xFFFF | + + +## Response Structure +| Parameter | Size | Value/Description | +|---------------|---------|----------------------| +| Function code | 1 Byte | 0x45 | +| Length | 2 Bytes | 0x0004 | +| Data | 8 Bytes | 0x0000 to 0xFFFF | + + +## Error Handling +| Parameter | Size | Description | +|----------------|---------|-----------------------------------| +| Function code | 1 Byte | Function code + 0x80 = 0xC5 (197) | +| Exception code | 1 Byte | 01 or 02 or 03 or 04 | + +### Error Codes: +- **01**: Illegal Function +- **02**: Illegal Data Address +- **03**: Illegal Data Value +- **04**: Server Device Failure + + +## Usage Example +### Request to send the custom buffer [0xC0, 0xDE, 0xCA, 0xFE]: + +| Request Field | Hex | Response Field | Hex | +|---------------------------|-----|------------------------|-----| +| Function | 45 | Function | 45 | +| Length | 04 | Byte Count | 04 | +| Arg1 | C0 | Arg1 | C0 | +| Arg2 | DE | Arg2 | DE | +| Arg3 | CA | Arg3 | CA | +| Arg4 | FE | Arg4 | FE | + + +## Modify and Test Server-Side Buffer Handling +The server currently forwards the Custom Function Code buffer to the client again without alteration. To test modifying or processing the buffer on the remote server device, edit the `write_custom_function_code()` function in `src/examples/client.rs` and `src/examples/server.rs` as needed. + + +## Troubleshooting Tips +- Ensure the server and client are using the same communication and are connected to each other. +- Check for any error codes in the response and refer to the error handling section for resolution. + + +## Additional Resources +- For more information on the MODBUS protocol and function codes, refer to the [MODBUS Application Protocol Specification V1.1b3](https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf). From 1926d686baeb7b8a35b6df8d2bdadf12d7a76054 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 29 Jan 2024 17:39:52 +0100 Subject: [PATCH 045/106] chore(send_cfc): Rename CFC README.me --- rodbus/{WCFC_README.md => SCFC_README.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename rodbus/{WCFC_README.md => SCFC_README.md} (93%) diff --git a/rodbus/WCFC_README.md b/rodbus/SCFC_README.md similarity index 93% rename from rodbus/WCFC_README.md rename to rodbus/SCFC_README.md index 1867d359..7e2e1692 100644 --- a/rodbus/WCFC_README.md +++ b/rodbus/SCFC_README.md @@ -1,4 +1,4 @@ -# 69 (0x45) Write Custom Function Code +# 69 (0x45) Send Custom Function Code This document provides a detailed overview of the custom function code (0x45) used in the MODBUS Application Protocol. This function code is user-defined and falls within the range of 65 to 72, as specified in the MODBUS Application Protocol Specification V1.1b3 (Page 10, Section 5: Function Code Categories). @@ -52,7 +52,7 @@ The 0x45 function code enables the implementation of user-defined logic on a rem ## Modify and Test Server-Side Buffer Handling -The server currently forwards the Custom Function Code buffer to the client again without alteration. To test modifying or processing the buffer on the remote server device, edit the `write_custom_function_code()` function in `src/examples/client.rs` and `src/examples/server.rs` as needed. +The server currently forwards the Custom Function Code buffer to the client again without alteration. To test modifying or processing the buffer on the remote server device, edit the `send_custom_function_code()` function in `src/examples/client.rs` and `src/examples/server.rs` as needed. ## Troubleshooting Tips From cf2a350fae1f09be169e54a9deda77804624167c Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 29 Jan 2024 17:43:34 +0100 Subject: [PATCH 046/106] fix(examples): Adjust TCP & TLS port for non-root Unix users --- rodbus/examples/client.rs | 4 ++-- rodbus/examples/server.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index f0841854..0d55f646 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -61,7 +61,7 @@ where async fn run_tcp() -> Result<(), Box> { // ANCHOR: create_tcp_channel let channel = spawn_tcp_client_task( - HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 502), + HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 10502), 1, default_retry_strategy(), DecodeLevel::default(), @@ -96,7 +96,7 @@ async fn run_rtu() -> Result<(), Box> { async fn run_tls(tls_config: TlsClientConfig) -> Result<(), Box> { // ANCHOR: create_tls_channel let channel = spawn_tls_client_task( - HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 802), + HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 10802), 1, default_retry_strategy(), tls_config, diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index 7fbaf4e2..897f6859 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -191,7 +191,7 @@ async fn run_tcp() -> Result<(), Box> { // ANCHOR: tcp_server_create let server = rodbus::server::spawn_tcp_server_task( 1, - "127.0.0.1:502".parse()?, + "127.0.0.1:10502".parse()?, map, AddressFilter::Any, DecodeLevel::default(), @@ -230,7 +230,7 @@ async fn run_tls(tls_config: TlsServerConfig) -> Result<(), Box Date: Mon, 29 Jan 2024 19:59:10 +0100 Subject: [PATCH 047/106] refactor(custom_fc): Rename WriteCustomFC to SendCustomFC --- rodbus/examples/client.rs | 8 ++++---- rodbus/examples/server.rs | 2 +- rodbus/src/client/channel.rs | 8 ++++---- rodbus/src/client/message.rs | 14 +++++++------- rodbus/src/client/requests/mod.rs | 2 +- .../{write_custom_fc.rs => send_custom_fc.rs} | 12 ++++++------ rodbus/src/common/function.rs | 10 +++++----- rodbus/src/serial/frame.rs | 4 ++-- rodbus/src/server/handler.rs | 10 +++++----- rodbus/src/server/request.rs | 16 ++++++++-------- rodbus/src/server/task.rs | 2 +- rodbus/tests/integration_test.rs | 4 ++-- 12 files changed, 46 insertions(+), 46 deletions(-) rename rodbus/src/client/requests/{write_custom_fc.rs => send_custom_fc.rs} (86%) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 0d55f646..4292b649 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -281,19 +281,19 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { - // ANCHOR: write_custom_function_code + "scfc" => { + // ANCHOR: send_custom_function_code let length = 0x04 as usize; let values = [0xC0, 0xDE, 0xCA, 0xFE]; // i.e.: Voltage Hi = 0xC0 / Voltage Lo = 0xDE / Current Hi = 0xCA / Current Lo = 0xFE let result = channel - .write_custom_function_code( + .send_custom_function_code( params, CustomFunctionCode::new(length, values) ) .await; print_write_result(result); - // ANCHOR_END: write_custom_function_code + // ANCHOR_END: send_custom_function_code } _ => println!("unknown command"), } diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index 897f6859..eace3f4f 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -92,7 +92,7 @@ impl RequestHandler for SimpleHandler { } } - fn write_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { + fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { let mut custom_fc_args = [0_u16; 4]; // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 for (i, &value) in values.iter().enumerate() { custom_fc_args[i] = value; diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index 80737db0..05726bdb 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -6,7 +6,7 @@ use crate::client::requests::read_registers::ReadRegisters; use crate::client::requests::write_multiple::{MultipleWriteRequest, WriteMultiple}; use crate::client::requests::write_single::SingleWrite; use crate::client::requests::send_buffer::SendBuffer; -use crate::client::requests::write_custom_fc::WriteCustomFunctionCode; +use crate::client::requests::send_custom_fc::CustomFCRequest; use crate::error::*; use crate::types::{AddressRange, BitIterator, Indexed, RegisterIterator, UnitId, CustomFunctionCode}; use crate::DecodeLevel; @@ -181,8 +181,8 @@ impl Channel { rx.await? } - /// Write a Custom Function Code to the server - pub async fn write_custom_function_code( + /// Send a Custom Function Code to the server + pub async fn send_custom_function_code( &mut self, param: RequestParam, request: CustomFunctionCode, @@ -190,7 +190,7 @@ impl Channel { let (tx, rx) = tokio::sync::oneshot::channel::>(); let request = wrap( param, - RequestDetails::WriteCustomFunctionCode(WriteCustomFunctionCode::new(request, Promise::channel(tx))), + RequestDetails::SendCustomFunctionCode(CustomFCRequest::new(request, Promise::channel(tx))), ); self.tx.send(request).await?; rx.await? diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index e4238b31..8ee4db5d 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -11,7 +11,7 @@ use crate::client::requests::read_registers::ReadRegisters; use crate::client::requests::write_multiple::MultipleWriteRequest; use crate::client::requests::write_single::SingleWrite; use crate::client::requests::send_buffer::SendBuffer; -use crate::client::requests::write_custom_fc::WriteCustomFunctionCode; +use crate::client::requests::send_custom_fc::CustomFCRequest; use crate::common::traits::Serialize; use crate::types::{Indexed, UnitId, CustomFunctionCode}; @@ -48,7 +48,7 @@ pub(crate) enum RequestDetails { WriteSingleRegister(SingleWrite>), WriteMultipleCoils(MultipleWriteRequest), WriteMultipleRegisters(MultipleWriteRequest), - WriteCustomFunctionCode(WriteCustomFunctionCode), + SendCustomFunctionCode(CustomFCRequest), } impl Request { @@ -134,7 +134,7 @@ impl RequestDetails { RequestDetails::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, RequestDetails::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, RequestDetails::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, - RequestDetails::WriteCustomFunctionCode(_) => FunctionCode::WriteCustomFunctionCode, + RequestDetails::SendCustomFunctionCode(_) => FunctionCode::SendCustomFunctionCode, } } @@ -149,7 +149,7 @@ impl RequestDetails { RequestDetails::WriteSingleRegister(x) => x.failure(err), RequestDetails::WriteMultipleCoils(x) => x.failure(err), RequestDetails::WriteMultipleRegisters(x) => x.failure(err), - RequestDetails::WriteCustomFunctionCode(x) => x.failure(err), + RequestDetails::SendCustomFunctionCode(x) => x.failure(err), } } @@ -171,7 +171,7 @@ impl RequestDetails { RequestDetails::WriteMultipleRegisters(x) => { x.handle_response(cursor, function, decode) }, - RequestDetails::WriteCustomFunctionCode(x) => { + RequestDetails::SendCustomFunctionCode(x) => { x.handle_response(cursor, function, decode) } } @@ -190,7 +190,7 @@ impl Serialize for RequestDetails { RequestDetails::WriteSingleRegister(x) => x.serialize(cursor), RequestDetails::WriteMultipleCoils(x) => x.serialize(cursor), RequestDetails::WriteMultipleRegisters(x) => x.serialize(cursor), - RequestDetails::WriteCustomFunctionCode(x) => x.serialize(cursor), + RequestDetails::SendCustomFunctionCode(x) => x.serialize(cursor), } } } @@ -258,7 +258,7 @@ impl std::fmt::Display for RequestDetailsDisplay<'_> { } } } - RequestDetails::WriteCustomFunctionCode(details) => { + RequestDetails::SendCustomFunctionCode(details) => { write!(f, "{}", details.request)?; } } diff --git a/rodbus/src/client/requests/mod.rs b/rodbus/src/client/requests/mod.rs index 7a2bd345..6260c59b 100644 --- a/rodbus/src/client/requests/mod.rs +++ b/rodbus/src/client/requests/mod.rs @@ -3,4 +3,4 @@ pub(crate) mod read_registers; pub(crate) mod write_multiple; pub(crate) mod write_single; pub(crate) mod send_buffer; -pub(crate) mod write_custom_fc; \ No newline at end of file +pub(crate) mod send_custom_fc; \ No newline at end of file diff --git a/rodbus/src/client/requests/write_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs similarity index 86% rename from rodbus/src/client/requests/write_custom_fc.rs rename to rodbus/src/client/requests/send_custom_fc.rs index 5bb7f9fa..3a7f1a61 100644 --- a/rodbus/src/client/requests/write_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -9,22 +9,22 @@ use crate::error::RequestError; use scursor::{ReadCursor, WriteCursor}; -pub(crate) trait WriteCustomFunctionCodeOperation: Sized + PartialEq { +pub(crate) trait CustomFCOperation: Sized + PartialEq { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError>; fn parse(cursor: &mut ReadCursor) -> Result; } -pub(crate) struct WriteCustomFunctionCode +pub(crate) struct CustomFCRequest where - T: WriteCustomFunctionCodeOperation + Display + Send + 'static, + T: CustomFCOperation + Display + Send + 'static, { pub(crate) request: T, promise: Promise, } -impl WriteCustomFunctionCode +impl CustomFCRequest where - T: WriteCustomFunctionCodeOperation + Display + Send + 'static, + T: CustomFCOperation + Display + Send + 'static, { pub(crate) fn new(request: T, promise: Promise) -> Self { Self { request, promise } @@ -66,7 +66,7 @@ where } } -impl WriteCustomFunctionCodeOperation for CustomFunctionCode { +impl CustomFCOperation for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u16_be(self.len() as u16)?; diff --git a/rodbus/src/common/function.rs b/rodbus/src/common/function.rs index f30ba928..8874fb41 100644 --- a/rodbus/src/common/function.rs +++ b/rodbus/src/common/function.rs @@ -10,7 +10,7 @@ mod constants { pub(crate) const WRITE_MULTIPLE_COILS: u8 = 15; pub(crate) const WRITE_MULTIPLE_REGISTERS: u8 = 16; pub(crate) const SEND_CUSTOM_BUFFERS: u8 = 68; - pub(crate) const WRITE_CUSTOM_FUNCTION_CODE: u8 = 69; + pub(crate) const SEND_CUSTOM_FUNCTION_CODE: u8 = 69; } #[derive(Debug, Copy, Clone, PartialEq)] @@ -25,7 +25,7 @@ pub(crate) enum FunctionCode { WriteMultipleCoils = constants::WRITE_MULTIPLE_COILS, WriteMultipleRegisters = constants::WRITE_MULTIPLE_REGISTERS, SendCustomBuffers = constants::SEND_CUSTOM_BUFFERS, - WriteCustomFunctionCode = constants::WRITE_CUSTOM_FUNCTION_CODE, + SendCustomFunctionCode = constants::SEND_CUSTOM_FUNCTION_CODE, } impl Display for FunctionCode { @@ -56,8 +56,8 @@ impl Display for FunctionCode { FunctionCode::SendCustomBuffers => { write!(f, "SEND CUSTOM BUFFER ({:#04X})", self.get_value()) } - FunctionCode::WriteCustomFunctionCode => { - write!(f, "WRITE CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + FunctionCode::SendCustomFunctionCode => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) } } } @@ -83,7 +83,7 @@ impl FunctionCode { constants::WRITE_MULTIPLE_COILS => Some(FunctionCode::WriteMultipleCoils), constants::WRITE_MULTIPLE_REGISTERS => Some(FunctionCode::WriteMultipleRegisters), constants::SEND_CUSTOM_BUFFERS => Some(FunctionCode::SendCustomBuffers), - constants::WRITE_CUSTOM_FUNCTION_CODE => Some(FunctionCode::WriteCustomFunctionCode), + constants::SEND_CUSTOM_FUNCTION_CODE => Some(FunctionCode::SendCustomFunctionCode), _ => None, } } diff --git a/rodbus/src/serial/frame.rs b/rodbus/src/serial/frame.rs index cbe54da7..07697695 100644 --- a/rodbus/src/serial/frame.rs +++ b/rodbus/src/serial/frame.rs @@ -88,7 +88,7 @@ impl RtuParser { FunctionCode::WriteSingleRegister => LengthMode::Fixed(4), FunctionCode::WriteMultipleCoils => LengthMode::Offset(5), FunctionCode::WriteMultipleRegisters => LengthMode::Offset(5), - FunctionCode::WriteCustomFunctionCode => LengthMode::Offset(1), + FunctionCode::SendCustomFunctionCode => LengthMode::Offset(1), }, ParserType::Response => match function_code { FunctionCode::ReadCoils => LengthMode::Offset(1), @@ -100,7 +100,7 @@ impl RtuParser { FunctionCode::WriteSingleRegister => LengthMode::Fixed(4), FunctionCode::WriteMultipleCoils => LengthMode::Fixed(4), FunctionCode::WriteMultipleRegisters => LengthMode::Fixed(4), - FunctionCode::WriteCustomFunctionCode => LengthMode::Offset(1), + FunctionCode::SendCustomFunctionCode => LengthMode::Offset(1), }, } } diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index d189e8aa..a660052e 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -69,7 +69,7 @@ pub trait RequestHandler: Send + 'static { } /// Write a custom function code - fn write_custom_function_code(&mut self, _values: CustomFunctionCode) -> Result<(), ExceptionCode> { + fn process_custom_function_code(&mut self, _values: CustomFunctionCode) -> Result<(), ExceptionCode> { Err(ExceptionCode::IllegalFunction) } } @@ -245,8 +245,8 @@ pub trait AuthorizationHandler: Send + Sync + 'static { Authorization::Deny } - /// Authorize a Write Custom Function Code request - fn write_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { + /// Authorize a Send Custom Function Code request + fn process_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } } @@ -328,8 +328,8 @@ impl AuthorizationHandler for ReadOnlyAuthorizationHandler { Authorization::Deny } - /// Authorize a Write Custom Function Code request - fn write_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { + /// Authorize a Send Custom Function Code request + fn process_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } } diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 0b34d196..752eb6e2 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -22,7 +22,7 @@ pub(crate) enum Request<'a> { WriteSingleRegister(Indexed), WriteMultipleCoils(WriteCoils<'a>), WriteMultipleRegisters(WriteRegisters<'a>), - WriteCustomFunctionCode(CustomFunctionCode), + SendCustomFunctionCode(CustomFunctionCode), } /// All requests that support broadcast @@ -67,7 +67,7 @@ impl<'a> Request<'a> { Request::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, Request::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, Request::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, - Request::WriteCustomFunctionCode(_) => FunctionCode::WriteCustomFunctionCode, + Request::SendCustomFunctionCode(_) => FunctionCode::SendCustomFunctionCode, } } @@ -82,7 +82,7 @@ impl<'a> Request<'a> { Request::WriteSingleRegister(x) => Some(BroadcastRequest::WriteSingleRegister(x)), Request::WriteMultipleCoils(x) => Some(BroadcastRequest::WriteMultipleCoils(x)), Request::WriteMultipleRegisters(x) => Some(BroadcastRequest::WriteMultipleRegisters(x)), - Request::WriteCustomFunctionCode(_) => None, + Request::SendCustomFunctionCode(_) => None, } } @@ -151,8 +151,8 @@ impl<'a> Request<'a> { .map(|_| items.range); write_result(function, header, writer, result, level) } - Request::WriteCustomFunctionCode(request) => { - let result = handler.write_custom_function_code(*request).map(|_| *request); + Request::SendCustomFunctionCode(request) => { + let result = handler.process_custom_function_code(*request).map(|_| *request); write_result(function, header, writer, result, level) } } @@ -220,9 +220,9 @@ impl<'a> Request<'a> { RegisterIterator::parse_all(range, cursor)?, ))) } - FunctionCode::WriteCustomFunctionCode => { + FunctionCode::SendCustomFunctionCode => { let x = - Request::WriteCustomFunctionCode(CustomFunctionCode::parse(cursor)?); + Request::SendCustomFunctionCode(CustomFunctionCode::parse(cursor)?); cursor.expect_empty()?; Ok(x) } @@ -282,7 +282,7 @@ impl std::fmt::Display for RequestDisplay<'_, '_> { RegisterIteratorDisplay::new(self.level, items.iterator) )?; } - Request::WriteCustomFunctionCode(request) => { + Request::SendCustomFunctionCode(request) => { write!(f, " {request}")?; } } diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index a96164e7..8171a329 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -265,7 +265,7 @@ impl AuthorizationType { Request::WriteMultipleRegisters(x) => { handler.write_multiple_registers(unit_id, x.range, role) } - Request::WriteCustomFunctionCode(x) => handler.write_custom_function_code(*x, role), + Request::SendCustomFunctionCode(x) => handler.process_custom_function_code(*x, role), } } diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index 00b23664..0bc423cf 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -97,7 +97,7 @@ impl RequestHandler for Handler { Ok(()) } - fn write_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { + fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { for (i, &value) in values.iter().enumerate() { match self.custom_function_code.get_mut(i) { Some(c) => *c = value, @@ -236,7 +236,7 @@ async fn test_requests_and_responses() { ); assert_eq!( channel - .write_custom_function_code(params, CustomFunctionCode::new(0x04, [0xC0, 0xDE, 0xCA, 0xFE])) + .send_custom_function_code(params, CustomFunctionCode::new(0x04, [0xC0, 0xDE, 0xCA, 0xFE])) .await .unwrap(), CustomFunctionCode::new(4, [0xC0, 0xDE, 0xCA, 0xFE]) From 70547300599bec048c5d85a4de99df79fa5ae011 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 29 Jan 2024 20:16:09 +0100 Subject: [PATCH 048/106] refactor(custom_fc): Remove SendCustomBuffer functionality (PR 2 on Iniationware/rodbus) --- rodbus/examples/client.rs | 14 ---- rodbus/examples/server.rs | 14 ---- rodbus/src/client/channel.rs | 16 ---- rodbus/src/client/message.rs | 9 --- rodbus/src/client/requests/mod.rs | 1 - rodbus/src/client/requests/send_buffer.rs | 94 ----------------------- rodbus/src/common/function.rs | 6 -- rodbus/src/serial/frame.rs | 22 +++--- rodbus/src/server/handler.rs | 13 ---- rodbus/src/server/request.rs | 16 ---- rodbus/src/server/task.rs | 1 - 11 files changed, 10 insertions(+), 196 deletions(-) delete mode 100644 rodbus/src/client/requests/send_buffer.rs diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 4292b649..a2e15b25 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -267,20 +267,6 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { - // ANCHOR: send_custom_buffer - let result = channel - .send_custom_buffer( - params, - Indexed { - index: (0x1), - value: (0xAB), - }, - ) - .await; - print_write_result(result); - // ANCHOR_END: write_multiple_registers - } "scfc" => { // ANCHOR: send_custom_function_code let length = 0x04 as usize; diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index eace3f4f..0f87c235 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -78,20 +78,6 @@ impl RequestHandler for SimpleHandler { } } - fn receive_custom_buffer(&self, value: Indexed) -> Result { - tracing::info!( - "receive custom buffer, index: {} value: {}", - value.index, - value.value - ); - - if value.value == 0xAB { - Ok(0xCD) - } else { - Err(ExceptionCode::IllegalFunction) - } - } - fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { let mut custom_fc_args = [0_u16; 4]; // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 for (i, &value) in values.iter().enumerate() { diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index 05726bdb..9a143896 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -5,7 +5,6 @@ use crate::client::requests::read_bits::ReadBits; use crate::client::requests::read_registers::ReadRegisters; use crate::client::requests::write_multiple::{MultipleWriteRequest, WriteMultiple}; use crate::client::requests::write_single::SingleWrite; -use crate::client::requests::send_buffer::SendBuffer; use crate::client::requests::send_custom_fc::CustomFCRequest; use crate::error::*; use crate::types::{AddressRange, BitIterator, Indexed, RegisterIterator, UnitId, CustomFunctionCode}; @@ -166,21 +165,6 @@ impl Channel { rx.await? } - /// Send buffer to the server - pub async fn send_custom_buffer( - &mut self, - param: RequestParam, - request: Indexed, - ) -> Result, RequestError> { - let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); - let request = wrap( - param, - RequestDetails::SendCustomBuffers(SendBuffer::new(request, Promise::channel(tx))), - ); - self.tx.send(request).await?; - rx.await? - } - /// Send a Custom Function Code to the server pub async fn send_custom_function_code( &mut self, diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 8ee4db5d..5adb335e 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -10,7 +10,6 @@ use crate::client::requests::read_bits::ReadBits; use crate::client::requests::read_registers::ReadRegisters; use crate::client::requests::write_multiple::MultipleWriteRequest; use crate::client::requests::write_single::SingleWrite; -use crate::client::requests::send_buffer::SendBuffer; use crate::client::requests::send_custom_fc::CustomFCRequest; use crate::common::traits::Serialize; use crate::types::{Indexed, UnitId, CustomFunctionCode}; @@ -43,7 +42,6 @@ pub(crate) enum RequestDetails { ReadDiscreteInputs(ReadBits), ReadHoldingRegisters(ReadRegisters), ReadInputRegisters(ReadRegisters), - SendCustomBuffers(SendBuffer>), WriteSingleCoil(SingleWrite>), WriteSingleRegister(SingleWrite>), WriteMultipleCoils(MultipleWriteRequest), @@ -129,7 +127,6 @@ impl RequestDetails { RequestDetails::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs, RequestDetails::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters, RequestDetails::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters, - RequestDetails::SendCustomBuffers(_) => FunctionCode::SendCustomBuffers, RequestDetails::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil, RequestDetails::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, RequestDetails::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, @@ -144,7 +141,6 @@ impl RequestDetails { RequestDetails::ReadDiscreteInputs(x) => x.failure(err), RequestDetails::ReadHoldingRegisters(x) => x.failure(err), RequestDetails::ReadInputRegisters(x) => x.failure(err), - RequestDetails::SendCustomBuffers(x) => x.failure(err), RequestDetails::WriteSingleCoil(x) => x.failure(err), RequestDetails::WriteSingleRegister(x) => x.failure(err), RequestDetails::WriteMultipleCoils(x) => x.failure(err), @@ -164,7 +160,6 @@ impl RequestDetails { RequestDetails::ReadDiscreteInputs(x) => x.handle_response(cursor, function, decode), RequestDetails::ReadHoldingRegisters(x) => x.handle_response(cursor, function, decode), RequestDetails::ReadInputRegisters(x) => x.handle_response(cursor, function, decode), - RequestDetails::SendCustomBuffers(x) => x.handle_response(cursor, function, decode), RequestDetails::WriteSingleCoil(x) => x.handle_response(cursor, function, decode), RequestDetails::WriteSingleRegister(x) => x.handle_response(cursor, function, decode), RequestDetails::WriteMultipleCoils(x) => x.handle_response(cursor, function, decode), @@ -185,7 +180,6 @@ impl Serialize for RequestDetails { RequestDetails::ReadDiscreteInputs(x) => x.serialize(cursor), RequestDetails::ReadHoldingRegisters(x) => x.serialize(cursor), RequestDetails::ReadInputRegisters(x) => x.serialize(cursor), - RequestDetails::SendCustomBuffers(x) => x.serialize(cursor), RequestDetails::WriteSingleCoil(x) => x.serialize(cursor), RequestDetails::WriteSingleRegister(x) => x.serialize(cursor), RequestDetails::WriteMultipleCoils(x) => x.serialize(cursor), @@ -233,9 +227,6 @@ impl std::fmt::Display for RequestDetailsDisplay<'_> { RequestDetails::ReadInputRegisters(details) => { write!(f, "{}", details.request.get())?; } - RequestDetails::SendCustomBuffers(details) => { - write!(f, "{}", details.request)?; - } RequestDetails::WriteSingleCoil(details) => { write!(f, "{}", details.request)?; } diff --git a/rodbus/src/client/requests/mod.rs b/rodbus/src/client/requests/mod.rs index 6260c59b..2e5e306a 100644 --- a/rodbus/src/client/requests/mod.rs +++ b/rodbus/src/client/requests/mod.rs @@ -2,5 +2,4 @@ pub(crate) mod read_bits; pub(crate) mod read_registers; pub(crate) mod write_multiple; pub(crate) mod write_single; -pub(crate) mod send_buffer; pub(crate) mod send_custom_fc; \ No newline at end of file diff --git a/rodbus/src/client/requests/send_buffer.rs b/rodbus/src/client/requests/send_buffer.rs deleted file mode 100644 index 2e19d731..00000000 --- a/rodbus/src/client/requests/send_buffer.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::fmt::Display; - -use crate::client::message::Promise; -use crate::common::function::FunctionCode; -use crate::decode::AppDecodeLevel; -use crate::error::AduParseError; -use crate::error::RequestError; -use crate::types::{coil_from_u16, coil_to_u16, Indexed}; - -use scursor::{ReadCursor, WriteCursor}; - -pub(crate) trait SendBufferOperation: Sized + PartialEq { - fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError>; - fn parse(cursor: &mut ReadCursor) -> Result; -} - -pub(crate) struct SendBuffer -where - T: SendBufferOperation + Display + Send + 'static, -{ - pub(crate) request: T, - promise: Promise, -} - -impl SendBuffer -where - T: SendBufferOperation + Display + Send + 'static, -{ - pub(crate) fn new(request: T, promise: Promise) -> Self { - Self { request, promise } - } - - pub(crate) fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { - self.request.serialize(cursor) - } - - pub(crate) fn failure(&mut self, err: RequestError) { - self.promise.failure(err) - } - - pub(crate) fn handle_response( - &mut self, - cursor: ReadCursor, - function: FunctionCode, - decode: AppDecodeLevel, - ) -> Result<(), RequestError> { - let response = self.parse_all(cursor)?; - - if decode.data_headers() { - tracing::info!("PDU RX - {} {}", function, response); - } else if decode.header() { - tracing::info!("PDU RX - {}", function); - } - - self.promise.success(response); - Ok(()) - } - - fn parse_all(&self, mut cursor: ReadCursor) -> Result { - let response = T::parse(&mut cursor)?; - cursor.expect_empty()?; - if self.request != response { - return Err(AduParseError::ReplyEchoMismatch.into()); - } - Ok(response) - } -} - -impl SendBufferOperation for Indexed { - fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { - cursor.write_u16_be(self.index)?; - cursor.write_u16_be(coil_to_u16(self.value))?; - Ok(()) - } - - fn parse(cursor: &mut ReadCursor) -> Result { - Ok(Indexed::new( - cursor.read_u16_be()?, - coil_from_u16(cursor.read_u16_be()?)?, - )) - } -} - -impl SendBufferOperation for Indexed { - fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { - cursor.write_u16_be(self.index)?; - cursor.write_u16_be(self.value)?; - Ok(()) - } - - fn parse(cursor: &mut ReadCursor) -> Result { - Ok(Indexed::new(cursor.read_u16_be()?, cursor.read_u16_be()?)) - } -} diff --git a/rodbus/src/common/function.rs b/rodbus/src/common/function.rs index 8874fb41..d53c45b1 100644 --- a/rodbus/src/common/function.rs +++ b/rodbus/src/common/function.rs @@ -9,7 +9,6 @@ mod constants { pub(crate) const WRITE_SINGLE_REGISTER: u8 = 6; pub(crate) const WRITE_MULTIPLE_COILS: u8 = 15; pub(crate) const WRITE_MULTIPLE_REGISTERS: u8 = 16; - pub(crate) const SEND_CUSTOM_BUFFERS: u8 = 68; pub(crate) const SEND_CUSTOM_FUNCTION_CODE: u8 = 69; } @@ -24,7 +23,6 @@ pub(crate) enum FunctionCode { WriteSingleRegister = constants::WRITE_SINGLE_REGISTER, WriteMultipleCoils = constants::WRITE_MULTIPLE_COILS, WriteMultipleRegisters = constants::WRITE_MULTIPLE_REGISTERS, - SendCustomBuffers = constants::SEND_CUSTOM_BUFFERS, SendCustomFunctionCode = constants::SEND_CUSTOM_FUNCTION_CODE, } @@ -53,9 +51,6 @@ impl Display for FunctionCode { FunctionCode::WriteMultipleRegisters => { write!(f, "WRITE MULTIPLE REGISTERS ({:#04X})", self.get_value()) } - FunctionCode::SendCustomBuffers => { - write!(f, "SEND CUSTOM BUFFER ({:#04X})", self.get_value()) - } FunctionCode::SendCustomFunctionCode => { write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) } @@ -82,7 +77,6 @@ impl FunctionCode { constants::WRITE_SINGLE_REGISTER => Some(FunctionCode::WriteSingleRegister), constants::WRITE_MULTIPLE_COILS => Some(FunctionCode::WriteMultipleCoils), constants::WRITE_MULTIPLE_REGISTERS => Some(FunctionCode::WriteMultipleRegisters), - constants::SEND_CUSTOM_BUFFERS => Some(FunctionCode::SendCustomBuffers), constants::SEND_CUSTOM_FUNCTION_CODE => Some(FunctionCode::SendCustomFunctionCode), _ => None, } diff --git a/rodbus/src/serial/frame.rs b/rodbus/src/serial/frame.rs index 07697695..0331fd58 100644 --- a/rodbus/src/serial/frame.rs +++ b/rodbus/src/serial/frame.rs @@ -83,7 +83,6 @@ impl RtuParser { FunctionCode::ReadDiscreteInputs => LengthMode::Fixed(4), FunctionCode::ReadHoldingRegisters => LengthMode::Fixed(4), FunctionCode::ReadInputRegisters => LengthMode::Fixed(4), - FunctionCode::SendCustomBuffers => LengthMode::Offset(1), FunctionCode::WriteSingleCoil => LengthMode::Fixed(4), FunctionCode::WriteSingleRegister => LengthMode::Fixed(4), FunctionCode::WriteMultipleCoils => LengthMode::Offset(5), @@ -95,7 +94,6 @@ impl RtuParser { FunctionCode::ReadDiscreteInputs => LengthMode::Offset(1), FunctionCode::ReadHoldingRegisters => LengthMode::Offset(1), FunctionCode::ReadInputRegisters => LengthMode::Offset(1), - FunctionCode::SendCustomBuffers => LengthMode::Offset(1), FunctionCode::WriteSingleCoil => LengthMode::Fixed(4), FunctionCode::WriteSingleRegister => LengthMode::Fixed(4), FunctionCode::WriteMultipleCoils => LengthMode::Fixed(4), @@ -349,19 +347,19 @@ mod tests { 0x71, 0x86, // crc ]; - const SEND_CUSTOM_BUFFER_REQUEST: &[u8] = &[ + const SEND_CUSTOM_FUNCTION_CODE_REQUEST: &[u8] = &[ UNIT_ID, // unit id 0x44, // function code - 0x02, // byte count - 0x01, 0xAB, // additonal data + 0x08, // byte count (length of data) + 0xC0, 0xDE, 0xCA, 0xFE, // data 0xC8, 0xD9, // crc ]; - const SEND_CUSTOM_BUFFER_RESPONSE: &[u8] = &[ + const SEND_CUSTOM_FUNCTION_CODE_RESPONSE: &[u8] = &[ UNIT_ID, // unit id 0x44, // function code - 0x01, // byte count - 0xCD, // return value + 0x08, // byte count + 0xC0, 0xDE, 0xCA, 0xFE, // data 0x88, 0x2C, // crc ]; const WRITE_SINGLE_COIL_REQUEST: &[u8] = &[ @@ -447,8 +445,8 @@ mod tests { READ_INPUT_REGISTERS_REQUEST, ), ( - FunctionCode::SendCustomBuffers, - SEND_CUSTOM_BUFFER_REQUEST, + FunctionCode::SendCustomFunctionCode, + SEND_CUSTOM_FUNCTION_CODE_REQUEST, ), (FunctionCode::WriteSingleCoil, WRITE_SINGLE_COIL_REQUEST), ( @@ -480,8 +478,8 @@ mod tests { READ_INPUT_REGISTERS_RESPONSE, ), ( - FunctionCode::SendCustomBuffers, - SEND_CUSTOM_BUFFER_RESPONSE, + FunctionCode::SendCustomFunctionCode, + SEND_CUSTOM_FUNCTION_CODE_RESPONSE, ), (FunctionCode::WriteSingleCoil, WRITE_SINGLE_COIL_RESPONSE), ( diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index a660052e..32af45a3 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -43,11 +43,6 @@ pub trait RequestHandler: Send + 'static { Err(ExceptionCode::IllegalFunction) } - /// Read single custom buffer or return an ExceptionCode - fn receive_custom_buffer(&self, _value: Indexed) -> Result { - Err(ExceptionCode::IllegalFunction) - } - /// Write a single coil value fn write_single_coil(&mut self, _value: Indexed) -> Result<(), ExceptionCode> { Err(ExceptionCode::IllegalFunction) @@ -187,14 +182,6 @@ pub trait AuthorizationHandler: Send + Sync + 'static { Authorization::Deny } - /// Authorize a Read Custom Buffer request - fn receive_custom_buffer( - &self, - _value: Indexed, - _role: &str, - ) -> Authorization { - Authorization::Deny - } /// Authorize a Read Holding Registers request fn read_holding_registers( &self, diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 752eb6e2..5df56f7f 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -17,7 +17,6 @@ pub(crate) enum Request<'a> { ReadDiscreteInputs(ReadBitsRange), ReadHoldingRegisters(ReadRegistersRange), ReadInputRegisters(ReadRegistersRange), - SendCustomBuffers(Indexed), WriteSingleCoil(Indexed), WriteSingleRegister(Indexed), WriteMultipleCoils(WriteCoils<'a>), @@ -62,7 +61,6 @@ impl<'a> Request<'a> { Request::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs, Request::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters, Request::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters, - Request::SendCustomBuffers(_) => FunctionCode::SendCustomBuffers, Request::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil, Request::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, Request::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, @@ -77,7 +75,6 @@ impl<'a> Request<'a> { Request::ReadDiscreteInputs(_) => None, Request::ReadHoldingRegisters(_) => None, Request::ReadInputRegisters(_) => None, - Request::SendCustomBuffers(_) => None, Request::WriteSingleCoil(x) => Some(BroadcastRequest::WriteSingleCoil(x)), Request::WriteSingleRegister(x) => Some(BroadcastRequest::WriteSingleRegister(x)), Request::WriteMultipleCoils(x) => Some(BroadcastRequest::WriteMultipleCoils(x)), @@ -129,10 +126,6 @@ impl<'a> Request<'a> { let registers = RegisterWriter::new(*range, |i| handler.read_input_register(i)); writer.format_reply(header, function, ®isters, level) } - Request::SendCustomBuffers(request) => { - let result = handler.receive_custom_buffer(*request).map(|_| *request); - write_result(function, header, writer, result, level) - } Request::WriteSingleCoil(request) => { let result = handler.write_single_coil(*request).map(|_| *request); write_result(function, header, writer, result, level) @@ -186,12 +179,6 @@ impl<'a> Request<'a> { cursor.expect_empty()?; Ok(x) } - FunctionCode::SendCustomBuffers => { - let x = - Request::SendCustomBuffers(Indexed::::parse(cursor)?); - cursor.expect_empty()?; - Ok(x) - } FunctionCode::WriteSingleCoil => { let x = Request::WriteSingleCoil(Indexed::::parse(cursor)?); cursor.expect_empty()?; @@ -259,9 +246,6 @@ impl std::fmt::Display for RequestDisplay<'_, '_> { Request::ReadInputRegisters(range) => { write!(f, " {}", range.get())?; } - Request::SendCustomBuffers(request) => { - write!(f, " {request}")?; - } Request::WriteSingleCoil(request) => { write!(f, " {request}")?; } diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index 8171a329..95d6f368 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -256,7 +256,6 @@ impl AuthorizationType { handler.read_holding_registers(unit_id, x.inner, role) } Request::ReadInputRegisters(x) => handler.read_input_registers(unit_id, x.inner, role), - Request::SendCustomBuffers(x) => handler.receive_custom_buffer(*x, role), Request::WriteSingleCoil(x) => handler.write_single_coil(unit_id, x.index, role), Request::WriteSingleRegister(x) => { handler.write_single_register(unit_id, x.index, role) From 076f66cdf3853c997ba9df150e9c7460e9888c33 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 1 Feb 2024 20:09:25 +0100 Subject: [PATCH 049/106] feat(custom examples): add custom client and server examples for CFC69 (CustomFC) tests --- rodbus/examples/custom_client.rs | 287 +++++++++++++++++++++++++++ rodbus/examples/custom_server.rs | 330 +++++++++++++++++++++++++++++++ 2 files changed, 617 insertions(+) create mode 100644 rodbus/examples/custom_client.rs create mode 100644 rodbus/examples/custom_server.rs diff --git a/rodbus/examples/custom_client.rs b/rodbus/examples/custom_client.rs new file mode 100644 index 00000000..dafdb853 --- /dev/null +++ b/rodbus/examples/custom_client.rs @@ -0,0 +1,287 @@ +use std::error::Error; +use std::net::{IpAddr, Ipv4Addr}; +use std::process::exit; +use std::time::Duration; + +use tokio_stream::StreamExt; +use tokio_util::codec::{FramedRead, LinesCodec}; + +use rodbus::client::*; +use rodbus::*; + +// ANCHOR: runtime_init +#[tokio::main(flavor = "multi_thread")] +async fn main() -> Result<(), Box> { + // ANCHOR_END: runtime_init + + // ANCHOR: logging + tracing_subscriber::fmt() + .with_max_level(tracing::Level::INFO) + .with_target(false) + .init(); + // ANCHOR_END: logging + + let args: Vec = std::env::args().collect(); + let transport: &str = match &args[..] { + [_, x] => x, + _ => { + eprintln!("please specify a transport:"); + eprintln!("usage: outstation (tcp, rtu, tls-ca, tls-self-signed)"); + exit(-1); + } + }; + match transport { + "tcp" => run_tcp().await, + #[cfg(feature = "serial")] + "rtu" => run_rtu().await, + #[cfg(feature = "tls")] + "tls-ca" => run_tls(get_ca_chain_config()?).await, + #[cfg(feature = "tls")] + "tls-self-signed" => run_tls(get_self_signed_config()?).await, + _ => { + eprintln!( + "unknown transport '{transport}', options are (tcp, rtu, tls-ca, tls-self-signed)" + ); + exit(-1); + } + } +} + +struct LoggingListener; +impl Listener for LoggingListener +where + T: std::fmt::Debug, +{ + fn update(&mut self, value: T) -> MaybeAsync<()> { + tracing::info!("Channel Listener: {:?}", value); + MaybeAsync::ready(()) + } +} + +async fn run_tcp() -> Result<(), Box> { + // ANCHOR: create_tcp_channel + let channel = spawn_tcp_client_task( + HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 11502), + 1, + default_retry_strategy(), + DecodeLevel::default(), + Some(Box::new(LoggingListener)), + ); + // ANCHOR_END: create_tcp_channel + + run_channel(channel).await +} + +#[cfg(feature = "serial")] +async fn run_rtu() -> Result<(), Box> { + // ANCHOR: create_rtu_channel + let channel = spawn_rtu_client_task( + "/dev/ttySIM0", // path + rodbus::SerialSettings::default(), // serial settings + 1, // max queued requests + default_retry_strategy(), // retry delays + DecodeLevel::new( + AppDecodeLevel::DataValues, + FrameDecodeLevel::Payload, + PhysDecodeLevel::Nothing, + ), + Some(Box::new(LoggingListener)), + ); + // ANCHOR_END: create_rtu_channel + + run_channel(channel).await +} + +#[cfg(feature = "tls")] +async fn run_tls(tls_config: TlsClientConfig) -> Result<(), Box> { + // ANCHOR: create_tls_channel + let channel = spawn_tls_client_task( + HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 11802), + 1, + default_retry_strategy(), + tls_config, + DecodeLevel::new( + AppDecodeLevel::DataValues, + FrameDecodeLevel::Nothing, + PhysDecodeLevel::Nothing, + ), + Some(Box::new(LoggingListener)), + ); + // ANCHOR_END: create_tls_channel + + run_channel(channel).await +} + +#[cfg(feature = "tls")] +fn get_self_signed_config() -> Result> { + use std::path::Path; + // ANCHOR: tls_self_signed_config + let tls_config = TlsClientConfig::self_signed( + Path::new("./certs/self_signed/entity2_cert.pem"), + Path::new("./certs/self_signed/entity1_cert.pem"), + Path::new("./certs/self_signed/entity1_key.pem"), + None, // no password + MinTlsVersion::V1_2, + )?; + // ANCHOR_END: tls_self_signed_config + + Ok(tls_config) +} + +#[cfg(feature = "tls")] +fn get_ca_chain_config() -> Result> { + use std::path::Path; + // ANCHOR: tls_ca_chain_config + let tls_config = TlsClientConfig::full_pki( + Some("test.com".to_string()), + Path::new("./certs/ca_chain/ca_cert.pem"), + Path::new("./certs/ca_chain/client_cert.pem"), + Path::new("./certs/ca_chain/client_key.pem"), + None, // no password + MinTlsVersion::V1_2, + )?; + // ANCHOR_END: tls_ca_chain_config + + Ok(tls_config) +} + +fn print_read_result(result: Result>, RequestError>) +where + T: std::fmt::Display, +{ + match result { + Ok(coils) => { + for bit in coils { + println!("index: {} value: {}", bit.index, bit.value); + } + } + Err(rodbus::RequestError::Exception(exception)) => { + println!("Modbus exception: {exception}"); + } + Err(err) => println!("read error: {err}"), + } +} + +fn print_write_result(result: Result) { + match result { + Ok(_) => { + println!("write successful"); + } + Err(rodbus::RequestError::Exception(exception)) => { + println!("Modbus exception: {exception}"); + } + Err(err) => println!("writer error: {err}"), + } +} + +async fn run_channel(mut channel: Channel) -> Result<(), Box> { + channel.enable().await?; + + // ANCHOR: request_param + let params = RequestParam::new(UnitId::new(1), Duration::from_secs(1)); + // ANCHOR_END: request_param + + let mut reader = FramedRead::new(tokio::io::stdin(), LinesCodec::new()); + loop { + match reader.next().await.unwrap()?.as_str() { + "x" => return Ok(()), + "ec" => { + // enable channel + channel.enable().await?; + } + "dc" => { + // disable channel + channel.disable().await?; + } + "ed" => { + // enable decoding + channel + .set_decode_level(DecodeLevel::new( + AppDecodeLevel::DataValues, + FrameDecodeLevel::Header, + PhysDecodeLevel::Length, + )) + .await?; + } + "dd" => { + // disable decoded + channel.set_decode_level(DecodeLevel::nothing()).await?; + } + "rc" => { + // ANCHOR: read_coils + let result = channel + .read_coils(params, AddressRange::try_from(0, 5).unwrap()) + .await; + // ANCHOR_END: read_coils + print_read_result(result); + } + "rdi" => { + let result = channel + .read_discrete_inputs(params, AddressRange::try_from(0, 5).unwrap()) + .await; + print_read_result(result); + } + "rhr" => { + let result = channel + .read_holding_registers(params, AddressRange::try_from(0, 5).unwrap()) + .await; + print_read_result(result); + } + "rir" => { + let result = channel + .read_input_registers(params, AddressRange::try_from(0, 5).unwrap()) + .await; + print_read_result(result); + } + "wsc" => { + // ANCHOR: write_single_coil + let result = channel + .write_single_coil(params, Indexed::new(0, true)) + .await; + // ANCHOR_END: write_single_coil + print_write_result(result); + } + "wsr" => { + let result = channel + .write_single_register(params, Indexed::new(0, 76)) + .await; + print_write_result(result); + } + "wmc" => { + let result = channel + .write_multiple_coils( + params, + WriteMultiple::from(0, vec![true, false]).unwrap(), + ) + .await; + print_write_result(result); + } + "wmr" => { + // ANCHOR: write_multiple_registers + let result = channel + .write_multiple_registers( + params, + WriteMultiple::from(0, vec![0xCA, 0xFE]).unwrap(), + ) + .await; + print_write_result(result); + // ANCHOR_END: write_multiple_registers + } + "scfc" => { + // ANCHOR: send_custom_function_code + let length = 0x04 as usize; + let values = [0xC0, 0xDE, 0xCA, 0xFE]; // i.e.: Voltage Hi = 0xC0 / Voltage Lo = 0xDE / Current Hi = 0xCA / Current Lo = 0xFE + + let result = channel + .send_custom_function_code( + params, + CustomFunctionCode::new(length, values) + ) + .await; + print_write_result(result); + // ANCHOR_END: send_custom_function_code + } + _ => println!("unknown command"), + } + } +} diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs new file mode 100644 index 00000000..368db9a2 --- /dev/null +++ b/rodbus/examples/custom_server.rs @@ -0,0 +1,330 @@ +use std::process::exit; + +use tokio_stream::StreamExt; +use tokio_util::codec::{FramedRead, LinesCodec}; + +use rodbus::server::*; +use rodbus::*; + +struct SimpleHandler { + coils: Vec, + discrete_inputs: Vec, + holding_registers: Vec, + input_registers: Vec, +} + +impl SimpleHandler { + fn new( + coils: Vec, + discrete_inputs: Vec, + holding_registers: Vec, + input_registers: Vec, + ) -> Self { + Self { + coils, + discrete_inputs, + holding_registers, + input_registers, + } + } + + fn coils_as_mut(&mut self) -> &mut [bool] { + self.coils.as_mut_slice() + } + + fn discrete_inputs_as_mut(&mut self) -> &mut [bool] { + self.discrete_inputs.as_mut_slice() + } + + fn holding_registers_as_mut(&mut self) -> &mut [u16] { + self.holding_registers.as_mut_slice() + } + + fn input_registers_as_mut(&mut self) -> &mut [u16] { + self.input_registers.as_mut_slice() + } +} + +// ANCHOR: request_handler +impl RequestHandler for SimpleHandler { + fn read_coil(&self, address: u16) -> Result { + self.coils.get(address as usize).to_result() + } + + fn read_discrete_input(&self, address: u16) -> Result { + self.discrete_inputs.get(address as usize).to_result() + } + + fn read_holding_register(&self, address: u16) -> Result { + self.holding_registers.get(address as usize).to_result() + } + + fn read_input_register(&self, address: u16) -> Result { + self.input_registers.get(address as usize).to_result() + } + + fn write_single_coil(&mut self, value: Indexed) -> Result<(), ExceptionCode> { + tracing::info!( + "write single coil, index: {} value: {}", + value.index, + value.value + ); + + if let Some(coil) = self.coils.get_mut(value.index as usize) { + *coil = value.value; + Ok(()) + } else { + Err(ExceptionCode::IllegalDataAddress) + } + } + + fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { + let mut custom_fc_args = [0_u16; 4]; // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 + for (i, &value) in values.iter().enumerate() { + custom_fc_args[i] = value; + } + tracing::info!("processing custom function code arguments: {:?}", custom_fc_args); + + Ok(()) + } + + fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { + tracing::info!( + "write single register, index: {} value: {}", + value.index, + value.value + ); + + if let Some(reg) = self.holding_registers.get_mut(value.index as usize) { + *reg = value.value; + Ok(()) + } else { + Err(ExceptionCode::IllegalDataAddress) + } + } + + fn write_multiple_coils(&mut self, values: WriteCoils) -> Result<(), ExceptionCode> { + tracing::info!("write multiple coils {:?}", values.range); + + let mut result = Ok(()); + + for value in values.iterator { + if let Some(coil) = self.coils.get_mut(value.index as usize) { + *coil = value.value; + } else { + result = Err(ExceptionCode::IllegalDataAddress) + } + } + + result + } + + fn write_multiple_registers(&mut self, values: WriteRegisters) -> Result<(), ExceptionCode> { + tracing::info!("write multiple registers {:?}", values.range); + + let mut result = Ok(()); + + for value in values.iterator { + if let Some(reg) = self.holding_registers.get_mut(value.index as usize) { + *reg = value.value; + } else { + result = Err(ExceptionCode::IllegalDataAddress) + } + } + + result + } +} +// ANCHOR_END: request_handler + +#[tokio::main(flavor = "multi_thread")] +async fn main() -> Result<(), Box> { + // initialize logging + tracing_subscriber::fmt() + .with_max_level(tracing::Level::INFO) + .with_target(false) + .init(); + + let args: Vec = std::env::args().collect(); + let transport: &str = match &args[..] { + [_, x] => x, + _ => { + eprintln!("please specify a transport:"); + eprintln!("usage: outstation (tcp, rtu, tls-ca, tls-self-signed)"); + exit(-1); + } + }; + match transport { + "tcp" => run_tcp().await, + #[cfg(feature = "serial")] + "rtu" => run_rtu().await, + #[cfg(feature = "tls")] + "tls-ca" => run_tls(get_ca_chain_config()?).await, + #[cfg(feature = "tls")] + "tls-self-signed" => run_tls(get_self_signed_config()?).await, + _ => { + eprintln!( + "unknown transport '{transport}', options are (tcp, rtu, tls-ca, tls-self-signed)" + ); + exit(-1); + } + } +} + +async fn run_tcp() -> Result<(), Box> { + let (handler, map) = create_handler(); + + // ANCHOR: tcp_server_create + let server = rodbus::server::spawn_tcp_server_task( + 1, + "127.0.0.1:11502".parse()?, + map, + AddressFilter::Any, + DecodeLevel::default(), + ) + .await?; + // ANCHOR_END: tcp_server_create + + run_server(server, handler).await +} + +#[cfg(feature = "serial")] +async fn run_rtu() -> Result<(), Box> { + let (handler, map) = create_handler(); + + // ANCHOR: rtu_server_create + let server = rodbus::server::spawn_rtu_server_task( + "/dev/ttySIM1", + rodbus::SerialSettings::default(), + default_retry_strategy(), + map, + DecodeLevel::new( + AppDecodeLevel::DataValues, + FrameDecodeLevel::Payload, + PhysDecodeLevel::Data, + ), + )?; + // ANCHOR_END: rtu_server_create + + run_server(server, handler).await +} + +#[cfg(feature = "tls")] +async fn run_tls(tls_config: TlsServerConfig) -> Result<(), Box> { + let (handler, map) = create_handler(); + + // ANCHOR: tls_server_create + let server = rodbus::server::spawn_tls_server_task_with_authz( + 1, + "127.0.0.1:11802".parse()?, + map, + ReadOnlyAuthorizationHandler::create(), + tls_config, + AddressFilter::Any, + DecodeLevel::default(), + ) + .await?; + // ANCHOR_END: tls_server_create + + run_server(server, handler).await +} + +fn create_handler() -> ( + ServerHandlerType, + ServerHandlerMap, +) { + // ANCHOR: handler_map_create + let handler = + SimpleHandler::new(vec![false; 10], vec![false; 10], vec![0; 10], vec![0; 10]).wrap(); + + // map unit ids to a handler for processing requests + let map = ServerHandlerMap::single(UnitId::new(1), handler.clone()); + // ANCHOR_END: handler_map_create + + (handler, map) +} + +#[cfg(feature = "tls")] +fn get_self_signed_config() -> Result> { + use std::path::Path; + // ANCHOR: tls_self_signed_config + let tls_config = TlsServerConfig::new( + Path::new("./certs/self_signed/entity1_cert.pem"), + Path::new("./certs/self_signed/entity2_cert.pem"), + Path::new("./certs/self_signed/entity2_key.pem"), + None, // no password + MinTlsVersion::V1_2, + CertificateMode::SelfSigned, + )?; + // ANCHOR_END: tls_self_signed_config + + Ok(tls_config) +} + +#[cfg(feature = "tls")] +fn get_ca_chain_config() -> Result> { + use std::path::Path; + // ANCHOR: tls_ca_chain_config + let tls_config = TlsServerConfig::new( + Path::new("./certs/ca_chain/ca_cert.pem"), + Path::new("./certs/ca_chain/server_cert.pem"), + Path::new("./certs/ca_chain/server_key.pem"), + None, // no password + MinTlsVersion::V1_2, + CertificateMode::AuthorityBased, + )?; + // ANCHOR_END: tls_ca_chain_config + + Ok(tls_config) +} + +async fn run_server( + mut server: ServerHandle, + handler: ServerHandlerType, +) -> Result<(), Box> { + let mut reader = FramedRead::new(tokio::io::stdin(), LinesCodec::new()); + loop { + match reader.next().await.unwrap()?.as_str() { + "x" => return Ok(()), + "ed" => { + // enable decoding + server + .set_decode_level(DecodeLevel::new( + AppDecodeLevel::DataValues, + FrameDecodeLevel::Header, + PhysDecodeLevel::Length, + )) + .await?; + } + "dd" => { + // disable decoding + server.set_decode_level(DecodeLevel::nothing()).await?; + } + "uc" => { + let mut handler = handler.lock().unwrap(); + for coil in handler.coils_as_mut() { + *coil = !*coil; + } + } + "udi" => { + let mut handler = handler.lock().unwrap(); + for discrete_input in handler.discrete_inputs_as_mut() { + *discrete_input = !*discrete_input; + } + } + "uhr" => { + let mut handler = handler.lock().unwrap(); + for holding_register in handler.holding_registers_as_mut() { + *holding_register += 1; + } + } + "uir" => { + let mut handler = handler.lock().unwrap(); + for input_register in handler.input_registers_as_mut() { + *input_register += 1; + } + } + _ => println!("unknown command"), + } + } +} From 0e0221910aaf1760982680ab9037371b331453c9 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 1 Feb 2024 20:59:08 +0100 Subject: [PATCH 050/106] feat(cli): add CLI tool to call CFC69 (CustomFunctionCode) via the CLI using changing CFC data --- rodbus/examples/cli.rs | 210 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 rodbus/examples/cli.rs diff --git a/rodbus/examples/cli.rs b/rodbus/examples/cli.rs new file mode 100644 index 00000000..68d03531 --- /dev/null +++ b/rodbus/examples/cli.rs @@ -0,0 +1,210 @@ +use std::error::Error; +use std::net::{IpAddr, Ipv4Addr}; +use std::process::exit; +use std::time::Duration; + +use rodbus::client::*; +use rodbus::*; +use rx509::der::parse_all; + +// ANCHOR: runtime_init +#[tokio::main(flavor = "multi_thread")] +async fn main() -> Result<(), Box> { + // ANCHOR_END: runtime_init + + // ANCHOR: logging + tracing_subscriber::fmt() + .with_max_level(tracing::Level::INFO) + .with_target(false) + .init(); + // ANCHOR_END: logging + + let args: Vec = std::env::args().collect(); + + if args.len() < 6 { + eprintln!("Incorrect number of arguments provided."); + eprintln!("Usage: outstation "); + exit(-1); + } + + let transport = &args[1]; + let data_args = &args[2..]; + + let values = data_args.iter().map(|x| x.parse::().unwrap()).collect::>(); + + let custom_fc = CustomFunctionCode::new( + values[0] as usize, [values[1], values[2], values[3], values[4]] + ); + + match transport.as_str() { + "tcp" => run_tcp(custom_fc).await?, + #[cfg(feature = "serial")] + "rtu" => run_rtu(custom_fc).await?, + #[cfg(feature = "tls")] + "tls-ca" => run_tls(get_ca_chain_config()?, custom_fc).await?, + #[cfg(feature = "tls")] + "tls-self-signed" => run_tls(get_self_signed_config()?, custom_fc).await?, + _ => { + eprintln!( + "unknown transport '{transport}', options are (tcp, rtu, tls-ca, tls-self-signed)" + ); + exit(-1); + } + } + + Ok(()) +} + +struct LoggingListener; +impl Listener for LoggingListener +where + T: std::fmt::Debug, +{ + fn update(&mut self, value: T) -> MaybeAsync<()> { + tracing::info!("Channel Listener: {:?}", value); + MaybeAsync::ready(()) + } +} + +async fn run_tcp(custom_fc: CustomFunctionCode) -> Result<(), Box> { + // ANCHOR: create_tcp_channel + let channel = spawn_tcp_client_task( + HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 11502), + 1, + default_retry_strategy(), + DecodeLevel::default(), + Some(Box::new(LoggingListener)), + ); + // ANCHOR_END: create_tcp_channel + + run_channel(channel, custom_fc).await +} + +#[cfg(feature = "serial")] +async fn run_rtu(custom_fc: CustomFunctionCode) -> Result<(), Box> { + // ANCHOR: create_rtu_channel + let channel = spawn_rtu_client_task( + "/dev/ttySIM0", // path + rodbus::SerialSettings::default(), // serial settings + 1, // max queued requests + default_retry_strategy(), // retry delays + DecodeLevel::new( + AppDecodeLevel::DataValues, + FrameDecodeLevel::Payload, + PhysDecodeLevel::Nothing, + ), + Some(Box::new(LoggingListener)), + ); + // ANCHOR_END: create_rtu_channel + + run_channel(channel, custom_fc).await +} + +#[cfg(feature = "tls")] +async fn run_tls(tls_config: TlsClientConfig, custom_fc: CustomFunctionCode) -> Result<(), Box> { + // ANCHOR: create_tls_channel + let channel = spawn_tls_client_task( + HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 11802), + 1, + default_retry_strategy(), + tls_config, + DecodeLevel::new( + AppDecodeLevel::DataValues, + FrameDecodeLevel::Nothing, + PhysDecodeLevel::Nothing, + ), + Some(Box::new(LoggingListener)), + ); + // ANCHOR_END: create_tls_channel + + run_channel(channel, custom_fc).await +} + +#[cfg(feature = "tls")] +fn get_self_signed_config() -> Result> { + use std::path::Path; + // ANCHOR: tls_self_signed_config + let tls_config = TlsClientConfig::self_signed( + Path::new("./certs/self_signed/entity2_cert.pem"), + Path::new("./certs/self_signed/entity1_cert.pem"), + Path::new("./certs/self_signed/entity1_key.pem"), + None, // no password + MinTlsVersion::V1_2, + )?; + // ANCHOR_END: tls_self_signed_config + + Ok(tls_config) +} + +#[cfg(feature = "tls")] +fn get_ca_chain_config() -> Result> { + use std::path::Path; + // ANCHOR: tls_ca_chain_config + let tls_config = TlsClientConfig::full_pki( + Some("test.com".to_string()), + Path::new("./certs/ca_chain/ca_cert.pem"), + Path::new("./certs/ca_chain/client_cert.pem"), + Path::new("./certs/ca_chain/client_key.pem"), + None, // no password + MinTlsVersion::V1_2, + )?; + // ANCHOR_END: tls_ca_chain_config + + Ok(tls_config) +} + +fn print_read_result(result: Result>, RequestError>) +where + T: std::fmt::Display, +{ + match result { + Ok(coils) => { + for bit in coils { + println!("index: {} value: {}", bit.index, bit.value); + } + } + Err(rodbus::RequestError::Exception(exception)) => { + println!("Modbus exception: {exception}"); + } + Err(err) => println!("read error: {err}"), + } +} + +fn print_write_result(result: Result) { + match result { + Ok(_) => { + println!("write successful"); + } + Err(rodbus::RequestError::Exception(exception)) => { + println!("Modbus exception: {exception}"); + } + Err(err) => println!("writer error: {err}"), + } +} + +async fn run_channel(mut channel: Channel, custom_fc: CustomFunctionCode) -> Result<(), Box> { + channel.enable().await?; + + // ANCHOR: request_param + let params = RequestParam::new(UnitId::new(1), Duration::from_secs(1)); + // ANCHOR_END: request_param + + // enable decoding + channel.set_decode_level(DecodeLevel::new( + AppDecodeLevel::DataValues, + FrameDecodeLevel::Header, + PhysDecodeLevel::Length, + )) + .await?; + + let result = channel + .send_custom_function_code( + params, + custom_fc, + ) + .await; + print_write_result(result); + // ANCHOR_END: send_custom_function_code + + Ok(()) +} From 1f9a046f02a3233f63a982a2f43246a25bc2a3eb Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 12 Feb 2024 18:59:28 +0100 Subject: [PATCH 051/106] refactor: move cli functionality --- rodbus/examples/cli.rs | 210 ----------------------------------------- 1 file changed, 210 deletions(-) delete mode 100644 rodbus/examples/cli.rs diff --git a/rodbus/examples/cli.rs b/rodbus/examples/cli.rs deleted file mode 100644 index 68d03531..00000000 --- a/rodbus/examples/cli.rs +++ /dev/null @@ -1,210 +0,0 @@ -use std::error::Error; -use std::net::{IpAddr, Ipv4Addr}; -use std::process::exit; -use std::time::Duration; - -use rodbus::client::*; -use rodbus::*; -use rx509::der::parse_all; - -// ANCHOR: runtime_init -#[tokio::main(flavor = "multi_thread")] -async fn main() -> Result<(), Box> { - // ANCHOR_END: runtime_init - - // ANCHOR: logging - tracing_subscriber::fmt() - .with_max_level(tracing::Level::INFO) - .with_target(false) - .init(); - // ANCHOR_END: logging - - let args: Vec = std::env::args().collect(); - - if args.len() < 6 { - eprintln!("Incorrect number of arguments provided."); - eprintln!("Usage: outstation "); - exit(-1); - } - - let transport = &args[1]; - let data_args = &args[2..]; - - let values = data_args.iter().map(|x| x.parse::().unwrap()).collect::>(); - - let custom_fc = CustomFunctionCode::new( - values[0] as usize, [values[1], values[2], values[3], values[4]] - ); - - match transport.as_str() { - "tcp" => run_tcp(custom_fc).await?, - #[cfg(feature = "serial")] - "rtu" => run_rtu(custom_fc).await?, - #[cfg(feature = "tls")] - "tls-ca" => run_tls(get_ca_chain_config()?, custom_fc).await?, - #[cfg(feature = "tls")] - "tls-self-signed" => run_tls(get_self_signed_config()?, custom_fc).await?, - _ => { - eprintln!( - "unknown transport '{transport}', options are (tcp, rtu, tls-ca, tls-self-signed)" - ); - exit(-1); - } - } - - Ok(()) -} - -struct LoggingListener; -impl Listener for LoggingListener -where - T: std::fmt::Debug, -{ - fn update(&mut self, value: T) -> MaybeAsync<()> { - tracing::info!("Channel Listener: {:?}", value); - MaybeAsync::ready(()) - } -} - -async fn run_tcp(custom_fc: CustomFunctionCode) -> Result<(), Box> { - // ANCHOR: create_tcp_channel - let channel = spawn_tcp_client_task( - HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 11502), - 1, - default_retry_strategy(), - DecodeLevel::default(), - Some(Box::new(LoggingListener)), - ); - // ANCHOR_END: create_tcp_channel - - run_channel(channel, custom_fc).await -} - -#[cfg(feature = "serial")] -async fn run_rtu(custom_fc: CustomFunctionCode) -> Result<(), Box> { - // ANCHOR: create_rtu_channel - let channel = spawn_rtu_client_task( - "/dev/ttySIM0", // path - rodbus::SerialSettings::default(), // serial settings - 1, // max queued requests - default_retry_strategy(), // retry delays - DecodeLevel::new( - AppDecodeLevel::DataValues, - FrameDecodeLevel::Payload, - PhysDecodeLevel::Nothing, - ), - Some(Box::new(LoggingListener)), - ); - // ANCHOR_END: create_rtu_channel - - run_channel(channel, custom_fc).await -} - -#[cfg(feature = "tls")] -async fn run_tls(tls_config: TlsClientConfig, custom_fc: CustomFunctionCode) -> Result<(), Box> { - // ANCHOR: create_tls_channel - let channel = spawn_tls_client_task( - HostAddr::ip(IpAddr::V4(Ipv4Addr::LOCALHOST), 11802), - 1, - default_retry_strategy(), - tls_config, - DecodeLevel::new( - AppDecodeLevel::DataValues, - FrameDecodeLevel::Nothing, - PhysDecodeLevel::Nothing, - ), - Some(Box::new(LoggingListener)), - ); - // ANCHOR_END: create_tls_channel - - run_channel(channel, custom_fc).await -} - -#[cfg(feature = "tls")] -fn get_self_signed_config() -> Result> { - use std::path::Path; - // ANCHOR: tls_self_signed_config - let tls_config = TlsClientConfig::self_signed( - Path::new("./certs/self_signed/entity2_cert.pem"), - Path::new("./certs/self_signed/entity1_cert.pem"), - Path::new("./certs/self_signed/entity1_key.pem"), - None, // no password - MinTlsVersion::V1_2, - )?; - // ANCHOR_END: tls_self_signed_config - - Ok(tls_config) -} - -#[cfg(feature = "tls")] -fn get_ca_chain_config() -> Result> { - use std::path::Path; - // ANCHOR: tls_ca_chain_config - let tls_config = TlsClientConfig::full_pki( - Some("test.com".to_string()), - Path::new("./certs/ca_chain/ca_cert.pem"), - Path::new("./certs/ca_chain/client_cert.pem"), - Path::new("./certs/ca_chain/client_key.pem"), - None, // no password - MinTlsVersion::V1_2, - )?; - // ANCHOR_END: tls_ca_chain_config - - Ok(tls_config) -} - -fn print_read_result(result: Result>, RequestError>) -where - T: std::fmt::Display, -{ - match result { - Ok(coils) => { - for bit in coils { - println!("index: {} value: {}", bit.index, bit.value); - } - } - Err(rodbus::RequestError::Exception(exception)) => { - println!("Modbus exception: {exception}"); - } - Err(err) => println!("read error: {err}"), - } -} - -fn print_write_result(result: Result) { - match result { - Ok(_) => { - println!("write successful"); - } - Err(rodbus::RequestError::Exception(exception)) => { - println!("Modbus exception: {exception}"); - } - Err(err) => println!("writer error: {err}"), - } -} - -async fn run_channel(mut channel: Channel, custom_fc: CustomFunctionCode) -> Result<(), Box> { - channel.enable().await?; - - // ANCHOR: request_param - let params = RequestParam::new(UnitId::new(1), Duration::from_secs(1)); - // ANCHOR_END: request_param - - // enable decoding - channel.set_decode_level(DecodeLevel::new( - AppDecodeLevel::DataValues, - FrameDecodeLevel::Header, - PhysDecodeLevel::Length, - )) - .await?; - - let result = channel - .send_custom_function_code( - params, - custom_fc, - ) - .await; - print_write_result(result); - // ANCHOR_END: send_custom_function_code - - Ok(()) -} From 8d8441f71b9a30e3581027a2adea9daf7b30f063 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 14 Feb 2024 15:30:47 +0100 Subject: [PATCH 052/106] feat(custom client example): Extend the custom client example for the CFC69 request --- rodbus/examples/custom_client.rs | 115 ++++++++----------------------- 1 file changed, 28 insertions(+), 87 deletions(-) diff --git a/rodbus/examples/custom_client.rs b/rodbus/examples/custom_client.rs index dafdb853..e7da304c 100644 --- a/rodbus/examples/custom_client.rs +++ b/rodbus/examples/custom_client.rs @@ -150,9 +150,9 @@ where T: std::fmt::Display, { match result { - Ok(coils) => { - for bit in coils { - println!("index: {} value: {}", bit.index, bit.value); + Ok(registers) => { + for register in registers { + println!("index: {} value: {}", register.index, register.value); } } Err(rodbus::RequestError::Exception(exception)) => { @@ -182,19 +182,18 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box return Ok(()), - "ec" => { - // enable channel + while let Some(line) = reader.next().await { + let line = line?; // This handles the Some(Err(e)) case by returning Err(e) + let parts = line.split_whitespace().collect::>(); + match parts.as_slice() { + ["x"] => return Ok(()), + ["ec"] => { channel.enable().await?; } - "dc" => { - // disable channel + ["dc"] => { channel.disable().await?; } - "ed" => { - // enable decoding + ["ed"] => { channel .set_decode_level(DecodeLevel::new( AppDecodeLevel::DataValues, @@ -203,85 +202,27 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { - // disable decoded + ["dd"] => { channel.set_decode_level(DecodeLevel::nothing()).await?; } - "rc" => { - // ANCHOR: read_coils - let result = channel - .read_coils(params, AddressRange::try_from(0, 5).unwrap()) - .await; - // ANCHOR_END: read_coils - print_read_result(result); - } - "rdi" => { - let result = channel - .read_discrete_inputs(params, AddressRange::try_from(0, 5).unwrap()) - .await; - print_read_result(result); - } - "rhr" => { - let result = channel - .read_holding_registers(params, AddressRange::try_from(0, 5).unwrap()) - .await; - print_read_result(result); - } - "rir" => { - let result = channel - .read_input_registers(params, AddressRange::try_from(0, 5).unwrap()) - .await; - print_read_result(result); - } - "wsc" => { - // ANCHOR: write_single_coil - let result = channel - .write_single_coil(params, Indexed::new(0, true)) - .await; - // ANCHOR_END: write_single_coil - print_write_result(result); - } - "wsr" => { - let result = channel - .write_single_register(params, Indexed::new(0, 76)) - .await; - print_write_result(result); - } - "wmc" => { - let result = channel - .write_multiple_coils( - params, - WriteMultiple::from(0, vec![true, false]).unwrap(), - ) - .await; - print_write_result(result); - } - "wmr" => { - // ANCHOR: write_multiple_registers - let result = channel - .write_multiple_registers( - params, - WriteMultiple::from(0, vec![0xCA, 0xFE]).unwrap(), - ) - .await; - print_write_result(result); - // ANCHOR_END: write_multiple_registers - } - "scfc" => { - // ANCHOR: send_custom_function_code - let length = 0x04 as usize; - let values = [0xC0, 0xDE, 0xCA, 0xFE]; // i.e.: Voltage Hi = 0xC0 / Voltage Lo = 0xDE / Current Hi = 0xCA / Current Lo = 0xFE - - let result = channel - .send_custom_function_code( - params, - CustomFunctionCode::new(length, values) - ) - .await; - print_write_result(result); - // ANCHOR_END: send_custom_function_code + ["scfc", length_str, values @ ..] => { + let length = length_str.parse::().unwrap_or(0); + let values: Vec = values.iter().filter_map(|&v| u8::from_str_radix(v.trim_start_matches("0x"), 16).ok()).collect(); + + if values.len() == length { + let result = channel + .send_custom_function_code( + params, + CustomFunctionCode::new(length, &values) + ) + .await; + print_write_result(result); + } else { + println!("Error: Length does not match the number of provided values"); + } } _ => println!("unknown command"), } } + Ok(()) } From 4dc5972783af51c38280deeadd94535c491ba917 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 14 Feb 2024 17:09:18 +0100 Subject: [PATCH 053/106] refactor(send_custom_fc): change CFC69 CustomFC implementation to support variable-length data --- rodbus/src/client/channel.rs | 6 +++--- rodbus/src/client/message.rs | 2 +- rodbus/src/client/requests/send_custom_fc.rs | 2 +- rodbus/src/common/parse.rs | 2 +- rodbus/src/common/serialize.rs | 8 ++++---- rodbus/src/server/handler.rs | 6 +++--- rodbus/src/server/request.rs | 2 +- rodbus/src/types.rs | 12 ++++++------ 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index 9a143896..c47f5bf8 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -169,9 +169,9 @@ impl Channel { pub async fn send_custom_function_code( &mut self, param: RequestParam, - request: CustomFunctionCode, - ) -> Result { - let (tx, rx) = tokio::sync::oneshot::channel::>(); + request: CustomFunctionCode, + ) -> Result, RequestError> { + let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); let request = wrap( param, RequestDetails::SendCustomFunctionCode(CustomFCRequest::new(request, Promise::channel(tx))), diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 5adb335e..6ebd17dc 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -46,7 +46,7 @@ pub(crate) enum RequestDetails { WriteSingleRegister(SingleWrite>), WriteMultipleCoils(MultipleWriteRequest), WriteMultipleRegisters(MultipleWriteRequest), - SendCustomFunctionCode(CustomFCRequest), + SendCustomFunctionCode(CustomFCRequest>), } impl Request { diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index 3a7f1a61..8afd20e1 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -66,7 +66,7 @@ where } } -impl CustomFCOperation for CustomFunctionCode { +impl CustomFCOperation for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u16_be(self.len() as u16)?; diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 73e7bcc3..c445beb6 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -28,7 +28,7 @@ impl Parse for Indexed { } } -impl Parse for CustomFunctionCode { +impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { let len = cursor.read_u16_be()? as usize; let values = [cursor.read_u16_be()?, cursor.read_u16_be()?, cursor.read_u16_be()?, cursor.read_u16_be()?]; diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 9bf8b7c7..835f1e5e 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -290,7 +290,7 @@ impl Serialize for WriteMultiple { } } -impl Serialize for CustomFunctionCode { +impl Serialize for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u16_be(self.len() as u16)?; @@ -301,7 +301,7 @@ impl Serialize for CustomFunctionCode { } } -impl Loggable for CustomFunctionCode { +impl Loggable for CustomFunctionCode { fn log( &self, payload: &[u8], @@ -325,7 +325,7 @@ impl Loggable for CustomFunctionCode { }; } - let custom_fc = CustomFunctionCode::new(len, data); + let custom_fc = CustomFunctionCode::::new(len, data); write!(f, "{:?}", custom_fc)?; @@ -350,7 +350,7 @@ mod tests { #[test] fn serializes_valid_custom_function_code() { - let custom_fc = CustomFunctionCode::new(4, [0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE]); + let custom_fc = CustomFunctionCode::new(4, vec![0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE]); let mut buffer = [0u8; 10]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 32af45a3..cc361c02 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -64,7 +64,7 @@ pub trait RequestHandler: Send + 'static { } /// Write a custom function code - fn process_custom_function_code(&mut self, _values: CustomFunctionCode) -> Result<(), ExceptionCode> { + fn process_custom_function_code(&mut self, _values: CustomFunctionCode) -> Result<(), ExceptionCode> { Err(ExceptionCode::IllegalFunction) } } @@ -233,7 +233,7 @@ pub trait AuthorizationHandler: Send + Sync + 'static { } /// Authorize a Send Custom Function Code request - fn process_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } } @@ -316,7 +316,7 @@ impl AuthorizationHandler for ReadOnlyAuthorizationHandler { } /// Authorize a Send Custom Function Code request - fn process_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } } diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 5df56f7f..dceb826b 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -21,7 +21,7 @@ pub(crate) enum Request<'a> { WriteSingleRegister(Indexed), WriteMultipleCoils(WriteCoils<'a>), WriteMultipleRegisters(WriteRegisters<'a>), - SendCustomFunctionCode(CustomFunctionCode), + SendCustomFunctionCode(CustomFunctionCode), } /// All requests that support broadcast diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index a952384d..7b413d3c 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -86,10 +86,10 @@ pub(crate) struct RegisterIteratorDisplay<'a> { } /// Custom Function Code -#[derive(Clone, Debug, Copy, PartialEq)] -pub struct CustomFunctionCode { +#[derive(Clone, Debug, PartialEq)] +pub struct CustomFunctionCode { len: usize, - data: [u16; 4], + data: Vec, } impl std::fmt::Display for UnitId { @@ -374,9 +374,9 @@ impl Default for UnitId { } } -impl CustomFunctionCode { +impl CustomFunctionCode { /// Create a new custom function code - pub fn new(len: usize, data: [u16; 4]) -> Self { + pub fn new(len: usize, data: Vec) -> Self { Self { len, data } } @@ -391,7 +391,7 @@ impl CustomFunctionCode { } } -impl std::fmt::Display for CustomFunctionCode { +impl std::fmt::Display for CustomFunctionCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "values: [")?; for (i, val) in self.data.iter().enumerate() { From 97cbef14cf6582e4d8e4ed09cfd5c40ddb4a5f5d Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 14 Feb 2024 19:30:26 +0100 Subject: [PATCH 054/106] refactor(send_custom_fc): implement parsing and serializing logic for the CFC69 CustomFC request to support variable-length data --- rodbus/examples/client.rs | 2 +- rodbus/examples/custom_client.rs | 4 ++-- rodbus/examples/custom_server.rs | 9 +++++---- rodbus/examples/server.rs | 9 +++++---- rodbus/src/client/requests/send_custom_fc.rs | 10 ++++------ rodbus/src/common/parse.rs | 10 +++++++--- rodbus/src/common/serialize.rs | 6 +++--- rodbus/src/server/request.rs | 2 +- rodbus/src/server/task.rs | 2 +- 9 files changed, 29 insertions(+), 25 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index a2e15b25..67ac7fc9 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -270,7 +270,7 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { // ANCHOR: send_custom_function_code let length = 0x04 as usize; - let values = [0xC0, 0xDE, 0xCA, 0xFE]; // i.e.: Voltage Hi = 0xC0 / Voltage Lo = 0xDE / Current Hi = 0xCA / Current Lo = 0xFE + let values = vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]; // i.e.: Voltage Hi = 0xC0 / Voltage Lo = 0xDE / Current Hi = 0xCA / Current Lo = 0xFE let result = channel .send_custom_function_code( diff --git a/rodbus/examples/custom_client.rs b/rodbus/examples/custom_client.rs index e7da304c..e45ba744 100644 --- a/rodbus/examples/custom_client.rs +++ b/rodbus/examples/custom_client.rs @@ -207,13 +207,13 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { let length = length_str.parse::().unwrap_or(0); - let values: Vec = values.iter().filter_map(|&v| u8::from_str_radix(v.trim_start_matches("0x"), 16).ok()).collect(); + let values: Vec = values.iter().filter_map(|&v| u16::from_str_radix(v.trim_start_matches("0x"), 16).ok()).collect(); if values.len() == length { let result = channel .send_custom_function_code( params, - CustomFunctionCode::new(length, &values) + CustomFunctionCode::new(length, values) ) .await; print_write_result(result); diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 368db9a2..84982649 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -78,10 +78,11 @@ impl RequestHandler for SimpleHandler { } } - fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { - let mut custom_fc_args = [0_u16; 4]; // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 - for (i, &value) in values.iter().enumerate() { - custom_fc_args[i] = value; + fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { + let mut custom_fc_args = Vec::with_capacity(values.len()); // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 + + for &item in values.iter() { + custom_fc_args.push(item); } tracing::info!("processing custom function code arguments: {:?}", custom_fc_args); diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index 0f87c235..ce854dd9 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -78,10 +78,11 @@ impl RequestHandler for SimpleHandler { } } - fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { - let mut custom_fc_args = [0_u16; 4]; // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 - for (i, &value) in values.iter().enumerate() { - custom_fc_args[i] = value; + fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { + let mut custom_fc_args = Vec::with_capacity(values.len()); // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 + + for &item in values.iter() { + custom_fc_args.push(item); } tracing::info!("processing custom function code arguments: {:?}", custom_fc_args); diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index 8afd20e1..685b0e11 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -78,12 +78,10 @@ impl CustomFCOperation for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { let len = cursor.read_u16_be()? as usize; - let val1 = cursor.read_u16_be()?; - let val2 = cursor.read_u16_be()?; - let val3 = cursor.read_u16_be()?; - let val4 = cursor.read_u16_be()?; - - let values = [val1, val2, val3, val4]; + let mut values = Vec::with_capacity(len); + for _ in 0..len { + values.push(cursor.read_u16_be()?); + } Ok(CustomFunctionCode::new(len, values)) } diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index c445beb6..cb3ef002 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -30,8 +30,12 @@ impl Parse for Indexed { impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { - let len = cursor.read_u16_be()? as usize; - let values = [cursor.read_u16_be()?, cursor.read_u16_be()?, cursor.read_u16_be()?, cursor.read_u16_be()?]; + let len = cursor.read_u16_be()? as usize; + let mut values = Vec::with_capacity(len); + for _ in 0..len { + values.push(cursor.read_u16_be()?); + } + cursor.expect_empty()?; Ok(CustomFunctionCode::new(len, values)) } @@ -77,7 +81,7 @@ mod coils { fn parse_succeeds_for_valid_custom_function_code() { let mut cursor = ReadCursor::new(&[0x00, 0x04, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE, 0xC0, 0xDE]); let result = crate::types::CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(crate::types::CustomFunctionCode::new(4, [0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE]))); + assert_eq!(result, Ok(crate::types::CustomFunctionCode::new(4, vec![0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE]))); } #[test] diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 835f1e5e..943f2883 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -316,9 +316,9 @@ impl Loggable for CustomFunctionCode { Err(_) => return Ok(()), }; - let mut data = [0_u16; 4]; - - for i in 0..4 { + let mut data = Vec::with_capacity(len); + + for i in 0..len { data[i] = match cursor.read_u16_be() { Ok(value) => value, Err(_) => return Ok(()), diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index dceb826b..bfbf1010 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -145,7 +145,7 @@ impl<'a> Request<'a> { write_result(function, header, writer, result, level) } Request::SendCustomFunctionCode(request) => { - let result = handler.process_custom_function_code(*request).map(|_| *request); + let result = handler.process_custom_function_code(request.clone()).map(|_| request.clone()); write_result(function, header, writer, result, level) } } diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index 95d6f368..f2a00efb 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -264,7 +264,7 @@ impl AuthorizationType { Request::WriteMultipleRegisters(x) => { handler.write_multiple_registers(unit_id, x.range, role) } - Request::SendCustomFunctionCode(x) => handler.process_custom_function_code(*x, role), + Request::SendCustomFunctionCode(x) => handler.process_custom_function_code(x.clone(), role), } } From 5355f2413e8b3e2c1cf02c0ac3a68210c6dfa0e3 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 15 Feb 2024 17:49:04 +0100 Subject: [PATCH 055/106] fix(custom_fc): fix serialize error for CFC request --- rodbus/examples/custom_client.rs | 6 +++--- rodbus/src/common/serialize.rs | 5 +++-- rodbus/tests/integration_test.rs | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/rodbus/examples/custom_client.rs b/rodbus/examples/custom_client.rs index e45ba744..22685e2e 100644 --- a/rodbus/examples/custom_client.rs +++ b/rodbus/examples/custom_client.rs @@ -197,8 +197,8 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box Result<(), Box { - let length = length_str.parse::().unwrap_or(0); + let length = u16::from_str_radix(length_str.trim_start_matches("0x"), 16).unwrap_or(0) as usize; let values: Vec = values.iter().filter_map(|&v| u16::from_str_radix(v.trim_start_matches("0x"), 16).ok()).collect(); if values.len() == length { diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 943f2883..e035ed3d 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -318,11 +318,12 @@ impl Loggable for CustomFunctionCode { let mut data = Vec::with_capacity(len); - for i in 0..len { - data[i] = match cursor.read_u16_be() { + for _ in 0..len { + let item = match cursor.read_u16_be() { Ok(value) => value, Err(_) => return Ok(()), }; + data.push(item); } let custom_fc = CustomFunctionCode::::new(len, data); diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index 0bc423cf..d6e87227 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -97,7 +97,7 @@ impl RequestHandler for Handler { Ok(()) } - fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { + fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { for (i, &value) in values.iter().enumerate() { match self.custom_function_code.get_mut(i) { Some(c) => *c = value, @@ -236,10 +236,10 @@ async fn test_requests_and_responses() { ); assert_eq!( channel - .send_custom_function_code(params, CustomFunctionCode::new(0x04, [0xC0, 0xDE, 0xCA, 0xFE])) + .send_custom_function_code(params, CustomFunctionCode::new(0x04, vec![0xC0, 0xDE, 0xCA, 0xFE])) .await .unwrap(), - CustomFunctionCode::new(4, [0xC0, 0xDE, 0xCA, 0xFE]) + CustomFunctionCode::new(4, vec![0xC0, 0xDE, 0xCA, 0xFE]) ); } From 7a150c25f920461a98eca5252ee51507270c085f Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 15 Feb 2024 19:45:26 +0100 Subject: [PATCH 056/106] test(parse): Extend CFC69 parse unit test cases --- rodbus/src/common/parse.rs | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index cb3ef002..4e0beafb 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -76,18 +76,42 @@ mod coils { let result = Indexed::::parse(&mut cursor); assert_eq!(result, Ok(Indexed::new(1, 0xCAFE))); } +} + + +#[cfg(test)] +mod custom_fc { + use crate::common::traits::Parse; + use crate::error::AduParseError; + use crate::types::CustomFunctionCode; + + use scursor::ReadCursor; #[test] - fn parse_succeeds_for_valid_custom_function_code() { - let mut cursor = ReadCursor::new(&[0x00, 0x04, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE, 0xC0, 0xDE]); - let result = crate::types::CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(crate::types::CustomFunctionCode::new(4, vec![0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE]))); + fn parse_succeeds_for_single_value() { + let mut cursor = ReadCursor::new(&[0x00, 0x01, 0xCA, 0xFE]); + let result = CustomFunctionCode::parse(&mut cursor); + assert_eq!(result, Ok(CustomFunctionCode::new(1, vec![0xCAFE]))); } #[test] - fn parse_fails_for_invalid_custom_function_code() { + fn parse_succeeds_for_multiple_values() { + let mut cursor = ReadCursor::new(&[0x00, 0x03, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE]); + let result = CustomFunctionCode::parse(&mut cursor); + assert_eq!(result, Ok(CustomFunctionCode::new(3, vec![0xCAFE, 0xC0DE, 0xCAFE]))); + } + + #[test] + fn parse_fails_for_missing_len_byte() { + let mut cursor = ReadCursor::new(&[0x04, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE, 0xC0, 0xDE]); + let result = CustomFunctionCode::parse(&mut cursor); + assert_eq!(result, Err(AduParseError::InsufficientBytes.into())); + } + + #[test] + fn parse_fails_for_missing_data_byte() { let mut cursor = ReadCursor::new(&[0x00, 0x04, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE, 0xC0]); - let result = crate::types::CustomFunctionCode::parse(&mut cursor); + let result = CustomFunctionCode::parse(&mut cursor); assert_eq!(result, Err(AduParseError::InsufficientBytes.into())); } -} +} \ No newline at end of file From f696091de0dda731468573876341f6f1d60609e6 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 15 Feb 2024 20:03:08 +0100 Subject: [PATCH 057/106] test(serialize): extend CFC69 serialize unit test cases --- rodbus/src/common/serialize.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index e035ed3d..09d2f7b1 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -350,11 +350,20 @@ mod tests { } #[test] - fn serializes_valid_custom_function_code() { - let custom_fc = CustomFunctionCode::new(4, vec![0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE]); - let mut buffer = [0u8; 10]; + fn serialize_succeeds_for_valid_cfc_of_single_value() { + let custom_fc = CustomFunctionCode::new(1, vec![0xCAFE]); + let mut buffer = [0u8; 4]; + let mut cursor = WriteCursor::new(&mut buffer); + custom_fc.serialize(&mut cursor).unwrap(); + assert_eq!(buffer, [0x00, 0x01, 0xCA, 0xFE]); + } + + #[test] + fn serialize_succeeds_for_valid_cfc_of_multiple_values() { + let custom_fc = CustomFunctionCode::new(3, vec![0xCAFE, 0xC0DE, 0xCAFE]); + let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); - assert_eq!(buffer, [0x00, 0x04, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE, 0xC0, 0xDE]); + assert_eq!(buffer, [0x00, 0x03, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE]); } } From 4c9510fc553620842ce120a2c63dc669d076d93d Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 15 Feb 2024 20:08:21 +0100 Subject: [PATCH 058/106] test(parse): extend CFC69 unit test cases --- rodbus/src/common/parse.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 4e0beafb..0acfc745 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -88,29 +88,43 @@ mod custom_fc { use scursor::ReadCursor; #[test] - fn parse_succeeds_for_single_value() { - let mut cursor = ReadCursor::new(&[0x00, 0x01, 0xCA, 0xFE]); + fn parse_succeeds_for_single_min_value() { + let mut cursor = ReadCursor::new(&[0x00, 0x01, 0x00, 0x00]); + let result = CustomFunctionCode::parse(&mut cursor); + assert_eq!(result, Ok(CustomFunctionCode::new(1, vec![0x0000]))); + } + + #[test] + fn parse_succeeds_for_single_max_value() { + let mut cursor = ReadCursor::new(&[0x00, 0x01, 0xFF, 0xFF]); + let result = CustomFunctionCode::parse(&mut cursor); + assert_eq!(result, Ok(CustomFunctionCode::new(1, vec![0xFFFF]))); + } + + #[test] + fn parse_succeeds_for_multiple_min_values() { + let mut cursor = ReadCursor::new(&[0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(1, vec![0xCAFE]))); + assert_eq!(result, Ok(CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]))); } #[test] - fn parse_succeeds_for_multiple_values() { - let mut cursor = ReadCursor::new(&[0x00, 0x03, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE]); + fn parse_succeeds_for_multiple_max_values() { + let mut cursor = ReadCursor::new(&[0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(3, vec![0xCAFE, 0xC0DE, 0xCAFE]))); + assert_eq!(result, Ok(CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]))); } #[test] fn parse_fails_for_missing_len_byte() { - let mut cursor = ReadCursor::new(&[0x04, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE, 0xC0, 0xDE]); + let mut cursor = ReadCursor::new(&[0x01, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); assert_eq!(result, Err(AduParseError::InsufficientBytes.into())); } #[test] fn parse_fails_for_missing_data_byte() { - let mut cursor = ReadCursor::new(&[0x00, 0x04, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE, 0xC0]); + let mut cursor = ReadCursor::new(&[0x00, 0x01, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); assert_eq!(result, Err(AduParseError::InsufficientBytes.into())); } From 3b072949388ac545e8ef8a2bfe46c7ddc5efbb93 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 15 Feb 2024 20:13:20 +0100 Subject: [PATCH 059/106] test(serialize): extend CFC69 serialize unit test cases --- rodbus/src/common/serialize.rs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 09d2f7b1..87f528c4 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -350,20 +350,38 @@ mod tests { } #[test] - fn serialize_succeeds_for_valid_cfc_of_single_value() { - let custom_fc = CustomFunctionCode::new(1, vec![0xCAFE]); + fn serialize_succeeds_for_valid_cfc_of_single_min_value() { + let custom_fc = CustomFunctionCode::new(1, vec![0x0000]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); - assert_eq!(buffer, [0x00, 0x01, 0xCA, 0xFE]); + assert_eq!(buffer, [0x00, 0x01, 0x00, 0x00]); } #[test] - fn serialize_succeeds_for_valid_cfc_of_multiple_values() { - let custom_fc = CustomFunctionCode::new(3, vec![0xCAFE, 0xC0DE, 0xCAFE]); + fn serialize_succeeds_for_valid_cfc_of_single_max_value() { + let custom_fc = CustomFunctionCode::new(1, vec![0xFFFF]); + let mut buffer = [0u8; 4]; + let mut cursor = WriteCursor::new(&mut buffer); + custom_fc.serialize(&mut cursor).unwrap(); + assert_eq!(buffer, [0x00, 0x01, 0xFF, 0xFF]); + } + + #[test] + fn serialize_succeeds_for_valid_cfc_of_multiple_min_values() { + let custom_fc = CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]); + let mut buffer = [0u8; 8]; + let mut cursor = WriteCursor::new(&mut buffer); + custom_fc.serialize(&mut cursor).unwrap(); + assert_eq!(buffer, [0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + } + + #[test] + fn serialize_succeeds_for_valid_cfc_of_multiple_max_values() { + let custom_fc = CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); - assert_eq!(buffer, [0x00, 0x03, 0xCA, 0xFE, 0xC0, 0xDE, 0xCA, 0xFE]); + assert_eq!(buffer, [0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); } } From be7a3f130194e68954195da24197d0d8c2fba7ab Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 15 Feb 2024 20:21:31 +0100 Subject: [PATCH 060/106] refactor(send_custom_fc): add expect empty cursor statement to parse impl --- rodbus/src/client/requests/send_custom_fc.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index 685b0e11..beea33b4 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -82,6 +82,7 @@ impl CustomFCOperation for CustomFunctionCode { for _ in 0..len { values.push(cursor.read_u16_be()?); } + cursor.expect_empty()?; Ok(CustomFunctionCode::new(len, values)) } From 64d60b26818cc2e21bb337b2fc26af3c39007fdf Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 16 Feb 2024 19:44:04 +0100 Subject: [PATCH 061/106] docs(send_custom_fc): extend documentation for the CFC69 implementation --- rodbus/SCFC_README.md | 58 ++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/rodbus/SCFC_README.md b/rodbus/SCFC_README.md index 7e2e1692..c5b31098 100644 --- a/rodbus/SCFC_README.md +++ b/rodbus/SCFC_README.md @@ -4,25 +4,23 @@ This document provides a detailed overview of the custom function code (0x45) us ## Introduction -The 0x45 function code enables the implementation of user-defined logic on a remote server device. It facilitates the transmission, reception, and processing of a custom function code with a fixed-size data buffer. This buffer currently supports 4 arguments, each 2 bytes (u16) in size, allowing for the execution of custom logic remotely. - -**Note:** To increase flexibility, support for a variable-length data buffer will be included in a future update. +The 0x45 function code enables the implementation of user-defined logic on a remote server device. It facilitates the transmission, reception, and processing of a custom function code with a variable-size data buffer. ## Request Structure -| Parameter | Size | Range / Value | -|--------------------|----------|-----------------------| -| Function code | 1 Byte | 0x45 | -| Length | 2 Bytes | 0x0004 | -| Data | 8 Bytes | 0x0000 to 0xFFFF | +| Parameter | Size | Range / Value | +|--------------------|---------------|-----------------------| +| Function code | 1 Byte | 0x45 | +| Byte Count | 2 Bytes | 0x0000 to 0xFFFF (N*) | +| Data | N* x 2 Bytes | 0x0000 to 0xFFFF | ## Response Structure -| Parameter | Size | Value/Description | -|---------------|---------|----------------------| -| Function code | 1 Byte | 0x45 | -| Length | 2 Bytes | 0x0004 | -| Data | 8 Bytes | 0x0000 to 0xFFFF | +| Parameter | Size | Value/Description | +|---------------|--------------|-----------------------| +| Function code | 1 Byte | 0x45 | +| Byte Count | 2 Bytes | 0x0000 to 0xFFFF (N*) | +| Data | N* x 2 Bytes | 0x0000 to 0xFFFF | ## Error Handling @@ -39,21 +37,41 @@ The 0x45 function code enables the implementation of user-defined logic on a rem ## Usage Example -### Request to send the custom buffer [0xC0, 0xDE, 0xCA, 0xFE]: - +### Request to send the custom buffer [0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE] (Byte Count = 4 -> 8 bytes): | Request Field | Hex | Response Field | Hex | |---------------------------|-----|------------------------|-----| | Function | 45 | Function | 45 | -| Length | 04 | Byte Count | 04 | -| Arg1 | C0 | Arg1 | C0 | -| Arg2 | DE | Arg2 | DE | -| Arg3 | CA | Arg3 | CA | -| Arg4 | FE | Arg4 | FE | +| Byte Count Hi | 00 | Byte Count Hi | 00 | +| Byte Count Lo | 04 | Byte Count Lo | 04 | +| Arg1 Hi | C0 | Arg1 Hi | C0 | +| Arg1 Lo | DE | Arg1 Lo | DE | +| Arg2 Hi | CA | Arg2 Hi | CA | +| Arg2 Lo | FE | Arg2 Lo | FE | +| Arg3 Hi | C0 | Arg3 Hi | C0 | +| Arg3 Lo | DE | Arg3 Lo | DE | +| Arg4 Hi | CA | Arg4 Hi | CA | +| Arg4 Lo | FE | Arg4 Lo | FE | ## Modify and Test Server-Side Buffer Handling The server currently forwards the Custom Function Code buffer to the client again without alteration. To test modifying or processing the buffer on the remote server device, edit the `send_custom_function_code()` function in `src/examples/client.rs` and `src/examples/server.rs` as needed. +## Usage +Make sure that you are in the `rodbus` project directory. + +### Start the custom_server example +- `cargo run --example custom_server -- tcp` + +Leave the terminal open and open another terminal. + +### Start the custom_client example +- `cargo run --example custom_client -- tcp` + +### Send the Custom Function Code CFC69 request +In the terminal with the running custom_client example, run: +- `scfc ` +- e.g. `scfc 0x02 0xC0DE 0xCAFE` + ## Troubleshooting Tips - Ensure the server and client are using the same communication and are connected to each other. From 6bc65489e7abb4d1e0bfb6621cd78822b402d645 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 16 Feb 2024 21:58:58 +0100 Subject: [PATCH 062/106] test(integration cfc): improve integration test readability --- rodbus/tests/integration_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index d6e87227..8cb3cfc8 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -236,10 +236,10 @@ async fn test_requests_and_responses() { ); assert_eq!( channel - .send_custom_function_code(params, CustomFunctionCode::new(0x04, vec![0xC0, 0xDE, 0xCA, 0xFE])) + .send_custom_function_code(params, CustomFunctionCode::new(0x04, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])) .await .unwrap(), - CustomFunctionCode::new(4, vec![0xC0, 0xDE, 0xCA, 0xFE]) + CustomFunctionCode::new(4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) ); } From 0cdc13758dd766c676cc61d6b9f84daf9955b6fe Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 21 Feb 2024 22:48:31 +0100 Subject: [PATCH 063/106] refactor(custom_FC): implement dynamic CFC handling --- rodbus/examples/custom_client.rs | 10 +- rodbus/examples/custom_server.rs | 22 ++-- rodbus/src/client/message.rs | 25 +++- rodbus/src/client/requests/send_custom_fc.rs | 11 +- rodbus/src/common/function.rs | 116 ++++++++++++++++++- rodbus/src/common/parse.rs | 7 +- rodbus/src/common/serialize.rs | 8 +- rodbus/src/serial/frame.rs | 59 ++++++++-- rodbus/src/server/handler.rs | 9 ++ rodbus/src/server/request.rs | 35 +++++- rodbus/src/types.rs | 12 +- rodbus/tests/integration_test.rs | 2 +- 12 files changed, 268 insertions(+), 48 deletions(-) diff --git a/rodbus/examples/custom_client.rs b/rodbus/examples/custom_client.rs index 22685e2e..68bb6a9c 100644 --- a/rodbus/examples/custom_client.rs +++ b/rodbus/examples/custom_client.rs @@ -205,20 +205,20 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { channel.set_decode_level(DecodeLevel::nothing()).await?; } - ["scfc", length_str, values @ ..] => { - let length = u16::from_str_radix(length_str.trim_start_matches("0x"), 16).unwrap_or(0) as usize; + ["scfc", fc_str, values @ ..] => { + let fc = u8::from_str_radix(fc_str.trim_start_matches("0x"), 16).unwrap(); let values: Vec = values.iter().filter_map(|&v| u16::from_str_radix(v.trim_start_matches("0x"), 16).ok()).collect(); - if values.len() == length { + if (fc >= 65 && fc <= 72) || (fc >= 100 && fc <= 110) { let result = channel .send_custom_function_code( params, - CustomFunctionCode::new(length, values) + CustomFunctionCode::new(fc, values) ) .await; print_write_result(result); } else { - println!("Error: Length does not match the number of provided values"); + println!("Error: CFC number is not inside the range of 65-72 or 100-110."); } } _ => println!("unknown command"), diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 84982649..375d2dd9 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -79,14 +79,18 @@ impl RequestHandler for SimpleHandler { } fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { - let mut custom_fc_args = Vec::with_capacity(values.len()); // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 - - for &item in values.iter() { - custom_fc_args.push(item); + tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); + + match values.function_code() { + 0x45 => { + tracing::info!("custom function code 0x45"); + // call CFC69Handler.handle and return result + Ok(()) + }, + _ => { + return Err(ExceptionCode::IllegalFunction); + } } - tracing::info!("processing custom function code arguments: {:?}", custom_fc_args); - - Ok(()) } fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { @@ -292,8 +296,8 @@ async fn run_server( server .set_decode_level(DecodeLevel::new( AppDecodeLevel::DataValues, - FrameDecodeLevel::Header, - PhysDecodeLevel::Length, + FrameDecodeLevel::Payload, + PhysDecodeLevel::Data, )) .await?; } diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 6ebd17dc..01385b82 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -131,7 +131,30 @@ impl RequestDetails { RequestDetails::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, RequestDetails::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, RequestDetails::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, - RequestDetails::SendCustomFunctionCode(_) => FunctionCode::SendCustomFunctionCode, + RequestDetails::SendCustomFunctionCode(x) => { + match x.request.function_code() { + 65 => FunctionCode::SendCFC65, + 66 => FunctionCode::SendCFC66, + 67 => FunctionCode::SendCFC67, + 68 => FunctionCode::SendCFC68, + 69 => FunctionCode::SendCFC69, + 70 => FunctionCode::SendCFC70, + 71 => FunctionCode::SendCFC71, + 72 => FunctionCode::SendCFC72, + 100 => FunctionCode::SendCFC100, + 101 => FunctionCode::SendCFC101, + 102 => FunctionCode::SendCFC102, + 103 => FunctionCode::SendCFC103, + 104 => FunctionCode::SendCFC104, + 105 => FunctionCode::SendCFC105, + 106 => FunctionCode::SendCFC106, + 107 => FunctionCode::SendCFC107, + 108 => FunctionCode::SendCFC108, + 109 => FunctionCode::SendCFC109, + 110 => FunctionCode::SendCFC110, + _ => panic!("unsupported custom function code"), + } + }, } } diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index beea33b4..52ebdf44 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -68,22 +68,25 @@ where impl CustomFCOperation for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { - cursor.write_u16_be(self.len() as u16)?; + cursor.write_u8(self.function_code())?; for &item in self.iter() { cursor.write_u16_be(item)?; } + Ok(()) } fn parse(cursor: &mut ReadCursor) -> Result { - let len = cursor.read_u16_be()? as usize; - let mut values = Vec::with_capacity(len); + let fc = cursor.read_u8()?; + + let mut values = Vec::new(); + let len = cursor.remaining() / 2; for _ in 0..len { values.push(cursor.read_u16_be()?); } cursor.expect_empty()?; - Ok(CustomFunctionCode::new(len, values)) + Ok(CustomFunctionCode::new(fc, values)) } } diff --git a/rodbus/src/common/function.rs b/rodbus/src/common/function.rs index d53c45b1..de25aeb6 100644 --- a/rodbus/src/common/function.rs +++ b/rodbus/src/common/function.rs @@ -9,7 +9,25 @@ mod constants { pub(crate) const WRITE_SINGLE_REGISTER: u8 = 6; pub(crate) const WRITE_MULTIPLE_COILS: u8 = 15; pub(crate) const WRITE_MULTIPLE_REGISTERS: u8 = 16; - pub(crate) const SEND_CUSTOM_FUNCTION_CODE: u8 = 69; + pub(crate) const SEND_CFC_65: u8 = 65; + pub(crate) const SEND_CFC_66: u8 = 66; + pub(crate) const SEND_CFC_67: u8 = 67; + pub(crate) const SEND_CFC_68: u8 = 68; + pub(crate) const SEND_CFC_69: u8 = 69; + pub(crate) const SEND_CFC_70: u8 = 70; + pub(crate) const SEND_CFC_71: u8 = 71; + pub(crate) const SEND_CFC_72: u8 = 72; + pub(crate) const SEND_CFC_100: u8 = 100; + pub(crate) const SEND_CFC_101: u8 = 101; + pub(crate) const SEND_CFC_102: u8 = 102; + pub(crate) const SEND_CFC_103: u8 = 103; + pub(crate) const SEND_CFC_104: u8 = 104; + pub(crate) const SEND_CFC_105: u8 = 105; + pub(crate) const SEND_CFC_106: u8 = 106; + pub(crate) const SEND_CFC_107: u8 = 107; + pub(crate) const SEND_CFC_108: u8 = 108; + pub(crate) const SEND_CFC_109: u8 = 109; + pub(crate) const SEND_CFC_110: u8 = 110; } #[derive(Debug, Copy, Clone, PartialEq)] @@ -23,7 +41,25 @@ pub(crate) enum FunctionCode { WriteSingleRegister = constants::WRITE_SINGLE_REGISTER, WriteMultipleCoils = constants::WRITE_MULTIPLE_COILS, WriteMultipleRegisters = constants::WRITE_MULTIPLE_REGISTERS, - SendCustomFunctionCode = constants::SEND_CUSTOM_FUNCTION_CODE, + SendCFC65 = constants::SEND_CFC_65, + SendCFC66 = constants::SEND_CFC_66, + SendCFC67 = constants::SEND_CFC_67, + SendCFC68 = constants::SEND_CFC_68, + SendCFC69 = constants::SEND_CFC_69, + SendCFC70 = constants::SEND_CFC_70, + SendCFC71 = constants::SEND_CFC_71, + SendCFC72 = constants::SEND_CFC_72, + SendCFC100 = constants::SEND_CFC_100, + SendCFC101 = constants::SEND_CFC_101, + SendCFC102 = constants::SEND_CFC_102, + SendCFC103 = constants::SEND_CFC_103, + SendCFC104 = constants::SEND_CFC_104, + SendCFC105 = constants::SEND_CFC_105, + SendCFC106 = constants::SEND_CFC_106, + SendCFC107 = constants::SEND_CFC_107, + SendCFC108 = constants::SEND_CFC_108, + SendCFC109 = constants::SEND_CFC_109, + SendCFC110 = constants::SEND_CFC_110, } impl Display for FunctionCode { @@ -51,7 +87,61 @@ impl Display for FunctionCode { FunctionCode::WriteMultipleRegisters => { write!(f, "WRITE MULTIPLE REGISTERS ({:#04X})", self.get_value()) } - FunctionCode::SendCustomFunctionCode => { + FunctionCode::SendCFC65 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC66 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC67 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC68 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC69 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC70 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC71 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC72 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC100 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC101 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC102 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC103 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC104 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC105 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC106 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC107 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC108 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC109 => { + write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) + } + FunctionCode::SendCFC110 => { write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) } } @@ -77,7 +167,25 @@ impl FunctionCode { constants::WRITE_SINGLE_REGISTER => Some(FunctionCode::WriteSingleRegister), constants::WRITE_MULTIPLE_COILS => Some(FunctionCode::WriteMultipleCoils), constants::WRITE_MULTIPLE_REGISTERS => Some(FunctionCode::WriteMultipleRegisters), - constants::SEND_CUSTOM_FUNCTION_CODE => Some(FunctionCode::SendCustomFunctionCode), + constants::SEND_CFC_65 => Some(FunctionCode::SendCFC65), + constants::SEND_CFC_66 => Some(FunctionCode::SendCFC66), + constants::SEND_CFC_67 => Some(FunctionCode::SendCFC67), + constants::SEND_CFC_68 => Some(FunctionCode::SendCFC68), + constants::SEND_CFC_69 => Some(FunctionCode::SendCFC69), + constants::SEND_CFC_70 => Some(FunctionCode::SendCFC70), + constants::SEND_CFC_71 => Some(FunctionCode::SendCFC71), + constants::SEND_CFC_72 => Some(FunctionCode::SendCFC72), + constants::SEND_CFC_100 => Some(FunctionCode::SendCFC100), + constants::SEND_CFC_101 => Some(FunctionCode::SendCFC101), + constants::SEND_CFC_102 => Some(FunctionCode::SendCFC102), + constants::SEND_CFC_103 => Some(FunctionCode::SendCFC103), + constants::SEND_CFC_104 => Some(FunctionCode::SendCFC104), + constants::SEND_CFC_105 => Some(FunctionCode::SendCFC105), + constants::SEND_CFC_106 => Some(FunctionCode::SendCFC106), + constants::SEND_CFC_107 => Some(FunctionCode::SendCFC107), + constants::SEND_CFC_108 => Some(FunctionCode::SendCFC108), + constants::SEND_CFC_109 => Some(FunctionCode::SendCFC109), + constants::SEND_CFC_110 => Some(FunctionCode::SendCFC110), _ => None, } } diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 0acfc745..20ef4044 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -30,14 +30,15 @@ impl Parse for Indexed { impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { - let len = cursor.read_u16_be()? as usize; - let mut values = Vec::with_capacity(len); + let fc = cursor.read_u8()?; + let mut values = Vec::new(); + let len = cursor.remaining() / 2; for _ in 0..len { values.push(cursor.read_u16_be()?); } cursor.expect_empty()?; - Ok(CustomFunctionCode::new(len, values)) + Ok(CustomFunctionCode::new(fc, values)) } } diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 87f528c4..d4920b20 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -311,11 +311,13 @@ impl Loggable for CustomFunctionCode { if level.data_headers() { let mut cursor = ReadCursor::new(payload); - let len = match cursor.read_u16_be() { - Ok(value) => value as usize, + + let fc = match cursor.read_u8() { + Ok(value) => value, Err(_) => return Ok(()), }; + let len = cursor.remaining() / 2; let mut data = Vec::with_capacity(len); for _ in 0..len { @@ -326,7 +328,7 @@ impl Loggable for CustomFunctionCode { data.push(item); } - let custom_fc = CustomFunctionCode::::new(len, data); + let custom_fc = CustomFunctionCode::::new(fc, data); write!(f, "{:?}", custom_fc)?; diff --git a/rodbus/src/serial/frame.rs b/rodbus/src/serial/frame.rs index 0331fd58..e9651f5d 100644 --- a/rodbus/src/serial/frame.rs +++ b/rodbus/src/serial/frame.rs @@ -87,7 +87,25 @@ impl RtuParser { FunctionCode::WriteSingleRegister => LengthMode::Fixed(4), FunctionCode::WriteMultipleCoils => LengthMode::Offset(5), FunctionCode::WriteMultipleRegisters => LengthMode::Offset(5), - FunctionCode::SendCustomFunctionCode => LengthMode::Offset(1), + FunctionCode::SendCFC65 => LengthMode::Offset(1), + FunctionCode::SendCFC66 => LengthMode::Offset(1), + FunctionCode::SendCFC67 => LengthMode::Offset(1), + FunctionCode::SendCFC68 => LengthMode::Offset(1), + FunctionCode::SendCFC69 => LengthMode::Offset(1), + FunctionCode::SendCFC70 => LengthMode::Offset(1), + FunctionCode::SendCFC71 => LengthMode::Offset(1), + FunctionCode::SendCFC72 => LengthMode::Offset(1), + FunctionCode::SendCFC100 => LengthMode::Offset(1), + FunctionCode::SendCFC101 => LengthMode::Offset(1), + FunctionCode::SendCFC102 => LengthMode::Offset(1), + FunctionCode::SendCFC103 => LengthMode::Offset(1), + FunctionCode::SendCFC104 => LengthMode::Offset(1), + FunctionCode::SendCFC105 => LengthMode::Offset(1), + FunctionCode::SendCFC106 => LengthMode::Offset(1), + FunctionCode::SendCFC107 => LengthMode::Offset(1), + FunctionCode::SendCFC108 => LengthMode::Offset(1), + FunctionCode::SendCFC109 => LengthMode::Offset(1), + FunctionCode::SendCFC110 => LengthMode::Offset(1), }, ParserType::Response => match function_code { FunctionCode::ReadCoils => LengthMode::Offset(1), @@ -98,7 +116,25 @@ impl RtuParser { FunctionCode::WriteSingleRegister => LengthMode::Fixed(4), FunctionCode::WriteMultipleCoils => LengthMode::Fixed(4), FunctionCode::WriteMultipleRegisters => LengthMode::Fixed(4), - FunctionCode::SendCustomFunctionCode => LengthMode::Offset(1), + FunctionCode::SendCFC65 => LengthMode::Offset(1), + FunctionCode::SendCFC66 => LengthMode::Offset(1), + FunctionCode::SendCFC67 => LengthMode::Offset(1), + FunctionCode::SendCFC68 => LengthMode::Offset(1), + FunctionCode::SendCFC69 => LengthMode::Offset(1), + FunctionCode::SendCFC70 => LengthMode::Offset(1), + FunctionCode::SendCFC71 => LengthMode::Offset(1), + FunctionCode::SendCFC72 => LengthMode::Offset(1), + FunctionCode::SendCFC100 => LengthMode::Offset(1), + FunctionCode::SendCFC101 => LengthMode::Offset(1), + FunctionCode::SendCFC102 => LengthMode::Offset(1), + FunctionCode::SendCFC103 => LengthMode::Offset(1), + FunctionCode::SendCFC104 => LengthMode::Offset(1), + FunctionCode::SendCFC105 => LengthMode::Offset(1), + FunctionCode::SendCFC106 => LengthMode::Offset(1), + FunctionCode::SendCFC107 => LengthMode::Offset(1), + FunctionCode::SendCFC108 => LengthMode::Offset(1), + FunctionCode::SendCFC109 => LengthMode::Offset(1), + FunctionCode::SendCFC110 => LengthMode::Offset(1), }, } } @@ -347,21 +383,22 @@ mod tests { 0x71, 0x86, // crc ]; - const SEND_CUSTOM_FUNCTION_CODE_REQUEST: &[u8] = &[ + const SEND_CFC_69_REQUEST: &[u8] = &[ UNIT_ID, // unit id - 0x44, // function code - 0x08, // byte count (length of data) + 0x45, // function code + 0x08, // byte count 0xC0, 0xDE, 0xCA, 0xFE, // data 0xC8, 0xD9, // crc ]; - const SEND_CUSTOM_FUNCTION_CODE_RESPONSE: &[u8] = &[ + const SEND_CFC_69_RESPONSE: &[u8] = &[ UNIT_ID, // unit id - 0x44, // function code + 0x45, // function code 0x08, // byte count 0xC0, 0xDE, 0xCA, 0xFE, // data 0x88, 0x2C, // crc ]; + const WRITE_SINGLE_COIL_REQUEST: &[u8] = &[ UNIT_ID, // unit id 0x05, // function code @@ -445,8 +482,8 @@ mod tests { READ_INPUT_REGISTERS_REQUEST, ), ( - FunctionCode::SendCustomFunctionCode, - SEND_CUSTOM_FUNCTION_CODE_REQUEST, + FunctionCode::SendCFC69, + SEND_CFC_69_REQUEST, ), (FunctionCode::WriteSingleCoil, WRITE_SINGLE_COIL_REQUEST), ( @@ -478,8 +515,8 @@ mod tests { READ_INPUT_REGISTERS_RESPONSE, ), ( - FunctionCode::SendCustomFunctionCode, - SEND_CUSTOM_FUNCTION_CODE_RESPONSE, + FunctionCode::SendCFC69, + SEND_CFC_69_RESPONSE, ), (FunctionCode::WriteSingleCoil, WRITE_SINGLE_COIL_RESPONSE), ( diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index cc361c02..79f9627d 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -328,6 +328,15 @@ mod tests { struct DefaultHandler; impl RequestHandler for DefaultHandler {} + #[test] + fn custom_fc() { + let mut handler = DefaultHandler {}; + assert_eq!( + handler.process_custom_function_code(CustomFunctionCode::new(0x46, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + } + #[test] fn default_handler_returns_illegal_function() { let mut handler = DefaultHandler {}; diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index bfbf1010..9b8fe108 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -65,7 +65,30 @@ impl<'a> Request<'a> { Request::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, Request::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, Request::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, - Request::SendCustomFunctionCode(_) => FunctionCode::SendCustomFunctionCode, + Request::SendCustomFunctionCode(x) => { + match x.function_code() { + 65 => FunctionCode::SendCFC65, + 66 => FunctionCode::SendCFC66, + 67 => FunctionCode::SendCFC67, + 68 => FunctionCode::SendCFC68, + 69 => FunctionCode::SendCFC69, + 70 => FunctionCode::SendCFC70, + 71 => FunctionCode::SendCFC71, + 72 => FunctionCode::SendCFC72, + 100 => FunctionCode::SendCFC100, + 101 => FunctionCode::SendCFC101, + 102 => FunctionCode::SendCFC102, + 103 => FunctionCode::SendCFC103, + 104 => FunctionCode::SendCFC104, + 105 => FunctionCode::SendCFC105, + 106 => FunctionCode::SendCFC106, + 107 => FunctionCode::SendCFC107, + 108 => FunctionCode::SendCFC108, + 109 => FunctionCode::SendCFC109, + 110 => FunctionCode::SendCFC110, + _ => panic!("Invalid custom function code"), + } + }, } } @@ -207,9 +230,13 @@ impl<'a> Request<'a> { RegisterIterator::parse_all(range, cursor)?, ))) } - FunctionCode::SendCustomFunctionCode => { - let x = - Request::SendCustomFunctionCode(CustomFunctionCode::parse(cursor)?); + FunctionCode::SendCFC65 | FunctionCode::SendCFC66 | FunctionCode::SendCFC67 | FunctionCode::SendCFC68 | + FunctionCode::SendCFC69 | FunctionCode::SendCFC70 | FunctionCode::SendCFC71 | FunctionCode::SendCFC72 | + FunctionCode::SendCFC100 | FunctionCode::SendCFC101 | FunctionCode::SendCFC102 | FunctionCode::SendCFC103 | + FunctionCode::SendCFC104 | FunctionCode::SendCFC105 | FunctionCode::SendCFC106 | FunctionCode::SendCFC107 | + FunctionCode::SendCFC108 | FunctionCode::SendCFC109 | FunctionCode::SendCFC110 => { + let x = Request::SendCustomFunctionCode(CustomFunctionCode::parse(cursor)?); + cursor.expect_empty()?; Ok(x) } diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 7b413d3c..42706202 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -88,7 +88,7 @@ pub(crate) struct RegisterIteratorDisplay<'a> { /// Custom Function Code #[derive(Clone, Debug, PartialEq)] pub struct CustomFunctionCode { - len: usize, + fc: u8, data: Vec, } @@ -376,8 +376,13 @@ impl Default for UnitId { impl CustomFunctionCode { /// Create a new custom function code - pub fn new(len: usize, data: Vec) -> Self { - Self { len, data } + pub fn new(fc: u8, data: Vec) -> Self { + Self { fc, data } + } + + /// Get the function code + pub fn function_code(&self) -> u8 { + self.fc } /// Get the length of the underlying vector @@ -476,4 +481,5 @@ mod tests { assert!(UnitId::new(255).is_rtu_reserved()); assert!(!UnitId::new(41).is_rtu_reserved()); } + } diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index 8cb3cfc8..9759004e 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -236,7 +236,7 @@ async fn test_requests_and_responses() { ); assert_eq!( channel - .send_custom_function_code(params, CustomFunctionCode::new(0x04, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])) + .send_custom_function_code(params, CustomFunctionCode::new(0x45, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])) .await .unwrap(), CustomFunctionCode::new(4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) From d85e629a8bba887a14a3ad04f6e2fb10dc613cd4 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:41:55 +0100 Subject: [PATCH 064/106] refactor(custom_client): remove unused print_read_result() function --- rodbus/examples/custom_client.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rodbus/examples/custom_client.rs b/rodbus/examples/custom_client.rs index 68bb6a9c..7767048f 100644 --- a/rodbus/examples/custom_client.rs +++ b/rodbus/examples/custom_client.rs @@ -145,7 +145,7 @@ fn get_ca_chain_config() -> Result> Ok(tls_config) } -fn print_read_result(result: Result>, RequestError>) +/*fn print_read_result(result: Result>, RequestError>) where T: std::fmt::Display, { @@ -160,7 +160,7 @@ where } Err(err) => println!("read error: {err}"), } -} +}*/ fn print_write_result(result: Result) { match result { From 9e1fb98a5bf81f81af78a892e16ebbf9c2e3b4e2 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:44:04 +0100 Subject: [PATCH 065/106] feat(function): make CFC FC const more readable --- rodbus/src/common/function.rs | 60 +++-------------------------------- 1 file changed, 5 insertions(+), 55 deletions(-) diff --git a/rodbus/src/common/function.rs b/rodbus/src/common/function.rs index de25aeb6..56be19fd 100644 --- a/rodbus/src/common/function.rs +++ b/rodbus/src/common/function.rs @@ -87,61 +87,11 @@ impl Display for FunctionCode { FunctionCode::WriteMultipleRegisters => { write!(f, "WRITE MULTIPLE REGISTERS ({:#04X})", self.get_value()) } - FunctionCode::SendCFC65 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC66 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC67 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC68 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC69 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC70 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC71 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC72 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC100 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC101 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC102 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC103 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC104 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC105 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC106 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC107 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC108 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC109 => { - write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) - } - FunctionCode::SendCFC110 => { + FunctionCode::SendCFC65 | FunctionCode::SendCFC66 | FunctionCode::SendCFC67 | FunctionCode::SendCFC68 | + FunctionCode::SendCFC69 | FunctionCode::SendCFC70 | FunctionCode::SendCFC71 | FunctionCode::SendCFC72 | + FunctionCode::SendCFC100 | FunctionCode::SendCFC101 | FunctionCode::SendCFC102 | FunctionCode::SendCFC103 | + FunctionCode::SendCFC104 | FunctionCode::SendCFC105 | FunctionCode::SendCFC106 | FunctionCode::SendCFC107 | + FunctionCode::SendCFC108 | FunctionCode::SendCFC109 | FunctionCode::SendCFC110 => { write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) } } From 988ef20fe2922fe3b1d902a07ca473a9eebb6578 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:45:45 +0100 Subject: [PATCH 066/106] refactor(integration_test): add CFC69 integration test --- rodbus/tests/integration_test.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index 9759004e..a79c898d 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -12,8 +12,7 @@ struct Handler { pub coils: [bool; 10], pub discrete_inputs: [bool; 10], pub holding_registers: [u16; 10], - pub input_registers: [u16; 10], - pub custom_function_code: [u16; 5], + pub input_registers: [u16; 10] } impl Handler { @@ -22,8 +21,7 @@ impl Handler { coils: [false; 10], discrete_inputs: [false; 10], holding_registers: [0; 10], - input_registers: [0; 10], - custom_function_code: [0; 5], + input_registers: [0; 10] } } } @@ -97,16 +95,13 @@ impl RequestHandler for Handler { Ok(()) } - fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { - for (i, &value) in values.iter().enumerate() { - match self.custom_function_code.get_mut(i) { - Some(c) => *c = value, - None => return Err(ExceptionCode::IllegalDataAddress), - } - } - Ok(()) - } + fn process_cfc_69(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { + tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); + // increment each CFC value by 1 and return the result + let incremented_values = values.iter().map(|&x| x + 1).collect(); + Ok(CustomFunctionCode::new(values.function_code(), incremented_values)) } +} async fn test_requests_and_responses() { let handler = Handler::new().wrap(); @@ -239,7 +234,7 @@ async fn test_requests_and_responses() { .send_custom_function_code(params, CustomFunctionCode::new(0x45, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])) .await .unwrap(), - CustomFunctionCode::new(4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + CustomFunctionCode::new(0x45, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) ); } From 4f994dd04af544d39e6ef7c5f792181c404411ac Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:47:22 +0100 Subject: [PATCH 067/106] refactor(send_custom_fc): improve vector handling --- rodbus/src/client/requests/send_custom_fc.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index 52ebdf44..c51aa22e 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -79,9 +79,8 @@ impl CustomFCOperation for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { let fc = cursor.read_u8()?; - - let mut values = Vec::new(); let len = cursor.remaining() / 2; + let mut values = Vec::with_capacity(len); for _ in 0..len { values.push(cursor.read_u16_be()?); } From 280607ad6ddfec9d0e312557800ace3baf98a7ab Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:48:08 +0100 Subject: [PATCH 068/106] refactor(parse): improve CFC vector handling --- rodbus/src/common/parse.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 20ef4044..89baa648 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -31,8 +31,9 @@ impl Parse for Indexed { impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { let fc = cursor.read_u8()?; - let mut values = Vec::new(); let len = cursor.remaining() / 2; + let mut values = Vec::with_capacity(len); + for _ in 0..len { values.push(cursor.read_u16_be()?); } From 7d4c3b960362b992908415dc5b417eed75b79ae2 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:49:16 +0100 Subject: [PATCH 069/106] fix(serialize): adjust CFC serialization to new CFC struct --- rodbus/src/common/serialize.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index d4920b20..131ecfcb 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -292,7 +292,7 @@ impl Serialize for WriteMultiple { impl Serialize for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { - cursor.write_u16_be(self.len() as u16)?; + cursor.write_u8(self.function_code())?; for &item in self.iter() { cursor.write_u16_be(item)?; From 5f5ca0b4a29ee6eec8ce0fec19d0986efed79e91 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:50:04 +0100 Subject: [PATCH 070/106] feat(handler): add cfc handler traits --- rodbus/src/server/handler.rs | 300 +++++++++++++++++++++++++++++++++-- 1 file changed, 285 insertions(+), 15 deletions(-) diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 79f9627d..de1296f9 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -63,8 +63,98 @@ pub trait RequestHandler: Send + 'static { Err(ExceptionCode::IllegalFunction) } - /// Write a custom function code - fn process_custom_function_code(&mut self, _values: CustomFunctionCode) -> Result<(), ExceptionCode> { + /// Write the CFC65 custom function code + fn process_cfc_65(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC66 custom function code + fn process_cfc_66(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC67 custom function code + fn process_cfc_67(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC68 custom function code + fn process_cfc_68(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC69 custom function code + fn process_cfc_69(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC70 custom function code + fn process_cfc_70(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC71 custom function code + fn process_cfc_71(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC72 custom function code + fn process_cfc_72(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC100 custom function code + fn process_cfc_100(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC7101 custom function code + fn process_cfc_101(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC102 custom function code + fn process_cfc_102(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC103 custom function code + fn process_cfc_103(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC104 custom function code + fn process_cfc_104(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC105 custom function code + fn process_cfc_105(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC106 custom function code + fn process_cfc_106(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC107 custom function code + fn process_cfc_107(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC108 custom function code + fn process_cfc_108(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC109 custom function code + fn process_cfc_109(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + Err(ExceptionCode::IllegalFunction) + } + + /// Write the CFC110 custom function code + fn process_cfc_110(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } } @@ -232,8 +322,98 @@ pub trait AuthorizationHandler: Send + Sync + 'static { Authorization::Deny } - /// Authorize a Send Custom Function Code request - fn process_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { + /// Authorize a Send CFC65 request + fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC66 request + fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC67 request + fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC68 request + fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC69 request + fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC70 request + fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC71 request + fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC72 request + fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC100 request + fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC101 request + fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC102 request + fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC103 request + fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC104 request + fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC105 request + fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC106 request + fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC107 request + fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC108 request + fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC109 request + fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny + } + + /// Authorize a Send CFC110 request + fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } } @@ -315,8 +495,98 @@ impl AuthorizationHandler for ReadOnlyAuthorizationHandler { Authorization::Deny } - /// Authorize a Send Custom Function Code request - fn process_custom_function_code(&self, _value: CustomFunctionCode, _role: &str) -> Authorization { + /// Authorize a Send CFC65 request + fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC66 request + fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC67 request + fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC68 request + fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC69 request + fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC70 request + fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC71 request + fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC72 request + fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC100 request + fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC101 request + fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC102 request + fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC103 request + fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC104 request + fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC105 request + fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC106 request + fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC107 request + fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC108 request + fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC109 request + fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Allow + } + + /// Authorize a Send CFC110 request + fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } } @@ -328,15 +598,6 @@ mod tests { struct DefaultHandler; impl RequestHandler for DefaultHandler {} - #[test] - fn custom_fc() { - let mut handler = DefaultHandler {}; - assert_eq!( - handler.process_custom_function_code(CustomFunctionCode::new(0x46, vec![0x01, 0x02, 0x03, 0x04])), - Err(ExceptionCode::IllegalFunction) - ); - } - #[test] fn default_handler_returns_illegal_function() { let mut handler = DefaultHandler {}; @@ -361,6 +622,15 @@ mod tests { handler.write_single_register(Indexed::new(0, 0)), Err(ExceptionCode::IllegalFunction) ); + + assert_eq!( + handler.process_cfc_69(CustomFunctionCode::new(0x45, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_70(CustomFunctionCode::new(0x46, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); } #[test] From f7781022e12c7ab6239b9bd11e7d2b8fdddd40a9 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:50:56 +0100 Subject: [PATCH 071/106] refactor(task): adjust CFC request logic to use the correct handler for each CFC --- rodbus/src/server/task.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index f2a00efb..a0a01bc6 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -264,7 +264,31 @@ impl AuthorizationType { Request::WriteMultipleRegisters(x) => { handler.write_multiple_registers(unit_id, x.range, role) } - Request::SendCustomFunctionCode(x) => handler.process_custom_function_code(x.clone(), role), + Request::SendCustomFunctionCode(x) => { + match x.function_code() { + 0x41 => handler.process_cfc_65(unit_id, x.clone(), role), + 0x42 => handler.process_cfc_66(unit_id, x.clone(), role), + 0x43 => handler.process_cfc_67(unit_id, x.clone(), role), + 0x44 => handler.process_cfc_68(unit_id, x.clone(), role), + 0x45 => handler.process_cfc_69(unit_id, x.clone(), role), + 0x46 => handler.process_cfc_70(unit_id, x.clone(), role), + 0x47 => handler.process_cfc_71(unit_id, x.clone(), role), + 0x48 => handler.process_cfc_72(unit_id, x.clone(), role), + 0x64 => handler.process_cfc_100(unit_id, x.clone(), role), + 0x65 => handler.process_cfc_101(unit_id, x.clone(), role), + 0x66 => handler.process_cfc_102(unit_id, x.clone(), role), + 0x67 => handler.process_cfc_103(unit_id, x.clone(), role), + 0x68 => handler.process_cfc_104(unit_id, x.clone(), role), + 0x69 => handler.process_cfc_105(unit_id, x.clone(), role), + 0x6A => handler.process_cfc_106(unit_id, x.clone(), role), + 0x6B => handler.process_cfc_107(unit_id, x.clone(), role), + 0x6C => handler.process_cfc_108(unit_id, x.clone(), role), + 0x6D => handler.process_cfc_109(unit_id, x.clone(), role), + 0x6E => handler.process_cfc_110(unit_id, x.clone(), role), + _ => Authorization::Deny, + } + + }, } } From 5e6761488ac1684950b55121c786b3c84ee8803c Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:51:41 +0100 Subject: [PATCH 072/106] refactor(request): adjust CFC request logic to use the correct const FC number for each CFC request --- rodbus/src/server/request.rs | 62 ++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 9b8fe108..17a7f8a2 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -67,25 +67,25 @@ impl<'a> Request<'a> { Request::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, Request::SendCustomFunctionCode(x) => { match x.function_code() { - 65 => FunctionCode::SendCFC65, - 66 => FunctionCode::SendCFC66, - 67 => FunctionCode::SendCFC67, - 68 => FunctionCode::SendCFC68, - 69 => FunctionCode::SendCFC69, - 70 => FunctionCode::SendCFC70, - 71 => FunctionCode::SendCFC71, - 72 => FunctionCode::SendCFC72, - 100 => FunctionCode::SendCFC100, - 101 => FunctionCode::SendCFC101, - 102 => FunctionCode::SendCFC102, - 103 => FunctionCode::SendCFC103, - 104 => FunctionCode::SendCFC104, - 105 => FunctionCode::SendCFC105, - 106 => FunctionCode::SendCFC106, - 107 => FunctionCode::SendCFC107, - 108 => FunctionCode::SendCFC108, - 109 => FunctionCode::SendCFC109, - 110 => FunctionCode::SendCFC110, + 0x41 => FunctionCode::SendCFC65, + 0x42 => FunctionCode::SendCFC66, + 0x43 => FunctionCode::SendCFC67, + 0x44 => FunctionCode::SendCFC68, + 0x45 => FunctionCode::SendCFC69, + 0x46 => FunctionCode::SendCFC70, + 0x47 => FunctionCode::SendCFC71, + 0x48 => FunctionCode::SendCFC72, + 0x64 => FunctionCode::SendCFC100, + 0x65 => FunctionCode::SendCFC101, + 0x66 => FunctionCode::SendCFC102, + 0x67 => FunctionCode::SendCFC103, + 0x68 => FunctionCode::SendCFC104, + 0x69 => FunctionCode::SendCFC105, + 0x6A => FunctionCode::SendCFC106, + 0x6B => FunctionCode::SendCFC107, + 0x6C => FunctionCode::SendCFC108, + 0x6D => FunctionCode::SendCFC109, + 0x6E => FunctionCode::SendCFC110, _ => panic!("Invalid custom function code"), } }, @@ -168,7 +168,28 @@ impl<'a> Request<'a> { write_result(function, header, writer, result, level) } Request::SendCustomFunctionCode(request) => { - let result = handler.process_custom_function_code(request.clone()).map(|_| request.clone()); + let result = match function { + FunctionCode::SendCFC65 => handler.process_cfc_65(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC66 => handler.process_cfc_66(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC67 => handler.process_cfc_67(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC68 => handler.process_cfc_68(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC69 => handler.process_cfc_69(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC70 => handler.process_cfc_70(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC71 => handler.process_cfc_71(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC72 => handler.process_cfc_72(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC100 => handler.process_cfc_100(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC101 => handler.process_cfc_101(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC102 => handler.process_cfc_102(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC103 => handler.process_cfc_103(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC104 => handler.process_cfc_104(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC105 => handler.process_cfc_105(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC106 => handler.process_cfc_106(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC107 => handler.process_cfc_107(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC108 => handler.process_cfc_108(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC109 => handler.process_cfc_109(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC110 => handler.process_cfc_110(request.clone()).map(|_| request.clone()), + _ => Err(ExceptionCode::IllegalFunction), + }; write_result(function, header, writer, result, level) } } @@ -236,7 +257,6 @@ impl<'a> Request<'a> { FunctionCode::SendCFC104 | FunctionCode::SendCFC105 | FunctionCode::SendCFC106 | FunctionCode::SendCFC107 | FunctionCode::SendCFC108 | FunctionCode::SendCFC109 | FunctionCode::SendCFC110 => { let x = Request::SendCustomFunctionCode(CustomFunctionCode::parse(cursor)?); - cursor.expect_empty()?; Ok(x) } From dcfdfe6defc14fcb23bfc5eabff0595c2d9abaef Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:52:43 +0100 Subject: [PATCH 073/106] refactor(custom_client): add cfc69 requesthandler to test the CFC69 request --- rodbus/examples/custom_server.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 375d2dd9..0662b16f 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -78,19 +78,11 @@ impl RequestHandler for SimpleHandler { } } - fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { - tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); - - match values.function_code() { - 0x45 => { - tracing::info!("custom function code 0x45"); - // call CFC69Handler.handle and return result - Ok(()) - }, - _ => { - return Err(ExceptionCode::IllegalFunction); - } - } + fn process_cfc_69(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { + tracing::info!("processing custom function code: {}", values.function_code()); + // increment each CFC value by 1 and return the result + let incremented_values = values.iter().map(|v| v + 1).collect(); + Ok(CustomFunctionCode::new(values.function_code(), incremented_values)) } fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { From 9c7abd01a9b5c803a40aed9435cafec63b9ff268 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Thu, 22 Feb 2024 20:54:08 +0100 Subject: [PATCH 074/106] refactor(task): adjust CFC cient request logic to use the correct const FC number for each CFC --- rodbus/src/client/message.rs | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 01385b82..91ac61d2 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -133,25 +133,25 @@ impl RequestDetails { RequestDetails::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, RequestDetails::SendCustomFunctionCode(x) => { match x.request.function_code() { - 65 => FunctionCode::SendCFC65, - 66 => FunctionCode::SendCFC66, - 67 => FunctionCode::SendCFC67, - 68 => FunctionCode::SendCFC68, - 69 => FunctionCode::SendCFC69, - 70 => FunctionCode::SendCFC70, - 71 => FunctionCode::SendCFC71, - 72 => FunctionCode::SendCFC72, - 100 => FunctionCode::SendCFC100, - 101 => FunctionCode::SendCFC101, - 102 => FunctionCode::SendCFC102, - 103 => FunctionCode::SendCFC103, - 104 => FunctionCode::SendCFC104, - 105 => FunctionCode::SendCFC105, - 106 => FunctionCode::SendCFC106, - 107 => FunctionCode::SendCFC107, - 108 => FunctionCode::SendCFC108, - 109 => FunctionCode::SendCFC109, - 110 => FunctionCode::SendCFC110, + 0x41 => FunctionCode::SendCFC65, + 0x42 => FunctionCode::SendCFC66, + 0x43 => FunctionCode::SendCFC67, + 0x44 => FunctionCode::SendCFC68, + 0x45 => FunctionCode::SendCFC69, + 0x46 => FunctionCode::SendCFC70, + 0x47 => FunctionCode::SendCFC71, + 0x48 => FunctionCode::SendCFC72, + 0x64 => FunctionCode::SendCFC100, + 0x65 => FunctionCode::SendCFC101, + 0x66 => FunctionCode::SendCFC102, + 0x67 => FunctionCode::SendCFC103, + 0x68 => FunctionCode::SendCFC104, + 0x69 => FunctionCode::SendCFC105, + 0x6A => FunctionCode::SendCFC106, + 0x6B => FunctionCode::SendCFC107, + 0x6C => FunctionCode::SendCFC108, + 0x6D => FunctionCode::SendCFC109, + 0x6E => FunctionCode::SendCFC110, _ => panic!("unsupported custom function code"), } }, From c0261cdf50a2866316b5d1f902968f4f24fef171 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 23 Feb 2024 19:14:15 +0100 Subject: [PATCH 075/106] fix(custom_fc): fix CFC69 response always the same --- rodbus/examples/custom_server.rs | 12 +++++++--- rodbus/src/common/serialize.rs | 12 +++++----- rodbus/src/server/handler.rs | 4 ++-- rodbus/src/server/request.rs | 38 ++++++++++++++++---------------- rodbus/src/server/task.rs | 2 +- rodbus/tests/integration_test.rs | 2 +- 6 files changed, 38 insertions(+), 32 deletions(-) diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 0662b16f..899e43df 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -78,11 +78,17 @@ impl RequestHandler for SimpleHandler { } } - fn process_cfc_69(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_69(&mut self, values: &CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}", values.function_code()); // increment each CFC value by 1 and return the result - let incremented_values = values.iter().map(|v| v + 1).collect(); - Ok(CustomFunctionCode::new(values.function_code(), incremented_values)) + // Create a new vector to hold the incremented values + let incremented_data = values.iter().map(|&val| val + 1).collect(); + + // Create a new CustomFunctionCode with the incremented data + let modified_values = CustomFunctionCode::new(values.function_code(), incremented_data); + + Ok(modified_values) + //Ok(&CustomFunctionCode::new(values.function_code(), incremented_values)) } fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 131ecfcb..c7572d13 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -290,7 +290,7 @@ impl Serialize for WriteMultiple { } } -impl Serialize for CustomFunctionCode { +impl Serialize for &CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u8(self.function_code())?; @@ -301,7 +301,7 @@ impl Serialize for CustomFunctionCode { } } -impl Loggable for CustomFunctionCode { +impl Loggable for &CustomFunctionCode { fn log( &self, payload: &[u8], @@ -353,7 +353,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_min_value() { - let custom_fc = CustomFunctionCode::new(1, vec![0x0000]); + let custom_fc = &CustomFunctionCode::new(1, vec![0x0000]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -362,7 +362,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_max_value() { - let custom_fc = CustomFunctionCode::new(1, vec![0xFFFF]); + let custom_fc = &CustomFunctionCode::new(1, vec![0xFFFF]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -371,7 +371,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_min_values() { - let custom_fc = CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]); + let custom_fc = &CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -380,7 +380,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_max_values() { - let custom_fc = CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]); + let custom_fc = &CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index de1296f9..338eb570 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -84,7 +84,7 @@ pub trait RequestHandler: Send + 'static { } /// Write the CFC69 custom function code - fn process_cfc_69(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_69(&mut self, _values: &CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } @@ -624,7 +624,7 @@ mod tests { ); assert_eq!( - handler.process_cfc_69(CustomFunctionCode::new(0x45, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc_69(&CustomFunctionCode::new(0x45, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 17a7f8a2..8ae90405 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -169,25 +169,25 @@ impl<'a> Request<'a> { } Request::SendCustomFunctionCode(request) => { let result = match function { - FunctionCode::SendCFC65 => handler.process_cfc_65(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC66 => handler.process_cfc_66(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC67 => handler.process_cfc_67(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC68 => handler.process_cfc_68(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC69 => handler.process_cfc_69(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC70 => handler.process_cfc_70(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC71 => handler.process_cfc_71(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC72 => handler.process_cfc_72(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC100 => handler.process_cfc_100(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC101 => handler.process_cfc_101(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC102 => handler.process_cfc_102(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC103 => handler.process_cfc_103(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC104 => handler.process_cfc_104(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC105 => handler.process_cfc_105(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC106 => handler.process_cfc_106(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC107 => handler.process_cfc_107(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC108 => handler.process_cfc_108(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC109 => handler.process_cfc_109(request.clone()).map(|_| request.clone()), - FunctionCode::SendCFC110 => handler.process_cfc_110(request.clone()).map(|_| request.clone()), + FunctionCode::SendCFC65 => handler.process_cfc_65(request.clone()).map(|_| request), + FunctionCode::SendCFC66 => handler.process_cfc_66(request.clone()).map(|_| request), + FunctionCode::SendCFC67 => handler.process_cfc_67(request.clone()).map(|_| request), + FunctionCode::SendCFC68 => handler.process_cfc_68(request.clone()).map(|_| request), + FunctionCode::SendCFC69 => handler.process_cfc_69(request).map(|_| request), + FunctionCode::SendCFC70 => handler.process_cfc_70(request.clone()).map(|_| request), + FunctionCode::SendCFC71 => handler.process_cfc_71(request.clone()).map(|_| request), + FunctionCode::SendCFC72 => handler.process_cfc_72(request.clone()).map(|_| request), + FunctionCode::SendCFC100 => handler.process_cfc_100(request.clone()).map(|_| request), + FunctionCode::SendCFC101 => handler.process_cfc_101(request.clone()).map(|_| request), + FunctionCode::SendCFC102 => handler.process_cfc_102(request.clone()).map(|_| request), + FunctionCode::SendCFC103 => handler.process_cfc_103(request.clone()).map(|_| request), + FunctionCode::SendCFC104 => handler.process_cfc_104(request.clone()).map(|_| request), + FunctionCode::SendCFC105 => handler.process_cfc_105(request.clone()).map(|_| request), + FunctionCode::SendCFC106 => handler.process_cfc_106(request.clone()).map(|_| request), + FunctionCode::SendCFC107 => handler.process_cfc_107(request.clone()).map(|_| request), + FunctionCode::SendCFC108 => handler.process_cfc_108(request.clone()).map(|_| request), + FunctionCode::SendCFC109 => handler.process_cfc_109(request.clone()).map(|_| request), + FunctionCode::SendCFC110 => handler.process_cfc_110(request.clone()).map(|_| request), _ => Err(ExceptionCode::IllegalFunction), }; write_result(function, header, writer, result, level) diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index a0a01bc6..a1b578c4 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -270,7 +270,7 @@ impl AuthorizationType { 0x42 => handler.process_cfc_66(unit_id, x.clone(), role), 0x43 => handler.process_cfc_67(unit_id, x.clone(), role), 0x44 => handler.process_cfc_68(unit_id, x.clone(), role), - 0x45 => handler.process_cfc_69(unit_id, x.clone(), role), + 0x45 => handler.process_cfc_69(unit_id, *x, role), 0x46 => handler.process_cfc_70(unit_id, x.clone(), role), 0x47 => handler.process_cfc_71(unit_id, x.clone(), role), 0x48 => handler.process_cfc_72(unit_id, x.clone(), role), diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index a79c898d..3260451f 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -95,7 +95,7 @@ impl RequestHandler for Handler { Ok(()) } - fn process_cfc_69(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_69(&mut self, values: &CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); // increment each CFC value by 1 and return the result let incremented_values = values.iter().map(|&x| x + 1).collect(); From 5262d8768fdc392c42ee73fdb01749ffec84af51 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 23 Feb 2024 19:25:49 +0100 Subject: [PATCH 076/106] refactor(custom_fc): implement copy trait for CFC --- rodbus/examples/custom_server.rs | 2 +- rodbus/src/client/channel.rs | 4 +- rodbus/src/client/message.rs | 2 +- rodbus/src/client/requests/send_custom_fc.rs | 2 +- rodbus/src/common/parse.rs | 2 +- rodbus/src/common/serialize.rs | 12 +- rodbus/src/server/handler.rs | 116 +++++++++---------- rodbus/src/server/request.rs | 2 +- rodbus/src/types.rs | 10 +- rodbus/tests/integration_test.rs | 2 +- 10 files changed, 77 insertions(+), 77 deletions(-) diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 899e43df..43d52292 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -78,7 +78,7 @@ impl RequestHandler for SimpleHandler { } } - fn process_cfc_69(&mut self, values: &CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_69(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}", values.function_code()); // increment each CFC value by 1 and return the result // Create a new vector to hold the incremented values diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index c47f5bf8..5509f412 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -169,8 +169,8 @@ impl Channel { pub async fn send_custom_function_code( &mut self, param: RequestParam, - request: CustomFunctionCode, - ) -> Result, RequestError> { + request: CustomFunctionCode, + ) -> Result { let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); let request = wrap( param, diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 91ac61d2..43ef934b 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -46,7 +46,7 @@ pub(crate) enum RequestDetails { WriteSingleRegister(SingleWrite>), WriteMultipleCoils(MultipleWriteRequest), WriteMultipleRegisters(MultipleWriteRequest), - SendCustomFunctionCode(CustomFCRequest>), + SendCustomFunctionCode(CustomFCRequest), } impl Request { diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index c51aa22e..50ef9d45 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -66,7 +66,7 @@ where } } -impl CustomFCOperation for CustomFunctionCode { +impl CustomFCOperation for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u8(self.function_code())?; diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 89baa648..c8a69712 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -28,7 +28,7 @@ impl Parse for Indexed { } } -impl Parse for CustomFunctionCode { +impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { let fc = cursor.read_u8()?; let len = cursor.remaining() / 2; diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index c7572d13..8da1530c 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -290,7 +290,7 @@ impl Serialize for WriteMultiple { } } -impl Serialize for &CustomFunctionCode { +impl Serialize for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u8(self.function_code())?; @@ -301,7 +301,7 @@ impl Serialize for &CustomFunctionCode { } } -impl Loggable for &CustomFunctionCode { +impl Loggable for CustomFunctionCode { fn log( &self, payload: &[u8], @@ -353,7 +353,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_min_value() { - let custom_fc = &CustomFunctionCode::new(1, vec![0x0000]); + let custom_fc = CustomFunctionCode::new(1, vec![0x0000]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -362,7 +362,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_max_value() { - let custom_fc = &CustomFunctionCode::new(1, vec![0xFFFF]); + let custom_fc = CustomFunctionCode::new(1, vec![0xFFFF]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -371,7 +371,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_min_values() { - let custom_fc = &CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]); + let custom_fc = CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -380,7 +380,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_max_values() { - let custom_fc = &CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]); + let custom_fc = CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 338eb570..772b7a32 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -64,97 +64,97 @@ pub trait RequestHandler: Send + 'static { } /// Write the CFC65 custom function code - fn process_cfc_65(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_65(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC66 custom function code - fn process_cfc_66(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_66(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC67 custom function code - fn process_cfc_67(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_67(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC68 custom function code - fn process_cfc_68(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_68(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC69 custom function code - fn process_cfc_69(&mut self, _values: &CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_69(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC70 custom function code - fn process_cfc_70(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_70(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC71 custom function code - fn process_cfc_71(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_71(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC72 custom function code - fn process_cfc_72(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_72(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC100 custom function code - fn process_cfc_100(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_100(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC7101 custom function code - fn process_cfc_101(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_101(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC102 custom function code - fn process_cfc_102(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_102(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC103 custom function code - fn process_cfc_103(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_103(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC104 custom function code - fn process_cfc_104(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_104(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC105 custom function code - fn process_cfc_105(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_105(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC106 custom function code - fn process_cfc_106(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_106(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC107 custom function code - fn process_cfc_107(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_107(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC108 custom function code - fn process_cfc_108(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_108(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC109 custom function code - fn process_cfc_109(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_109(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } /// Write the CFC110 custom function code - fn process_cfc_110(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_110(&mut self, _values: CustomFunctionCode) -> Result { Err(ExceptionCode::IllegalFunction) } } @@ -323,97 +323,97 @@ pub trait AuthorizationHandler: Send + Sync + 'static { } /// Authorize a Send CFC65 request - fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC66 request - fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC67 request - fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC68 request - fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC69 request - fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC70 request - fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC71 request - fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC72 request - fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC100 request - fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC101 request - fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC102 request - fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC103 request - fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC104 request - fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC105 request - fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC106 request - fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC107 request - fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC108 request - fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC109 request - fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC110 request - fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } } @@ -496,97 +496,97 @@ impl AuthorizationHandler for ReadOnlyAuthorizationHandler { } /// Authorize a Send CFC65 request - fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC66 request - fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC67 request - fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC68 request - fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC69 request - fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC70 request - fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC71 request - fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC72 request - fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC100 request - fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC101 request - fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC102 request - fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC103 request - fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC104 request - fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC105 request - fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC106 request - fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC107 request - fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC108 request - fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC109 request - fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC110 request - fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } } @@ -624,7 +624,7 @@ mod tests { ); assert_eq!( - handler.process_cfc_69(&CustomFunctionCode::new(0x45, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc_69(CustomFunctionCode::new(0x45, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 8ae90405..3bb53772 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -21,7 +21,7 @@ pub(crate) enum Request<'a> { WriteSingleRegister(Indexed), WriteMultipleCoils(WriteCoils<'a>), WriteMultipleRegisters(WriteRegisters<'a>), - SendCustomFunctionCode(CustomFunctionCode), + SendCustomFunctionCode(CustomFunctionCode), } /// All requests that support broadcast diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 42706202..d230bf8c 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -86,10 +86,10 @@ pub(crate) struct RegisterIteratorDisplay<'a> { } /// Custom Function Code -#[derive(Clone, Debug, PartialEq)] -pub struct CustomFunctionCode { +#[derive(Clone, Debug, Copy, PartialEq)] +pub struct CustomFunctionCode{ fc: u8, - data: Vec, + data: Vec, } impl std::fmt::Display for UnitId { @@ -374,7 +374,7 @@ impl Default for UnitId { } } -impl CustomFunctionCode { +impl CustomFunctionCode { /// Create a new custom function code pub fn new(fc: u8, data: Vec) -> Self { Self { fc, data } @@ -396,7 +396,7 @@ impl CustomFunctionCode { } } -impl std::fmt::Display for CustomFunctionCode { +impl std::fmt::Display for CustomFunctionCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "values: [")?; for (i, val) in self.data.iter().enumerate() { diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index 3260451f..a79c898d 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -95,7 +95,7 @@ impl RequestHandler for Handler { Ok(()) } - fn process_cfc_69(&mut self, values: &CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_69(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); // increment each CFC value by 1 and return the result let incremented_values = values.iter().map(|&x| x + 1).collect(); From 0048131fcb0eeb2bc94216a4d873fce42af67715 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Fri, 23 Feb 2024 21:57:18 +0100 Subject: [PATCH 077/106] fix(custom_fc): add lifetime specifier to CFC --- rodbus/src/client/channel.rs | 6 +-- rodbus/src/client/message.rs | 2 +- rodbus/src/client/requests/send_custom_fc.rs | 4 +- rodbus/src/common/parse.rs | 14 +++---- rodbus/src/common/serialize.rs | 14 +++---- rodbus/src/server/handler.rs | 4 +- rodbus/src/server/request.rs | 40 ++++++++++---------- rodbus/src/types.rs | 10 ++--- 8 files changed, 47 insertions(+), 47 deletions(-) diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index 5509f412..7d2b9b83 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -169,9 +169,9 @@ impl Channel { pub async fn send_custom_function_code( &mut self, param: RequestParam, - request: CustomFunctionCode, - ) -> Result { - let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); + request: CustomFunctionCode<'static>, + ) -> Result, RequestError> { + let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); let request = wrap( param, RequestDetails::SendCustomFunctionCode(CustomFCRequest::new(request, Promise::channel(tx))), diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 43ef934b..c69bad69 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -46,7 +46,7 @@ pub(crate) enum RequestDetails { WriteSingleRegister(SingleWrite>), WriteMultipleCoils(MultipleWriteRequest), WriteMultipleRegisters(MultipleWriteRequest), - SendCustomFunctionCode(CustomFCRequest), + SendCustomFunctionCode(CustomFCRequest>), } impl Request { diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index 50ef9d45..f7fdb3cf 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -66,7 +66,7 @@ where } } -impl CustomFCOperation for CustomFunctionCode { +impl<'a> CustomFCOperation for CustomFunctionCode<'a> { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u8(self.function_code())?; @@ -86,6 +86,6 @@ impl CustomFCOperation for CustomFunctionCode { } cursor.expect_empty()?; - Ok(CustomFunctionCode::new(fc, values)) + Ok(CustomFunctionCode::new(fc, &values)) } } diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index c8a69712..f49b9873 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -28,7 +28,7 @@ impl Parse for Indexed { } } -impl Parse for CustomFunctionCode { +impl<'a> Parse for CustomFunctionCode<'a> { fn parse(cursor: &mut ReadCursor) -> Result { let fc = cursor.read_u8()?; let len = cursor.remaining() / 2; @@ -38,8 +38,8 @@ impl Parse for CustomFunctionCode { values.push(cursor.read_u16_be()?); } cursor.expect_empty()?; - - Ok(CustomFunctionCode::new(fc, values)) + + Ok(CustomFunctionCode::new(fc, &values)) } } @@ -93,28 +93,28 @@ mod custom_fc { fn parse_succeeds_for_single_min_value() { let mut cursor = ReadCursor::new(&[0x00, 0x01, 0x00, 0x00]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(1, vec![0x0000]))); + assert_eq!(result, Ok(CustomFunctionCode::new(1, &[0x0000]))); } #[test] fn parse_succeeds_for_single_max_value() { let mut cursor = ReadCursor::new(&[0x00, 0x01, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(1, vec![0xFFFF]))); + assert_eq!(result, Ok(CustomFunctionCode::new(1, &[0xFFFF]))); } #[test] fn parse_succeeds_for_multiple_min_values() { let mut cursor = ReadCursor::new(&[0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]))); + assert_eq!(result, Ok(CustomFunctionCode::new(3, &[0x0000, 0x0000, 0x0000]))); } #[test] fn parse_succeeds_for_multiple_max_values() { let mut cursor = ReadCursor::new(&[0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]))); + assert_eq!(result, Ok(CustomFunctionCode::new(3, &[0xFFFF, 0xFFFF, 0xFFFF]))); } #[test] diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 8da1530c..82544cee 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -290,7 +290,7 @@ impl Serialize for WriteMultiple { } } -impl Serialize for CustomFunctionCode { +impl<'a> Serialize for CustomFunctionCode<'a> { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u8(self.function_code())?; @@ -301,7 +301,7 @@ impl Serialize for CustomFunctionCode { } } -impl Loggable for CustomFunctionCode { +impl<'a> Loggable for CustomFunctionCode<'a> { fn log( &self, payload: &[u8], @@ -328,7 +328,7 @@ impl Loggable for CustomFunctionCode { data.push(item); } - let custom_fc = CustomFunctionCode::::new(fc, data); + let custom_fc = CustomFunctionCode::new(fc, &data); write!(f, "{:?}", custom_fc)?; @@ -353,7 +353,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_min_value() { - let custom_fc = CustomFunctionCode::new(1, vec![0x0000]); + let custom_fc = CustomFunctionCode::new(1, &[0x0000]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -362,7 +362,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_max_value() { - let custom_fc = CustomFunctionCode::new(1, vec![0xFFFF]); + let custom_fc = CustomFunctionCode::new(1, &[0xFFFF]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -371,7 +371,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_min_values() { - let custom_fc = CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]); + let custom_fc = CustomFunctionCode::new(3, &[0x0000, 0x0000, 0x0000]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -380,7 +380,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_max_values() { - let custom_fc = CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]); + let custom_fc = CustomFunctionCode::new(3, &[0xFFFF, 0xFFFF, 0xFFFF]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 772b7a32..00571aa5 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -624,11 +624,11 @@ mod tests { ); assert_eq!( - handler.process_cfc_69(CustomFunctionCode::new(0x45, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc_69(CustomFunctionCode::new(0x45, &[0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_70(CustomFunctionCode::new(0x46, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc_70(CustomFunctionCode::new(0x46, &[0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); } diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 3bb53772..38984d09 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -21,7 +21,7 @@ pub(crate) enum Request<'a> { WriteSingleRegister(Indexed), WriteMultipleCoils(WriteCoils<'a>), WriteMultipleRegisters(WriteRegisters<'a>), - SendCustomFunctionCode(CustomFunctionCode), + SendCustomFunctionCode(CustomFunctionCode<'a>), } /// All requests that support broadcast @@ -169,25 +169,25 @@ impl<'a> Request<'a> { } Request::SendCustomFunctionCode(request) => { let result = match function { - FunctionCode::SendCFC65 => handler.process_cfc_65(request.clone()).map(|_| request), - FunctionCode::SendCFC66 => handler.process_cfc_66(request.clone()).map(|_| request), - FunctionCode::SendCFC67 => handler.process_cfc_67(request.clone()).map(|_| request), - FunctionCode::SendCFC68 => handler.process_cfc_68(request.clone()).map(|_| request), - FunctionCode::SendCFC69 => handler.process_cfc_69(request).map(|_| request), - FunctionCode::SendCFC70 => handler.process_cfc_70(request.clone()).map(|_| request), - FunctionCode::SendCFC71 => handler.process_cfc_71(request.clone()).map(|_| request), - FunctionCode::SendCFC72 => handler.process_cfc_72(request.clone()).map(|_| request), - FunctionCode::SendCFC100 => handler.process_cfc_100(request.clone()).map(|_| request), - FunctionCode::SendCFC101 => handler.process_cfc_101(request.clone()).map(|_| request), - FunctionCode::SendCFC102 => handler.process_cfc_102(request.clone()).map(|_| request), - FunctionCode::SendCFC103 => handler.process_cfc_103(request.clone()).map(|_| request), - FunctionCode::SendCFC104 => handler.process_cfc_104(request.clone()).map(|_| request), - FunctionCode::SendCFC105 => handler.process_cfc_105(request.clone()).map(|_| request), - FunctionCode::SendCFC106 => handler.process_cfc_106(request.clone()).map(|_| request), - FunctionCode::SendCFC107 => handler.process_cfc_107(request.clone()).map(|_| request), - FunctionCode::SendCFC108 => handler.process_cfc_108(request.clone()).map(|_| request), - FunctionCode::SendCFC109 => handler.process_cfc_109(request.clone()).map(|_| request), - FunctionCode::SendCFC110 => handler.process_cfc_110(request.clone()).map(|_| request), + FunctionCode::SendCFC65 => handler.process_cfc_65(request.clone()).map(|_| *request), + FunctionCode::SendCFC66 => handler.process_cfc_66(request.clone()).map(|_| *request), + FunctionCode::SendCFC67 => handler.process_cfc_67(request.clone()).map(|_| *request), + FunctionCode::SendCFC68 => handler.process_cfc_68(request.clone()).map(|_| *request), + FunctionCode::SendCFC69 => handler.process_cfc_69(*request).map(|_| *request), + FunctionCode::SendCFC70 => handler.process_cfc_70(request.clone()).map(|_| *request), + FunctionCode::SendCFC71 => handler.process_cfc_71(request.clone()).map(|_| *request), + FunctionCode::SendCFC72 => handler.process_cfc_72(request.clone()).map(|_| *request), + FunctionCode::SendCFC100 => handler.process_cfc_100(request.clone()).map(|_| *request), + FunctionCode::SendCFC101 => handler.process_cfc_101(request.clone()).map(|_| *request), + FunctionCode::SendCFC102 => handler.process_cfc_102(request.clone()).map(|_| *request), + FunctionCode::SendCFC103 => handler.process_cfc_103(request.clone()).map(|_| *request), + FunctionCode::SendCFC104 => handler.process_cfc_104(request.clone()).map(|_| *request), + FunctionCode::SendCFC105 => handler.process_cfc_105(request.clone()).map(|_| *request), + FunctionCode::SendCFC106 => handler.process_cfc_106(request.clone()).map(|_| *request), + FunctionCode::SendCFC107 => handler.process_cfc_107(request.clone()).map(|_| *request), + FunctionCode::SendCFC108 => handler.process_cfc_108(request.clone()).map(|_| *request), + FunctionCode::SendCFC109 => handler.process_cfc_109(request.clone()).map(|_| *request), + FunctionCode::SendCFC110 => handler.process_cfc_110(request.clone()).map(|_| *request), _ => Err(ExceptionCode::IllegalFunction), }; write_result(function, header, writer, result, level) diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index d230bf8c..8f744078 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -87,9 +87,9 @@ pub(crate) struct RegisterIteratorDisplay<'a> { /// Custom Function Code #[derive(Clone, Debug, Copy, PartialEq)] -pub struct CustomFunctionCode{ +pub struct CustomFunctionCode<'a> { fc: u8, - data: Vec, + data: &'a [u16], } impl std::fmt::Display for UnitId { @@ -374,9 +374,9 @@ impl Default for UnitId { } } -impl CustomFunctionCode { +impl<'a> CustomFunctionCode<'a> { /// Create a new custom function code - pub fn new(fc: u8, data: Vec) -> Self { + pub fn new(fc: u8, data: &'a [u16]) -> Self { Self { fc, data } } @@ -396,7 +396,7 @@ impl CustomFunctionCode { } } -impl std::fmt::Display for CustomFunctionCode { +impl<'a> std::fmt::Display for CustomFunctionCode<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "values: [")?; for (i, val) in self.data.iter().enumerate() { From 7318370c2936a9998fb7d3bf583277d28dcc4d86 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 4 Mar 2024 15:58:19 +0100 Subject: [PATCH 078/106] fix(send_custom_fc): remove the introduced lifetime specifier from the CustomFunctionCode trait --- rodbus/src/client/channel.rs | 6 +- rodbus/src/client/message.rs | 2 +- rodbus/src/client/requests/send_custom_fc.rs | 4 +- rodbus/src/common/parse.rs | 12 +- rodbus/src/common/serialize.rs | 14 +-- rodbus/src/server/handler.rs | 118 +++++++++---------- rodbus/src/types.rs | 12 +- 7 files changed, 84 insertions(+), 84 deletions(-) diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index 7d2b9b83..c47f5bf8 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -169,9 +169,9 @@ impl Channel { pub async fn send_custom_function_code( &mut self, param: RequestParam, - request: CustomFunctionCode<'static>, - ) -> Result, RequestError> { - let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); + request: CustomFunctionCode, + ) -> Result, RequestError> { + let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); let request = wrap( param, RequestDetails::SendCustomFunctionCode(CustomFCRequest::new(request, Promise::channel(tx))), diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index c69bad69..91ac61d2 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -46,7 +46,7 @@ pub(crate) enum RequestDetails { WriteSingleRegister(SingleWrite>), WriteMultipleCoils(MultipleWriteRequest), WriteMultipleRegisters(MultipleWriteRequest), - SendCustomFunctionCode(CustomFCRequest>), + SendCustomFunctionCode(CustomFCRequest>), } impl Request { diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index f7fdb3cf..c51aa22e 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -66,7 +66,7 @@ where } } -impl<'a> CustomFCOperation for CustomFunctionCode<'a> { +impl CustomFCOperation for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u8(self.function_code())?; @@ -86,6 +86,6 @@ impl<'a> CustomFCOperation for CustomFunctionCode<'a> { } cursor.expect_empty()?; - Ok(CustomFunctionCode::new(fc, &values)) + Ok(CustomFunctionCode::new(fc, values)) } } diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index f49b9873..1c021680 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -28,7 +28,7 @@ impl Parse for Indexed { } } -impl<'a> Parse for CustomFunctionCode<'a> { +impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { let fc = cursor.read_u8()?; let len = cursor.remaining() / 2; @@ -39,7 +39,7 @@ impl<'a> Parse for CustomFunctionCode<'a> { } cursor.expect_empty()?; - Ok(CustomFunctionCode::new(fc, &values)) + Ok(CustomFunctionCode::new(fc, values)) } } @@ -93,28 +93,28 @@ mod custom_fc { fn parse_succeeds_for_single_min_value() { let mut cursor = ReadCursor::new(&[0x00, 0x01, 0x00, 0x00]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(1, &[0x0000]))); + assert_eq!(result, Ok(CustomFunctionCode::new(1, vec![0x0000]))); } #[test] fn parse_succeeds_for_single_max_value() { let mut cursor = ReadCursor::new(&[0x00, 0x01, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(1, &[0xFFFF]))); + assert_eq!(result, Ok(CustomFunctionCode::new(1, vec![0xFFFF]))); } #[test] fn parse_succeeds_for_multiple_min_values() { let mut cursor = ReadCursor::new(&[0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(3, &[0x0000, 0x0000, 0x0000]))); + assert_eq!(result, Ok(CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]))); } #[test] fn parse_succeeds_for_multiple_max_values() { let mut cursor = ReadCursor::new(&[0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(3, &[0xFFFF, 0xFFFF, 0xFFFF]))); + assert_eq!(result, Ok(CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]))); } #[test] diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 82544cee..a7234dc5 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -290,7 +290,7 @@ impl Serialize for WriteMultiple { } } -impl<'a> Serialize for CustomFunctionCode<'a> { +impl Serialize for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u8(self.function_code())?; @@ -301,7 +301,7 @@ impl<'a> Serialize for CustomFunctionCode<'a> { } } -impl<'a> Loggable for CustomFunctionCode<'a> { +impl Loggable for CustomFunctionCode { fn log( &self, payload: &[u8], @@ -328,7 +328,7 @@ impl<'a> Loggable for CustomFunctionCode<'a> { data.push(item); } - let custom_fc = CustomFunctionCode::new(fc, &data); + let custom_fc = CustomFunctionCode::new(fc, data); write!(f, "{:?}", custom_fc)?; @@ -353,7 +353,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_min_value() { - let custom_fc = CustomFunctionCode::new(1, &[0x0000]); + let custom_fc = CustomFunctionCode::new(1, vec![0x0000]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -362,7 +362,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_max_value() { - let custom_fc = CustomFunctionCode::new(1, &[0xFFFF]); + let custom_fc = CustomFunctionCode::new(1, vec![0xFFFF]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -371,7 +371,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_min_values() { - let custom_fc = CustomFunctionCode::new(3, &[0x0000, 0x0000, 0x0000]); + let custom_fc = CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -380,7 +380,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_max_values() { - let custom_fc = CustomFunctionCode::new(3, &[0xFFFF, 0xFFFF, 0xFFFF]); + let custom_fc = CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 00571aa5..de1296f9 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -64,97 +64,97 @@ pub trait RequestHandler: Send + 'static { } /// Write the CFC65 custom function code - fn process_cfc_65(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_65(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC66 custom function code - fn process_cfc_66(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_66(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC67 custom function code - fn process_cfc_67(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_67(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC68 custom function code - fn process_cfc_68(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_68(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC69 custom function code - fn process_cfc_69(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_69(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC70 custom function code - fn process_cfc_70(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_70(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC71 custom function code - fn process_cfc_71(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_71(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC72 custom function code - fn process_cfc_72(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_72(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC100 custom function code - fn process_cfc_100(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_100(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC7101 custom function code - fn process_cfc_101(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_101(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC102 custom function code - fn process_cfc_102(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_102(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC103 custom function code - fn process_cfc_103(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_103(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC104 custom function code - fn process_cfc_104(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_104(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC105 custom function code - fn process_cfc_105(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_105(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC106 custom function code - fn process_cfc_106(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_106(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC107 custom function code - fn process_cfc_107(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_107(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC108 custom function code - fn process_cfc_108(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_108(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC109 custom function code - fn process_cfc_109(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_109(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } /// Write the CFC110 custom function code - fn process_cfc_110(&mut self, _values: CustomFunctionCode) -> Result { + fn process_cfc_110(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } } @@ -323,97 +323,97 @@ pub trait AuthorizationHandler: Send + Sync + 'static { } /// Authorize a Send CFC65 request - fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC66 request - fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC67 request - fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC68 request - fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC69 request - fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC70 request - fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC71 request - fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC72 request - fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC100 request - fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC101 request - fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC102 request - fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC103 request - fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC104 request - fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC105 request - fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC106 request - fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC107 request - fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC108 request - fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC109 request - fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } /// Authorize a Send CFC110 request - fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } } @@ -496,97 +496,97 @@ impl AuthorizationHandler for ReadOnlyAuthorizationHandler { } /// Authorize a Send CFC65 request - fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC66 request - fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC67 request - fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC68 request - fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC69 request - fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC70 request - fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC71 request - fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC72 request - fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC100 request - fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC101 request - fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC102 request - fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC103 request - fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC104 request - fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC105 request - fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC106 request - fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC107 request - fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC108 request - fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC109 request - fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } /// Authorize a Send CFC110 request - fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Allow } } @@ -624,11 +624,11 @@ mod tests { ); assert_eq!( - handler.process_cfc_69(CustomFunctionCode::new(0x45, &[0x01, 0x02, 0x03, 0x04])), + handler.process_cfc_69(CustomFunctionCode::new(0x45, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_70(CustomFunctionCode::new(0x46, &[0x01, 0x02, 0x03, 0x04])), + handler.process_cfc_70(CustomFunctionCode::new(0x46, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); } diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 8f744078..42706202 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -86,10 +86,10 @@ pub(crate) struct RegisterIteratorDisplay<'a> { } /// Custom Function Code -#[derive(Clone, Debug, Copy, PartialEq)] -pub struct CustomFunctionCode<'a> { +#[derive(Clone, Debug, PartialEq)] +pub struct CustomFunctionCode { fc: u8, - data: &'a [u16], + data: Vec, } impl std::fmt::Display for UnitId { @@ -374,9 +374,9 @@ impl Default for UnitId { } } -impl<'a> CustomFunctionCode<'a> { +impl CustomFunctionCode { /// Create a new custom function code - pub fn new(fc: u8, data: &'a [u16]) -> Self { + pub fn new(fc: u8, data: Vec) -> Self { Self { fc, data } } @@ -396,7 +396,7 @@ impl<'a> CustomFunctionCode<'a> { } } -impl<'a> std::fmt::Display for CustomFunctionCode<'a> { +impl std::fmt::Display for CustomFunctionCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "values: [")?; for (i, val) in self.data.iter().enumerate() { From 9912be2131bbe7397a3fad98b6601865b0a127df Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 4 Mar 2024 16:10:24 +0100 Subject: [PATCH 079/106] fix(send_custom_fc): fix the unmodified CFC return object --- rodbus/src/server/request.rs | 40 ++++++++++++++++++------------------ rodbus/src/server/task.rs | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 38984d09..7a2f2507 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -21,7 +21,7 @@ pub(crate) enum Request<'a> { WriteSingleRegister(Indexed), WriteMultipleCoils(WriteCoils<'a>), WriteMultipleRegisters(WriteRegisters<'a>), - SendCustomFunctionCode(CustomFunctionCode<'a>), + SendCustomFunctionCode(CustomFunctionCode), } /// All requests that support broadcast @@ -169,25 +169,25 @@ impl<'a> Request<'a> { } Request::SendCustomFunctionCode(request) => { let result = match function { - FunctionCode::SendCFC65 => handler.process_cfc_65(request.clone()).map(|_| *request), - FunctionCode::SendCFC66 => handler.process_cfc_66(request.clone()).map(|_| *request), - FunctionCode::SendCFC67 => handler.process_cfc_67(request.clone()).map(|_| *request), - FunctionCode::SendCFC68 => handler.process_cfc_68(request.clone()).map(|_| *request), - FunctionCode::SendCFC69 => handler.process_cfc_69(*request).map(|_| *request), - FunctionCode::SendCFC70 => handler.process_cfc_70(request.clone()).map(|_| *request), - FunctionCode::SendCFC71 => handler.process_cfc_71(request.clone()).map(|_| *request), - FunctionCode::SendCFC72 => handler.process_cfc_72(request.clone()).map(|_| *request), - FunctionCode::SendCFC100 => handler.process_cfc_100(request.clone()).map(|_| *request), - FunctionCode::SendCFC101 => handler.process_cfc_101(request.clone()).map(|_| *request), - FunctionCode::SendCFC102 => handler.process_cfc_102(request.clone()).map(|_| *request), - FunctionCode::SendCFC103 => handler.process_cfc_103(request.clone()).map(|_| *request), - FunctionCode::SendCFC104 => handler.process_cfc_104(request.clone()).map(|_| *request), - FunctionCode::SendCFC105 => handler.process_cfc_105(request.clone()).map(|_| *request), - FunctionCode::SendCFC106 => handler.process_cfc_106(request.clone()).map(|_| *request), - FunctionCode::SendCFC107 => handler.process_cfc_107(request.clone()).map(|_| *request), - FunctionCode::SendCFC108 => handler.process_cfc_108(request.clone()).map(|_| *request), - FunctionCode::SendCFC109 => handler.process_cfc_109(request.clone()).map(|_| *request), - FunctionCode::SendCFC110 => handler.process_cfc_110(request.clone()).map(|_| *request), + FunctionCode::SendCFC65 => handler.process_cfc_65(request.clone()), + FunctionCode::SendCFC66 => handler.process_cfc_66(request.clone()), + FunctionCode::SendCFC67 => handler.process_cfc_67(request.clone()), + FunctionCode::SendCFC68 => handler.process_cfc_68(request.clone()), + FunctionCode::SendCFC69 => handler.process_cfc_69(request.clone()), + FunctionCode::SendCFC70 => handler.process_cfc_70(request.clone()), + FunctionCode::SendCFC71 => handler.process_cfc_71(request.clone()), + FunctionCode::SendCFC72 => handler.process_cfc_72(request.clone()), + FunctionCode::SendCFC100 => handler.process_cfc_100(request.clone()), + FunctionCode::SendCFC101 => handler.process_cfc_101(request.clone()), + FunctionCode::SendCFC102 => handler.process_cfc_102(request.clone()), + FunctionCode::SendCFC103 => handler.process_cfc_103(request.clone()), + FunctionCode::SendCFC104 => handler.process_cfc_104(request.clone()), + FunctionCode::SendCFC105 => handler.process_cfc_105(request.clone()), + FunctionCode::SendCFC106 => handler.process_cfc_106(request.clone()), + FunctionCode::SendCFC107 => handler.process_cfc_107(request.clone()), + FunctionCode::SendCFC108 => handler.process_cfc_108(request.clone()), + FunctionCode::SendCFC109 => handler.process_cfc_109(request.clone()), + FunctionCode::SendCFC110 => handler.process_cfc_110(request.clone()), _ => Err(ExceptionCode::IllegalFunction), }; write_result(function, header, writer, result, level) diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index a1b578c4..a0a01bc6 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -270,7 +270,7 @@ impl AuthorizationType { 0x42 => handler.process_cfc_66(unit_id, x.clone(), role), 0x43 => handler.process_cfc_67(unit_id, x.clone(), role), 0x44 => handler.process_cfc_68(unit_id, x.clone(), role), - 0x45 => handler.process_cfc_69(unit_id, *x, role), + 0x45 => handler.process_cfc_69(unit_id, x.clone(), role), 0x46 => handler.process_cfc_70(unit_id, x.clone(), role), 0x47 => handler.process_cfc_71(unit_id, x.clone(), role), 0x48 => handler.process_cfc_72(unit_id, x.clone(), role), From 8700378230f7923844869341039007a25f3da201 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 4 Mar 2024 22:34:55 +0100 Subject: [PATCH 080/106] refactor(send_custom_fc): fix unmodified CFC object, introduce byte_count in and out parameter --- rodbus/examples/client.rs | 6 +++-- rodbus/examples/custom_client.rs | 4 ++- rodbus/examples/custom_server.rs | 7 ++---- rodbus/examples/server.rs | 11 --------- rodbus/src/client/requests/send_custom_fc.rs | 20 +++++++++------ rodbus/src/common/parse.rs | 22 +++++++++++------ rodbus/src/common/serialize.rs | 26 ++++++++++++++------ rodbus/src/server/handler.rs | 4 +-- rodbus/src/types.rs | 16 ++++++++++-- rodbus/tests/integration_test.rs | 8 +++--- 10 files changed, 73 insertions(+), 51 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index 67ac7fc9..b8924a4d 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -269,13 +269,15 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { // ANCHOR: send_custom_function_code - let length = 0x04 as usize; + let fc = 0x45 as u8; + let byte_count_in = 0x04 as u8; + let byte_count_out = 0x04 as u8; let values = vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]; // i.e.: Voltage Hi = 0xC0 / Voltage Lo = 0xDE / Current Hi = 0xCA / Current Lo = 0xFE let result = channel .send_custom_function_code( params, - CustomFunctionCode::new(length, values) + CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values) ) .await; print_write_result(result); diff --git a/rodbus/examples/custom_client.rs b/rodbus/examples/custom_client.rs index 7767048f..4405becd 100644 --- a/rodbus/examples/custom_client.rs +++ b/rodbus/examples/custom_client.rs @@ -207,13 +207,15 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { let fc = u8::from_str_radix(fc_str.trim_start_matches("0x"), 16).unwrap(); + let byte_count_in = u8::from_str_radix(fc_str.trim_start_matches("0x"), 16).unwrap(); + let byte_count_out = u8::from_str_radix(fc_str.trim_start_matches("0x"), 16).unwrap(); let values: Vec = values.iter().filter_map(|&v| u16::from_str_radix(v.trim_start_matches("0x"), 16).ok()).collect(); if (fc >= 65 && fc <= 72) || (fc >= 100 && fc <= 110) { let result = channel .send_custom_function_code( params, - CustomFunctionCode::new(fc, values) + CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values) ) .await; print_write_result(result); diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 43d52292..5ebd056a 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -84,11 +84,8 @@ impl RequestHandler for SimpleHandler { // Create a new vector to hold the incremented values let incremented_data = values.iter().map(|&val| val + 1).collect(); - // Create a new CustomFunctionCode with the incremented data - let modified_values = CustomFunctionCode::new(values.function_code(), incremented_data); - - Ok(modified_values) - //Ok(&CustomFunctionCode::new(values.function_code(), incremented_values)) + // Return a new CustomFunctionCode with the incremented data + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) } fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { diff --git a/rodbus/examples/server.rs b/rodbus/examples/server.rs index ce854dd9..c132eb2c 100644 --- a/rodbus/examples/server.rs +++ b/rodbus/examples/server.rs @@ -78,17 +78,6 @@ impl RequestHandler for SimpleHandler { } } - fn process_custom_function_code(&mut self, values: CustomFunctionCode) -> Result<(), ExceptionCode> { - let mut custom_fc_args = Vec::with_capacity(values.len()); // i.e.: Voltage Hi = 0x02, Voltage Lo = 0x03, Current Hi = 0x04, Current Lo = 0x05 - - for &item in values.iter() { - custom_fc_args.push(item); - } - tracing::info!("processing custom function code arguments: {:?}", custom_fc_args); - - Ok(()) - } - fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { tracing::info!( "write single register, index: {} value: {}", diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index c51aa22e..aa046f91 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -18,7 +18,7 @@ pub(crate) struct CustomFCRequest where T: CustomFCOperation + Display + Send + 'static, { - pub(crate) request: T, + pub(crate) request: CustomFunctionCode, promise: Promise, } @@ -26,7 +26,7 @@ impl CustomFCRequest where T: CustomFCOperation + Display + Send + 'static, { - pub(crate) fn new(request: T, promise: Promise) -> Self { + pub(crate) fn new(request: CustomFunctionCode, promise: Promise) -> Self { Self { request, promise } } @@ -51,7 +51,7 @@ where } else if decode.header() { tracing::info!("PDU RX - {}", function); } - + self.promise.success(response); Ok(()) } @@ -59,9 +59,6 @@ where fn parse_all(&self, mut cursor: ReadCursor) -> Result { let response = T::parse(&mut cursor)?; cursor.expect_empty()?; - if self.request != response { - return Err(AduParseError::ReplyEchoMismatch.into()); - } Ok(response) } } @@ -79,13 +76,20 @@ impl CustomFCOperation for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { let fc = cursor.read_u8()?; - let len = cursor.remaining() / 2; + let byte_count_in = cursor.read_u8()?; + let byte_count_out = cursor.read_u8()?; + let len = byte_count_in as usize; + + if len != cursor.remaining() / 2 { + return Err(AduParseError::InsufficientBytesForByteCount(len, cursor.remaining() / 2).into()); + } + let mut values = Vec::with_capacity(len); for _ in 0..len { values.push(cursor.read_u16_be()?); } cursor.expect_empty()?; - Ok(CustomFunctionCode::new(fc, values)) + Ok(CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values)) } } diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 1c021680..eb8d0170 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -31,15 +31,21 @@ impl Parse for Indexed { impl Parse for CustomFunctionCode { fn parse(cursor: &mut ReadCursor) -> Result { let fc = cursor.read_u8()?; - let len = cursor.remaining() / 2; + let byte_count_in = cursor.read_u8()?; + let byte_count_out = cursor.read_u8()?; + let len = byte_count_in as usize; + + if len != cursor.remaining() / 2 { + return Err(AduParseError::InsufficientBytesForByteCount(len, cursor.remaining() / 2).into()); + } + let mut values = Vec::with_capacity(len); - for _ in 0..len { values.push(cursor.read_u16_be()?); } cursor.expect_empty()?; - - Ok(CustomFunctionCode::new(fc, values)) + + Ok(CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values)) } } @@ -93,28 +99,28 @@ mod custom_fc { fn parse_succeeds_for_single_min_value() { let mut cursor = ReadCursor::new(&[0x00, 0x01, 0x00, 0x00]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(1, vec![0x0000]))); + assert_eq!(result, Ok(CustomFunctionCode::new(69, 1, 1, vec![0x0000]))); } #[test] fn parse_succeeds_for_single_max_value() { let mut cursor = ReadCursor::new(&[0x00, 0x01, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(1, vec![0xFFFF]))); + assert_eq!(result, Ok(CustomFunctionCode::new(69, 1, 1, vec![0xFFFF]))); } #[test] fn parse_succeeds_for_multiple_min_values() { let mut cursor = ReadCursor::new(&[0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]))); + assert_eq!(result, Ok(CustomFunctionCode::new(69, 3, 3, vec![0x0000, 0x0000, 0x0000]))); } #[test] fn parse_succeeds_for_multiple_max_values() { let mut cursor = ReadCursor::new(&[0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]))); + assert_eq!(result, Ok(CustomFunctionCode::new(69, 3, 3, vec![0xFFFF, 0xFFFF, 0xFFFF]))); } #[test] diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index a7234dc5..2aeaf6e0 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -310,16 +310,26 @@ impl Loggable for CustomFunctionCode { ) -> std::fmt::Result { if level.data_headers() { let mut cursor = ReadCursor::new(payload); - let fc = match cursor.read_u8() { Ok(value) => value, Err(_) => return Ok(()), }; + let byte_count_in = match cursor.read_u8() { + Ok(value) => value, + Err(_) => return Ok(()), + }; + let byte_count_out = match cursor.read_u8() { + Ok(value) => value, + Err(_) => return Ok(()), + }; + let len = byte_count_in as usize; - let len = cursor.remaining() / 2; - let mut data = Vec::with_capacity(len); + if len != cursor.remaining() / 2 { + return Ok(()); + } + let mut data = Vec::with_capacity(len); for _ in 0..len { let item = match cursor.read_u16_be() { Ok(value) => value, @@ -328,7 +338,7 @@ impl Loggable for CustomFunctionCode { data.push(item); } - let custom_fc = CustomFunctionCode::new(fc, data); + let custom_fc = CustomFunctionCode::new(fc, byte_count_in, byte_count_out, data); write!(f, "{:?}", custom_fc)?; @@ -353,7 +363,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_min_value() { - let custom_fc = CustomFunctionCode::new(1, vec![0x0000]); + let custom_fc = CustomFunctionCode::new(69, 1, 1, vec![0x0000]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -362,7 +372,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_max_value() { - let custom_fc = CustomFunctionCode::new(1, vec![0xFFFF]); + let custom_fc = CustomFunctionCode::new(69, 1, 1, vec![0xFFFF]); let mut buffer = [0u8; 4]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -371,7 +381,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_min_values() { - let custom_fc = CustomFunctionCode::new(3, vec![0x0000, 0x0000, 0x0000]); + let custom_fc = CustomFunctionCode::new(69, 3, 3, vec![0x0000, 0x0000, 0x0000]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); @@ -380,7 +390,7 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_max_values() { - let custom_fc = CustomFunctionCode::new(3, vec![0xFFFF, 0xFFFF, 0xFFFF]); + let custom_fc = CustomFunctionCode::new(69, 3, 3, vec![0xFFFF, 0xFFFF, 0xFFFF]); let mut buffer = [0u8; 8]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index de1296f9..fa399080 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -624,11 +624,11 @@ mod tests { ); assert_eq!( - handler.process_cfc_69(CustomFunctionCode::new(0x45, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc_69(CustomFunctionCode::new(0x45, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_70(CustomFunctionCode::new(0x46, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc_70(CustomFunctionCode::new(0x46, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); } diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 42706202..4a7cfe0a 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -89,6 +89,8 @@ pub(crate) struct RegisterIteratorDisplay<'a> { #[derive(Clone, Debug, PartialEq)] pub struct CustomFunctionCode { fc: u8, + byte_count_in: u8, + byte_count_out: u8, data: Vec, } @@ -376,8 +378,8 @@ impl Default for UnitId { impl CustomFunctionCode { /// Create a new custom function code - pub fn new(fc: u8, data: Vec) -> Self { - Self { fc, data } + pub fn new(fc: u8, byte_count_in: u8, byte_count_out: u8, data: Vec) -> Self { + Self { fc, byte_count_in, byte_count_out, data } } /// Get the function code @@ -385,6 +387,16 @@ impl CustomFunctionCode { self.fc } + /// Get the function code + pub fn byte_count_in(&self) -> u8 { + self.byte_count_in + } + + /// Get the function code + pub fn byte_count_out(&self) -> u8 { + self.byte_count_out + } + /// Get the length of the underlying vector pub fn len(&self) -> usize { self.data.len() diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index a79c898d..1d2c5770 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -98,8 +98,8 @@ impl RequestHandler for Handler { fn process_cfc_69(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); // increment each CFC value by 1 and return the result - let incremented_values = values.iter().map(|&x| x + 1).collect(); - Ok(CustomFunctionCode::new(values.function_code(), incremented_values)) + let incremented_values = values.iter().map(|&x| x + 1).collect::>(); + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_values)) } } @@ -231,10 +231,10 @@ async fn test_requests_and_responses() { ); assert_eq!( channel - .send_custom_function_code(params, CustomFunctionCode::new(0x45, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])) + .send_custom_function_code(params, CustomFunctionCode::new(0x45, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])) .await .unwrap(), - CustomFunctionCode::new(0x45, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + CustomFunctionCode::new(0x45, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) ); } From 554b6c7f8de3964105c4e20c2023049dcb7695f9 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Tue, 5 Mar 2024 22:25:28 +0100 Subject: [PATCH 081/106] fix(send_custom_fc): fix incorect CFC buffer population --- rodbus/examples/custom_client.rs | 6 +++--- rodbus/src/client/requests/send_custom_fc.rs | 2 ++ rodbus/src/common/serialize.rs | 19 +++++++++++-------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/rodbus/examples/custom_client.rs b/rodbus/examples/custom_client.rs index 4405becd..a2752361 100644 --- a/rodbus/examples/custom_client.rs +++ b/rodbus/examples/custom_client.rs @@ -205,10 +205,10 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box { channel.set_decode_level(DecodeLevel::nothing()).await?; } - ["scfc", fc_str, values @ ..] => { + ["scfc", fc_str, bytes_in_str, bytes_out_str, values @ ..] => { let fc = u8::from_str_radix(fc_str.trim_start_matches("0x"), 16).unwrap(); - let byte_count_in = u8::from_str_radix(fc_str.trim_start_matches("0x"), 16).unwrap(); - let byte_count_out = u8::from_str_radix(fc_str.trim_start_matches("0x"), 16).unwrap(); + let byte_count_in = u8::from_str_radix(bytes_in_str.trim_start_matches("0x"), 16).unwrap(); + let byte_count_out = u8::from_str_radix(bytes_out_str.trim_start_matches("0x"), 16).unwrap(); let values: Vec = values.iter().filter_map(|&v| u16::from_str_radix(v.trim_start_matches("0x"), 16).ok()).collect(); if (fc >= 65 && fc <= 72) || (fc >= 100 && fc <= 110) { diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index aa046f91..297f969f 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -66,6 +66,8 @@ where impl CustomFCOperation for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u8(self.function_code())?; + cursor.write_u8(self.byte_count_in())?; + cursor.write_u8(self.byte_count_out())?; for &item in self.iter() { cursor.write_u16_be(item)?; diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index 2aeaf6e0..d875a62f 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -293,10 +293,13 @@ impl Serialize for WriteMultiple { impl Serialize for CustomFunctionCode { fn serialize(&self, cursor: &mut WriteCursor) -> Result<(), RequestError> { cursor.write_u8(self.function_code())?; + cursor.write_u8(self.byte_count_in())?; + cursor.write_u8(self.byte_count_out())?; for &item in self.iter() { cursor.write_u16_be(item)?; } + Ok(()) } } @@ -364,36 +367,36 @@ mod tests { #[test] fn serialize_succeeds_for_valid_cfc_of_single_min_value() { let custom_fc = CustomFunctionCode::new(69, 1, 1, vec![0x0000]); - let mut buffer = [0u8; 4]; + let mut buffer = [0u8; 5]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); - assert_eq!(buffer, [0x00, 0x01, 0x00, 0x00]); + assert_eq!(buffer, [0x45, 0x01, 0x01, 0x00, 0x00]); } #[test] fn serialize_succeeds_for_valid_cfc_of_single_max_value() { let custom_fc = CustomFunctionCode::new(69, 1, 1, vec![0xFFFF]); - let mut buffer = [0u8; 4]; + let mut buffer = [0u8; 5]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); - assert_eq!(buffer, [0x00, 0x01, 0xFF, 0xFF]); + assert_eq!(buffer, [0x45, 0x01, 0x01, 0xFF, 0xFF]); } #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_min_values() { let custom_fc = CustomFunctionCode::new(69, 3, 3, vec![0x0000, 0x0000, 0x0000]); - let mut buffer = [0u8; 8]; + let mut buffer = [0u8; 9]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); - assert_eq!(buffer, [0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + assert_eq!(buffer, [0x45, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); } #[test] fn serialize_succeeds_for_valid_cfc_of_multiple_max_values() { let custom_fc = CustomFunctionCode::new(69, 3, 3, vec![0xFFFF, 0xFFFF, 0xFFFF]); - let mut buffer = [0u8; 8]; + let mut buffer = [0u8; 9]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); - assert_eq!(buffer, [0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); + assert_eq!(buffer, [0x45, 0x03, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); } } From 62e7bcc3c5906690c5ed58bee4c11a679d7b7438 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Tue, 5 Mar 2024 22:26:38 +0100 Subject: [PATCH 082/106] test(send_custom_fc): adjust the CFC parse/serialize and integration tests to the new logic --- rodbus/src/common/parse.rs | 16 ++++---- rodbus/src/server/handler.rs | 69 +++++++++++++++++++++++++++++++- rodbus/tests/integration_test.rs | 11 ++--- 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index eb8d0170..238ea9e8 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -97,43 +97,43 @@ mod custom_fc { #[test] fn parse_succeeds_for_single_min_value() { - let mut cursor = ReadCursor::new(&[0x00, 0x01, 0x00, 0x00]); + let mut cursor = ReadCursor::new(&[0x45, 0x01, 0x01, 0x00, 0x00]); let result = CustomFunctionCode::parse(&mut cursor); assert_eq!(result, Ok(CustomFunctionCode::new(69, 1, 1, vec![0x0000]))); } #[test] fn parse_succeeds_for_single_max_value() { - let mut cursor = ReadCursor::new(&[0x00, 0x01, 0xFF, 0xFF]); + let mut cursor = ReadCursor::new(&[0x45, 0x01, 0x01, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); assert_eq!(result, Ok(CustomFunctionCode::new(69, 1, 1, vec![0xFFFF]))); } #[test] fn parse_succeeds_for_multiple_min_values() { - let mut cursor = ReadCursor::new(&[0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + let mut cursor = ReadCursor::new(&[0x45, 0x03, 0x3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); let result = CustomFunctionCode::parse(&mut cursor); assert_eq!(result, Ok(CustomFunctionCode::new(69, 3, 3, vec![0x0000, 0x0000, 0x0000]))); } #[test] fn parse_succeeds_for_multiple_max_values() { - let mut cursor = ReadCursor::new(&[0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); + let mut cursor = ReadCursor::new(&[0x45, 0x03, 0x3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); assert_eq!(result, Ok(CustomFunctionCode::new(69, 3, 3, vec![0xFFFF, 0xFFFF, 0xFFFF]))); } #[test] - fn parse_fails_for_missing_len_byte() { - let mut cursor = ReadCursor::new(&[0x01, 0xFF, 0xFF]); + fn parse_fails_for_missing_byte_count() { + let mut cursor = ReadCursor::new(&[0x45, 0x01, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Err(AduParseError::InsufficientBytes.into())); + assert_eq!(result, Err(AduParseError::InsufficientBytesForByteCount(1, 0).into())); } #[test] fn parse_fails_for_missing_data_byte() { let mut cursor = ReadCursor::new(&[0x00, 0x01, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Err(AduParseError::InsufficientBytes.into())); + assert_eq!(result, Err(AduParseError::InsufficientBytesForByteCount(1, 0).into())); } } \ No newline at end of file diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index fa399080..ca80ee90 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -622,7 +622,22 @@ mod tests { handler.write_single_register(Indexed::new(0, 0)), Err(ExceptionCode::IllegalFunction) ); - + assert_eq!( + handler.process_cfc_65(CustomFunctionCode::new(0x41, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_66(CustomFunctionCode::new(0x42, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_67(CustomFunctionCode::new(0x43, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_68(CustomFunctionCode::new(0x44, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); assert_eq!( handler.process_cfc_69(CustomFunctionCode::new(0x45, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) @@ -631,6 +646,58 @@ mod tests { handler.process_cfc_70(CustomFunctionCode::new(0x46, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); + assert_eq!( + handler.process_cfc_71(CustomFunctionCode::new(0x47, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_72(CustomFunctionCode::new(0x48, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_100(CustomFunctionCode::new(0x64, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_101(CustomFunctionCode::new(0x65, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_102(CustomFunctionCode::new(0x66, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_103(CustomFunctionCode::new(0x67, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_104(CustomFunctionCode::new(0x68, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_105(CustomFunctionCode::new(0x69, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_106(CustomFunctionCode::new(0x6A, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_107(CustomFunctionCode::new(0x6B, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_108(CustomFunctionCode::new(0x6C, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_109(CustomFunctionCode::new(0x6D, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); + assert_eq!( + handler.process_cfc_110(CustomFunctionCode::new(0x6E, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + Err(ExceptionCode::IllegalFunction) + ); } #[test] diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index 1d2c5770..9a641daa 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -12,7 +12,7 @@ struct Handler { pub coils: [bool; 10], pub discrete_inputs: [bool; 10], pub holding_registers: [u16; 10], - pub input_registers: [u16; 10] + pub input_registers: [u16; 10], } impl Handler { @@ -21,7 +21,7 @@ impl Handler { coils: [false; 10], discrete_inputs: [false; 10], holding_registers: [0; 10], - input_registers: [0; 10] + input_registers: [0; 10], } } } @@ -98,8 +98,9 @@ impl RequestHandler for Handler { fn process_cfc_69(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); // increment each CFC value by 1 and return the result - let incremented_values = values.iter().map(|&x| x + 1).collect::>(); - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_values)) + let incremented_data = values.iter().map(|&val| val + 1).collect(); + + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) } } @@ -234,7 +235,7 @@ async fn test_requests_and_responses() { .send_custom_function_code(params, CustomFunctionCode::new(0x45, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])) .await .unwrap(), - CustomFunctionCode::new(0x45, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + CustomFunctionCode::new(0x45, 4, 4, vec![0xC0DF, 0xCAFF, 0xC0DF, 0xCAFF]) ); } From 6c11ee237e8dff748908810912b1915486b1f44e Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Tue, 5 Mar 2024 23:51:35 +0100 Subject: [PATCH 083/106] test(send_custom_fc): add CFC integration tests --- rodbus/tests/integration_test.rs | 81 +++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index 9a641daa..4b80b626 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -95,7 +95,7 @@ impl RequestHandler for Handler { Ok(()) } - fn process_cfc_69(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc_65(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); // increment each CFC value by 1 and return the result let incremented_data = values.iter().map(|&val| val + 1).collect(); @@ -231,11 +231,80 @@ async fn test_requests_and_responses() { ] ); assert_eq!( - channel - .send_custom_function_code(params, CustomFunctionCode::new(0x45, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])) - .await - .unwrap(), - CustomFunctionCode::new(0x45, 4, 4, vec![0xC0DF, 0xCAFF, 0xC0DF, 0xCAFF]) + channel.send_custom_function_code(params, CustomFunctionCode::new(0x41, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Ok(CustomFunctionCode::new(0x41, 4, 4, vec![0xC0DF, 0xCAFF, 0xC0DF, 0xCAFF])) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x42, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x43, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x44, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x45, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x46, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x47, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x48, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x64, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x65, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x66, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x67, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x68, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x69, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x6A, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x6B, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x6C, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x6D, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(0x6E, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) ); } From 5a52c1da435e9be428662589eea4b15b425ea6e5 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 6 Mar 2024 18:53:29 +0100 Subject: [PATCH 084/106] refactor(send_custom_fc): improved CFC output --- rodbus/src/types.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 4a7cfe0a..e88d2620 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -410,6 +410,9 @@ impl CustomFunctionCode { impl std::fmt::Display for CustomFunctionCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "fc: {:#X}, ", self.fc)?; + write!(f, "bytes in: {}, ", self.byte_count_in)?; + write!(f, "bytes out: {}, ", self.byte_count_out)?; write!(f, "values: [")?; for (i, val) in self.data.iter().enumerate() { if i != 0 { @@ -417,6 +420,14 @@ impl std::fmt::Display for CustomFunctionCode { } write!(f, "{}", val)?; } + write!(f, "], ")?; + write!(f, "hex: [")?; + for (i, val) in self.data.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "{:#X}", val)?; + } write!(f, "]") } } From 9e81fdb07b46feb83717ac4703b15bd1df8d5bda Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 6 Mar 2024 21:11:00 +0100 Subject: [PATCH 085/106] test(custom_server): Implement test CFC handlers to simulate different behavior --- rodbus/examples/custom_server.rs | 28 ++++++++++++++++++++ rodbus/src/client/requests/send_custom_fc.rs | 4 +-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 5ebd056a..a3f074dc 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -88,6 +88,34 @@ impl RequestHandler for SimpleHandler { Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) } + fn process_cfc_70(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { + tracing::info!("processing custom function code: {}", values.function_code()); + // add a new value to the buffer and return the result + // Create a new vector to hold the incremented values + let extended_data = { + let mut extended_data = values.iter().map(|val| *val).collect::>(); + extended_data.push(0xC0DE); + extended_data + }; + + // Return a new CustomFunctionCode with the incremented data + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), extended_data)) + } + + fn process_cfc_71(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { + tracing::info!("processing custom function code: {}", values.function_code()); + // remove the first value from the buffer and return the result + // Create a new vector to hold the incremented values + let truncated_data = { + let mut truncated_data = values.iter().map(|val| *val).collect::>(); + truncated_data.pop(); + truncated_data + }; + + // Return a new CustomFunctionCode with the incremented data + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), truncated_data)) + } + fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { tracing::info!( "write single register, index: {} value: {}", diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index 297f969f..e95b599b 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -45,13 +45,13 @@ where decode: AppDecodeLevel, ) -> Result<(), RequestError> { let response = self.parse_all(cursor)?; - + if decode.data_headers() { tracing::info!("PDU RX - {} {}", function, response); } else if decode.header() { tracing::info!("PDU RX - {}", function); } - + self.promise.success(response); Ok(()) } From 2ba9ce0114bbe617bf4b21d4dd00ab8093a8b9ff Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 6 Mar 2024 21:39:06 +0100 Subject: [PATCH 086/106] feat(send_custom_fc): improve CFC documentation --- rodbus/SCFC_README.md | 69 +++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/rodbus/SCFC_README.md b/rodbus/SCFC_README.md index c5b31098..4df4f99e 100644 --- a/rodbus/SCFC_README.md +++ b/rodbus/SCFC_README.md @@ -1,33 +1,35 @@ -# 69 (0x45) Send Custom Function Code +# Send Custom Function Code (65-72 & 100-110) -This document provides a detailed overview of the custom function code (0x45) used in the MODBUS Application Protocol. This function code is user-defined and falls within the range of 65 to 72, as specified in the MODBUS Application Protocol Specification V1.1b3 (Page 10, Section 5: Function Code Categories). +This document provides a detailed overview of the implemented rodbus custom function code feature. These user-defined function codes fall within the range of 65 to 72 and 100 to 110, as specified in the MODBUS Application Protocol Specification V1.1b3 (Page 10, Section 5: Function Code Categories), and allow custom server-side execution logic. ## Introduction -The 0x45 function code enables the implementation of user-defined logic on a remote server device. It facilitates the transmission, reception, and processing of a custom function code with a variable-size data buffer. +The custom function codes enable the implementation of user-defined logic on a remote server device. It facilitates the transmission, reception, and processing of a custom function code with a variable-size data buffer. ## Request Structure -| Parameter | Size | Range / Value | -|--------------------|---------------|-----------------------| -| Function code | 1 Byte | 0x45 | -| Byte Count | 2 Bytes | 0x0000 to 0xFFFF (N*) | -| Data | N* x 2 Bytes | 0x0000 to 0xFFFF | +| Parameter | Size | Range / Value | +|---------------------|---------------|-----------------------| +| Function code | 1 Byte | 0x41-0x48 / 0x64-0x6E | +| Byte Count (Input) | 1 Byte | 0x00 to 0xFF (N*) | +| Byte Count (Output) | 1 Byte | 0x00 to 0xFF | +| Data | N* x 2 Bytes | 0x0000 to 0xFFFF | ## Response Structure -| Parameter | Size | Value/Description | -|---------------|--------------|-----------------------| -| Function code | 1 Byte | 0x45 | -| Byte Count | 2 Bytes | 0x0000 to 0xFFFF (N*) | -| Data | N* x 2 Bytes | 0x0000 to 0xFFFF | +| Parameter | Size | Value/Description | +|---------------------|--------------|-----------------------| +| Function code | 1 Byte | 0x41-0x48 / 0x64-0x6E | +| Byte Count (Input) | 1 Byte | 0x00 to 0xFF (N*) | +| Byte Count (Output) | 1 Byte | 0x00 to 0xFF | +| Data | N* x 2 Bytes | 0x0000 to 0xFFFF | ## Error Handling -| Parameter | Size | Description | -|----------------|---------|-----------------------------------| -| Function code | 1 Byte | Function code + 0x80 = 0xC5 (197) | -| Exception code | 1 Byte | 01 or 02 or 03 or 04 | +| Parameter | Size | Description | +|----------------|---------|----------------------| +| Function code | 1 Byte | Function code + 0x80 | +| Exception code | 1 Byte | 01 or 02 or 03 or 04 | ### Error Codes: - **01**: Illegal Function @@ -37,44 +39,47 @@ The 0x45 function code enables the implementation of user-defined logic on a rem ## Usage Example -### Request to send the custom buffer [0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE] (Byte Count = 4 -> 8 bytes): +### Request to send the custom FC 69 with a buffer of [0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE] (Byte Count = 4 -> 8 bytes): | Request Field | Hex | Response Field | Hex | |---------------------------|-----|------------------------|-----| -| Function | 45 | Function | 45 | -| Byte Count Hi | 00 | Byte Count Hi | 00 | -| Byte Count Lo | 04 | Byte Count Lo | 04 | +| Function code | 45 | Function code | 45 | +| Byte Count (Input) | 04 | Byte Count (Input) | 04 | +| Byte Count (Output) | 04 | Byte Count (Output) | 04 | | Arg1 Hi | C0 | Arg1 Hi | C0 | -| Arg1 Lo | DE | Arg1 Lo | DE | +| Arg1 Lo | DE | Arg1 Lo | DF | | Arg2 Hi | CA | Arg2 Hi | CA | -| Arg2 Lo | FE | Arg2 Lo | FE | +| Arg2 Lo | FE | Arg2 Lo | FF | | Arg3 Hi | C0 | Arg3 Hi | C0 | -| Arg3 Lo | DE | Arg3 Lo | DE | +| Arg3 Lo | DE | Arg3 Lo | DF | | Arg4 Hi | CA | Arg4 Hi | CA | -| Arg4 Lo | FE | Arg4 Lo | FE | +| Arg4 Lo | FE | Arg4 Lo | FF | -## Modify and Test Server-Side Buffer Handling -The server currently forwards the Custom Function Code buffer to the client again without alteration. To test modifying or processing the buffer on the remote server device, edit the `send_custom_function_code()` function in `src/examples/client.rs` and `src/examples/server.rs` as needed. - ## Usage Make sure that you are in the `rodbus` project directory. + ### Start the custom_server example - `cargo run --example custom_server -- tcp` +- Once it's up, run `ed` to enable decoding Leave the terminal open and open another terminal. + ### Start the custom_client example - `cargo run --example custom_client -- tcp` +- Once it's up, run `ed` to enable decoding + -### Send the Custom Function Code CFC69 request +### Send the Custom Function Code 69 In the terminal with the running custom_client example, run: -- `scfc ` -- e.g. `scfc 0x02 0xC0DE 0xCAFE` +- `scfc ` +- E.g. `scfc 0x45 0x02 0x02 0xC0DE 0xCAFE` +- The response would be for example: `fc: 0x45, bytes in: 2, bytes out: 2, values: [49375, 51967], hex: [0xC0DF, 0xCAFF]` ## Troubleshooting Tips -- Ensure the server and client are using the same communication and are connected to each other. +- Ensure the server and client are using the same communication method and are connected to each other. - Check for any error codes in the response and refer to the error handling section for resolution. From 52cd82af208577cad312ab292e0a9b67a3aa0f0d Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 6 Mar 2024 21:54:53 +0100 Subject: [PATCH 087/106] test(send_custom_fc): remove failing incorrect CFC69 serial test --- rodbus/src/serial/frame.rs | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/rodbus/src/serial/frame.rs b/rodbus/src/serial/frame.rs index e9651f5d..663d42e8 100644 --- a/rodbus/src/serial/frame.rs +++ b/rodbus/src/serial/frame.rs @@ -383,22 +383,6 @@ mod tests { 0x71, 0x86, // crc ]; - const SEND_CFC_69_REQUEST: &[u8] = &[ - UNIT_ID, // unit id - 0x45, // function code - 0x08, // byte count - 0xC0, 0xDE, 0xCA, 0xFE, // data - 0xC8, 0xD9, // crc - ]; - - const SEND_CFC_69_RESPONSE: &[u8] = &[ - UNIT_ID, // unit id - 0x45, // function code - 0x08, // byte count - 0xC0, 0xDE, 0xCA, 0xFE, // data - 0x88, 0x2C, // crc - ]; - const WRITE_SINGLE_COIL_REQUEST: &[u8] = &[ UNIT_ID, // unit id 0x05, // function code @@ -481,10 +465,6 @@ mod tests { FunctionCode::ReadInputRegisters, READ_INPUT_REGISTERS_REQUEST, ), - ( - FunctionCode::SendCFC69, - SEND_CFC_69_REQUEST, - ), (FunctionCode::WriteSingleCoil, WRITE_SINGLE_COIL_REQUEST), ( FunctionCode::WriteSingleRegister, @@ -514,10 +494,6 @@ mod tests { FunctionCode::ReadInputRegisters, READ_INPUT_REGISTERS_RESPONSE, ), - ( - FunctionCode::SendCFC69, - SEND_CFC_69_RESPONSE, - ), (FunctionCode::WriteSingleCoil, WRITE_SINGLE_COIL_RESPONSE), ( FunctionCode::WriteSingleRegister, From d89f4033a329bcd30452d1b603fd0305b2c02153 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 11 Mar 2024 20:35:18 +0100 Subject: [PATCH 088/106] refactor(send_custom_fc): remove panic statement from CFC function code --- rodbus/src/client/message.rs | 62 ++++++++++++++++++------------------ rodbus/src/client/task.rs | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 91ac61d2..2af5e6d9 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -63,7 +63,7 @@ impl Request { payload: &[u8], decode: AppDecodeLevel, ) -> Result<(), RequestError> { - let expected_function = self.details.function(); + let expected_function = self.details.function()?; let mut cursor = ReadCursor::new(payload); let function = match cursor.read_u8() { Ok(x) => x, @@ -121,38 +121,38 @@ impl Request { } impl RequestDetails { - pub(crate) fn function(&self) -> FunctionCode { + pub(crate) fn function(&self) -> Result { match self { - RequestDetails::ReadCoils(_) => FunctionCode::ReadCoils, - RequestDetails::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs, - RequestDetails::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters, - RequestDetails::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters, - RequestDetails::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil, - RequestDetails::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, - RequestDetails::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, - RequestDetails::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, + RequestDetails::ReadCoils(_) => Ok(FunctionCode::ReadCoils), + RequestDetails::ReadDiscreteInputs(_) => Ok(FunctionCode::ReadDiscreteInputs), + RequestDetails::ReadHoldingRegisters(_) => Ok(FunctionCode::ReadHoldingRegisters), + RequestDetails::ReadInputRegisters(_) => Ok(FunctionCode::ReadInputRegisters), + RequestDetails::WriteSingleCoil(_) => Ok(FunctionCode::WriteSingleCoil), + RequestDetails::WriteSingleRegister(_) => Ok(FunctionCode::WriteSingleRegister), + RequestDetails::WriteMultipleCoils(_) => Ok(FunctionCode::WriteMultipleCoils), + RequestDetails::WriteMultipleRegisters(_) => Ok(FunctionCode::WriteMultipleRegisters), RequestDetails::SendCustomFunctionCode(x) => { match x.request.function_code() { - 0x41 => FunctionCode::SendCFC65, - 0x42 => FunctionCode::SendCFC66, - 0x43 => FunctionCode::SendCFC67, - 0x44 => FunctionCode::SendCFC68, - 0x45 => FunctionCode::SendCFC69, - 0x46 => FunctionCode::SendCFC70, - 0x47 => FunctionCode::SendCFC71, - 0x48 => FunctionCode::SendCFC72, - 0x64 => FunctionCode::SendCFC100, - 0x65 => FunctionCode::SendCFC101, - 0x66 => FunctionCode::SendCFC102, - 0x67 => FunctionCode::SendCFC103, - 0x68 => FunctionCode::SendCFC104, - 0x69 => FunctionCode::SendCFC105, - 0x6A => FunctionCode::SendCFC106, - 0x6B => FunctionCode::SendCFC107, - 0x6C => FunctionCode::SendCFC108, - 0x6D => FunctionCode::SendCFC109, - 0x6E => FunctionCode::SendCFC110, - _ => panic!("unsupported custom function code"), + 0x41 => Ok(FunctionCode::SendCFC65), + 0x42 => Ok(FunctionCode::SendCFC66), + 0x43 => Ok(FunctionCode::SendCFC67), + 0x44 => Ok(FunctionCode::SendCFC68), + 0x45 => Ok(FunctionCode::SendCFC69), + 0x46 => Ok(FunctionCode::SendCFC70), + 0x47 => Ok(FunctionCode::SendCFC71), + 0x48 => Ok(FunctionCode::SendCFC72), + 0x64 => Ok(FunctionCode::SendCFC100), + 0x65 => Ok(FunctionCode::SendCFC101), + 0x66 => Ok(FunctionCode::SendCFC102), + 0x67 => Ok(FunctionCode::SendCFC103), + 0x68 => Ok(FunctionCode::SendCFC104), + 0x69 => Ok(FunctionCode::SendCFC105), + 0x6A => Ok(FunctionCode::SendCFC106), + 0x6B => Ok(FunctionCode::SendCFC107), + 0x6C => Ok(FunctionCode::SendCFC108), + 0x6D => Ok(FunctionCode::SendCFC109), + 0x6E => Ok(FunctionCode::SendCFC110), + _ => Err(ExceptionCode::IllegalFunction), } }, } @@ -177,7 +177,7 @@ impl RequestDetails { cursor: ReadCursor, decode: AppDecodeLevel, ) -> Result<(), RequestError> { - let function = self.function(); + let function = self.function()?; match self { RequestDetails::ReadCoils(x) => x.handle_response(cursor, function, decode), RequestDetails::ReadDiscreteInputs(x) => x.handle_response(cursor, function, decode), diff --git a/rodbus/src/client/task.rs b/rodbus/src/client/task.rs index 6b92d238..d36f0144 100644 --- a/rodbus/src/client/task.rs +++ b/rodbus/src/client/task.rs @@ -193,7 +193,7 @@ impl ClientLoop { ) -> Result<(), RequestError> { let bytes = self.writer.format_request( FrameHeader::new_tcp_header(request.id, tx_id), - request.details.function(), + request.details.function()?, &request.details, self.decode, )?; From 563fc22bf6f21a36d6652092653a5ef5284f8c13 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 11 Mar 2024 21:03:55 +0100 Subject: [PATCH 089/106] refactor(send_custom_fc): replace individual CFC handlers with a general purpose CFC handler --- rodbus/examples/custom_server.rs | 71 +++---- rodbus/src/server/handler.rs | 322 +++---------------------------- rodbus/src/server/request.rs | 25 +-- rodbus/src/server/task.rs | 22 +-- rodbus/tests/integration_test.rs | 2 +- 5 files changed, 72 insertions(+), 370 deletions(-) diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index a3f074dc..0c7a1cab 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -78,42 +78,43 @@ impl RequestHandler for SimpleHandler { } } - fn process_cfc_69(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}", values.function_code()); - // increment each CFC value by 1 and return the result - // Create a new vector to hold the incremented values - let incremented_data = values.iter().map(|&val| val + 1).collect(); - - // Return a new CustomFunctionCode with the incremented data - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) - } - - fn process_cfc_70(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { - tracing::info!("processing custom function code: {}", values.function_code()); - // add a new value to the buffer and return the result - // Create a new vector to hold the incremented values - let extended_data = { - let mut extended_data = values.iter().map(|val| *val).collect::>(); - extended_data.push(0xC0DE); - extended_data - }; - - // Return a new CustomFunctionCode with the incremented data - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), extended_data)) - } - - fn process_cfc_71(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { - tracing::info!("processing custom function code: {}", values.function_code()); - // remove the first value from the buffer and return the result - // Create a new vector to hold the incremented values - let truncated_data = { - let mut truncated_data = values.iter().map(|val| *val).collect::>(); - truncated_data.pop(); - truncated_data - }; - - // Return a new CustomFunctionCode with the incremented data - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), truncated_data)) + match values.function_code() { + 0x69 => { + // increment each CFC value by 1 and return the result + // Create a new vector to hold the incremented values + let incremented_data = values.iter().map(|&val| val + 1).collect(); + + // Return a new CustomFunctionCode with the incremented data + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) + }, + 0x70 => { + // add a new value to the buffer and return the result + // Create a new vector to hold the incremented values + let extended_data = { + let mut extended_data = values.iter().map(|val| *val).collect::>(); + extended_data.push(0xC0DE); + extended_data + }; + + // Return a new CustomFunctionCode with the incremented data + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), extended_data)) + }, + 0x71 => { + // remove the first value from the buffer and return the result + // Create a new vector to hold the incremented values + let truncated_data = { + let mut truncated_data = values.iter().map(|val| *val).collect::>(); + truncated_data.pop(); + truncated_data + }; + + // Return a new CustomFunctionCode with the incremented data + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), truncated_data)) + }, + _ => Err(ExceptionCode::IllegalFunction), + } } fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index ca80ee90..600af48f 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -63,98 +63,8 @@ pub trait RequestHandler: Send + 'static { Err(ExceptionCode::IllegalFunction) } - /// Write the CFC65 custom function code - fn process_cfc_65(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC66 custom function code - fn process_cfc_66(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC67 custom function code - fn process_cfc_67(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC68 custom function code - fn process_cfc_68(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC69 custom function code - fn process_cfc_69(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC70 custom function code - fn process_cfc_70(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC71 custom function code - fn process_cfc_71(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC72 custom function code - fn process_cfc_72(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC100 custom function code - fn process_cfc_100(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC7101 custom function code - fn process_cfc_101(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC102 custom function code - fn process_cfc_102(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC103 custom function code - fn process_cfc_103(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC104 custom function code - fn process_cfc_104(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC105 custom function code - fn process_cfc_105(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC106 custom function code - fn process_cfc_106(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC107 custom function code - fn process_cfc_107(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC108 custom function code - fn process_cfc_108(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC109 custom function code - fn process_cfc_109(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { - Err(ExceptionCode::IllegalFunction) - } - - /// Write the CFC110 custom function code - fn process_cfc_110(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + /// Write the CFC custom function code + fn process_cfc(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } } @@ -322,98 +232,8 @@ pub trait AuthorizationHandler: Send + Sync + 'static { Authorization::Deny } - /// Authorize a Send CFC65 request - fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC66 request - fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC67 request - fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC68 request - fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC69 request - fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC70 request - fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC71 request - fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC72 request - fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC100 request - fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC101 request - fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC102 request - fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC103 request - fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC104 request - fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC105 request - fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC106 request - fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC107 request - fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC108 request - fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC109 request - fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Deny - } - - /// Authorize a Send CFC110 request - fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + /// Authorize a Send CFC request + fn process_cfc(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { Authorization::Deny } } @@ -495,99 +315,9 @@ impl AuthorizationHandler for ReadOnlyAuthorizationHandler { Authorization::Deny } - /// Authorize a Send CFC65 request - fn process_cfc_65(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC66 request - fn process_cfc_66(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC67 request - fn process_cfc_67(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC68 request - fn process_cfc_68(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC69 request - fn process_cfc_69(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC70 request - fn process_cfc_70(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC71 request - fn process_cfc_71(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC72 request - fn process_cfc_72(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC100 request - fn process_cfc_100(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC101 request - fn process_cfc_101(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC102 request - fn process_cfc_102(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC103 request - fn process_cfc_103(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC104 request - fn process_cfc_104(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC105 request - fn process_cfc_105(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC106 request - fn process_cfc_106(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC107 request - fn process_cfc_107(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC108 request - fn process_cfc_108(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC109 request - fn process_cfc_109(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow - } - - /// Authorize a Send CFC110 request - fn process_cfc_110(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { - Authorization::Allow + /// Authorize a Send CFC request + fn process_cfc(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + Authorization::Deny } } @@ -623,79 +353,79 @@ mod tests { Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_65(CustomFunctionCode::new(0x41, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x41, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_66(CustomFunctionCode::new(0x42, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x42, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_67(CustomFunctionCode::new(0x43, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x43, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_68(CustomFunctionCode::new(0x44, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x44, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_69(CustomFunctionCode::new(0x45, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x45, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_70(CustomFunctionCode::new(0x46, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x46, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_71(CustomFunctionCode::new(0x47, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x47, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_72(CustomFunctionCode::new(0x48, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x48, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_100(CustomFunctionCode::new(0x64, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x64, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_101(CustomFunctionCode::new(0x65, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x65, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_102(CustomFunctionCode::new(0x66, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x66, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_103(CustomFunctionCode::new(0x67, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x67, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_104(CustomFunctionCode::new(0x68, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x68, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_105(CustomFunctionCode::new(0x69, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x69, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_106(CustomFunctionCode::new(0x6A, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x6A, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_107(CustomFunctionCode::new(0x6B, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x6B, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_108(CustomFunctionCode::new(0x6C, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x6C, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_109(CustomFunctionCode::new(0x6D, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x6D, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc_110(CustomFunctionCode::new(0x6E, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new(0x6E, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), Err(ExceptionCode::IllegalFunction) ); } diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 7a2f2507..e24c59f0 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -169,25 +169,12 @@ impl<'a> Request<'a> { } Request::SendCustomFunctionCode(request) => { let result = match function { - FunctionCode::SendCFC65 => handler.process_cfc_65(request.clone()), - FunctionCode::SendCFC66 => handler.process_cfc_66(request.clone()), - FunctionCode::SendCFC67 => handler.process_cfc_67(request.clone()), - FunctionCode::SendCFC68 => handler.process_cfc_68(request.clone()), - FunctionCode::SendCFC69 => handler.process_cfc_69(request.clone()), - FunctionCode::SendCFC70 => handler.process_cfc_70(request.clone()), - FunctionCode::SendCFC71 => handler.process_cfc_71(request.clone()), - FunctionCode::SendCFC72 => handler.process_cfc_72(request.clone()), - FunctionCode::SendCFC100 => handler.process_cfc_100(request.clone()), - FunctionCode::SendCFC101 => handler.process_cfc_101(request.clone()), - FunctionCode::SendCFC102 => handler.process_cfc_102(request.clone()), - FunctionCode::SendCFC103 => handler.process_cfc_103(request.clone()), - FunctionCode::SendCFC104 => handler.process_cfc_104(request.clone()), - FunctionCode::SendCFC105 => handler.process_cfc_105(request.clone()), - FunctionCode::SendCFC106 => handler.process_cfc_106(request.clone()), - FunctionCode::SendCFC107 => handler.process_cfc_107(request.clone()), - FunctionCode::SendCFC108 => handler.process_cfc_108(request.clone()), - FunctionCode::SendCFC109 => handler.process_cfc_109(request.clone()), - FunctionCode::SendCFC110 => handler.process_cfc_110(request.clone()), + FunctionCode::SendCFC65 | FunctionCode::SendCFC66 | FunctionCode::SendCFC67 | FunctionCode::SendCFC68 | FunctionCode::SendCFC69 | + FunctionCode::SendCFC70 | FunctionCode::SendCFC71 | FunctionCode::SendCFC72 | FunctionCode::SendCFC100 | FunctionCode::SendCFC101 | + FunctionCode::SendCFC102 | FunctionCode::SendCFC103 | FunctionCode::SendCFC104 | FunctionCode::SendCFC105 | FunctionCode::SendCFC106 | + FunctionCode::SendCFC107 | FunctionCode::SendCFC108 | FunctionCode::SendCFC109 | FunctionCode::SendCFC110 => { + handler.process_cfc(request.clone()) + }, _ => Err(ExceptionCode::IllegalFunction), }; write_result(function, header, writer, result, level) diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index a0a01bc6..eaf8ad50 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -266,25 +266,9 @@ impl AuthorizationType { } Request::SendCustomFunctionCode(x) => { match x.function_code() { - 0x41 => handler.process_cfc_65(unit_id, x.clone(), role), - 0x42 => handler.process_cfc_66(unit_id, x.clone(), role), - 0x43 => handler.process_cfc_67(unit_id, x.clone(), role), - 0x44 => handler.process_cfc_68(unit_id, x.clone(), role), - 0x45 => handler.process_cfc_69(unit_id, x.clone(), role), - 0x46 => handler.process_cfc_70(unit_id, x.clone(), role), - 0x47 => handler.process_cfc_71(unit_id, x.clone(), role), - 0x48 => handler.process_cfc_72(unit_id, x.clone(), role), - 0x64 => handler.process_cfc_100(unit_id, x.clone(), role), - 0x65 => handler.process_cfc_101(unit_id, x.clone(), role), - 0x66 => handler.process_cfc_102(unit_id, x.clone(), role), - 0x67 => handler.process_cfc_103(unit_id, x.clone(), role), - 0x68 => handler.process_cfc_104(unit_id, x.clone(), role), - 0x69 => handler.process_cfc_105(unit_id, x.clone(), role), - 0x6A => handler.process_cfc_106(unit_id, x.clone(), role), - 0x6B => handler.process_cfc_107(unit_id, x.clone(), role), - 0x6C => handler.process_cfc_108(unit_id, x.clone(), role), - 0x6D => handler.process_cfc_109(unit_id, x.clone(), role), - 0x6E => handler.process_cfc_110(unit_id, x.clone(), role), + 0x41 | 0x42 | 0x43 | 0x44 | 0x45 | 0x46 | 0x47 | 0x48 | 0x64 | 0x65 | 0x66 | 0x67 | 0x68 | 0x69 | 0x6A | 0x6B | 0x6C | 0x6D | 0x6E => { + handler.process_cfc(unit_id, x.clone(), role) + }, _ => Authorization::Deny, } diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index 4b80b626..ad2bfec4 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -95,7 +95,7 @@ impl RequestHandler for Handler { Ok(()) } - fn process_cfc_65(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); // increment each CFC value by 1 and return the result let incremented_data = values.iter().map(|&val| val + 1).collect(); From 8b026af386fa96808e048065bcde53bc7e38284a Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Mon, 11 Mar 2024 21:40:15 +0100 Subject: [PATCH 090/106] refactor(send_custom_fc): implement general purpose CFC handler in custom server example --- rodbus/examples/custom_server.rs | 98 ++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 0c7a1cab..ea2c08bc 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -81,6 +81,104 @@ impl RequestHandler for SimpleHandler { fn process_cfc(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}", values.function_code()); match values.function_code() { + 0x01 => { + if values.len() != 2 { + return Err(ExceptionCode::IllegalDataValue); + } + let start = *values.iter().next().unwrap(); + let count = *values.iter().next().unwrap(); + // read coils + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) + }, + 0x02 => { + if values.len() != 2 { + return Err(ExceptionCode::IllegalDataValue); + } + let start = *values.iter().next().unwrap(); + let count = *values.iter().next().unwrap(); + // read discrete inputs + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) + }, + 0x03 => { + if values.len() != 2 { + return Err(ExceptionCode::IllegalDataValue); + } + let start = *values.iter().next().unwrap(); + let count = *values.iter().next().unwrap(); + // read holding registers + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) + }, + 0x04 => { + if values.len() != 2 { + return Err(ExceptionCode::IllegalDataValue); + } + let start = *values.iter().next().unwrap(); + let count = *values.iter().next().unwrap(); + // read input registers + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) + }, + 0x05 => { + if values.len() != 2 { + return Err(ExceptionCode::IllegalDataValue); + } + let address = *values.iter().next().unwrap(); + let value = *values.iter().next().unwrap() != 0; + // write single coil + let result = self.write_single_coil(Indexed::new(address, value)); + match result { + Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![address, value as u16])), + Err(exception) => Err(exception), + } + }, + 0x06 => { + if values.len() != 2 { + return Err(ExceptionCode::IllegalDataValue); + } + let address = *values.iter().next().unwrap(); + let value = *values.iter().next().unwrap(); + // write single register + let result = self.write_single_register(Indexed::new(address, value)); + match result { + Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![address, value])), + Err(exception) => Err(exception), + } + }, + 0x0F => { + if values.len() < 5 { + return Err(ExceptionCode::IllegalDataValue); + } + let start = *values.iter().next().unwrap(); + let count = *values.iter().next().unwrap(); + let mut iterator = values.iter().skip(2); + let mut coils = vec![]; + for _ in 0..count { + coils.push(*iterator.next().unwrap() != 0); + } + // write multiple coils + let result = self.write_multiple_coils(WriteCoils::new(AddressRange::try_from(start, count).unwrap(), coils.into_iter())); + match result { + Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])), + Err(exception) => Err(exception), + } + }, + 0x10 => { + if values.len() < 5 { + return Err(ExceptionCode::IllegalDataValue); + } + let start = *values.iter().next().unwrap(); + let count = *values.iter().next().unwrap(); + let mut iterator = values.iter().skip(2); + let mut registers = vec![]; + for _ in 0..count { + registers.push(*iterator.next().unwrap()); + } + // write multiple registers + let result = self.write_multiple_registers(WriteRegisters::new(AddressRange::try_from(start, count).unwrap(), registers.into_iter()); + match result { + Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])), + Err(exception) => Err(exception), + } + }, 0x69 => { // increment each CFC value by 1 and return the result // Create a new vector to hold the incremented values From 790eb3b3e730eab5f973f97d7e3293603e36aa5b Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Tue, 12 Mar 2024 22:44:07 +0100 Subject: [PATCH 091/106] fix(send_custom_fc): fix request/parsing error when handling the CFC response --- rodbus/src/client/requests/send_custom_fc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index e95b599b..dbc80772 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -80,7 +80,7 @@ impl CustomFCOperation for CustomFunctionCode { let fc = cursor.read_u8()?; let byte_count_in = cursor.read_u8()?; let byte_count_out = cursor.read_u8()?; - let len = byte_count_in as usize; + let len = byte_count_out as usize; if len != cursor.remaining() / 2 { return Err(AduParseError::InsufficientBytesForByteCount(len, cursor.remaining() / 2).into()); From afbfbca35f86bbac6f4ba36a9612e780b975e43b Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Tue, 12 Mar 2024 22:45:19 +0100 Subject: [PATCH 092/106] refactor(send_custom_fc): remove the blocking of standard function codes when sending a CFC request --- rodbus/examples/custom_client.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/rodbus/examples/custom_client.rs b/rodbus/examples/custom_client.rs index a2752361..364a066f 100644 --- a/rodbus/examples/custom_client.rs +++ b/rodbus/examples/custom_client.rs @@ -211,17 +211,13 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box = values.iter().filter_map(|&v| u16::from_str_radix(v.trim_start_matches("0x"), 16).ok()).collect(); - if (fc >= 65 && fc <= 72) || (fc >= 100 && fc <= 110) { - let result = channel - .send_custom_function_code( - params, - CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values) - ) - .await; - print_write_result(result); - } else { - println!("Error: CFC number is not inside the range of 65-72 or 100-110."); - } + let result = channel + .send_custom_function_code( + params, + CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values) + ) + .await; + print_write_result(result); } _ => println!("unknown command"), } From 2d1ccab1bd671e163514063ca531b82681e30893 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Tue, 12 Mar 2024 22:46:02 +0100 Subject: [PATCH 093/106] feat(custom_server): add standard FC handling in the server implementation --- rodbus/examples/custom_server.rs | 56 +++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index ea2c08bc..159afc63 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -85,46 +85,67 @@ impl RequestHandler for SimpleHandler { if values.len() != 2 { return Err(ExceptionCode::IllegalDataValue); } + let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); + // read coils - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) + let coil_values = self.coils_as_mut().get(start as usize..start as usize + count as usize).unwrap().to_vec(); + let result = coil_values.iter().map(|&val| val as u16).collect(); + + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), result)) }, 0x02 => { if values.len() != 2 { return Err(ExceptionCode::IllegalDataValue); } + let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); + // read discrete inputs - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) + let discrete_input_values = self.discrete_inputs_as_mut().get(start as usize..start as usize + count as usize).unwrap().to_vec(); + let result = discrete_input_values.iter().map(|&val| val as u16).collect(); + + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), result)) }, 0x03 => { if values.len() != 2 { return Err(ExceptionCode::IllegalDataValue); } + let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); + // read holding registers - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) + let result = self.holding_registers_as_mut().get(start as usize..start as usize + count as usize).unwrap().to_vec(); + + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), result)) }, 0x04 => { if values.len() != 2 { return Err(ExceptionCode::IllegalDataValue); } + let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); + // read input registers - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) + let result = self.input_registers_as_mut().get(start as usize..start as usize + count as usize).unwrap().to_vec(); + + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), result)) }, 0x05 => { if values.len() != 2 { return Err(ExceptionCode::IllegalDataValue); } + let address = *values.iter().next().unwrap(); let value = *values.iter().next().unwrap() != 0; + // write single coil let result = self.write_single_coil(Indexed::new(address, value)); + match result { Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![address, value as u16])), Err(exception) => Err(exception), @@ -136,8 +157,10 @@ impl RequestHandler for SimpleHandler { } let address = *values.iter().next().unwrap(); let value = *values.iter().next().unwrap(); + // write single register let result = self.write_single_register(Indexed::new(address, value)); + match result { Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![address, value])), Err(exception) => Err(exception), @@ -147,6 +170,7 @@ impl RequestHandler for SimpleHandler { if values.len() < 5 { return Err(ExceptionCode::IllegalDataValue); } + let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); let mut iterator = values.iter().skip(2); @@ -154,17 +178,25 @@ impl RequestHandler for SimpleHandler { for _ in 0..count { coils.push(*iterator.next().unwrap() != 0); } + // write multiple coils - let result = self.write_multiple_coils(WriteCoils::new(AddressRange::try_from(start, count).unwrap(), coils.into_iter())); + /* + let range = AddressRange::try_from(start, count).unwrap(); + let result = self.write_multiple_coils(WriteCoils::new(range, coils.into_iter())); + match result { Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])), Err(exception) => Err(exception), } + */ + + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) }, 0x10 => { if values.len() < 5 { return Err(ExceptionCode::IllegalDataValue); } + let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); let mut iterator = values.iter().skip(2); @@ -172,14 +204,20 @@ impl RequestHandler for SimpleHandler { for _ in 0..count { registers.push(*iterator.next().unwrap()); } + // write multiple registers - let result = self.write_multiple_registers(WriteRegisters::new(AddressRange::try_from(start, count).unwrap(), registers.into_iter()); + /* + let range = AddressRange::try_from(start, count).unwrap(); + let result = self.write_multiple_registers(WriteRegisters::new(range, registers.into_iter())); + match result { Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])), Err(exception) => Err(exception), } + */ + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) }, - 0x69 => { + 0x41 => { // increment each CFC value by 1 and return the result // Create a new vector to hold the incremented values let incremented_data = values.iter().map(|&val| val + 1).collect(); @@ -187,7 +225,7 @@ impl RequestHandler for SimpleHandler { // Return a new CustomFunctionCode with the incremented data Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) }, - 0x70 => { + 0x42 => { // add a new value to the buffer and return the result // Create a new vector to hold the incremented values let extended_data = { @@ -199,7 +237,7 @@ impl RequestHandler for SimpleHandler { // Return a new CustomFunctionCode with the incremented data Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), extended_data)) }, - 0x71 => { + 0x43 => { // remove the first value from the buffer and return the result // Create a new vector to hold the incremented values let truncated_data = { From 14e61340b14569cecebe17236f9c93d5e53f498f Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Tue, 12 Mar 2024 22:47:47 +0100 Subject: [PATCH 094/106] feat(send_custom_fc): add standard FC routing for a CFC request in message --- rodbus/src/client/message.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 2af5e6d9..0d1c75e7 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -133,6 +133,16 @@ impl RequestDetails { RequestDetails::WriteMultipleRegisters(_) => Ok(FunctionCode::WriteMultipleRegisters), RequestDetails::SendCustomFunctionCode(x) => { match x.request.function_code() { + // Standard FCs + 0x01 => Ok(FunctionCode::ReadCoils), + 0x02 => Ok(FunctionCode::ReadDiscreteInputs), + 0x03 => Ok(FunctionCode::ReadHoldingRegisters), + 0x04 => Ok(FunctionCode::ReadInputRegisters), + 0x05 => Ok(FunctionCode::WriteSingleCoil), + 0x06 => Ok(FunctionCode::WriteSingleRegister), + 0x0F => Ok(FunctionCode::WriteMultipleCoils), + 0x10 => Ok(FunctionCode::WriteMultipleRegisters), + // Custom FCs 0x41 => Ok(FunctionCode::SendCFC65), 0x42 => Ok(FunctionCode::SendCFC66), 0x43 => Ok(FunctionCode::SendCFC67), From d91ec3f6050bd938bb40ba29e404b6a67bdb6e01 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Tue, 12 Mar 2024 22:53:15 +0100 Subject: [PATCH 095/106] refactor(send_custom_fc): remove panic statement when no valid CFC is found --- rodbus/src/server/request.rs | 80 ++++++++++++++++++------------------ rodbus/src/server/task.rs | 2 +- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index e24c59f0..57c7077e 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -55,38 +55,38 @@ impl<'a> BroadcastRequest<'a> { } impl<'a> Request<'a> { - pub(crate) fn get_function(&self) -> FunctionCode { + pub(crate) fn get_function(&self) -> Result { match self { - Request::ReadCoils(_) => FunctionCode::ReadCoils, - Request::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs, - Request::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters, - Request::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters, - Request::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil, - Request::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, - Request::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, - Request::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, + Request::ReadCoils(_) => Ok(FunctionCode::ReadCoils), + Request::ReadDiscreteInputs(_) => Ok(FunctionCode::ReadDiscreteInputs), + Request::ReadHoldingRegisters(_) => Ok(FunctionCode::ReadHoldingRegisters), + Request::ReadInputRegisters(_) => Ok(FunctionCode::ReadInputRegisters), + Request::WriteSingleCoil(_) => Ok(FunctionCode::WriteSingleCoil), + Request::WriteSingleRegister(_) => Ok(FunctionCode::WriteSingleRegister), + Request::WriteMultipleCoils(_) => Ok(FunctionCode::WriteMultipleCoils), + Request::WriteMultipleRegisters(_) => Ok(FunctionCode::WriteMultipleRegisters), Request::SendCustomFunctionCode(x) => { match x.function_code() { - 0x41 => FunctionCode::SendCFC65, - 0x42 => FunctionCode::SendCFC66, - 0x43 => FunctionCode::SendCFC67, - 0x44 => FunctionCode::SendCFC68, - 0x45 => FunctionCode::SendCFC69, - 0x46 => FunctionCode::SendCFC70, - 0x47 => FunctionCode::SendCFC71, - 0x48 => FunctionCode::SendCFC72, - 0x64 => FunctionCode::SendCFC100, - 0x65 => FunctionCode::SendCFC101, - 0x66 => FunctionCode::SendCFC102, - 0x67 => FunctionCode::SendCFC103, - 0x68 => FunctionCode::SendCFC104, - 0x69 => FunctionCode::SendCFC105, - 0x6A => FunctionCode::SendCFC106, - 0x6B => FunctionCode::SendCFC107, - 0x6C => FunctionCode::SendCFC108, - 0x6D => FunctionCode::SendCFC109, - 0x6E => FunctionCode::SendCFC110, - _ => panic!("Invalid custom function code"), + 0x41 => Ok(FunctionCode::SendCFC65), + 0x42 => Ok(FunctionCode::SendCFC66), + 0x43 => Ok(FunctionCode::SendCFC67), + 0x44 => Ok(FunctionCode::SendCFC68), + 0x45 => Ok(FunctionCode::SendCFC69), + 0x46 => Ok(FunctionCode::SendCFC70), + 0x47 => Ok(FunctionCode::SendCFC71), + 0x48 => Ok(FunctionCode::SendCFC72), + 0x64 => Ok(FunctionCode::SendCFC100), + 0x65 => Ok(FunctionCode::SendCFC101), + 0x66 => Ok(FunctionCode::SendCFC102), + 0x67 => Ok(FunctionCode::SendCFC103), + 0x68 => Ok(FunctionCode::SendCFC104), + 0x69 => Ok(FunctionCode::SendCFC105), + 0x6A => Ok(FunctionCode::SendCFC106), + 0x6B => Ok(FunctionCode::SendCFC107), + 0x6C => Ok(FunctionCode::SendCFC108), + 0x6D => Ok(FunctionCode::SendCFC109), + 0x6E => Ok(FunctionCode::SendCFC110), + _ => Err(ExceptionCode::IllegalFunction), } }, } @@ -135,40 +135,40 @@ impl<'a> Request<'a> { match self { Request::ReadCoils(range) => { let bits = BitWriter::new(*range, |i| handler.read_coil(i)); - writer.format_reply(header, function, &bits, level) + writer.format_reply(header, function.unwrap(), &bits, level) } Request::ReadDiscreteInputs(range) => { let bits = BitWriter::new(*range, |i| handler.read_discrete_input(i)); - writer.format_reply(header, function, &bits, level) + writer.format_reply(header, function.unwrap(), &bits, level) } Request::ReadHoldingRegisters(range) => { let registers = RegisterWriter::new(*range, |i| handler.read_holding_register(i)); - writer.format_reply(header, function, ®isters, level) + writer.format_reply(header, function.unwrap(), ®isters, level) } Request::ReadInputRegisters(range) => { let registers = RegisterWriter::new(*range, |i| handler.read_input_register(i)); - writer.format_reply(header, function, ®isters, level) + writer.format_reply(header, function.unwrap(), ®isters, level) } Request::WriteSingleCoil(request) => { let result = handler.write_single_coil(*request).map(|_| *request); - write_result(function, header, writer, result, level) + write_result(function.unwrap(), header, writer, result, level) } Request::WriteSingleRegister(request) => { let result = handler.write_single_register(*request).map(|_| *request); - write_result(function, header, writer, result, level) + write_result(function.unwrap(), header, writer, result, level) } Request::WriteMultipleCoils(items) => { let result = handler.write_multiple_coils(*items).map(|_| items.range); - write_result(function, header, writer, result, level) + write_result(function.unwrap(), header, writer, result, level) } Request::WriteMultipleRegisters(items) => { let result = handler .write_multiple_registers(*items) .map(|_| items.range); - write_result(function, header, writer, result, level) + write_result(function.unwrap(), header, writer, result, level) } Request::SendCustomFunctionCode(request) => { - let result = match function { + let result = match function.unwrap() { FunctionCode::SendCFC65 | FunctionCode::SendCFC66 | FunctionCode::SendCFC67 | FunctionCode::SendCFC68 | FunctionCode::SendCFC69 | FunctionCode::SendCFC70 | FunctionCode::SendCFC71 | FunctionCode::SendCFC72 | FunctionCode::SendCFC100 | FunctionCode::SendCFC101 | FunctionCode::SendCFC102 | FunctionCode::SendCFC103 | FunctionCode::SendCFC104 | FunctionCode::SendCFC105 | FunctionCode::SendCFC106 | @@ -177,7 +177,7 @@ impl<'a> Request<'a> { }, _ => Err(ExceptionCode::IllegalFunction), }; - write_result(function, header, writer, result, level) + write_result(function.unwrap(), header, writer, result, level) } } } @@ -264,7 +264,7 @@ impl<'a, 'b> RequestDisplay<'a, 'b> { impl std::fmt::Display for RequestDisplay<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.request.get_function())?; + write!(f, "{}", self.request.get_function().unwrap())?; if self.level.data_headers() { match self.request { diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index eaf8ad50..1fd0dc0f 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -190,7 +190,7 @@ where self.reply_with_error( io, frame.header, - request.get_function(), + request.get_function().unwrap(), ExceptionCode::IllegalFunction, ) .await?; From 0ef05e40b43c28be34fe57884a705157ae1fc4ad Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 13 Mar 2024 18:31:16 +0100 Subject: [PATCH 096/106] Revert "refactor(send_custom_fc): remove panic statement when no valid CFC is found" This reverts commit d91ec3f6050bd938bb40ba29e404b6a67bdb6e01. --- rodbus/src/server/request.rs | 80 ++++++++++++++++++------------------ rodbus/src/server/task.rs | 2 +- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 57c7077e..e24c59f0 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -55,38 +55,38 @@ impl<'a> BroadcastRequest<'a> { } impl<'a> Request<'a> { - pub(crate) fn get_function(&self) -> Result { + pub(crate) fn get_function(&self) -> FunctionCode { match self { - Request::ReadCoils(_) => Ok(FunctionCode::ReadCoils), - Request::ReadDiscreteInputs(_) => Ok(FunctionCode::ReadDiscreteInputs), - Request::ReadHoldingRegisters(_) => Ok(FunctionCode::ReadHoldingRegisters), - Request::ReadInputRegisters(_) => Ok(FunctionCode::ReadInputRegisters), - Request::WriteSingleCoil(_) => Ok(FunctionCode::WriteSingleCoil), - Request::WriteSingleRegister(_) => Ok(FunctionCode::WriteSingleRegister), - Request::WriteMultipleCoils(_) => Ok(FunctionCode::WriteMultipleCoils), - Request::WriteMultipleRegisters(_) => Ok(FunctionCode::WriteMultipleRegisters), + Request::ReadCoils(_) => FunctionCode::ReadCoils, + Request::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs, + Request::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters, + Request::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters, + Request::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil, + Request::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, + Request::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, + Request::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, Request::SendCustomFunctionCode(x) => { match x.function_code() { - 0x41 => Ok(FunctionCode::SendCFC65), - 0x42 => Ok(FunctionCode::SendCFC66), - 0x43 => Ok(FunctionCode::SendCFC67), - 0x44 => Ok(FunctionCode::SendCFC68), - 0x45 => Ok(FunctionCode::SendCFC69), - 0x46 => Ok(FunctionCode::SendCFC70), - 0x47 => Ok(FunctionCode::SendCFC71), - 0x48 => Ok(FunctionCode::SendCFC72), - 0x64 => Ok(FunctionCode::SendCFC100), - 0x65 => Ok(FunctionCode::SendCFC101), - 0x66 => Ok(FunctionCode::SendCFC102), - 0x67 => Ok(FunctionCode::SendCFC103), - 0x68 => Ok(FunctionCode::SendCFC104), - 0x69 => Ok(FunctionCode::SendCFC105), - 0x6A => Ok(FunctionCode::SendCFC106), - 0x6B => Ok(FunctionCode::SendCFC107), - 0x6C => Ok(FunctionCode::SendCFC108), - 0x6D => Ok(FunctionCode::SendCFC109), - 0x6E => Ok(FunctionCode::SendCFC110), - _ => Err(ExceptionCode::IllegalFunction), + 0x41 => FunctionCode::SendCFC65, + 0x42 => FunctionCode::SendCFC66, + 0x43 => FunctionCode::SendCFC67, + 0x44 => FunctionCode::SendCFC68, + 0x45 => FunctionCode::SendCFC69, + 0x46 => FunctionCode::SendCFC70, + 0x47 => FunctionCode::SendCFC71, + 0x48 => FunctionCode::SendCFC72, + 0x64 => FunctionCode::SendCFC100, + 0x65 => FunctionCode::SendCFC101, + 0x66 => FunctionCode::SendCFC102, + 0x67 => FunctionCode::SendCFC103, + 0x68 => FunctionCode::SendCFC104, + 0x69 => FunctionCode::SendCFC105, + 0x6A => FunctionCode::SendCFC106, + 0x6B => FunctionCode::SendCFC107, + 0x6C => FunctionCode::SendCFC108, + 0x6D => FunctionCode::SendCFC109, + 0x6E => FunctionCode::SendCFC110, + _ => panic!("Invalid custom function code"), } }, } @@ -135,40 +135,40 @@ impl<'a> Request<'a> { match self { Request::ReadCoils(range) => { let bits = BitWriter::new(*range, |i| handler.read_coil(i)); - writer.format_reply(header, function.unwrap(), &bits, level) + writer.format_reply(header, function, &bits, level) } Request::ReadDiscreteInputs(range) => { let bits = BitWriter::new(*range, |i| handler.read_discrete_input(i)); - writer.format_reply(header, function.unwrap(), &bits, level) + writer.format_reply(header, function, &bits, level) } Request::ReadHoldingRegisters(range) => { let registers = RegisterWriter::new(*range, |i| handler.read_holding_register(i)); - writer.format_reply(header, function.unwrap(), ®isters, level) + writer.format_reply(header, function, ®isters, level) } Request::ReadInputRegisters(range) => { let registers = RegisterWriter::new(*range, |i| handler.read_input_register(i)); - writer.format_reply(header, function.unwrap(), ®isters, level) + writer.format_reply(header, function, ®isters, level) } Request::WriteSingleCoil(request) => { let result = handler.write_single_coil(*request).map(|_| *request); - write_result(function.unwrap(), header, writer, result, level) + write_result(function, header, writer, result, level) } Request::WriteSingleRegister(request) => { let result = handler.write_single_register(*request).map(|_| *request); - write_result(function.unwrap(), header, writer, result, level) + write_result(function, header, writer, result, level) } Request::WriteMultipleCoils(items) => { let result = handler.write_multiple_coils(*items).map(|_| items.range); - write_result(function.unwrap(), header, writer, result, level) + write_result(function, header, writer, result, level) } Request::WriteMultipleRegisters(items) => { let result = handler .write_multiple_registers(*items) .map(|_| items.range); - write_result(function.unwrap(), header, writer, result, level) + write_result(function, header, writer, result, level) } Request::SendCustomFunctionCode(request) => { - let result = match function.unwrap() { + let result = match function { FunctionCode::SendCFC65 | FunctionCode::SendCFC66 | FunctionCode::SendCFC67 | FunctionCode::SendCFC68 | FunctionCode::SendCFC69 | FunctionCode::SendCFC70 | FunctionCode::SendCFC71 | FunctionCode::SendCFC72 | FunctionCode::SendCFC100 | FunctionCode::SendCFC101 | FunctionCode::SendCFC102 | FunctionCode::SendCFC103 | FunctionCode::SendCFC104 | FunctionCode::SendCFC105 | FunctionCode::SendCFC106 | @@ -177,7 +177,7 @@ impl<'a> Request<'a> { }, _ => Err(ExceptionCode::IllegalFunction), }; - write_result(function.unwrap(), header, writer, result, level) + write_result(function, header, writer, result, level) } } } @@ -264,7 +264,7 @@ impl<'a, 'b> RequestDisplay<'a, 'b> { impl std::fmt::Display for RequestDisplay<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.request.get_function().unwrap())?; + write!(f, "{}", self.request.get_function())?; if self.level.data_headers() { match self.request { diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index 1fd0dc0f..eaf8ad50 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -190,7 +190,7 @@ where self.reply_with_error( io, frame.header, - request.get_function().unwrap(), + request.get_function(), ExceptionCode::IllegalFunction, ) .await?; From cbf2d2e7f89abc57a9faaa5c9a751a407ece397f Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 13 Mar 2024 18:31:23 +0100 Subject: [PATCH 097/106] Revert "feat(send_custom_fc): add standard FC routing for a CFC request in message" This reverts commit 14e61340b14569cecebe17236f9c93d5e53f498f. --- rodbus/src/client/message.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 0d1c75e7..2af5e6d9 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -133,16 +133,6 @@ impl RequestDetails { RequestDetails::WriteMultipleRegisters(_) => Ok(FunctionCode::WriteMultipleRegisters), RequestDetails::SendCustomFunctionCode(x) => { match x.request.function_code() { - // Standard FCs - 0x01 => Ok(FunctionCode::ReadCoils), - 0x02 => Ok(FunctionCode::ReadDiscreteInputs), - 0x03 => Ok(FunctionCode::ReadHoldingRegisters), - 0x04 => Ok(FunctionCode::ReadInputRegisters), - 0x05 => Ok(FunctionCode::WriteSingleCoil), - 0x06 => Ok(FunctionCode::WriteSingleRegister), - 0x0F => Ok(FunctionCode::WriteMultipleCoils), - 0x10 => Ok(FunctionCode::WriteMultipleRegisters), - // Custom FCs 0x41 => Ok(FunctionCode::SendCFC65), 0x42 => Ok(FunctionCode::SendCFC66), 0x43 => Ok(FunctionCode::SendCFC67), From 19d99d75918e2b9e2a403d830e8e0342e83f0a91 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 13 Mar 2024 18:31:25 +0100 Subject: [PATCH 098/106] Revert "feat(custom_server): add standard FC handling in the server implementation" This reverts commit 2d1ccab1bd671e163514063ca531b82681e30893. --- rodbus/examples/custom_server.rs | 56 +++++--------------------------- 1 file changed, 9 insertions(+), 47 deletions(-) diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 159afc63..ea2c08bc 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -85,67 +85,46 @@ impl RequestHandler for SimpleHandler { if values.len() != 2 { return Err(ExceptionCode::IllegalDataValue); } - let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); - // read coils - let coil_values = self.coils_as_mut().get(start as usize..start as usize + count as usize).unwrap().to_vec(); - let result = coil_values.iter().map(|&val| val as u16).collect(); - - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), result)) + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) }, 0x02 => { if values.len() != 2 { return Err(ExceptionCode::IllegalDataValue); } - let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); - // read discrete inputs - let discrete_input_values = self.discrete_inputs_as_mut().get(start as usize..start as usize + count as usize).unwrap().to_vec(); - let result = discrete_input_values.iter().map(|&val| val as u16).collect(); - - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), result)) + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) }, 0x03 => { if values.len() != 2 { return Err(ExceptionCode::IllegalDataValue); } - let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); - // read holding registers - let result = self.holding_registers_as_mut().get(start as usize..start as usize + count as usize).unwrap().to_vec(); - - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), result)) + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) }, 0x04 => { if values.len() != 2 { return Err(ExceptionCode::IllegalDataValue); } - let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); - // read input registers - let result = self.input_registers_as_mut().get(start as usize..start as usize + count as usize).unwrap().to_vec(); - - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), result)) + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) }, 0x05 => { if values.len() != 2 { return Err(ExceptionCode::IllegalDataValue); } - let address = *values.iter().next().unwrap(); let value = *values.iter().next().unwrap() != 0; - // write single coil let result = self.write_single_coil(Indexed::new(address, value)); - match result { Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![address, value as u16])), Err(exception) => Err(exception), @@ -157,10 +136,8 @@ impl RequestHandler for SimpleHandler { } let address = *values.iter().next().unwrap(); let value = *values.iter().next().unwrap(); - // write single register let result = self.write_single_register(Indexed::new(address, value)); - match result { Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![address, value])), Err(exception) => Err(exception), @@ -170,7 +147,6 @@ impl RequestHandler for SimpleHandler { if values.len() < 5 { return Err(ExceptionCode::IllegalDataValue); } - let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); let mut iterator = values.iter().skip(2); @@ -178,25 +154,17 @@ impl RequestHandler for SimpleHandler { for _ in 0..count { coils.push(*iterator.next().unwrap() != 0); } - // write multiple coils - /* - let range = AddressRange::try_from(start, count).unwrap(); - let result = self.write_multiple_coils(WriteCoils::new(range, coils.into_iter())); - + let result = self.write_multiple_coils(WriteCoils::new(AddressRange::try_from(start, count).unwrap(), coils.into_iter())); match result { Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])), Err(exception) => Err(exception), } - */ - - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) }, 0x10 => { if values.len() < 5 { return Err(ExceptionCode::IllegalDataValue); } - let start = *values.iter().next().unwrap(); let count = *values.iter().next().unwrap(); let mut iterator = values.iter().skip(2); @@ -204,20 +172,14 @@ impl RequestHandler for SimpleHandler { for _ in 0..count { registers.push(*iterator.next().unwrap()); } - // write multiple registers - /* - let range = AddressRange::try_from(start, count).unwrap(); - let result = self.write_multiple_registers(WriteRegisters::new(range, registers.into_iter())); - + let result = self.write_multiple_registers(WriteRegisters::new(AddressRange::try_from(start, count).unwrap(), registers.into_iter()); match result { Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])), Err(exception) => Err(exception), } - */ - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) }, - 0x41 => { + 0x69 => { // increment each CFC value by 1 and return the result // Create a new vector to hold the incremented values let incremented_data = values.iter().map(|&val| val + 1).collect(); @@ -225,7 +187,7 @@ impl RequestHandler for SimpleHandler { // Return a new CustomFunctionCode with the incremented data Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) }, - 0x42 => { + 0x70 => { // add a new value to the buffer and return the result // Create a new vector to hold the incremented values let extended_data = { @@ -237,7 +199,7 @@ impl RequestHandler for SimpleHandler { // Return a new CustomFunctionCode with the incremented data Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), extended_data)) }, - 0x43 => { + 0x71 => { // remove the first value from the buffer and return the result // Create a new vector to hold the incremented values let truncated_data = { From 6957bae85b02942003d5499a3647f384e405f6ec Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 13 Mar 2024 18:31:25 +0100 Subject: [PATCH 099/106] Revert "refactor(send_custom_fc): remove the blocking of standard function codes when sending a CFC request" This reverts commit afbfbca35f86bbac6f4ba36a9612e780b975e43b. --- rodbus/examples/custom_client.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/rodbus/examples/custom_client.rs b/rodbus/examples/custom_client.rs index 364a066f..a2752361 100644 --- a/rodbus/examples/custom_client.rs +++ b/rodbus/examples/custom_client.rs @@ -211,13 +211,17 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box = values.iter().filter_map(|&v| u16::from_str_radix(v.trim_start_matches("0x"), 16).ok()).collect(); - let result = channel - .send_custom_function_code( - params, - CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values) - ) - .await; - print_write_result(result); + if (fc >= 65 && fc <= 72) || (fc >= 100 && fc <= 110) { + let result = channel + .send_custom_function_code( + params, + CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values) + ) + .await; + print_write_result(result); + } else { + println!("Error: CFC number is not inside the range of 65-72 or 100-110."); + } } _ => println!("unknown command"), } From aca481f7f88d3213070fda4cb9929ea96cb670cc Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 13 Mar 2024 19:43:08 +0100 Subject: [PATCH 100/106] feat(send_custom_fc): updated CFC logic because of reversal conflicts --- rodbus/examples/custom_server.rs | 98 -------------------------------- rodbus/src/server/request.rs | 80 +++++++++++++------------- rodbus/src/server/task.rs | 2 +- 3 files changed, 41 insertions(+), 139 deletions(-) diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index ea2c08bc..0c7a1cab 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -81,104 +81,6 @@ impl RequestHandler for SimpleHandler { fn process_cfc(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}", values.function_code()); match values.function_code() { - 0x01 => { - if values.len() != 2 { - return Err(ExceptionCode::IllegalDataValue); - } - let start = *values.iter().next().unwrap(); - let count = *values.iter().next().unwrap(); - // read coils - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) - }, - 0x02 => { - if values.len() != 2 { - return Err(ExceptionCode::IllegalDataValue); - } - let start = *values.iter().next().unwrap(); - let count = *values.iter().next().unwrap(); - // read discrete inputs - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) - }, - 0x03 => { - if values.len() != 2 { - return Err(ExceptionCode::IllegalDataValue); - } - let start = *values.iter().next().unwrap(); - let count = *values.iter().next().unwrap(); - // read holding registers - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) - }, - 0x04 => { - if values.len() != 2 { - return Err(ExceptionCode::IllegalDataValue); - } - let start = *values.iter().next().unwrap(); - let count = *values.iter().next().unwrap(); - // read input registers - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])) - }, - 0x05 => { - if values.len() != 2 { - return Err(ExceptionCode::IllegalDataValue); - } - let address = *values.iter().next().unwrap(); - let value = *values.iter().next().unwrap() != 0; - // write single coil - let result = self.write_single_coil(Indexed::new(address, value)); - match result { - Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![address, value as u16])), - Err(exception) => Err(exception), - } - }, - 0x06 => { - if values.len() != 2 { - return Err(ExceptionCode::IllegalDataValue); - } - let address = *values.iter().next().unwrap(); - let value = *values.iter().next().unwrap(); - // write single register - let result = self.write_single_register(Indexed::new(address, value)); - match result { - Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![address, value])), - Err(exception) => Err(exception), - } - }, - 0x0F => { - if values.len() < 5 { - return Err(ExceptionCode::IllegalDataValue); - } - let start = *values.iter().next().unwrap(); - let count = *values.iter().next().unwrap(); - let mut iterator = values.iter().skip(2); - let mut coils = vec![]; - for _ in 0..count { - coils.push(*iterator.next().unwrap() != 0); - } - // write multiple coils - let result = self.write_multiple_coils(WriteCoils::new(AddressRange::try_from(start, count).unwrap(), coils.into_iter())); - match result { - Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])), - Err(exception) => Err(exception), - } - }, - 0x10 => { - if values.len() < 5 { - return Err(ExceptionCode::IllegalDataValue); - } - let start = *values.iter().next().unwrap(); - let count = *values.iter().next().unwrap(); - let mut iterator = values.iter().skip(2); - let mut registers = vec![]; - for _ in 0..count { - registers.push(*iterator.next().unwrap()); - } - // write multiple registers - let result = self.write_multiple_registers(WriteRegisters::new(AddressRange::try_from(start, count).unwrap(), registers.into_iter()); - match result { - Ok(_) => Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), vec![start, count])), - Err(exception) => Err(exception), - } - }, 0x69 => { // increment each CFC value by 1 and return the result // Create a new vector to hold the incremented values diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index e24c59f0..57c7077e 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -55,38 +55,38 @@ impl<'a> BroadcastRequest<'a> { } impl<'a> Request<'a> { - pub(crate) fn get_function(&self) -> FunctionCode { + pub(crate) fn get_function(&self) -> Result { match self { - Request::ReadCoils(_) => FunctionCode::ReadCoils, - Request::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs, - Request::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters, - Request::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters, - Request::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil, - Request::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister, - Request::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils, - Request::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters, + Request::ReadCoils(_) => Ok(FunctionCode::ReadCoils), + Request::ReadDiscreteInputs(_) => Ok(FunctionCode::ReadDiscreteInputs), + Request::ReadHoldingRegisters(_) => Ok(FunctionCode::ReadHoldingRegisters), + Request::ReadInputRegisters(_) => Ok(FunctionCode::ReadInputRegisters), + Request::WriteSingleCoil(_) => Ok(FunctionCode::WriteSingleCoil), + Request::WriteSingleRegister(_) => Ok(FunctionCode::WriteSingleRegister), + Request::WriteMultipleCoils(_) => Ok(FunctionCode::WriteMultipleCoils), + Request::WriteMultipleRegisters(_) => Ok(FunctionCode::WriteMultipleRegisters), Request::SendCustomFunctionCode(x) => { match x.function_code() { - 0x41 => FunctionCode::SendCFC65, - 0x42 => FunctionCode::SendCFC66, - 0x43 => FunctionCode::SendCFC67, - 0x44 => FunctionCode::SendCFC68, - 0x45 => FunctionCode::SendCFC69, - 0x46 => FunctionCode::SendCFC70, - 0x47 => FunctionCode::SendCFC71, - 0x48 => FunctionCode::SendCFC72, - 0x64 => FunctionCode::SendCFC100, - 0x65 => FunctionCode::SendCFC101, - 0x66 => FunctionCode::SendCFC102, - 0x67 => FunctionCode::SendCFC103, - 0x68 => FunctionCode::SendCFC104, - 0x69 => FunctionCode::SendCFC105, - 0x6A => FunctionCode::SendCFC106, - 0x6B => FunctionCode::SendCFC107, - 0x6C => FunctionCode::SendCFC108, - 0x6D => FunctionCode::SendCFC109, - 0x6E => FunctionCode::SendCFC110, - _ => panic!("Invalid custom function code"), + 0x41 => Ok(FunctionCode::SendCFC65), + 0x42 => Ok(FunctionCode::SendCFC66), + 0x43 => Ok(FunctionCode::SendCFC67), + 0x44 => Ok(FunctionCode::SendCFC68), + 0x45 => Ok(FunctionCode::SendCFC69), + 0x46 => Ok(FunctionCode::SendCFC70), + 0x47 => Ok(FunctionCode::SendCFC71), + 0x48 => Ok(FunctionCode::SendCFC72), + 0x64 => Ok(FunctionCode::SendCFC100), + 0x65 => Ok(FunctionCode::SendCFC101), + 0x66 => Ok(FunctionCode::SendCFC102), + 0x67 => Ok(FunctionCode::SendCFC103), + 0x68 => Ok(FunctionCode::SendCFC104), + 0x69 => Ok(FunctionCode::SendCFC105), + 0x6A => Ok(FunctionCode::SendCFC106), + 0x6B => Ok(FunctionCode::SendCFC107), + 0x6C => Ok(FunctionCode::SendCFC108), + 0x6D => Ok(FunctionCode::SendCFC109), + 0x6E => Ok(FunctionCode::SendCFC110), + _ => Err(ExceptionCode::IllegalFunction), } }, } @@ -135,40 +135,40 @@ impl<'a> Request<'a> { match self { Request::ReadCoils(range) => { let bits = BitWriter::new(*range, |i| handler.read_coil(i)); - writer.format_reply(header, function, &bits, level) + writer.format_reply(header, function.unwrap(), &bits, level) } Request::ReadDiscreteInputs(range) => { let bits = BitWriter::new(*range, |i| handler.read_discrete_input(i)); - writer.format_reply(header, function, &bits, level) + writer.format_reply(header, function.unwrap(), &bits, level) } Request::ReadHoldingRegisters(range) => { let registers = RegisterWriter::new(*range, |i| handler.read_holding_register(i)); - writer.format_reply(header, function, ®isters, level) + writer.format_reply(header, function.unwrap(), ®isters, level) } Request::ReadInputRegisters(range) => { let registers = RegisterWriter::new(*range, |i| handler.read_input_register(i)); - writer.format_reply(header, function, ®isters, level) + writer.format_reply(header, function.unwrap(), ®isters, level) } Request::WriteSingleCoil(request) => { let result = handler.write_single_coil(*request).map(|_| *request); - write_result(function, header, writer, result, level) + write_result(function.unwrap(), header, writer, result, level) } Request::WriteSingleRegister(request) => { let result = handler.write_single_register(*request).map(|_| *request); - write_result(function, header, writer, result, level) + write_result(function.unwrap(), header, writer, result, level) } Request::WriteMultipleCoils(items) => { let result = handler.write_multiple_coils(*items).map(|_| items.range); - write_result(function, header, writer, result, level) + write_result(function.unwrap(), header, writer, result, level) } Request::WriteMultipleRegisters(items) => { let result = handler .write_multiple_registers(*items) .map(|_| items.range); - write_result(function, header, writer, result, level) + write_result(function.unwrap(), header, writer, result, level) } Request::SendCustomFunctionCode(request) => { - let result = match function { + let result = match function.unwrap() { FunctionCode::SendCFC65 | FunctionCode::SendCFC66 | FunctionCode::SendCFC67 | FunctionCode::SendCFC68 | FunctionCode::SendCFC69 | FunctionCode::SendCFC70 | FunctionCode::SendCFC71 | FunctionCode::SendCFC72 | FunctionCode::SendCFC100 | FunctionCode::SendCFC101 | FunctionCode::SendCFC102 | FunctionCode::SendCFC103 | FunctionCode::SendCFC104 | FunctionCode::SendCFC105 | FunctionCode::SendCFC106 | @@ -177,7 +177,7 @@ impl<'a> Request<'a> { }, _ => Err(ExceptionCode::IllegalFunction), }; - write_result(function, header, writer, result, level) + write_result(function.unwrap(), header, writer, result, level) } } } @@ -264,7 +264,7 @@ impl<'a, 'b> RequestDisplay<'a, 'b> { impl std::fmt::Display for RequestDisplay<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.request.get_function())?; + write!(f, "{}", self.request.get_function().unwrap())?; if self.level.data_headers() { match self.request { diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index eaf8ad50..1fd0dc0f 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -190,7 +190,7 @@ where self.reply_with_error( io, frame.header, - request.get_function(), + request.get_function().unwrap(), ExceptionCode::IllegalFunction, ) .await?; From 5b4694c38f37f0f228ff526e6e41293eb54cde1c Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 13 Mar 2024 20:21:06 +0100 Subject: [PATCH 101/106] test(send_custom_fc): adjust integration test for CFCs --- rodbus/tests/integration_test.rs | 123 +++++++++++++------------------ 1 file changed, 52 insertions(+), 71 deletions(-) diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index ad2bfec4..cd9350a3 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -97,10 +97,41 @@ impl RequestHandler for Handler { fn process_cfc(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); - // increment each CFC value by 1 and return the result - let incremented_data = values.iter().map(|&val| val + 1).collect(); + match values.function_code() { + 0x41 => { + // increment each CFC value by 1 and return the result + // Create a new vector to hold the incremented values + let incremented_data = values.iter().map(|&val| val + 1).collect(); - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) + // Return a new CustomFunctionCode with the incremented data + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) + }, + 0x42 => { + // add a new value to the buffer and return the result + // Create a new vector to hold the incremented values + let extended_data = { + let mut extended_data = values.iter().map(|val| *val).collect::>(); + extended_data.push(0xC0DE); + extended_data + }; + + // Return a new CustomFunctionCode with the incremented data + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), extended_data)) + }, + 0x43 => { + // remove the first value from the buffer and return the result + // Create a new vector to hold the incremented values + let truncated_data = { + let mut truncated_data = values.iter().map(|val| *val).collect::>(); + truncated_data.pop(); + truncated_data + }; + + // Return a new CustomFunctionCode with the incremented data + Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), truncated_data)) + }, + _ => Err(ExceptionCode::IllegalFunction), + } } } @@ -230,82 +261,32 @@ async fn test_requests_and_responses() { Indexed::new(2, 0x0506) ] ); + // Test the implemented test handlers assert_eq!( channel.send_custom_function_code(params, CustomFunctionCode::new(0x41, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, Ok(CustomFunctionCode::new(0x41, 4, 4, vec![0xC0DF, 0xCAFF, 0xC0DF, 0xCAFF])) ); assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x42, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x43, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x44, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x45, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x46, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x47, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x48, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x64, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x65, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) + channel.send_custom_function_code(params, CustomFunctionCode::new(0x42, 4, 5, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Ok(CustomFunctionCode::new(0x42, 4, 5, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE])) ); assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x66, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x67, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x68, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x69, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x6A, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x6B, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x6C, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x6D, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) - ); - assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x6E, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Err(rodbus::ExceptionCode::IllegalFunction.into()) + channel.send_custom_function_code(params, CustomFunctionCode::new(0x43, 4, 3, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Ok(CustomFunctionCode::new(0x43, 4, 3, vec![0xC0DE, 0xCAFE, 0xC0DE])) ); + // Test the unimplemented test handlers + for i in 68..72 { + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + } + for i in 100..110 { + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + } } #[test] From e6d374d362597ae1af39596f3d7036c10236b61e Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 13 Mar 2024 21:19:37 +0100 Subject: [PATCH 102/106] refactor(send_custom_fc): correct invalid CFC server handler fc match arms --- rodbus/examples/custom_server.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 0c7a1cab..945fb5f9 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -81,7 +81,7 @@ impl RequestHandler for SimpleHandler { fn process_cfc(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { tracing::info!("processing custom function code: {}", values.function_code()); match values.function_code() { - 0x69 => { + 0x41 => { // increment each CFC value by 1 and return the result // Create a new vector to hold the incremented values let incremented_data = values.iter().map(|&val| val + 1).collect(); @@ -89,7 +89,7 @@ impl RequestHandler for SimpleHandler { // Return a new CustomFunctionCode with the incremented data Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) }, - 0x70 => { + 0x42 => { // add a new value to the buffer and return the result // Create a new vector to hold the incremented values let extended_data = { @@ -101,7 +101,7 @@ impl RequestHandler for SimpleHandler { // Return a new CustomFunctionCode with the incremented data Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), extended_data)) }, - 0x71 => { + 0x43 => { // remove the first value from the buffer and return the result // Create a new vector to hold the incremented values let truncated_data = { From c2cb3f03c9870c326bdcebba510b59a27b7810f1 Mon Sep 17 00:00:00 2001 From: dkoehler-dev Date: Wed, 13 Mar 2024 21:45:47 +0100 Subject: [PATCH 103/106] test(send_custom_fc): add integration test for the CFC range from 0 to 255 --- rodbus/tests/integration_test.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index cd9350a3..9ab8afd3 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -261,7 +261,14 @@ async fn test_requests_and_responses() { Indexed::new(2, 0x0506) ] ); - // Test the implemented test handlers + // Test the invalid CFC handlers below 65 + for i in 0..65 { + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + } + // Test the implemented valid test handlers 65, 66, 67 assert_eq!( channel.send_custom_function_code(params, CustomFunctionCode::new(0x41, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, Ok(CustomFunctionCode::new(0x41, 4, 4, vec![0xC0DF, 0xCAFF, 0xC0DF, 0xCAFF])) @@ -274,19 +281,34 @@ async fn test_requests_and_responses() { channel.send_custom_function_code(params, CustomFunctionCode::new(0x43, 4, 3, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, Ok(CustomFunctionCode::new(0x43, 4, 3, vec![0xC0DE, 0xCAFE, 0xC0DE])) ); - // Test the unimplemented test handlers - for i in 68..72 { + // Test the unimplemented valid handlers from 68 to 72 + for i in 68..73 { assert_eq!( channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, Err(rodbus::ExceptionCode::IllegalFunction.into()) ); } + // Test the invalid handlers from 73 to 99 + for i in 73..100 { + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + } + // Test the unimplemented valid handlers from 100 to 110 for i in 100..110 { assert_eq!( channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, Err(rodbus::ExceptionCode::IllegalFunction.into()) ); } + // Test the invalid CFC handlers from 111 to 255 + for i in 111..=255 { + assert_eq!( + channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + Err(rodbus::ExceptionCode::IllegalFunction.into()) + ); + } } #[test] From b0e2245d3bf9bf2da6ad15c145fb1c80f0d836a0 Mon Sep 17 00:00:00 2001 From: Kay Grewe Date: Wed, 3 Apr 2024 13:36:43 +0200 Subject: [PATCH 104/106] refactor(CustomFunctionCode): add is_empty() func as suggested by clippy --- rodbus/src/types.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index e88d2620..2dc0bb95 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -379,7 +379,12 @@ impl Default for UnitId { impl CustomFunctionCode { /// Create a new custom function code pub fn new(fc: u8, byte_count_in: u8, byte_count_out: u8, data: Vec) -> Self { - Self { fc, byte_count_in, byte_count_out, data } + Self { + fc, + byte_count_in, + byte_count_out, + data, + } } /// Get the function code @@ -402,6 +407,9 @@ impl CustomFunctionCode { self.data.len() } + /// Check if the underlying vector is empty + pub fn is_empty(&self) -> bool { self.data.is_empty() } + /// Iterate over the underlying vector pub fn iter(&self) -> std::slice::Iter { self.data.iter() @@ -504,5 +512,4 @@ mod tests { assert!(UnitId::new(255).is_rtu_reserved()); assert!(!UnitId::new(41).is_rtu_reserved()); } - } From ba0838959104388782a3ccb6afed0bb8a154a0e3 Mon Sep 17 00:00:00 2001 From: Kay Grewe Date: Wed, 3 Apr 2024 13:48:14 +0200 Subject: [PATCH 105/106] refactor(check_auth): collapsed cfc codes into ranges --- rodbus/src/server/task.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index 1fd0dc0f..a48d1f4a 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -264,14 +264,11 @@ impl AuthorizationType { Request::WriteMultipleRegisters(x) => { handler.write_multiple_registers(unit_id, x.range, role) } - Request::SendCustomFunctionCode(x) => { - match x.function_code() { - 0x41 | 0x42 | 0x43 | 0x44 | 0x45 | 0x46 | 0x47 | 0x48 | 0x64 | 0x65 | 0x66 | 0x67 | 0x68 | 0x69 | 0x6A | 0x6B | 0x6C | 0x6D | 0x6E => { - handler.process_cfc(unit_id, x.clone(), role) - }, - _ => Authorization::Deny, + Request::SendCustomFunctionCode(x) => match x.function_code() { + 0x41..=0x48 | 0x64..=0x6E => { + handler.process_cfc(unit_id, x.clone(), role) } - + _ => Authorization::Deny, }, } } From 2f85e28e7dc00128b6f791cc4c5bdea1493253d6 Mon Sep 17 00:00:00 2001 From: Kay Grewe Date: Wed, 3 Apr 2024 13:55:00 +0200 Subject: [PATCH 106/106] chore(fmt): changed formatting by running cargo fmt --- rodbus/examples/client.rs | 2 +- rodbus/examples/custom_client.rs | 13 +- rodbus/examples/custom_server.rs | 37 ++++- rodbus/src/client/channel.rs | 15 +- rodbus/src/client/message.rs | 50 +++--- rodbus/src/client/requests/mod.rs | 2 +- rodbus/src/client/requests/send_custom_fc.rs | 15 +- rodbus/src/common/function.rs | 24 ++- rodbus/src/common/parse.rs | 46 ++++-- rodbus/src/common/serialize.rs | 17 ++- rodbus/src/server/handler.rs | 152 ++++++++++++++++--- rodbus/src/server/request.rs | 93 +++++++----- rodbus/src/server/task.rs | 4 +- rodbus/src/types.rs | 4 +- rodbus/tests/integration_test.rs | 115 +++++++++++--- 15 files changed, 441 insertions(+), 148 deletions(-) diff --git a/rodbus/examples/client.rs b/rodbus/examples/client.rs index b8924a4d..baca1464 100644 --- a/rodbus/examples/client.rs +++ b/rodbus/examples/client.rs @@ -277,7 +277,7 @@ async fn run_channel(mut channel: Channel) -> Result<(), Box Result<(), Box { let fc = u8::from_str_radix(fc_str.trim_start_matches("0x"), 16).unwrap(); - let byte_count_in = u8::from_str_radix(bytes_in_str.trim_start_matches("0x"), 16).unwrap(); - let byte_count_out = u8::from_str_radix(bytes_out_str.trim_start_matches("0x"), 16).unwrap(); - let values: Vec = values.iter().filter_map(|&v| u16::from_str_radix(v.trim_start_matches("0x"), 16).ok()).collect(); + let byte_count_in = + u8::from_str_radix(bytes_in_str.trim_start_matches("0x"), 16).unwrap(); + let byte_count_out = + u8::from_str_radix(bytes_out_str.trim_start_matches("0x"), 16).unwrap(); + let values: Vec = values + .iter() + .filter_map(|&v| u16::from_str_radix(v.trim_start_matches("0x"), 16).ok()) + .collect(); if (fc >= 65 && fc <= 72) || (fc >= 100 && fc <= 110) { let result = channel .send_custom_function_code( params, - CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values) + CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values), ) .await; print_write_result(result); diff --git a/rodbus/examples/custom_server.rs b/rodbus/examples/custom_server.rs index 945fb5f9..74d8ed1b 100644 --- a/rodbus/examples/custom_server.rs +++ b/rodbus/examples/custom_server.rs @@ -78,8 +78,14 @@ impl RequestHandler for SimpleHandler { } } - fn process_cfc(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { - tracing::info!("processing custom function code: {}", values.function_code()); + fn process_cfc( + &mut self, + values: CustomFunctionCode, + ) -> Result, ExceptionCode> { + tracing::info!( + "processing custom function code: {}", + values.function_code() + ); match values.function_code() { 0x41 => { // increment each CFC value by 1 and return the result @@ -87,8 +93,13 @@ impl RequestHandler for SimpleHandler { let incremented_data = values.iter().map(|&val| val + 1).collect(); // Return a new CustomFunctionCode with the incremented data - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) - }, + Ok(CustomFunctionCode::new( + values.function_code(), + values.byte_count_in(), + values.byte_count_out(), + incremented_data, + )) + } 0x42 => { // add a new value to the buffer and return the result // Create a new vector to hold the incremented values @@ -99,8 +110,13 @@ impl RequestHandler for SimpleHandler { }; // Return a new CustomFunctionCode with the incremented data - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), extended_data)) - }, + Ok(CustomFunctionCode::new( + values.function_code(), + values.byte_count_in(), + values.byte_count_out(), + extended_data, + )) + } 0x43 => { // remove the first value from the buffer and return the result // Create a new vector to hold the incremented values @@ -111,8 +127,13 @@ impl RequestHandler for SimpleHandler { }; // Return a new CustomFunctionCode with the incremented data - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), truncated_data)) - }, + Ok(CustomFunctionCode::new( + values.function_code(), + values.byte_count_in(), + values.byte_count_out(), + truncated_data, + )) + } _ => Err(ExceptionCode::IllegalFunction), } } diff --git a/rodbus/src/client/channel.rs b/rodbus/src/client/channel.rs index c47f5bf8..1e1cbed5 100644 --- a/rodbus/src/client/channel.rs +++ b/rodbus/src/client/channel.rs @@ -3,14 +3,15 @@ use std::time::Duration; use crate::client::message::{Command, Promise, Request, RequestDetails, Setting}; use crate::client::requests::read_bits::ReadBits; use crate::client::requests::read_registers::ReadRegisters; +use crate::client::requests::send_custom_fc::CustomFCRequest; use crate::client::requests::write_multiple::{MultipleWriteRequest, WriteMultiple}; use crate::client::requests::write_single::SingleWrite; -use crate::client::requests::send_custom_fc::CustomFCRequest; use crate::error::*; -use crate::types::{AddressRange, BitIterator, Indexed, RegisterIterator, UnitId, CustomFunctionCode}; +use crate::types::{ + AddressRange, BitIterator, CustomFunctionCode, Indexed, RegisterIterator, UnitId, +}; use crate::DecodeLevel; - /// Async channel used to make requests #[derive(Debug, Clone)] pub struct Channel { @@ -171,10 +172,14 @@ impl Channel { param: RequestParam, request: CustomFunctionCode, ) -> Result, RequestError> { - let (tx, rx) = tokio::sync::oneshot::channel::, RequestError>>(); + let (tx, rx) = + tokio::sync::oneshot::channel::, RequestError>>(); let request = wrap( param, - RequestDetails::SendCustomFunctionCode(CustomFCRequest::new(request, Promise::channel(tx))), + RequestDetails::SendCustomFunctionCode(CustomFCRequest::new( + request, + Promise::channel(tx), + )), ); self.tx.send(request).await?; rx.await? diff --git a/rodbus/src/client/message.rs b/rodbus/src/client/message.rs index 2af5e6d9..6d118ecb 100644 --- a/rodbus/src/client/message.rs +++ b/rodbus/src/client/message.rs @@ -8,11 +8,11 @@ use crate::DecodeLevel; use crate::client::requests::read_bits::ReadBits; use crate::client::requests::read_registers::ReadRegisters; +use crate::client::requests::send_custom_fc::CustomFCRequest; use crate::client::requests::write_multiple::MultipleWriteRequest; use crate::client::requests::write_single::SingleWrite; -use crate::client::requests::send_custom_fc::CustomFCRequest; use crate::common::traits::Serialize; -use crate::types::{Indexed, UnitId, CustomFunctionCode}; +use crate::types::{CustomFunctionCode, Indexed, UnitId}; use scursor::{ReadCursor, WriteCursor}; use std::time::Duration; @@ -131,29 +131,27 @@ impl RequestDetails { RequestDetails::WriteSingleRegister(_) => Ok(FunctionCode::WriteSingleRegister), RequestDetails::WriteMultipleCoils(_) => Ok(FunctionCode::WriteMultipleCoils), RequestDetails::WriteMultipleRegisters(_) => Ok(FunctionCode::WriteMultipleRegisters), - RequestDetails::SendCustomFunctionCode(x) => { - match x.request.function_code() { - 0x41 => Ok(FunctionCode::SendCFC65), - 0x42 => Ok(FunctionCode::SendCFC66), - 0x43 => Ok(FunctionCode::SendCFC67), - 0x44 => Ok(FunctionCode::SendCFC68), - 0x45 => Ok(FunctionCode::SendCFC69), - 0x46 => Ok(FunctionCode::SendCFC70), - 0x47 => Ok(FunctionCode::SendCFC71), - 0x48 => Ok(FunctionCode::SendCFC72), - 0x64 => Ok(FunctionCode::SendCFC100), - 0x65 => Ok(FunctionCode::SendCFC101), - 0x66 => Ok(FunctionCode::SendCFC102), - 0x67 => Ok(FunctionCode::SendCFC103), - 0x68 => Ok(FunctionCode::SendCFC104), - 0x69 => Ok(FunctionCode::SendCFC105), - 0x6A => Ok(FunctionCode::SendCFC106), - 0x6B => Ok(FunctionCode::SendCFC107), - 0x6C => Ok(FunctionCode::SendCFC108), - 0x6D => Ok(FunctionCode::SendCFC109), - 0x6E => Ok(FunctionCode::SendCFC110), - _ => Err(ExceptionCode::IllegalFunction), - } + RequestDetails::SendCustomFunctionCode(x) => match x.request.function_code() { + 0x41 => Ok(FunctionCode::SendCFC65), + 0x42 => Ok(FunctionCode::SendCFC66), + 0x43 => Ok(FunctionCode::SendCFC67), + 0x44 => Ok(FunctionCode::SendCFC68), + 0x45 => Ok(FunctionCode::SendCFC69), + 0x46 => Ok(FunctionCode::SendCFC70), + 0x47 => Ok(FunctionCode::SendCFC71), + 0x48 => Ok(FunctionCode::SendCFC72), + 0x64 => Ok(FunctionCode::SendCFC100), + 0x65 => Ok(FunctionCode::SendCFC101), + 0x66 => Ok(FunctionCode::SendCFC102), + 0x67 => Ok(FunctionCode::SendCFC103), + 0x68 => Ok(FunctionCode::SendCFC104), + 0x69 => Ok(FunctionCode::SendCFC105), + 0x6A => Ok(FunctionCode::SendCFC106), + 0x6B => Ok(FunctionCode::SendCFC107), + 0x6C => Ok(FunctionCode::SendCFC108), + 0x6D => Ok(FunctionCode::SendCFC109), + 0x6E => Ok(FunctionCode::SendCFC110), + _ => Err(ExceptionCode::IllegalFunction), }, } } @@ -188,7 +186,7 @@ impl RequestDetails { RequestDetails::WriteMultipleCoils(x) => x.handle_response(cursor, function, decode), RequestDetails::WriteMultipleRegisters(x) => { x.handle_response(cursor, function, decode) - }, + } RequestDetails::SendCustomFunctionCode(x) => { x.handle_response(cursor, function, decode) } diff --git a/rodbus/src/client/requests/mod.rs b/rodbus/src/client/requests/mod.rs index 2e5e306a..83353546 100644 --- a/rodbus/src/client/requests/mod.rs +++ b/rodbus/src/client/requests/mod.rs @@ -1,5 +1,5 @@ pub(crate) mod read_bits; pub(crate) mod read_registers; +pub(crate) mod send_custom_fc; pub(crate) mod write_multiple; pub(crate) mod write_single; -pub(crate) mod send_custom_fc; \ No newline at end of file diff --git a/rodbus/src/client/requests/send_custom_fc.rs b/rodbus/src/client/requests/send_custom_fc.rs index dbc80772..f4f1820f 100644 --- a/rodbus/src/client/requests/send_custom_fc.rs +++ b/rodbus/src/client/requests/send_custom_fc.rs @@ -1,11 +1,11 @@ use std::fmt::Display; -use crate::CustomFunctionCode; use crate::client::message::Promise; use crate::common::function::FunctionCode; use crate::decode::AppDecodeLevel; use crate::error::AduParseError; use crate::error::RequestError; +use crate::CustomFunctionCode; use scursor::{ReadCursor, WriteCursor}; @@ -45,7 +45,7 @@ where decode: AppDecodeLevel, ) -> Result<(), RequestError> { let response = self.parse_all(cursor)?; - + if decode.data_headers() { tracing::info!("PDU RX - {} {}", function, response); } else if decode.header() { @@ -83,7 +83,9 @@ impl CustomFCOperation for CustomFunctionCode { let len = byte_count_out as usize; if len != cursor.remaining() / 2 { - return Err(AduParseError::InsufficientBytesForByteCount(len, cursor.remaining() / 2).into()); + return Err( + AduParseError::InsufficientBytesForByteCount(len, cursor.remaining() / 2).into(), + ); } let mut values = Vec::with_capacity(len); @@ -92,6 +94,11 @@ impl CustomFCOperation for CustomFunctionCode { } cursor.expect_empty()?; - Ok(CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values)) + Ok(CustomFunctionCode::new( + fc, + byte_count_in, + byte_count_out, + values, + )) } } diff --git a/rodbus/src/common/function.rs b/rodbus/src/common/function.rs index 56be19fd..2960afc4 100644 --- a/rodbus/src/common/function.rs +++ b/rodbus/src/common/function.rs @@ -87,11 +87,25 @@ impl Display for FunctionCode { FunctionCode::WriteMultipleRegisters => { write!(f, "WRITE MULTIPLE REGISTERS ({:#04X})", self.get_value()) } - FunctionCode::SendCFC65 | FunctionCode::SendCFC66 | FunctionCode::SendCFC67 | FunctionCode::SendCFC68 | - FunctionCode::SendCFC69 | FunctionCode::SendCFC70 | FunctionCode::SendCFC71 | FunctionCode::SendCFC72 | - FunctionCode::SendCFC100 | FunctionCode::SendCFC101 | FunctionCode::SendCFC102 | FunctionCode::SendCFC103 | - FunctionCode::SendCFC104 | FunctionCode::SendCFC105 | FunctionCode::SendCFC106 | FunctionCode::SendCFC107 | - FunctionCode::SendCFC108 | FunctionCode::SendCFC109 | FunctionCode::SendCFC110 => { + FunctionCode::SendCFC65 + | FunctionCode::SendCFC66 + | FunctionCode::SendCFC67 + | FunctionCode::SendCFC68 + | FunctionCode::SendCFC69 + | FunctionCode::SendCFC70 + | FunctionCode::SendCFC71 + | FunctionCode::SendCFC72 + | FunctionCode::SendCFC100 + | FunctionCode::SendCFC101 + | FunctionCode::SendCFC102 + | FunctionCode::SendCFC103 + | FunctionCode::SendCFC104 + | FunctionCode::SendCFC105 + | FunctionCode::SendCFC106 + | FunctionCode::SendCFC107 + | FunctionCode::SendCFC108 + | FunctionCode::SendCFC109 + | FunctionCode::SendCFC110 => { write!(f, "SEND CUSTOM FUNCTION CODE ({:#04X})", self.get_value()) } } diff --git a/rodbus/src/common/parse.rs b/rodbus/src/common/parse.rs index 238ea9e8..7d2e87e4 100644 --- a/rodbus/src/common/parse.rs +++ b/rodbus/src/common/parse.rs @@ -1,6 +1,6 @@ use crate::common::traits::Parse; use crate::error::*; -use crate::types::{coil_from_u16, AddressRange, Indexed, CustomFunctionCode}; +use crate::types::{coil_from_u16, AddressRange, CustomFunctionCode, Indexed}; use scursor::ReadCursor; @@ -36,7 +36,9 @@ impl Parse for CustomFunctionCode { let len = byte_count_in as usize; if len != cursor.remaining() / 2 { - return Err(AduParseError::InsufficientBytesForByteCount(len, cursor.remaining() / 2).into()); + return Err( + AduParseError::InsufficientBytesForByteCount(len, cursor.remaining() / 2).into(), + ); } let mut values = Vec::with_capacity(len); @@ -45,7 +47,12 @@ impl Parse for CustomFunctionCode { } cursor.expect_empty()?; - Ok(CustomFunctionCode::new(fc, byte_count_in, byte_count_out, values)) + Ok(CustomFunctionCode::new( + fc, + byte_count_in, + byte_count_out, + values, + )) } } @@ -86,7 +93,6 @@ mod coils { } } - #[cfg(test)] mod custom_fc { use crate::common::traits::Parse; @@ -113,27 +119,49 @@ mod custom_fc { fn parse_succeeds_for_multiple_min_values() { let mut cursor = ReadCursor::new(&[0x45, 0x03, 0x3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(69, 3, 3, vec![0x0000, 0x0000, 0x0000]))); + assert_eq!( + result, + Ok(CustomFunctionCode::new( + 69, + 3, + 3, + vec![0x0000, 0x0000, 0x0000] + )) + ); } #[test] fn parse_succeeds_for_multiple_max_values() { let mut cursor = ReadCursor::new(&[0x45, 0x03, 0x3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Ok(CustomFunctionCode::new(69, 3, 3, vec![0xFFFF, 0xFFFF, 0xFFFF]))); + assert_eq!( + result, + Ok(CustomFunctionCode::new( + 69, + 3, + 3, + vec![0xFFFF, 0xFFFF, 0xFFFF] + )) + ); } #[test] fn parse_fails_for_missing_byte_count() { let mut cursor = ReadCursor::new(&[0x45, 0x01, 0xFF, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Err(AduParseError::InsufficientBytesForByteCount(1, 0).into())); + assert_eq!( + result, + Err(AduParseError::InsufficientBytesForByteCount(1, 0).into()) + ); } #[test] fn parse_fails_for_missing_data_byte() { let mut cursor = ReadCursor::new(&[0x00, 0x01, 0xFF]); let result = CustomFunctionCode::parse(&mut cursor); - assert_eq!(result, Err(AduParseError::InsufficientBytesForByteCount(1, 0).into())); + assert_eq!( + result, + Err(AduParseError::InsufficientBytesForByteCount(1, 0).into()) + ); } -} \ No newline at end of file +} diff --git a/rodbus/src/common/serialize.rs b/rodbus/src/common/serialize.rs index d875a62f..dc56be85 100644 --- a/rodbus/src/common/serialize.rs +++ b/rodbus/src/common/serialize.rs @@ -7,8 +7,8 @@ use crate::common::traits::Serialize; use crate::error::{InternalError, RequestError}; use crate::server::response::{BitWriter, RegisterWriter}; use crate::types::{ - coil_from_u16, coil_to_u16, AddressRange, BitIterator, BitIteratorDisplay, Indexed, - RegisterIterator, RegisterIteratorDisplay, CustomFunctionCode, + coil_from_u16, coil_to_u16, AddressRange, BitIterator, BitIteratorDisplay, CustomFunctionCode, + Indexed, RegisterIterator, RegisterIteratorDisplay, }; use scursor::{ReadCursor, WriteCursor}; @@ -313,7 +313,7 @@ impl Loggable for CustomFunctionCode { ) -> std::fmt::Result { if level.data_headers() { let mut cursor = ReadCursor::new(payload); - + let fc = match cursor.read_u8() { Ok(value) => value, Err(_) => return Ok(()), @@ -344,7 +344,6 @@ impl Loggable for CustomFunctionCode { let custom_fc = CustomFunctionCode::new(fc, byte_count_in, byte_count_out, data); write!(f, "{:?}", custom_fc)?; - } Ok(()) @@ -388,7 +387,10 @@ mod tests { let mut buffer = [0u8; 9]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); - assert_eq!(buffer, [0x45, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + assert_eq!( + buffer, + [0x45, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + ); } #[test] @@ -397,6 +399,9 @@ mod tests { let mut buffer = [0u8; 9]; let mut cursor = WriteCursor::new(&mut buffer); custom_fc.serialize(&mut cursor).unwrap(); - assert_eq!(buffer, [0x45, 0x03, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); + assert_eq!( + buffer, + [0x45, 0x03, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] + ); } } diff --git a/rodbus/src/server/handler.rs b/rodbus/src/server/handler.rs index 600af48f..05099d1d 100644 --- a/rodbus/src/server/handler.rs +++ b/rodbus/src/server/handler.rs @@ -64,7 +64,10 @@ pub trait RequestHandler: Send + 'static { } /// Write the CFC custom function code - fn process_cfc(&mut self, _values: CustomFunctionCode) -> Result, ExceptionCode> { + fn process_cfc( + &mut self, + _values: CustomFunctionCode, + ) -> Result, ExceptionCode> { Err(ExceptionCode::IllegalFunction) } } @@ -233,7 +236,12 @@ pub trait AuthorizationHandler: Send + Sync + 'static { } /// Authorize a Send CFC request - fn process_cfc(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc( + &self, + _unit_id: UnitId, + _value: CustomFunctionCode, + _role: &str, + ) -> Authorization { Authorization::Deny } } @@ -316,7 +324,12 @@ impl AuthorizationHandler for ReadOnlyAuthorizationHandler { } /// Authorize a Send CFC request - fn process_cfc(&self, _unit_id: UnitId, _value: CustomFunctionCode, _role: &str) -> Authorization { + fn process_cfc( + &self, + _unit_id: UnitId, + _value: CustomFunctionCode, + _role: &str, + ) -> Authorization { Authorization::Deny } } @@ -353,79 +366,174 @@ mod tests { Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x41, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x41, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x42, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x42, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x43, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x43, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x44, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x44, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x45, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x45, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x46, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x46, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x47, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x47, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x48, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x48, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x64, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x64, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x65, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x65, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x66, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x66, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x67, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x67, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x68, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x68, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x69, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x69, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x6A, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x6A, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x6B, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x6B, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x6C, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x6C, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x6D, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x6D, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); assert_eq!( - handler.process_cfc(CustomFunctionCode::new(0x6E, 4, 4, vec![0x01, 0x02, 0x03, 0x04])), + handler.process_cfc(CustomFunctionCode::new( + 0x6E, + 4, + 4, + vec![0x01, 0x02, 0x03, 0x04] + )), Err(ExceptionCode::IllegalFunction) ); } diff --git a/rodbus/src/server/request.rs b/rodbus/src/server/request.rs index 57c7077e..ea3d580c 100644 --- a/rodbus/src/server/request.rs +++ b/rodbus/src/server/request.rs @@ -65,29 +65,27 @@ impl<'a> Request<'a> { Request::WriteSingleRegister(_) => Ok(FunctionCode::WriteSingleRegister), Request::WriteMultipleCoils(_) => Ok(FunctionCode::WriteMultipleCoils), Request::WriteMultipleRegisters(_) => Ok(FunctionCode::WriteMultipleRegisters), - Request::SendCustomFunctionCode(x) => { - match x.function_code() { - 0x41 => Ok(FunctionCode::SendCFC65), - 0x42 => Ok(FunctionCode::SendCFC66), - 0x43 => Ok(FunctionCode::SendCFC67), - 0x44 => Ok(FunctionCode::SendCFC68), - 0x45 => Ok(FunctionCode::SendCFC69), - 0x46 => Ok(FunctionCode::SendCFC70), - 0x47 => Ok(FunctionCode::SendCFC71), - 0x48 => Ok(FunctionCode::SendCFC72), - 0x64 => Ok(FunctionCode::SendCFC100), - 0x65 => Ok(FunctionCode::SendCFC101), - 0x66 => Ok(FunctionCode::SendCFC102), - 0x67 => Ok(FunctionCode::SendCFC103), - 0x68 => Ok(FunctionCode::SendCFC104), - 0x69 => Ok(FunctionCode::SendCFC105), - 0x6A => Ok(FunctionCode::SendCFC106), - 0x6B => Ok(FunctionCode::SendCFC107), - 0x6C => Ok(FunctionCode::SendCFC108), - 0x6D => Ok(FunctionCode::SendCFC109), - 0x6E => Ok(FunctionCode::SendCFC110), - _ => Err(ExceptionCode::IllegalFunction), - } + Request::SendCustomFunctionCode(x) => match x.function_code() { + 0x41 => Ok(FunctionCode::SendCFC65), + 0x42 => Ok(FunctionCode::SendCFC66), + 0x43 => Ok(FunctionCode::SendCFC67), + 0x44 => Ok(FunctionCode::SendCFC68), + 0x45 => Ok(FunctionCode::SendCFC69), + 0x46 => Ok(FunctionCode::SendCFC70), + 0x47 => Ok(FunctionCode::SendCFC71), + 0x48 => Ok(FunctionCode::SendCFC72), + 0x64 => Ok(FunctionCode::SendCFC100), + 0x65 => Ok(FunctionCode::SendCFC101), + 0x66 => Ok(FunctionCode::SendCFC102), + 0x67 => Ok(FunctionCode::SendCFC103), + 0x68 => Ok(FunctionCode::SendCFC104), + 0x69 => Ok(FunctionCode::SendCFC105), + 0x6A => Ok(FunctionCode::SendCFC106), + 0x6B => Ok(FunctionCode::SendCFC107), + 0x6C => Ok(FunctionCode::SendCFC108), + 0x6D => Ok(FunctionCode::SendCFC109), + 0x6E => Ok(FunctionCode::SendCFC110), + _ => Err(ExceptionCode::IllegalFunction), }, } } @@ -169,12 +167,25 @@ impl<'a> Request<'a> { } Request::SendCustomFunctionCode(request) => { let result = match function.unwrap() { - FunctionCode::SendCFC65 | FunctionCode::SendCFC66 | FunctionCode::SendCFC67 | FunctionCode::SendCFC68 | FunctionCode::SendCFC69 | - FunctionCode::SendCFC70 | FunctionCode::SendCFC71 | FunctionCode::SendCFC72 | FunctionCode::SendCFC100 | FunctionCode::SendCFC101 | - FunctionCode::SendCFC102 | FunctionCode::SendCFC103 | FunctionCode::SendCFC104 | FunctionCode::SendCFC105 | FunctionCode::SendCFC106 | - FunctionCode::SendCFC107 | FunctionCode::SendCFC108 | FunctionCode::SendCFC109 | FunctionCode::SendCFC110 => { - handler.process_cfc(request.clone()) - }, + FunctionCode::SendCFC65 + | FunctionCode::SendCFC66 + | FunctionCode::SendCFC67 + | FunctionCode::SendCFC68 + | FunctionCode::SendCFC69 + | FunctionCode::SendCFC70 + | FunctionCode::SendCFC71 + | FunctionCode::SendCFC72 + | FunctionCode::SendCFC100 + | FunctionCode::SendCFC101 + | FunctionCode::SendCFC102 + | FunctionCode::SendCFC103 + | FunctionCode::SendCFC104 + | FunctionCode::SendCFC105 + | FunctionCode::SendCFC106 + | FunctionCode::SendCFC107 + | FunctionCode::SendCFC108 + | FunctionCode::SendCFC109 + | FunctionCode::SendCFC110 => handler.process_cfc(request.clone()), _ => Err(ExceptionCode::IllegalFunction), }; write_result(function.unwrap(), header, writer, result, level) @@ -238,11 +249,25 @@ impl<'a> Request<'a> { RegisterIterator::parse_all(range, cursor)?, ))) } - FunctionCode::SendCFC65 | FunctionCode::SendCFC66 | FunctionCode::SendCFC67 | FunctionCode::SendCFC68 | - FunctionCode::SendCFC69 | FunctionCode::SendCFC70 | FunctionCode::SendCFC71 | FunctionCode::SendCFC72 | - FunctionCode::SendCFC100 | FunctionCode::SendCFC101 | FunctionCode::SendCFC102 | FunctionCode::SendCFC103 | - FunctionCode::SendCFC104 | FunctionCode::SendCFC105 | FunctionCode::SendCFC106 | FunctionCode::SendCFC107 | - FunctionCode::SendCFC108 | FunctionCode::SendCFC109 | FunctionCode::SendCFC110 => { + FunctionCode::SendCFC65 + | FunctionCode::SendCFC66 + | FunctionCode::SendCFC67 + | FunctionCode::SendCFC68 + | FunctionCode::SendCFC69 + | FunctionCode::SendCFC70 + | FunctionCode::SendCFC71 + | FunctionCode::SendCFC72 + | FunctionCode::SendCFC100 + | FunctionCode::SendCFC101 + | FunctionCode::SendCFC102 + | FunctionCode::SendCFC103 + | FunctionCode::SendCFC104 + | FunctionCode::SendCFC105 + | FunctionCode::SendCFC106 + | FunctionCode::SendCFC107 + | FunctionCode::SendCFC108 + | FunctionCode::SendCFC109 + | FunctionCode::SendCFC110 => { let x = Request::SendCustomFunctionCode(CustomFunctionCode::parse(cursor)?); cursor.expect_empty()?; Ok(x) diff --git a/rodbus/src/server/task.rs b/rodbus/src/server/task.rs index a48d1f4a..2c0c3288 100644 --- a/rodbus/src/server/task.rs +++ b/rodbus/src/server/task.rs @@ -265,9 +265,7 @@ impl AuthorizationType { handler.write_multiple_registers(unit_id, x.range, role) } Request::SendCustomFunctionCode(x) => match x.function_code() { - 0x41..=0x48 | 0x64..=0x6E => { - handler.process_cfc(unit_id, x.clone(), role) - } + 0x41..=0x48 | 0x64..=0x6E => handler.process_cfc(unit_id, x.clone(), role), _ => Authorization::Deny, }, } diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index 2dc0bb95..2de50a0f 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -408,7 +408,9 @@ impl CustomFunctionCode { } /// Check if the underlying vector is empty - pub fn is_empty(&self) -> bool { self.data.is_empty() } + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } /// Iterate over the underlying vector pub fn iter(&self) -> std::slice::Iter { diff --git a/rodbus/tests/integration_test.rs b/rodbus/tests/integration_test.rs index 9ab8afd3..011f4b19 100644 --- a/rodbus/tests/integration_test.rs +++ b/rodbus/tests/integration_test.rs @@ -95,8 +95,15 @@ impl RequestHandler for Handler { Ok(()) } - fn process_cfc(&mut self, values: CustomFunctionCode) -> Result, ExceptionCode> { - tracing::info!("processing custom function code: {}, data: {:?}", values.function_code(), values.iter()); + fn process_cfc( + &mut self, + values: CustomFunctionCode, + ) -> Result, ExceptionCode> { + tracing::info!( + "processing custom function code: {}, data: {:?}", + values.function_code(), + values.iter() + ); match values.function_code() { 0x41 => { // increment each CFC value by 1 and return the result @@ -104,8 +111,13 @@ impl RequestHandler for Handler { let incremented_data = values.iter().map(|&val| val + 1).collect(); // Return a new CustomFunctionCode with the incremented data - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), incremented_data)) - }, + Ok(CustomFunctionCode::new( + values.function_code(), + values.byte_count_in(), + values.byte_count_out(), + incremented_data, + )) + } 0x42 => { // add a new value to the buffer and return the result // Create a new vector to hold the incremented values @@ -116,8 +128,13 @@ impl RequestHandler for Handler { }; // Return a new CustomFunctionCode with the incremented data - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), extended_data)) - }, + Ok(CustomFunctionCode::new( + values.function_code(), + values.byte_count_in(), + values.byte_count_out(), + extended_data, + )) + } 0x43 => { // remove the first value from the buffer and return the result // Create a new vector to hold the incremented values @@ -128,8 +145,13 @@ impl RequestHandler for Handler { }; // Return a new CustomFunctionCode with the incremented data - Ok(CustomFunctionCode::new(values.function_code(), values.byte_count_in(), values.byte_count_out(), truncated_data)) - }, + Ok(CustomFunctionCode::new( + values.function_code(), + values.byte_count_in(), + values.byte_count_out(), + truncated_data, + )) + } _ => Err(ExceptionCode::IllegalFunction), } } @@ -264,48 +286,103 @@ async fn test_requests_and_responses() { // Test the invalid CFC handlers below 65 for i in 0..65 { assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + channel + .send_custom_function_code( + params, + CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + ) + .await, Err(rodbus::ExceptionCode::IllegalFunction.into()) ); } // Test the implemented valid test handlers 65, 66, 67 assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x41, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Ok(CustomFunctionCode::new(0x41, 4, 4, vec![0xC0DF, 0xCAFF, 0xC0DF, 0xCAFF])) + channel + .send_custom_function_code( + params, + CustomFunctionCode::new(0x41, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + ) + .await, + Ok(CustomFunctionCode::new( + 0x41, + 4, + 4, + vec![0xC0DF, 0xCAFF, 0xC0DF, 0xCAFF] + )) ); assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x42, 4, 5, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Ok(CustomFunctionCode::new(0x42, 4, 5, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE])) + channel + .send_custom_function_code( + params, + CustomFunctionCode::new(0x42, 4, 5, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + ) + .await, + Ok(CustomFunctionCode::new( + 0x42, + 4, + 5, + vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE, 0xC0DE] + )) ); assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(0x43, 4, 3, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, - Ok(CustomFunctionCode::new(0x43, 4, 3, vec![0xC0DE, 0xCAFE, 0xC0DE])) + channel + .send_custom_function_code( + params, + CustomFunctionCode::new(0x43, 4, 3, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + ) + .await, + Ok(CustomFunctionCode::new( + 0x43, + 4, + 3, + vec![0xC0DE, 0xCAFE, 0xC0DE] + )) ); // Test the unimplemented valid handlers from 68 to 72 for i in 68..73 { assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + channel + .send_custom_function_code( + params, + CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + ) + .await, Err(rodbus::ExceptionCode::IllegalFunction.into()) ); } // Test the invalid handlers from 73 to 99 for i in 73..100 { assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + channel + .send_custom_function_code( + params, + CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + ) + .await, Err(rodbus::ExceptionCode::IllegalFunction.into()) ); } // Test the unimplemented valid handlers from 100 to 110 for i in 100..110 { assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + channel + .send_custom_function_code( + params, + CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + ) + .await, Err(rodbus::ExceptionCode::IllegalFunction.into()) ); } // Test the invalid CFC handlers from 111 to 255 for i in 111..=255 { assert_eq!( - channel.send_custom_function_code(params, CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE])).await, + channel + .send_custom_function_code( + params, + CustomFunctionCode::new(i, 4, 4, vec![0xC0DE, 0xCAFE, 0xC0DE, 0xCAFE]) + ) + .await, Err(rodbus::ExceptionCode::IllegalFunction.into()) ); }