Skip to content

Commit 70d2720

Browse files
Add Config object for more convenient configuration of modbus and socket options
Discussed in #19
1 parent 682f1a3 commit 70d2720

File tree

5 files changed

+85
-53
lines changed

5 files changed

+85
-53
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "modbus"
3-
version = "0.4.2"
3+
version = "0.5.0"
44
authors = ["Falco Hirschenberger <falco.hirschenberger@gmail.com>"]
55
repository = "https://github.com/hirschenberger/modbus-rs.git"
66
homepage = "https://github.com/hirschenberger/modbus-rs.git"

src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
//! # if cfg!(feature = "modbus-server-tests") {
1313
//! # let (_s, port) = start_dummy_server(Some(22221));
1414
//!
15-
//! // let port = 502;
16-
//! let mut client = tcp::Transport::new_with_port("127.0.0.1", port).unwrap();
15+
//! let mut cfg = tcp::Config::default();
16+
//! # cfg.tcp_port = port;
17+
//! let mut client = tcp::Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
1718
//! assert!(client.write_single_coil(0, Coil::On).is_ok());
1819
//! # }
1920
//! # }
@@ -38,6 +39,7 @@ pub mod scoped;
3839
/// The Modbus TCP backend implements a Modbus variant used for communication over TCP/IPv4 networks.
3940
pub mod tcp;
4041
pub use tcp::Transport;
42+
pub use tcp::Config;
4143
pub use client::Client;
4244

4345
type Address = u16;

src/scoped.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
//! # if cfg!(feature = "modbus-server-tests") {
1616
//! # let (_s, port) = start_dummy_server(Some(22222));
1717
//!
18-
//! // let port = 502;
19-
//! let mut client = tcp::Transport::new_with_port("127.0.0.1", port).unwrap();
18+
//! let mut cfg = tcp::Config::default();
19+
//! # cfg.tcp_port = port;
20+
//! let mut client = tcp::Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
2021
//! {
2122
//! let mut auto = ScopedCoil::new(&mut client, 10, CoilDropFunction::On).unwrap();
2223
//! assert_eq!(auto.mut_transport().read_coils(10, 1).unwrap(), vec![Coil::Off]);
@@ -40,8 +41,9 @@
4041
//! # if cfg!(feature = "modbus-server-tests") {
4142
//! # let (_s, port) = start_dummy_server(Some(22223));
4243
//!
43-
//! // let port = 502;
44-
//! let mut client = tcp::Transport::new_with_port("127.0.0.1", port).unwrap();
44+
//! let mut cfg = tcp::Config::default();
45+
//! # cfg.tcp_port = port;
46+
//! let mut client = tcp::Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
4547
//! client.write_single_register(10, 1);
4648
//! {
4749
//! let fun = |v| v + 5;

src/tcp.rs

+30-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,30 @@ const MODBUS_TCP_DEFAULT_PORT: u16 = 502;
1313
const MODBUS_HEADER_SIZE: usize = 7;
1414
const MODBUS_MAX_PACKET_SIZE: usize = 260;
1515

16+
/// Config structure for more control over the tcp socket settings
17+
#[derive(Clone, Copy)]
18+
pub struct Config {
19+
/// The TCP port to use for communication (Default: `502`)
20+
pub tcp_port: u16,
21+
/// Timeout when reading from the TCP socket (Default: `infinite`)
22+
pub tcp_read_timeout: Option<Duration>,
23+
/// Timeout when writing to the TCP socket (Default: `infinite`)
24+
pub tcp_write_timeout: Option<Duration>,
25+
/// The modbus Unit Identifier used in the modbus layer (Default: `1`)
26+
pub modbus_uid: u8,
27+
}
28+
29+
impl Default for Config {
30+
fn default() -> Config {
31+
Config {
32+
tcp_port: MODBUS_TCP_DEFAULT_PORT,
33+
tcp_read_timeout: None,
34+
tcp_write_timeout: None,
35+
modbus_uid: 1,
36+
}
37+
}
38+
}
39+
1640
#[derive(RustcEncodable, RustcDecodable)]
1741
#[repr(packed)]
1842
struct Header {
@@ -44,22 +68,19 @@ impl Transport {
4468
/// Create a new context context object and connect it to `addr` on modbus-tcp default
4569
/// port (502)
4670
pub fn new(addr: &str) -> io::Result<Transport> {
47-
Self::new_with_port(addr, MODBUS_TCP_DEFAULT_PORT)
71+
Self::new_with_cfg(addr, Config::default())
4872
}
4973

5074
/// Create a new context object and connect it to `addr` on port `port`
51-
pub fn new_with_port(addr: &str, port: u16) -> io::Result<Transport> {
52-
match TcpStream::connect((addr, port)) {
75+
pub fn new_with_cfg(addr: &str, cfg: Config) -> io::Result<Transport> {
76+
match TcpStream::connect((addr, cfg.tcp_port)) {
5377
Ok(s) => {
54-
// set some sane tcp socket options
55-
let t = Duration::from_secs(5);
56-
s.set_read_timeout(Some(t))?;
57-
s.set_write_timeout(Some(t))?;
78+
s.set_read_timeout(cfg.tcp_read_timeout)?;
79+
s.set_write_timeout(cfg.tcp_write_timeout)?;
5880
s.set_nodelay(true)?;
59-
// try!(s.set_keepalive(None));
6081
Ok(Transport {
6182
tid: 0,
62-
uid: 1,
83+
uid: cfg.modbus_uid,
6384
stream: s,
6485
})
6586
}

tests/lib.rs

+44-37
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,48 @@ extern crate modbus;
33

44
#[cfg(feature="modbus-server-tests")]
55
mod modbus_server_tests {
6-
use test_server::start_dummy_server;
7-
use modbus::tcp::Transport;
6+
use test_server::{ChildKiller, start_dummy_server};
7+
use modbus::tcp::{Config, Transport};
88
use modbus::{Client, Coil};
99
use modbus::scoped::{ScopedCoil, ScopedRegister, CoilDropFunction, RegisterDropFunction};
1010

11+
fn start_dummy_server_with_cfg() -> (ChildKiller, Config) {
12+
let (k, port) = start_dummy_server(None);
13+
let mut cfg = Config::default();
14+
cfg.tcp_port = port;
15+
(k, cfg)
16+
}
17+
1118
/// /////////////////////
1219
/// simple READ tests
1320
#[test]
1421
fn test_read_coils() {
15-
let (_s, port) = start_dummy_server(None);
16-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
22+
let (_s, cfg) = start_dummy_server_with_cfg();
23+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
1724
assert_eq!(trans.read_coils(0, 5).unwrap().len(), 5);
1825
assert!(trans.read_coils(0, 5).unwrap().iter().all(|c| *c == Coil::Off));
1926
}
2027

2128
#[test]
2229
fn test_read_discrete_inputs() {
23-
let (_s, port) = start_dummy_server(None);
24-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
30+
let (_s, cfg) = start_dummy_server_with_cfg();
31+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
2532
assert_eq!(trans.read_discrete_inputs(0, 5).unwrap().len(), 5);
2633
assert!(trans.read_discrete_inputs(0, 5).unwrap().iter().all(|c| *c == Coil::Off));
2734
}
2835

2936
#[test]
3037
fn test_read_holding_registers() {
31-
let (_s, port) = start_dummy_server(None);
32-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
38+
let (_s, cfg) = start_dummy_server_with_cfg();
39+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
3340
assert_eq!(trans.read_holding_registers(0, 5).unwrap().len(), 5);
3441
assert!(trans.read_holding_registers(0, 5).unwrap().iter().all(|c| *c == 0));
3542
}
3643

3744
#[test]
3845
fn test_read_input_registers() {
39-
let (_s, port) = start_dummy_server(None);
40-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
46+
let (_s, cfg) = start_dummy_server_with_cfg();
47+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
4148
assert_eq!(trans.read_input_registers(0, 5).unwrap().len(), 5);
4249
assert!(trans.read_input_registers(0, 5).unwrap().iter().all(|c| *c == 0));
4350
}
@@ -46,30 +53,30 @@ mod modbus_server_tests {
4653
/// simple WRITE tests
4754
#[test]
4855
fn test_write_single_coil() {
49-
let (_s, port) = start_dummy_server(None);
50-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
56+
let (_s, cfg) = start_dummy_server_with_cfg();
57+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
5158
assert!(trans.write_single_coil(0, Coil::On).is_ok());
5259
}
5360

5461
#[test]
5562
fn test_write_single_register() {
56-
let (_s, port) = start_dummy_server(None);
57-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
63+
let (_s, cfg) = start_dummy_server_with_cfg();
64+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
5865
assert!(trans.write_single_register(0, 1).is_ok());
5966
}
6067

6168
#[test]
6269
fn test_write_multiple_coils() {
63-
let (_s, port) = start_dummy_server(None);
64-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
70+
let (_s, cfg) = start_dummy_server_with_cfg();
71+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
6572
assert!(trans.write_multiple_coils(0, &[Coil::On, Coil::Off]).is_ok());
6673
// assert!(write_multiple_coils(&mut trans, 0, &[]).is_err());
6774
}
6875

6976
#[test]
7077
fn test_write_multiple_registers() {
71-
let (_s, port) = start_dummy_server(None);
72-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
78+
let (_s, cfg) = start_dummy_server_with_cfg();
79+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
7380
assert!(trans.write_multiple_registers(0, &[0, 1, 2, 3]).is_ok());
7481
// assert!(write_multiple_registers(&mut trans, 0, &[]).is_err());
7582
}
@@ -78,8 +85,8 @@ mod modbus_server_tests {
7885
/// coil WRITE-READ tests
7986
#[test]
8087
fn test_write_read_single_coils() {
81-
let (_s, port) = start_dummy_server(None);
82-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
88+
let (_s, cfg) = start_dummy_server_with_cfg();
89+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
8390

8491
assert!(trans.write_single_coil(1, Coil::On).is_ok());
8592
assert!(trans.write_single_coil(3, Coil::On).is_ok());
@@ -99,8 +106,8 @@ mod modbus_server_tests {
99106

100107
#[test]
101108
fn test_write_read_single_register() {
102-
let (_s, port) = start_dummy_server(None);
103-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
109+
let (_s, cfg) = start_dummy_server_with_cfg();
110+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
104111
assert!(trans.write_single_register(0, 23).is_ok());
105112
assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![23]);
106113
assert!(trans.write_single_register(0, 0).is_ok());
@@ -113,8 +120,8 @@ mod modbus_server_tests {
113120

114121
#[test]
115122
fn test_write_read_multiple_coils() {
116-
let (_s, port) = start_dummy_server(None);
117-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
123+
let (_s, cfg) = start_dummy_server_with_cfg();
124+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
118125
assert!(trans.write_multiple_coils(0, &[Coil::Off, Coil::On]).is_ok());
119126
assert_eq!(trans.read_coils(0, 3).unwrap(),
120127
&[Coil::Off, Coil::On, Coil::Off]);
@@ -124,8 +131,8 @@ mod modbus_server_tests {
124131

125132
#[test]
126133
fn test_write_read_multiple_registers() {
127-
let (_s, port) = start_dummy_server(None);
128-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
134+
let (_s, cfg) = start_dummy_server_with_cfg();
135+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
129136
// assert!(write_multiple_registers(&mut trans, 0, &[]).is_err());
130137
assert!(trans.write_multiple_registers(0, &[23]).is_ok());
131138
assert_eq!(trans.read_holding_registers(0, 1).unwrap(), &[23]);
@@ -138,16 +145,16 @@ mod modbus_server_tests {
138145

139146
#[test]
140147
fn test_write_too_big() {
141-
let (_s, port) = start_dummy_server(None);
142-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
148+
let (_s, cfg) = start_dummy_server_with_cfg();
149+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
143150
assert!(trans.write_multiple_registers(0, &[0xdead; 123]).is_ok());
144151
assert!(trans.write_multiple_registers(0, &[0xdead; 124]).is_err());
145152
}
146153

147154
#[test]
148155
fn test_scoped_coil() {
149-
let (_s, port) = start_dummy_server(None);
150-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
156+
let (_s, cfg) = start_dummy_server_with_cfg();
157+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
151158

152159
{
153160
let mut auto = ScopedCoil::new(&mut trans, 0, CoilDropFunction::On).unwrap();
@@ -189,12 +196,12 @@ mod modbus_server_tests {
189196

190197
#[test]
191198
fn test_scoped_register() {
192-
let (_s, port) = start_dummy_server(None);
193-
let mut trans = Transport::new_with_port("127.0.0.1", port).unwrap();
199+
let (_s, cfg) = start_dummy_server_with_cfg();
200+
let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
194201

195202
{
196203
let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Value(0xbeef))
197-
.unwrap();
204+
.unwrap();
198205
assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
199206
vec![0x0000]);
200207
}
@@ -209,23 +216,23 @@ mod modbus_server_tests {
209216

210217
{
211218
let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Increment)
212-
.unwrap();
219+
.unwrap();
213220
assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
214221
vec![0x0000]);
215222
}
216223
assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0x0001]);
217224

218225
{
219226
let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Increment)
220-
.unwrap();
227+
.unwrap();
221228
assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
222229
vec![0x0001]);
223230
}
224231
assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0x0002]);
225232

226233
{
227234
let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Decrement)
228-
.unwrap();
235+
.unwrap();
229236
assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
230237
vec![0x0002]);
231238
}
@@ -234,7 +241,7 @@ mod modbus_server_tests {
234241
{
235242
let fun = |v| v + 0xbeee;
236243
let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Fun(&fun))
237-
.unwrap();
244+
.unwrap();
238245
assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
239246
vec![0x0001]);
240247
}

0 commit comments

Comments
 (0)