1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use failure::Error;
use libmodbus_sys as ffi;
use modbus::Modbus;
use std::ffi::CString;


/// The TCP PI (Protocol Independent) backend implements a Modbus variant used for communications over TCP IPv4 and
/// IPv6 networks.
/// It does not require a checksum calculation as lower layer takes care of the same.
///
/// Contrary to the TCP IPv4 only backend, the TCP PI backend offers hostname resolution but it consumes about 1Kb of
/// additional memory.
///
/// * Create a Modbus TCP context
///     - [`new_tcp_pi()`](struct.Modbus.html#method.new_tcp_pi)
///
pub trait ModbusTCPPI {
    fn new_tcp_pi(node: &str, service: &str) -> Result<Modbus, Error>;
    fn tcp_pi_accept(&mut self, socket: &mut i32) -> Result<i32, Error>;
    fn tcp_pi_listen(&mut self, num_connection: i32) -> Result<i32, Error>;
}

impl ModbusTCPPI for Modbus {
    /// `new_tcp_pi` - create a libmodbus context for TCP Protocol Independent
    ///
    /// The [`new_tcp_pi()`](#method.new_tcp_pi) function shall allocate and initialize a modbus_t structure to
    /// communicate with a Modbus TCP IPv4 or IPv6 server.
    ///
    /// The **node** argument specifies the host name or IP address of the host to connect to, eg. "192.168.0.5" ,
    /// "::1" or "server.com".
    /// A NULL value can be used to listen any addresses in server mode.
    ///
    /// The **service** argument is the service name/port number to connect to. To use the default Modbus port use the
    /// string "502".
    /// On many Unix systems, it’s convenient to use a port number greater than or equal to 1024 because it’s not
    /// necessary to have administrator privileges.
    ///
    /// # Examples
    ///
    /// ```
    /// use libmodbus_rs::{Modbus, ModbusTCPPI};
    ///
    /// let modbus = Modbus::new_tcp_pi("::1", "1502").unwrap();
    ///
    /// match modbus.connect() {
    ///     Ok(_) => {}
    ///     Err(e) => println!("Error: {}", e),
    /// }
    /// ```
    fn new_tcp_pi(node: &str, service: &str) -> Result<Modbus, Error> {
        unsafe {
            let node = CString::new(node).unwrap();
            let service = CString::new(service).unwrap();
            let ctx = ffi::modbus_new_tcp_pi(node.as_ptr(), service.as_ptr());

            if ctx.is_null() {
                bail!(::std::io::Error::last_os_error())
            } else {
                Ok(Modbus { ctx: ctx })
            }
        }
    }

    /// `tcp_pi_accept` - accept a new connection on a TCP PI Modbus socket (IPv6)
    ///
    /// The [`tcp_pi_accept()`](#method.tcp_pi_accept) function shall extract the first connection on the
    /// queue of pending connections and create a new socket given as argument.
    ///
    /// # Parameters
    ///
    /// * `socket`  - Socket
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use libmodbus_rs::{Modbus, ModbusMapping, ModbusServer, ModbusTCPPI};
    ///
    /// let mut modbus = Modbus::new_tcp_pi("::0", "1502").unwrap();
    /// let mut socket = modbus.tcp_pi_listen(1).unwrap();
    /// modbus.tcp_pi_accept(&mut socket).unwrap();
    /// ```
    fn tcp_pi_accept(&mut self, socket: &mut i32) -> Result<i32, Error> {
        unsafe {
            match ffi::modbus_tcp_pi_accept(self.ctx, socket) {
                -1 => bail!(::std::io::Error::last_os_error()),
                socket => Ok(socket),
            }
        }
    }

    /// `tcp_pi_listen` - create and listen a TCP PI Modbus socket (IPv6)
    ///
    /// The [`tcp_pi_listen()`](#method.tcp_pi_listen) function shall create a socket and listen to maximum
    /// `num_connection` incoming connections on the specifieded node.
    ///
    /// # Parameters
    ///
    /// * `num_connection`  - maximum number of incoming connections on the specified IP address
    ///
    /// If node is set to `""` or `0.0.0.0`, any addresses will be listen.
    ///
    /// # Examples
    ///
    /// For detailed examples, look at the examples directory of this crate.
    ///
    /// * unit-test-server.rs   - simple but handle only one connection
    /// * bandwidth-server-many-up.rs   - handles several connection at once
    ///
    /// ```rust,no_run
    /// use libmodbus_rs::{Modbus, ModbusMapping, ModbusServer, ModbusTCPPI};
    ///
    /// let mut modbus = Modbus::new_tcp_pi("::0", "1502").unwrap();
    /// let mut socket = modbus.tcp_pi_listen(1).unwrap();
    ///
    /// modbus.tcp_pi_accept(&mut socket);
    ///
    /// let modbus_mapping = ModbusMapping::new(500, 500, 500, 500).unwrap();
    /// let mut query = vec![0u8; Modbus::MAX_ADU_LENGTH as usize];
    ///
    /// loop {
    ///     let request_len = modbus.receive(&mut query).unwrap();
    ///     modbus.reply(&query, request_len, &modbus_mapping);
    /// }
    /// ```
    fn tcp_pi_listen(&mut self, num_connection: i32) -> Result<i32, Error> {
        unsafe {
            match ffi::modbus_tcp_pi_listen(self.ctx, num_connection) {
                -1 => bail!(::std::io::Error::last_os_error()),
                socket => Ok(socket),
            }
        }
    }
}