Skip to content

Commit

Permalink
reverted FunctionCode newtype
Browse files Browse the repository at this point in the history
  • Loading branch information
emcell committed Oct 18, 2023
1 parent df5a7f8 commit 0aa5d11
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 288 deletions.
7 changes: 1 addition & 6 deletions examples/tcp-client-custom-fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
use std::borrow::Cow;

use tokio_modbus::FunctionCode;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
use tokio_modbus::prelude::*;
Expand All @@ -17,10 +15,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

println!("Fetching the coupler ID");
let rsp = ctx
.call(Request::Custom(
FunctionCode::Custom(0x66),
Cow::Borrowed(&[0x11, 0x42]),
))
.call(Request::Custom(0x66, Cow::Borrowed(&[0x11, 0x42])))
.await?;

match rsp {
Expand Down
3 changes: 1 addition & 2 deletions examples/tcp-rtu-server-async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ use tokio::{net::TcpListener, sync::Mutex};
use tokio_modbus::{
prelude::*,
server::tcp::{accept_tcp_connection, Server},
Address, Exception, ExceptionResponse, ExtractExceptionResponse, GetFunctionCode, Quantity,
ResponsePdu,
Address, Exception, ExceptionResponse, ExtractExceptionResponse, Quantity, ResponsePdu,
};
use tokio_serial::SerialStream;

Expand Down
156 changes: 104 additions & 52 deletions src/codec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl<'a> TryFrom<Request<'a>> for Bytes {
use crate::frame::Request::*;
let cnt = request_byte_count(&req);
let mut data = BytesMut::with_capacity(cnt);
data.put_u8(req.function_code().into());
data.put_u8(req_to_fn_code(&req));
match req {
ReadCoils(address, quantity)
| ReadDiscreteInputs(address, quantity)
Expand Down Expand Up @@ -121,7 +121,7 @@ impl From<Response> for Bytes {
use crate::frame::Response::*;
let cnt = response_byte_count(&rsp);
let mut data = BytesMut::with_capacity(cnt);
data.put_u8(rsp.function_code().into());
data.put_u8(rsp_to_fn_code(&rsp));
match rsp {
ReadCoils(coils) | ReadDiscreteInputs(coils) => {
let packed_coils = pack_coils(&coils);
Expand Down Expand Up @@ -168,9 +168,8 @@ impl From<Response> for Bytes {
impl From<ExceptionResponse> for Bytes {
fn from(ex: ExceptionResponse) -> Bytes {
let mut data = BytesMut::with_capacity(2);
let function: u8 = ex.function.into();
debug_assert!(function < 0x80);
data.put_u8(function + 0x80);
debug_assert!(ex.function < 0x80);
data.put_u8(ex.function + 0x80);
data.put_u8(ex.exception.into());
data.freeze()
}
Expand All @@ -188,19 +187,15 @@ impl TryFrom<Bytes> for Request<'static> {
fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
use crate::frame::Request::*;
let mut rdr = Cursor::new(&bytes);
let fn_code: FunctionCode = rdr.read_u8()?.into();
let fn_code = rdr.read_u8()?;
let req = match fn_code {
FunctionCode::ReadCoils => {
ReadCoils(rdr.read_u16::<BigEndian>()?, rdr.read_u16::<BigEndian>()?)
}
FunctionCode::ReadDiscreteInputs => {
ReadDiscreteInputs(rdr.read_u16::<BigEndian>()?, rdr.read_u16::<BigEndian>()?)
}
FunctionCode::WriteSingleCoil => WriteSingleCoil(
0x01 => ReadCoils(rdr.read_u16::<BigEndian>()?, rdr.read_u16::<BigEndian>()?),
0x02 => ReadDiscreteInputs(rdr.read_u16::<BigEndian>()?, rdr.read_u16::<BigEndian>()?),
0x05 => WriteSingleCoil(
rdr.read_u16::<BigEndian>()?,
coil_to_bool(rdr.read_u16::<BigEndian>()?)?,
),
FunctionCode::WriteMultipleCoils => {
0x0F => {
let address = rdr.read_u16::<BigEndian>()?;
let quantity = rdr.read_u16::<BigEndian>()?;
let byte_count = rdr.read_u8()?;
Expand All @@ -210,17 +205,13 @@ impl TryFrom<Bytes> for Request<'static> {
let x = &bytes[6..];
WriteMultipleCoils(address, unpack_coils(x, quantity).into())
}
FunctionCode::ReadInputRegisters => {
ReadInputRegisters(rdr.read_u16::<BigEndian>()?, rdr.read_u16::<BigEndian>()?)
}
FunctionCode::ReadHoldingRegisters => {
0x04 => ReadInputRegisters(rdr.read_u16::<BigEndian>()?, rdr.read_u16::<BigEndian>()?),
0x03 => {
ReadHoldingRegisters(rdr.read_u16::<BigEndian>()?, rdr.read_u16::<BigEndian>()?)
}
FunctionCode::WriteSingleRegister => {
WriteSingleRegister(rdr.read_u16::<BigEndian>()?, rdr.read_u16::<BigEndian>()?)
}
0x06 => WriteSingleRegister(rdr.read_u16::<BigEndian>()?, rdr.read_u16::<BigEndian>()?),

FunctionCode::WriteMultipleRegisters => {
0x10 => {
let address = rdr.read_u16::<BigEndian>()?;
let quantity = rdr.read_u16::<BigEndian>()?;
let byte_count = rdr.read_u8()?;
Expand All @@ -233,13 +224,13 @@ impl TryFrom<Bytes> for Request<'static> {
}
WriteMultipleRegisters(address, data.into())
}
FunctionCode::MaskWriteRegister => {
0x16 => {
let address = rdr.read_u16::<BigEndian>()?;
let and_mask = rdr.read_u16::<BigEndian>()?;
let or_mask = rdr.read_u16::<BigEndian>()?;
MaskWriteRegister(address, and_mask, or_mask)
}
FunctionCode::ReadWriteMultipleRegisters => {
0x17 => {
let read_address = rdr.read_u16::<BigEndian>()?;
let read_quantity = rdr.read_u16::<BigEndian>()?;
let write_address = rdr.read_u16::<BigEndian>()?;
Expand All @@ -254,10 +245,8 @@ impl TryFrom<Bytes> for Request<'static> {
}
ReadWriteMultipleRegisters(read_address, read_quantity, write_address, data.into())
}
FunctionCode::Custom(fn_code) if fn_code < 0x80 => {
Custom(FunctionCode::Custom(fn_code), bytes[1..].to_vec().into())
}
FunctionCode::Custom(fn_code) => {
fn_code if fn_code < 0x80 => Custom(fn_code, bytes[1..].to_vec().into()),
fn_code => {
return Err(Error::new(
ErrorKind::InvalidData,
format!("Invalid function code: 0x{fn_code:0>2X}"),
Expand Down Expand Up @@ -346,7 +335,7 @@ impl TryFrom<Bytes> for Response {
}
_ => {
let mut bytes = bytes;
Custom(FunctionCode::Custom(fn_code), bytes.split_off(1))
Custom(fn_code, bytes.split_off(1))
}
};
Ok(rsp)
Expand All @@ -366,7 +355,6 @@ impl TryFrom<Bytes> for ExceptionResponse {
));
}
let function = fn_err_code - 0x80;
let function = FunctionCode::from(function);
let exception = Exception::try_from(rdr.read_u8()?)?;
Ok(ExceptionResponse {
function,
Expand Down Expand Up @@ -450,6 +438,41 @@ fn unpack_coils(bytes: &[u8], count: u16) -> Vec<Coil> {
res
}

pub(crate) const fn req_to_fn_code(req: &Request<'_>) -> u8 {
use crate::frame::Request::*;
match *req {
ReadCoils(_, _) => 0x01,
ReadDiscreteInputs(_, _) => 0x02,
WriteSingleCoil(_, _) => 0x05,
WriteMultipleCoils(_, _) => 0x0F,
ReadInputRegisters(_, _) => 0x04,
ReadHoldingRegisters(_, _) => 0x03,
WriteSingleRegister(_, _) => 0x06,
WriteMultipleRegisters(_, _) => 0x10,
MaskWriteRegister(_, _, _) => 0x16,
ReadWriteMultipleRegisters(_, _, _, _) => 0x17,
Custom(code, _) => code,
Disconnect => unreachable!(),
}
}

const fn rsp_to_fn_code(rsp: &Response) -> u8 {
use crate::frame::Response::*;
match *rsp {
ReadCoils(_) => 0x01,
ReadDiscreteInputs(_) => 0x02,
WriteSingleCoil(_, _) => 0x05,
WriteMultipleCoils(_, _) => 0x0F,
ReadInputRegisters(_) => 0x04,
ReadHoldingRegisters(_) => 0x03,
WriteSingleRegister(_, _) => 0x06,
WriteMultipleRegisters(_, _) => 0x10,
MaskWriteRegister(_, _, _) => 0x16,
ReadWriteMultipleRegisters(_) => 0x17,
Custom(code, _) => code,
}
}

fn request_byte_count(req: &Request<'_>) -> usize {
use crate::frame::Request::*;
match *req {
Expand Down Expand Up @@ -528,10 +551,51 @@ mod tests {
assert_eq!(unpack_coils(&[0xff, 0b11], 10), &[true; 10]);
}

#[test]
fn function_code_from_request() {
use crate::frame::Request::*;
assert_eq!(req_to_fn_code(&ReadCoils(0, 0)), 1);
assert_eq!(req_to_fn_code(&ReadDiscreteInputs(0, 0)), 2);
assert_eq!(req_to_fn_code(&WriteSingleCoil(0, true)), 5);
assert_eq!(
req_to_fn_code(&WriteMultipleCoils(0, Cow::Borrowed(&[]))),
0x0F
);
assert_eq!(req_to_fn_code(&ReadInputRegisters(0, 0)), 0x04);
assert_eq!(req_to_fn_code(&ReadHoldingRegisters(0, 0)), 0x03);
assert_eq!(req_to_fn_code(&WriteSingleRegister(0, 0)), 0x06);
assert_eq!(
req_to_fn_code(&WriteMultipleRegisters(0, Cow::Borrowed(&[]))),
0x10
);
assert_eq!(req_to_fn_code(&MaskWriteRegister(0, 0, 0)), 0x16);
assert_eq!(
req_to_fn_code(&ReadWriteMultipleRegisters(0, 0, 0, Cow::Borrowed(&[]))),
0x17
);
assert_eq!(req_to_fn_code(&Custom(88, Cow::Borrowed(&[]))), 88);
}

#[test]
fn function_code_from_response() {
use crate::frame::Response::*;
assert_eq!(rsp_to_fn_code(&ReadCoils(vec![])), 1);
assert_eq!(rsp_to_fn_code(&ReadDiscreteInputs(vec![])), 2);
assert_eq!(rsp_to_fn_code(&WriteSingleCoil(0x0, false)), 5);
assert_eq!(rsp_to_fn_code(&WriteMultipleCoils(0x0, 0x0)), 0x0F);
assert_eq!(rsp_to_fn_code(&ReadInputRegisters(vec![])), 0x04);
assert_eq!(rsp_to_fn_code(&ReadHoldingRegisters(vec![])), 0x03);
assert_eq!(rsp_to_fn_code(&WriteSingleRegister(0, 0)), 0x06);
assert_eq!(rsp_to_fn_code(&WriteMultipleRegisters(0, 0)), 0x10);
assert_eq!(rsp_to_fn_code(&MaskWriteRegister(0, 0, 0)), 0x16);
assert_eq!(rsp_to_fn_code(&ReadWriteMultipleRegisters(vec![])), 0x17);
assert_eq!(rsp_to_fn_code(&Custom(99, Bytes::from_static(&[]))), 99);
}

#[test]
fn exception_response_into_bytes() {
let bytes: Bytes = ExceptionResponse {
function: FunctionCode::ReadHoldingRegisters,
function: 0x03,
exception: Exception::IllegalDataAddress,
}
.into();
Expand All @@ -548,7 +612,7 @@ mod tests {
assert_eq!(
rsp,
ExceptionResponse {
function: FunctionCode::ReadHoldingRegisters,
function: 0x03,
exception: Exception::IllegalDataAddress,
}
);
Expand All @@ -559,7 +623,7 @@ mod tests {
let req_pdu: Bytes = Request::ReadCoils(0x01, 5).try_into().unwrap();
let rsp_pdu: Bytes = Response::ReadCoils(vec![]).into();
let ex_pdu: Bytes = ExceptionResponse {
function: FunctionCode::ReadHoldingRegisters,
function: 0x03,
exception: Exception::ServerDeviceFailure,
}
.into();
Expand Down Expand Up @@ -763,12 +827,9 @@ mod tests {

#[test]
fn custom() {
let bytes: Bytes = Request::Custom(
FunctionCode::Custom(0x55),
Cow::Borrowed(&[0xCC, 0x88, 0xAA, 0xFF]),
)
.try_into()
.unwrap();
let bytes: Bytes = Request::Custom(0x55, Cow::Borrowed(&[0xCC, 0x88, 0xAA, 0xFF]))
.try_into()
.unwrap();
assert_eq!(bytes[0], 0x55);
assert_eq!(bytes[1], 0xCC);
assert_eq!(bytes[2], 0x88);
Expand Down Expand Up @@ -899,10 +960,7 @@ mod tests {
let req = Request::try_from(bytes).unwrap();
assert_eq!(
req,
Request::Custom(
FunctionCode::Custom(0x55),
Cow::Borrowed(&[0xCC, 0x88, 0xAA, 0xFF])
)
Request::Custom(0x55, Cow::Borrowed(&[0xCC, 0x88, 0xAA, 0xFF]))
);
}
}
Expand Down Expand Up @@ -1014,11 +1072,8 @@ mod tests {

#[test]
fn custom() {
let bytes: Bytes = Response::Custom(
FunctionCode::Custom(0x55),
Bytes::from_static(&[0xCC, 0x88, 0xAA, 0xFF]),
)
.into();
let bytes: Bytes =
Response::Custom(0x55, Bytes::from_static(&[0xCC, 0x88, 0xAA, 0xFF])).into();
assert_eq!(bytes[0], 0x55);
assert_eq!(bytes[1], 0xCC);
assert_eq!(bytes[2], 0x88);
Expand Down Expand Up @@ -1142,10 +1197,7 @@ mod tests {
let rsp = Response::try_from(bytes).unwrap();
assert_eq!(
rsp,
Response::Custom(
FunctionCode::Custom(0x55),
Bytes::from_static(&[0xCC, 0x88, 0xAA, 0xFF])
)
Response::Custom(0x55, Bytes::from_static(&[0xCC, 0x88, 0xAA, 0xFF]))
);
}
}
Expand Down
5 changes: 1 addition & 4 deletions src/codec/rtu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,10 +710,7 @@ mod tests {

let ResponseAdu { pdu, .. } = codec.decode(&mut buf).unwrap().unwrap();
if let ResponsePdu(Err(err)) = pdu {
assert_eq!(
format!("{err}"),
"Modbus function FunctionCode::ReadDiscreteInputs: Illegal data value"
);
assert_eq!(format!("{err}"), "Modbus function 2: Illegal data value");
assert_eq!(buf.len(), 0);
} else {
panic!("unexpected response")
Expand Down
5 changes: 1 addition & 4 deletions src/codec/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,7 @@ mod tests {
assert_eq!(hdr.transaction_id, TRANSACTION_ID);
assert_eq!(hdr.unit_id, UNIT_ID);
if let ResponsePdu(Err(err)) = pdu {
assert_eq!(
format!("{err}"),
"Modbus function FunctionCode::ReadDiscreteInputs: Illegal data value"
);
assert_eq!(format!("{err}"), "Modbus function 2: Illegal data value");
assert_eq!(buf.len(), 1);
} else {
panic!("unexpected response")
Expand Down
Loading

0 comments on commit 0aa5d11

Please sign in to comment.