libmodbus/
modbus_tcp_pi.rs

1use crate::prelude::*;
2use libmodbus_sys as ffi;
3use std::ffi::CString;
4
5/// The TCP PI (Protocol Independent) backend implements a Modbus variant used for communications over TCP IPv4 and
6/// IPv6 networks.
7/// It does not require a checksum calculation as lower layer takes care of the same.
8///
9/// Contrary to the TCP IPv4 only backend, the TCP PI backend offers hostname resolution but it consumes about 1Kb of
10/// additional memory.
11///
12/// * Create a Modbus TCP context
13///     - [`new_tcp_pi()`](struct.Modbus.html#method.new_tcp_pi)
14///
15pub trait ModbusTCPPI {
16    fn new_tcp_pi(node: &str, service: &str) -> Result<Modbus, Error>;
17    fn tcp_pi_accept(&mut self, socket: &mut i32) -> Result<i32, Error>;
18    fn tcp_pi_listen(&mut self, num_connection: i32) -> Result<i32, Error>;
19}
20
21impl ModbusTCPPI for Modbus {
22    /// `new_tcp_pi` - create a libmodbus context for TCP Protocol Independent
23    ///
24    /// The [`new_tcp_pi()`](#method.new_tcp_pi) function shall allocate and initialize a modbus_t structure to
25    /// communicate with a Modbus TCP IPv4 or IPv6 server.
26    ///
27    /// The **node** argument specifies the host name or IP address of the host to connect to, eg. "192.168.0.5" ,
28    /// "::1" or "server.com".
29    /// A NULL value can be used to listen any addresses in server mode.
30    ///
31    /// The **service** argument is the service name/port number to connect to. To use the default Modbus port use the
32    /// string "502".
33    /// On many Unix systems, it’s convenient to use a port number greater than or equal to 1024 because it’s not
34    /// necessary to have administrator privileges.
35    ///
36    /// # Examples
37    ///
38    /// ```
39    /// use libmodbus::{Modbus, ModbusTCPPI};
40    ///
41    /// let modbus = Modbus::new_tcp_pi("::1", "1502").unwrap();
42    ///
43    /// match modbus.connect() {
44    ///     Ok(_) => {}
45    ///     Err(e) => println!("Error: {}", e),
46    /// }
47    /// ```
48    fn new_tcp_pi(node: &str, service: &str) -> Result<Modbus, Error> {
49        unsafe {
50            let node = CString::new(node).unwrap();
51            let service = CString::new(service).unwrap();
52            let ctx = ffi::modbus_new_tcp_pi(node.as_ptr(), service.as_ptr());
53
54            if ctx.is_null() {
55                Err(Error::TcpPi {
56                    msg: "new_tcp_pi".to_owned(),
57                    source: ::std::io::Error::last_os_error(),
58                })
59            } else {
60                Ok(Modbus { ctx: ctx })
61            }
62        }
63    }
64
65    /// `tcp_pi_accept` - accept a new connection on a TCP PI Modbus socket (IPv6)
66    ///
67    /// The [`tcp_pi_accept()`](#method.tcp_pi_accept) function shall extract the first connection on the
68    /// queue of pending connections and create a new socket given as argument.
69    ///
70    /// # Parameters
71    ///
72    /// * `socket`  - Socket
73    ///
74    /// # Examples
75    ///
76    /// ```rust,no_run
77    /// use libmodbus::{Modbus, ModbusMapping, ModbusServer, ModbusTCPPI};
78    ///
79    /// let mut modbus = Modbus::new_tcp_pi("::0", "1502").unwrap();
80    /// let mut socket = modbus.tcp_pi_listen(1).unwrap();
81    /// modbus.tcp_pi_accept(&mut socket).unwrap();
82    /// ```
83    fn tcp_pi_accept(&mut self, socket: &mut i32) -> Result<i32, Error> {
84        unsafe {
85            match ffi::modbus_tcp_pi_accept(self.ctx, socket) {
86                -1 => Err(Error::TcpPi {
87                    msg: "tcp_pi_accept".to_owned(),
88                    source: ::std::io::Error::last_os_error(),
89                }),
90                socket => Ok(socket),
91            }
92        }
93    }
94
95    /// `tcp_pi_listen` - create and listen a TCP PI Modbus socket (IPv6)
96    ///
97    /// The [`tcp_pi_listen()`](#method.tcp_pi_listen) function shall create a socket and listen to maximum
98    /// `num_connection` incoming connections on the specifieded node.
99    ///
100    /// # Parameters
101    ///
102    /// * `num_connection`  - maximum number of incoming connections on the specified IP address
103    ///
104    /// If node is set to `""` or `0.0.0.0`, any addresses will be listen.
105    ///
106    /// # Examples
107    ///
108    /// For detailed examples, look at the examples directory of this crate.
109    ///
110    /// * unit-test-server.rs   - simple but handle only one connection
111    /// * bandwidth-server-many-up.rs   - handles several connection at once
112    ///
113    /// ```rust,no_run
114    /// use libmodbus::{Modbus, ModbusMapping, ModbusServer, ModbusTCPPI};
115    ///
116    /// let mut modbus = Modbus::new_tcp_pi("::0", "1502").unwrap();
117    /// let mut socket = modbus.tcp_pi_listen(1).unwrap();
118    ///
119    /// modbus.tcp_pi_accept(&mut socket);
120    ///
121    /// let modbus_mapping = ModbusMapping::new(500, 500, 500, 500).unwrap();
122    /// let mut query = vec![0u8; Modbus::MAX_ADU_LENGTH as usize];
123    ///
124    /// loop {
125    ///     let request_len = modbus.receive(&mut query).unwrap();
126    ///     modbus.reply(&query, request_len, &modbus_mapping);
127    /// }
128    /// ```
129    fn tcp_pi_listen(&mut self, num_connection: i32) -> Result<i32, Error> {
130        unsafe {
131            match ffi::modbus_tcp_pi_listen(self.ctx, num_connection) {
132                -1 => Err(Error::TcpPi {
133                    msg: "tcp_pi_listen".to_owned(),
134                    source: ::std::io::Error::last_os_error(),
135                }),
136                socket => Ok(socket),
137            }
138        }
139    }
140}