Skip to content

Commit

Permalink
Fix panic on mismatching request/response function codes (#254)
Browse files Browse the repository at this point in the history
* Fix panic on mismatching request/response function codes

* Handle both mismatching headers and function codes in call()

* Remove ResponseResult type alias

* Use a new error type for protocol and network errors
  • Loading branch information
uklotzde committed Mar 27, 2024
1 parent 55f6787 commit 6aad370
Show file tree
Hide file tree
Showing 18 changed files with 225 additions and 237 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@

# Changelog

## v0.13.0 (Unreleased)

- Fix: Do not panic on mismatching request/response function codes.

### Breaking Changes

- Client: Replace `std::io::Error` with a custom error type that handles both
protocol and transport errors.

## v0.12.0 (2024-03-21)

- Client: Support handling of _Modbus_ exceptions by using nested `Result`s.
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ futures-util = { version = "0.3.30", optional = true, default-features = false }
log = "0.4.20"
smallvec = { version = "1.13.1", optional = true, default-features = false }
socket2 = { version = "0.5.5", optional = true, default-features = false }
thiserror = "1.0.58"
tokio = { version = "1.35.1", default-features = false }
# Disable default-features to exclude unused dependency on libudev
tokio-serial = { version = "5.4.4", optional = true, default-features = false }
Expand Down
8 changes: 4 additions & 4 deletions examples/rtu-over-tcp-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ async fn client_context(socket_addr: SocketAddr) {
println!("CLIENT: Reading 2 input registers...");
let response = ctx.read_input_registers(0x00, 2).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Ok(vec![1234, 5678]));
assert_eq!(response.unwrap(), vec![1234, 5678]);

println!("CLIENT: Writing 2 holding registers...");
ctx.write_multiple_registers(0x01, &[7777, 8888])
Expand All @@ -171,15 +171,15 @@ async fn client_context(socket_addr: SocketAddr) {
println!("CLIENT: Reading 4 holding registers...");
let response = ctx.read_holding_registers(0x00, 4).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Ok(vec![10, 7777, 8888, 40]));
assert_eq!(response.unwrap(), vec![10, 7777, 8888, 40]);

// Now we try to read with an invalid register address.
// This should return a Modbus exception response with the code
// IllegalDataAddress.
println!("CLIENT: Reading nonexisting holding register address... (should return IllegalDataAddress)");
println!("CLIENT: Reading nonexistent holding register address... (should return IllegalDataAddress)");
let response = ctx.read_holding_registers(0x100, 1).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Err(Exception::IllegalDataAddress));
assert!(matches!(response, Err(Exception::IllegalDataAddress)));

println!("CLIENT: Done.")
},
Expand Down
4 changes: 2 additions & 2 deletions examples/rtu-server-address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("CLIENT: Reading input registers...");
let rsp = ctx.read_input_registers(0x00, 7).await?;
println!("CLIENT: The result is '{rsp:#x?}'");
assert_eq!(rsp, Ok(vec![0x0, 0x0, 0x77, 0x0, 0x0, 0x0, 0x0]));
assert_eq!(rsp.unwrap(), vec![0x0, 0x0, 0x77, 0x0, 0x0, 0x0, 0x0]);

println!("CLIENT: Reading with illegal function... (should return IllegalFunction)");
let response = ctx.read_holding_registers(0x100, 1).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Err(Exception::IllegalFunction));
assert!(matches!(response, Err(Exception::IllegalFunction)));

println!("CLIENT: Done.");

Expand Down
6 changes: 3 additions & 3 deletions examples/rtu-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("CLIENT: Reading input registers...");
let rsp = ctx.read_input_registers(0x00, 7).await?;
println!("CLIENT: The result is '{rsp:#x?}'");
assert_eq!(rsp, Ok(vec![0x0, 0x0, 0x77, 0x0, 0x0, 0x0, 0x0]));
assert_eq!(rsp.unwrap(), vec![0x0, 0x0, 0x77, 0x0, 0x0, 0x0, 0x0]);

// Now we try to read with an invalid register address.
// This should return a Modbus exception response with the code
// IllegalDataAddress.
println!("CLIENT: Reading nonexisting holding register address... (should return IllegalDataAddress)");
println!("CLIENT: Reading nonexistent holding register address... (should return IllegalDataAddress)");
let response = ctx.read_holding_registers(0x100, 1).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Err(Exception::IllegalDataAddress));
assert!(matches!(response, Err(Exception::IllegalDataAddress)));

println!("CLIENT: Done.");
Ok(())
Expand Down
8 changes: 4 additions & 4 deletions examples/tcp-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ async fn client_context(socket_addr: SocketAddr) {
println!("CLIENT: Reading 2 input registers...");
let response = ctx.read_input_registers(0x00, 2).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Ok(vec![1234, 5678]));
assert_eq!(response.unwrap(), vec![1234, 5678]);

println!("CLIENT: Writing 2 holding registers...");
ctx.write_multiple_registers(0x01, &[7777, 8888])
Expand All @@ -169,15 +169,15 @@ async fn client_context(socket_addr: SocketAddr) {
println!("CLIENT: Reading 4 holding registers...");
let response = ctx.read_holding_registers(0x00, 4).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Ok(vec![10, 7777, 8888, 40]));
assert_eq!(response.unwrap(), vec![10, 7777, 8888, 40]);

// Now we try to read with an invalid register address.
// This should return a Modbus exception response with the code
// IllegalDataAddress.
println!("CLIENT: Reading nonexisting holding register address... (should return IllegalDataAddress)");
println!("CLIENT: Reading nonexistent holding register address... (should return IllegalDataAddress)");
let response = ctx.read_holding_registers(0x100, 1).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Err(Exception::IllegalDataAddress));
assert!(matches!(response, Err(Exception::IllegalDataAddress)));

println!("CLIENT: Done.")
},
Expand Down
8 changes: 4 additions & 4 deletions examples/tls-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ async fn client_context(socket_addr: SocketAddr) {
println!("CLIENT: Reading 2 input registers...");
let response = ctx.read_input_registers(0x00, 2).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Ok(vec![1234, 5678]));
assert_eq!(response.unwrap(), vec![1234, 5678]);

println!("CLIENT: Writing 2 holding registers...");
ctx.write_multiple_registers(0x01, &[7777, 8888])
Expand All @@ -274,15 +274,15 @@ async fn client_context(socket_addr: SocketAddr) {
println!("CLIENT: Reading 4 holding registers...");
let response = ctx.read_holding_registers(0x00, 4).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Ok(vec![10, 7777, 8888, 40]));
assert_eq!(response.unwrap(), vec![10, 7777, 8888, 40]);

// Now we try to read with an invalid register address.
// This should return a Modbus exception response with the code
// IllegalDataAddress.
println!("CLIENT: Reading nonexisting holding register address... (should return IllegalDataAddress)");
println!("CLIENT: Reading nonexistent holding register address... (should return IllegalDataAddress)");
let response = ctx.read_holding_registers(0x100, 1).await.unwrap();
println!("CLIENT: The result is '{response:?}'");
assert_eq!(response, Err(Exception::IllegalDataAddress));
assert!(matches!(response, Err(Exception::IllegalDataAddress)));

println!("CLIENT: Done.")
},
Expand Down
Loading

0 comments on commit 6aad370

Please sign in to comment.