#[macro_use] extern crate failure;
extern crate libmodbus_rs;
mod unit_test_config;
use failure::Error;
use libmodbus_rs::prelude::*;
use libmodbus_rs::{Modbus, ModbusClient, ModbusTCP, ModbusTCPPI, ModbusRTU,
Exception, FunctionCode, Timeout, ErrorRecoveryMode};
use std::env;
use unit_test_config::*;
use std::thread::sleep;
use std::time::Duration;
const EXCEPTION_RC: u16 = 2;
macro_rules! bug_report {
( $cond:expr, $format:expr $(, $args:expr)*) => {
let format = format!($format, $($args),*);
print!("\nLine: {}: assertion error for '{}': {}\n", line!(), stringify!($cond), format);
}
}
macro_rules! assert_true {
( $cond:expr, $format:expr $(, $args:expr)*) => {
if $cond {
println!("OK");
} else {
bug_report!($cond, $format $(, $args)* );
}
}
}
fn equal_dword(tab_reg: &[u16], value: u32) -> bool {
tab_reg[0] as u32 == (value >> 16) && tab_reg[1] as u32 == (value & 0xFFFF)
}
fn run() -> Result<(), Error> {
const NB_REPORT_SLAVE_ID: usize = 10;
let backend;
let args: Vec<_> = env::args().collect();
if args.len() > 1 {
match args[1].to_lowercase().as_ref() {
"tcp" => backend = Backend::TCP,
"tcppi" => backend = Backend::TCPPI,
"rtu" => backend = Backend::RTU,
_ => {
println!("Usage:\n {} [tcp|tcppi|rtu] - Modbus server for unit testing\n\n",
args[0]);
std::process::exit(-1);
}
}
} else {
backend = Backend::TCP;
}
let mut modbus = match backend {
Backend::TCP => {
Modbus::new_tcp("127.0.0.1", 1502)
}
Backend::TCPPI => {
Modbus::new_tcp_pi("::0", "1502")
}
Backend::RTU => {
Modbus::new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1)
}
}?;
modbus.set_debug(true).expect("could not set modbus DEBUG mode");
modbus.set_error_recovery(Some(&[ErrorRecoveryMode::Link, ErrorRecoveryMode::Protocol]))
.expect("could not set error recovery mode");
if backend == Backend::RTU {
modbus.set_slave(SERVER_ID)?;
}
let old_response_timeout = modbus.get_response_timeout()
.expect("could not get response timeout");
match modbus.connect() {
Err(err) => panic!("Connection failed: {}", err),
Ok(_) => {}
}
let new_response_timeout = modbus.get_response_timeout()
.expect("could not get response timeout");
println!("** UNIT TESTING **");
print!("1/1 No response timeout modification on connect: ");
assert_true!(old_response_timeout == new_response_timeout, "");
let nb_points = if UT_BITS_NB > UT_INPUT_BITS_NB { UT_BITS_NB } else { UT_INPUT_BITS_NB };
let mut tab_rp_bits = vec![0u8; nb_points as usize];
let nb_points = if UT_REGISTERS_NB > UT_INPUT_REGISTERS_NB { UT_REGISTERS_NB } else { UT_INPUT_REGISTERS_NB };
let mut tab_rp_registers = vec![0u16; nb_points as usize];
println!("\nTEST WRITE/READ:");
let rc = modbus.write_bit(UT_BITS_ADDRESS, true);
print!("1/2 write_bit: ");
assert_true!(rc.is_ok(), "");
let rc = modbus.read_bits(UT_BITS_ADDRESS, 1, &mut tab_rp_bits);
print!("2/2 read_bits: ");
assert_true!(rc.is_ok(), "FAILED (nb_points {})", rc.unwrap());
assert_true!(tab_rp_bits[0] == 1, "FAILED ({:0X} != {})",
&tab_rp_bits[0], true);
{
let mut tab_value = vec![0u8; UT_BITS_NB as usize];
set_bits_from_bytes(&mut tab_value, 0, UT_BITS_NB, UT_BITS_TAB);
let rc = modbus.write_bits(UT_BITS_ADDRESS, UT_BITS_NB, &tab_value).unwrap();
print!("1/2 write_bits: ");
assert_true!(rc == UT_BITS_NB, "");
}
let rc = modbus.read_bits(UT_BITS_ADDRESS, UT_BITS_NB, &mut tab_rp_bits).unwrap();
print!("2/2 read_bits: ");
assert_true!(rc == UT_BITS_NB, "FAILED (nb_points {:?})", rc);
let mut i: usize = 0;
let mut nb_points = UT_BITS_NB;
while nb_points > 0 {
let nb_bits = if nb_points > 8 { 8 } else { nb_points };
let value = get_byte_from_bits(&tab_rp_bits, i as u8 * 8, nb_bits);
assert_true!(value == UT_BITS_TAB[i], "FAILED ({:0X} != {:0X})", value, UT_BITS_TAB[i]);
nb_points -= nb_bits;
i += 1;
}
println!("OK");
let rc = modbus.read_input_bits(UT_INPUT_BITS_ADDRESS,
UT_INPUT_BITS_NB, &mut tab_rp_bits).unwrap();
print!("1/1 modbus_read_input_bits: ");
assert_true!(rc == UT_INPUT_BITS_NB, "FAILED (nb_points {})", rc);
let mut i = 0;
let mut nb_points = UT_INPUT_BITS_NB;
while nb_points > 0 {
let nb_bits = if nb_points > 8 { 8 } else { nb_points };
let value = get_byte_from_bits(&tab_rp_bits, i as u8 * 8, nb_bits);
assert_true!(value == UT_INPUT_BITS_TAB[i], "FAILED ({:0X} != {:0X})",
value, UT_INPUT_BITS_TAB[i]);
nb_points -= nb_bits;
i += 1;
}
println!("OK");
let rc = modbus.write_register(UT_REGISTERS_ADDRESS, 0x1234);
print!("1/2 modbus_write_register: ");
assert_true!(rc.is_ok(), "");
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS, 1, &mut tab_rp_registers).unwrap();
print!("2/2 modbus_read_registers: ");
assert_true!(rc == 1, "FAILED (nb_points {:?})", rc);
assert_true!(tab_rp_registers[0] == 0x1234, "FAILED ({:0x} != {:?})",
tab_rp_registers[0], 0x1234);
let rc = modbus.write_registers(UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, UT_REGISTERS_TAB).unwrap();
print!("1/5 modbus_write_registers: ");
assert_true!(rc == UT_REGISTERS_NB, "");
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, &mut tab_rp_registers).unwrap();
print!("2/5 modbus_read_registers: ");
assert_true!(rc == UT_REGISTERS_NB, "FAILED (nb_points {:?}", rc);
for i in 0..UT_REGISTERS_NB as usize {
assert_true!(tab_rp_registers[i] == UT_REGISTERS_TAB[i],
"FAILED ({:?} != {:?})",
tab_rp_registers[i], UT_REGISTERS_TAB[i]);
}
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS, 0, &mut tab_rp_registers);
print!("3/5 modbus_read_registers (0): ");
assert_true!(rc.is_err(), "FAILED (nb_points {})", 0);
let nb_points = if UT_REGISTERS_NB > UT_INPUT_REGISTERS_NB { UT_REGISTERS_NB } else { UT_INPUT_REGISTERS_NB };
let mut tab_rp_registers = vec![0u16; nb_points as usize];
let rc = modbus.write_and_read_registers(UT_REGISTERS_ADDRESS + 1,
UT_REGISTERS_NB - 1,
&tab_rp_registers.clone(),
UT_REGISTERS_ADDRESS,
UT_REGISTERS_NB,
&mut tab_rp_registers).unwrap();
print!("4/5 modbus_write_and_read_registers: ");
assert_true!(rc == UT_REGISTERS_NB, "FAILED (nb_points {} != {})", rc, UT_REGISTERS_NB);
assert_true!(tab_rp_registers[0] == UT_REGISTERS_TAB[0],
"FAILED ({:?} != {:?})", tab_rp_registers[0], UT_REGISTERS_TAB[0]);
for i in 1..UT_REGISTERS_NB as usize {
assert_true!(tab_rp_registers[i] == 0,
"FAILED ({:0X} != {:0X})", tab_rp_registers[i], 0);
}
let rc = modbus.read_input_registers(UT_INPUT_REGISTERS_ADDRESS,
UT_INPUT_REGISTERS_NB,
&mut tab_rp_registers).unwrap();
print!("1/1 modbus_read_input_registers: ");
assert_true!(rc == UT_INPUT_REGISTERS_NB, "FAILED (nb_points {})", rc);
for i in 0..UT_INPUT_REGISTERS_NB as usize {
assert_true!(tab_rp_registers[i] == UT_INPUT_REGISTERS_TAB[i],
"FAILED ({:0X} != {:0X})",
tab_rp_registers[i], UT_INPUT_REGISTERS_TAB[i]);
}
print!("1/1 Write mask: ");
let _rc = modbus.write_register(UT_REGISTERS_ADDRESS, 0x12).unwrap();
let rc = modbus.mask_write_register(UT_REGISTERS_ADDRESS, 0xF2, 0x25);
assert_true!(rc.is_ok(), "FAILED ({:?} == -1)", rc);
let _rc = modbus.read_registers(UT_REGISTERS_ADDRESS, 1, &mut tab_rp_registers).unwrap();
assert_true!(tab_rp_registers[0] == 0x17,
"FAILED ({:0X} != {:0X})",
tab_rp_registers[0], 0x17);
print!("\nTEST FLOATS");
print!("1/4 Set/get float ABCD: ");
set_float_abcd(UT_REAL, &mut tab_rp_registers);
assert_true!(equal_dword(&tab_rp_registers, UT_IREAL_ABCD), "FAILED Set float ABCD");
let real = get_float_abcd(&tab_rp_registers[0..2]);
assert_true!(real == UT_REAL, "FAILED ({} != {})", real, UT_REAL);
print!("2/4 Set/get float DCBA: ");
set_float_dcba(UT_REAL, &mut tab_rp_registers);
assert_true!(equal_dword(&tab_rp_registers, UT_IREAL_DCBA), "FAILED Set float DCBA");
let real = get_float_dcba(&tab_rp_registers[0..2]);
assert_true!(real == UT_REAL, "FAILED ({} != {})", real, UT_REAL);
print!("3/4 Set/get float BADC: ");
set_float_badc(UT_REAL, &mut tab_rp_registers);
assert_true!(equal_dword(&tab_rp_registers, UT_IREAL_BADC), "FAILED Set float BADC");
let real = get_float_badc(&tab_rp_registers[0..2]);
assert_true!(real == UT_REAL, "FAILED ({} != {})", real, UT_REAL);
print!("4/4 Set/get float CDAB: ");
set_float_cdab(UT_REAL, &mut tab_rp_registers);
assert_true!(equal_dword(&tab_rp_registers, UT_IREAL_CDAB), "FAILED Set float CDAB");
let real = get_float_cdab(&tab_rp_registers[0..2]);
assert_true!(real == UT_REAL, "FAILED ({} != {})", real, UT_REAL);
print!("\nAt this point, error messages doesn't mean the test has failed");
print!("\nTEST ILLEGAL DATA ADDRESS:");
let rc = modbus.read_bits(0, 1, &mut tab_rp_bits);
print!("* read_bits (0): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.read_bits(UT_BITS_ADDRESS, UT_BITS_NB + 1, &mut tab_rp_bits);
print!("* read_bits (max): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.read_input_bits(0, 1, &mut tab_rp_bits);
print!("* read_input_bits (0): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.read_input_bits(UT_INPUT_BITS_ADDRESS,
UT_INPUT_BITS_NB + 1, &mut tab_rp_bits);
print!("* read_input_bits (max): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.read_registers(0, 1, &mut tab_rp_registers);
print!("* read_registers (0): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS,
UT_REGISTERS_NB_MAX + 1, &mut tab_rp_registers);
print!("* read_registers (max): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.read_input_registers(0, 1, &mut tab_rp_registers);
print!("* read_input_registers (0): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.read_input_registers(UT_INPUT_REGISTERS_ADDRESS,
UT_INPUT_REGISTERS_NB + 1,
&mut tab_rp_registers);
print!("* read_input_registers (max): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.write_bit(0, true);
print!("* write_bit (0): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.write_bit(UT_BITS_ADDRESS + UT_BITS_NB, true);
print!("* write_bit (max): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.write_bits(0, 1, &mut tab_rp_bits);
print!("* write_coils (0): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.write_bits(UT_BITS_ADDRESS + UT_BITS_NB,
UT_BITS_NB, &mut tab_rp_bits);
print!("* write_coils (max): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.write_register(0, tab_rp_registers[0]);
print!("* write_register (0): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.write_register(UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX,
tab_rp_registers[0]);
print!("* write_register (max): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.write_registers(0, 1, &mut tab_rp_registers);
print!("* write_registers (0): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.write_registers(UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX,
UT_REGISTERS_NB, &mut tab_rp_registers);
print!("* write_registers (max): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.mask_write_register(0, 0xF2, 0x25);
print!("* mask_write_registers (0): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.mask_write_register(UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX,
0xF2, 0x25);
print!("* mask_write_registers (max): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.write_and_read_registers(0, 1, &tab_rp_registers.clone(), 0, 1, &mut tab_rp_registers);
print!("* write_and_read_registers (0): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
let rc = modbus.write_and_read_registers(UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX,
UT_REGISTERS_NB, &tab_rp_registers.clone(),
UT_REGISTERS_ADDRESS + UT_REGISTERS_NB_MAX,
UT_REGISTERS_NB, &mut tab_rp_registers);
print!("* write_and_read_registers (max): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Illegal data address", "");
print!("\nTOO MANY DATA ERROR:\n");
let rc = modbus.read_bits(UT_BITS_ADDRESS,
Modbus::MAX_READ_BITS as u16 + 1, &mut tab_rp_bits);
print!("* read_bits: ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Too many data", "");
let _rc = modbus.read_input_bits(UT_INPUT_BITS_ADDRESS,
Modbus::MAX_READ_BITS as u16 + 1, &mut tab_rp_bits);
print!("* read_input_bits: ");
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS,
Modbus::MAX_READ_REGISTERS as u16 + 1,
&mut tab_rp_registers);
print!("* read_registers: ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Too many data", "");
let rc = modbus.read_input_registers(UT_INPUT_REGISTERS_ADDRESS,
Modbus::MAX_READ_REGISTERS as u16 + 1,
&mut tab_rp_registers);
print!("* read_input_registers: ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Too many data", "");
let rc = modbus.write_bits(UT_BITS_ADDRESS,
Modbus::MAX_WRITE_BITS as u16 + 1, &mut tab_rp_bits);
print!("* write_bits: ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Too many data", "");
let rc = modbus.write_registers(UT_REGISTERS_ADDRESS,
Modbus::MAX_WRITE_REGISTERS as u16 + 1,
&mut tab_rp_registers);
print!("* write_registers: ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Too many data", "");
let old_slave = modbus.get_slave().unwrap();
modbus.set_slave(INVALID_SERVER_ID).expect("Could not set slave id");
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS,
UT_REGISTERS_NB, &mut tab_rp_registers);
if backend == Backend::RTU {
const RAW_REQ_LENGTH: usize = 6;
let mut raw_req = vec![INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0x01, 0x01];
let mut raw_invalid_req = vec![INVALID_SERVER_ID, 0x03, 0x00, 0x01, 0xFF, 0xFF];
const RAW_RSP_LENGTH: usize = 7;
let mut raw_rsp = vec![INVALID_SERVER_ID, 0x03, 0x04, 0, 0, 0, 0];
let mut rsp = vec![0; Modbus::RTU_MAX_ADU_LENGTH];
print!("1-A/3 No response from slave {}: ", INVALID_SERVER_ID);
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Timeout", "");
modbus.send_raw_request(&mut raw_req, RAW_REQ_LENGTH).expect("Could not send raw request");
modbus.send_raw_request(&mut raw_rsp, RAW_RSP_LENGTH).expect("Could not send raw request");
let rc = modbus.receive_confirmation(&mut rsp);
print!("1-B/3 No response from slave {} on indication/confirmation messages: ",
INVALID_SERVER_ID);
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Timeout", "");
modbus.send_raw_request(&mut raw_invalid_req, RAW_REQ_LENGTH).expect("Could not send raw request");
let rc = modbus.receive_confirmation(&mut rsp);
print!("1-C/3 No response from slave {} with invalid request: ",
INVALID_SERVER_ID);
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Timeout", "");
let rc = modbus.set_slave(Modbus::BROADCAST_ADDRESS);
assert_true!(rc.is_ok(), "Invalid broadcast address");
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS,
UT_REGISTERS_NB, &mut tab_rp_registers);
print!("2/3 No reply after a broadcast query: ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Timeout", "");
} else {
print!("1/3 Response from slave {}: ", INVALID_SERVER_ID);
assert_true!(rc.unwrap() == UT_REGISTERS_NB, "");
let rc = modbus.set_slave(Modbus::BROADCAST_ADDRESS);
assert_true!(rc.is_ok(), "Invalid broacast address");
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS,
UT_REGISTERS_NB, &mut tab_rp_registers);
print!("2/3 Reply after a query with unit id == 0: ");
assert_true!(rc.unwrap() == UT_REGISTERS_NB, "");
}
modbus.set_slave(old_slave).expect("Could not set slave id");
print!("3/3 Response with an invalid TID or slave: ");
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS_INVALID_TID_OR_SLAVE,
1, &mut tab_rp_registers);
assert_true!(rc.is_err(), "");
print!("1/2 Report slave ID truncated: \n");
tab_rp_bits[NB_REPORT_SLAVE_ID - 1] = 42;
let rc = modbus.report_slave_id(NB_REPORT_SLAVE_ID - 1, &mut tab_rp_bits).unwrap();
assert_true!(rc == NB_REPORT_SLAVE_ID as u16 &&
tab_rp_bits[NB_REPORT_SLAVE_ID - 1] == 42,
"Return is rc {} ({}) and marker is {} (42)",
rc, NB_REPORT_SLAVE_ID, &mut tab_rp_bits[NB_REPORT_SLAVE_ID - 1]);
print!("2/2 Report slave ID: \n");
let rc = modbus.report_slave_id(NB_REPORT_SLAVE_ID, &mut tab_rp_bits).unwrap();
assert_true!(rc == NB_REPORT_SLAVE_ID as u16, "");
assert_true!(rc > 0, "");
assert_true!(rc > 1 && tab_rp_bits[1] == 0xFF, "");
if rc > 2 {
print!("Additional data: ");
for i in 2..rc as usize {
print!("{}", tab_rp_bits[i] as char);
}
println!();
}
let old_response_timeout = modbus.get_response_timeout().unwrap();
let old_byte_timeout = modbus.get_byte_timeout().unwrap();
let rc = modbus.set_response_timeout(Default::default());
print!("1/6 Invalid response timeout (zero): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Invalid argument (os error 22)", "");
let rc = modbus.set_response_timeout( Timeout::new_usec( 1_000_000 ) );
print!("2/6 Invalid response timeout (too large us): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Invalid argument (os error 22)", "");
let rc = modbus.set_byte_timeout( Timeout::new_usec( 1_000_000 ) );
print!("3/6 Invalid byte timeout (too large us): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Invalid argument (os error 22)", "");
modbus.set_response_timeout(Timeout::new_usec(1)).expect("Could not set response timeout");
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, &mut tab_rp_registers);
print!("4/6 1us response timeout: ");
if rc.is_err() && rc.unwrap_err().to_string() == "Connection timed out (os error 110)" {
println!("OK");
} else {
println!("FAILED (can fail on some platforms)");
}
sleep(Duration::new(old_response_timeout.sec as u64, old_response_timeout.usec * 1_000));
modbus.flush().unwrap();
modbus.set_response_timeout(Timeout::new(0, 200)).unwrap();
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS_SLEEP_500_MS, 1, &mut tab_rp_registers);
print!("5/6 Too short response timeout (0.2s < 0.5s): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Connection timed out (os error 110)", "");
sleep(Duration::new(0, 400_000_000));
modbus.flush().unwrap();
modbus.set_response_timeout(Timeout::new(0, 600_000)).unwrap();
println!("{:?}", modbus.get_response_timeout());
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS_SLEEP_500_MS, 1, &mut tab_rp_registers);
print!("6/6 Adequate response timeout (0.6s > 0.5s): ");
assert_true!(rc.is_ok() && rc.unwrap() == 1, "");
modbus.set_byte_timeout(Timeout::new(0, 0)).unwrap();
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS_SLEEP_500_MS, 1, &mut tab_rp_registers);
print!("7/7 Disable byte timeout: ");
assert_true!(rc.is_ok(), "");
modbus.set_response_timeout(old_response_timeout).unwrap();
if backend == Backend::TCP {
modbus.set_byte_timeout(Timeout::new(0, 3000)).unwrap();
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS,
1, &mut tab_rp_registers);
print!("1/2 Too small byte timeout (3ms < 5ms): ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Connection timed out (os error 110)", "");
sleep(Duration::from_millis(11 * 5));
modbus.flush().unwrap();
modbus.set_byte_timeout(Timeout::new(0, 7000)).unwrap();
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS_BYTE_SLEEP_5_MS,
1, &mut tab_rp_registers);
print!("2/2 Adapted byte timeout (7ms > 5ms): ");
assert_true!(rc.is_ok(), "");
}
modbus.set_byte_timeout(old_byte_timeout).unwrap();
println!("\nTEST BAD RESPONSE ERROR:");
let mut tab_rp_registers_bad = vec![0u16; (UT_REGISTERS_NB_SPECIAL * std::mem::size_of::<u16>() as u16) as usize ];
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS,
UT_REGISTERS_NB_SPECIAL, &mut tab_rp_registers_bad);
print!("* modbus.read_registers: ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Invalid data", "");
println!("\nTEST MANUAL EXCEPTION:");
let rc = modbus.read_registers(UT_REGISTERS_ADDRESS_SPECIAL,
UT_REGISTERS_NB, &mut tab_rp_registers);
print!("* modbus.read_registers at special address: ");
assert_true!(rc.is_err() && rc.unwrap_err().to_string() == "Slave device or server is busy", "");
if test_server(&mut modbus, backend).is_err() {
std::process::exit(-1)
}
modbus.close();
modbus.free();
println!("\nTEST INVALID INITIALIZATION:");
let modbus = Modbus::new_rtu("", 1, 'A', 0, 0);
assert_true!(modbus.is_err() && modbus.unwrap_err().to_string() == "Invalid argument (os error 22)", "");
let modbus = Modbus::new_rtu("/dev/dummy", 0, 'A', 0, 0);
assert_true!(modbus.is_err() && modbus.unwrap_err().to_string() == "Invalid argument (os error 22)", "");
let modbus = Modbus::new_tcp_pi("", "");
assert_true!(modbus.is_err() && modbus.unwrap_err().to_string() == "Invalid argument (os error 22)", "");
print!("\nALL TESTS PASS WITH SUCCESS.\n");
let success = true;
if success { Ok(()) } else { Err(format_err!("Unit test client failure")) }
}
fn test_server(modbus: &mut Modbus, backend: Backend) -> Result<(), Error> {
const READ_RAW_REQ_LEN: usize = 6;
let slave = match backend {
Backend::RTU => SERVER_ID,
_ => Modbus::TCP_SLAVE,
};
let mut read_raw_req: Vec<u8> = vec![
slave,
FunctionCode::ReadHoldingRegisters as u8,
(UT_REGISTERS_ADDRESS >> 8) as u8, (UT_REGISTERS_ADDRESS & 0xFF) as u8,
0x0, 0x05
];
const RW_RAW_REQ_LEN: usize = 13;
let mut rw_raw_req = vec![
slave,
FunctionCode::WriteAndReadRegisters as u8,
(UT_REGISTERS_ADDRESS >> 8) as u8, (UT_REGISTERS_ADDRESS & 0xFF) as u8,
((Modbus::MAX_WR_READ_REGISTERS + 1) >> 8) as u8,
((Modbus::MAX_WR_READ_REGISTERS + 1) & 0xFF) as u8,
0, 0,
0, 1,
1 * 2,
0x12, 0x34
];
const WRITE_RAW_REQ_LEN: usize = 13;
let mut write_raw_req = vec![
slave,
FunctionCode::WriteMultipleRegisters as u8,
(UT_REGISTERS_ADDRESS >> 8) as u8, (UT_REGISTERS_ADDRESS & 0xFF) as u8,
0x00, 0x03, 0x06,
0x02, 0x2B, 0x00, 0x01, 0x00, 0x64
];
const INVALID_FC: u8 = 0x42;
const INVALID_FC_REQ_LEN: usize = 6;
let mut invalid_fc_raw_req: Vec<u8> = vec![
slave, 0x42, 0x00, 0x00, 0x00, 0x00
];
let mut rsp: Vec<u8> = vec![0u8; Modbus::TCP_MAX_ADU_LENGTH];
let tab_read_function = vec![
FunctionCode::ReadCoils,
FunctionCode::ReadDiscreteInputs,
FunctionCode::ReadHoldingRegisters,
FunctionCode::ReadInputRegisters,
];
let tab_read_nb_max = vec![
Modbus::MAX_READ_BITS + 1,
Modbus::MAX_READ_BITS + 1,
Modbus::MAX_READ_REGISTERS + 1,
Modbus::MAX_READ_REGISTERS + 1
];
let (backend_length, backend_offset) = match backend {
Backend::RTU => (3, 1),
_ => (7, 7),
};
print!("\nTEST RAW REQUESTS:\n");
let old_response_timeout = modbus.get_response_timeout().unwrap();
modbus.set_response_timeout(Timeout::new(0, 600000)).unwrap();
let req_length = modbus.send_raw_request(&mut read_raw_req.clone(), READ_RAW_REQ_LEN).unwrap();
print!("* modbus_send_raw_request: ");
assert_true!(req_length == (backend_length + 5), "FAILED ({})\n", req_length);
print!("* modbus_receive_confirmation: ");
let rc = modbus.receive_confirmation(&mut rsp.clone()).unwrap();
assert_true!(rc == (backend_length + 12), "FAILED ({})\n", rc);
for i in 0..4 {
let rc = send_crafted_request(modbus, tab_read_function[i],
&mut read_raw_req, READ_RAW_REQ_LEN,
tab_read_nb_max[i] as u16, 0,
backend_length, backend_offset);
if rc.is_err() {
modbus.set_response_timeout(old_response_timeout).unwrap();
std::process::exit(-1)
}
}
let rc = send_crafted_request(modbus, FunctionCode::WriteAndReadRegisters,
&mut rw_raw_req, RW_RAW_REQ_LEN,
(Modbus::MAX_WR_READ_REGISTERS + 1) as u16, 0,
backend_length, backend_offset);
if rc.is_err() {
modbus.set_response_timeout(old_response_timeout).unwrap();
std::process::exit(-1)
}
let rc = send_crafted_request(modbus, FunctionCode::WriteMultipleRegisters,
&mut write_raw_req, WRITE_RAW_REQ_LEN,
(Modbus::MAX_WRITE_REGISTERS + 1) as u16, 6,
backend_length, backend_offset);
if rc.is_err() {
modbus.set_response_timeout(old_response_timeout).unwrap();
std::process::exit(-1)
}
let rc = send_crafted_request(modbus, FunctionCode::WriteMultipleCoils,
&mut write_raw_req, WRITE_RAW_REQ_LEN,
(Modbus::MAX_WRITE_BITS + 1) as u16, 6,
backend_length, backend_offset);
if rc.is_err() {
modbus.set_response_timeout(old_response_timeout).unwrap();
std::process::exit(-1)
}
modbus.send_raw_request(&mut invalid_fc_raw_req, INVALID_FC_REQ_LEN).expect("Could not set raw request");
let rc = modbus.receive_confirmation(&mut rsp).unwrap();
print!("Return an exception on unknown function code: ");
assert_true!(rc == (backend_length + EXCEPTION_RC) && rsp[backend_offset as usize] == (0x80 + INVALID_FC), "");
modbus.set_response_timeout(old_response_timeout).unwrap();
Ok(())
}
fn send_crafted_request(modbus: &mut Modbus, function: FunctionCode,
mut req: &mut [u8], req_len: usize,
max_value: u16, bytes: u16,
backend_length: u16, backend_offset: u16) -> Result<(), Error>
{
let mut rsp = vec![0u8; Modbus::TCP_MAX_ADU_LENGTH];
for j in 0..2 {
req[1] = function as u8;
if j == 0 {
req[4] = 0x00;
req[5] = 0x00;
if bytes > 0 {
req[6] = 0x00;
}
} else {
req[4] = (max_value >> 8) as u8 & 0xFF;
req[5] = max_value as u8 & 0xFF;
if bytes > 0 {
req[6] = bytes as u8;
}
}
modbus.send_raw_request(&mut req, req_len).expect("Could not send raw request");
if j == 0 {
print!("* try function 0x{:X}: {} 0 values: ", function as u8, if bytes > 0 { "write" } else { "read" });
} else {
print!("* try function 0x{:X}: {} {} values: ", function as u8, if bytes > 0 { "write" } else { "read" }, max_value);
}
let rc = modbus.receive_confirmation(&mut rsp).unwrap();
assert_true!(rc == (backend_length + EXCEPTION_RC) &&
rsp[backend_offset as usize] == (0x80 + function as u8) &&
rsp[backend_offset as usize + 1] == Exception::IllegalDataValue as u8, "");
}
Ok(())
}
fn main() {
if let Err(ref err) = run() {
println!("Error: {}", err);
std::process::exit(1)
}
}