use crate::prelude::*;
use libc::{c_int, c_uint};
use libmodbus_sys as ffi;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Exception {
IllegalFunction = 1,
IllegalDataAddress = 2,
IllegalDataValue = 3,
SlaveOrServerFailure = 4,
Acknowledge = 5,
SlaveDeviceBusy = 6,
NegativeAcknowledge = 7,
MemoryParity = 8,
NotDefined = 9,
GatewayPath = 10,
GatewayTarget = 11,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum FunctionCode {
ReadCoils = 0x01,
ReadDiscreteInputs = 0x02,
ReadHoldingRegisters = 0x03,
ReadInputRegisters = 0x04,
WriteSingleCoil = 0x05,
WriteSingleRegister = 0x06,
ReadExceptionStatus = 0x07,
Diagnostic = 0x08,
WriteMultipleCoils = 0x15,
WriteMultipleRegisters = 0x16,
ReportSlaveId = 0x17,
MaskWriteRegister = 0x22,
WriteAndReadRegisters = 0x23,
}
#[derive(Debug, Copy, Clone)]
pub enum ErrorRecoveryMode {
Link,
Protocol,
}
impl ErrorRecoveryMode {
fn as_raw(&self) -> ffi::modbus_error_recovery_mode {
use ErrorRecoveryMode::*;
match *self {
Link => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_LINK,
Protocol => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_PROTOCOL,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Timeout {
pub sec: u32,
pub usec: u32,
}
impl Timeout {
pub fn new(sec: u32, usec: u32) -> Self {
Timeout { sec, usec }
}
pub fn new_sec(sec: u32) -> Self {
Timeout {
sec,
..Default::default()
}
}
pub fn new_usec(usec: u32) -> Self {
Timeout {
usec,
..Default::default()
}
}
}
impl Default for Timeout {
fn default() -> Timeout {
Timeout { sec: 0, usec: 0 }
}
}
#[derive(Debug, Clone)]
pub struct Modbus {
pub ctx: *mut ffi::modbus_t,
}
impl Modbus {
pub const MAX_READ_BITS: u32 = ffi::MODBUS_MAX_READ_BITS;
pub const MAX_WRITE_BITS: u32 = ffi::MODBUS_MAX_WRITE_BITS;
pub const MAX_READ_REGISTERS: u32 = ffi::MODBUS_MAX_READ_REGISTERS;
pub const MAX_WRITE_REGISTERS: u32 = ffi::MODBUS_MAX_WRITE_REGISTERS;
pub const MAX_WR_WRITE_REGISTERS: u32 = ffi::MODBUS_MAX_WR_WRITE_REGISTERS;
pub const MAX_WR_READ_REGISTERS: u32 = ffi::MODBUS_MAX_WR_READ_REGISTERS;
pub const MAX_PDU_LENGTH: usize = ffi::MODBUS_MAX_PDU_LENGTH as usize;
pub const MAX_ADU_LENGTH: usize = ffi::MODBUS_MAX_ADU_LENGTH as usize;
pub const ENOBASE: u32 = ffi::MODBUS_ENOBASE;
pub const RTU_MAX_ADU_LENGTH: usize = ffi::MODBUS_RTU_MAX_ADU_LENGTH as usize;
pub const TCP_DEFAULT_PORT: u32 = ffi::MODBUS_TCP_DEFAULT_PORT;
pub const TCP_MAX_ADU_LENGTH: usize = ffi::MODBUS_TCP_MAX_ADU_LENGTH as usize;
pub const TCP_SLAVE: u8 = ffi::MODBUS_TCP_SLAVE as u8;
pub const BROADCAST_ADDRESS: u8 = ffi::MODBUS_BROADCAST_ADDRESS as u8;
pub fn connect(&self) -> Result<(), Error> {
unsafe {
match ffi::modbus_connect(self.ctx) {
-1 => Err(Error::Modbus {
msg: "connect".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
0 => Ok(()),
_ => panic!("libmodbus API incompatible response"),
}
}
}
pub fn flush(&self) -> Result<(), Error> {
unsafe {
match ffi::modbus_flush(self.ctx) {
-1 => Err(Error::Modbus {
msg: "flush".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
0 => Ok(()),
_ => panic!("libmodbus API incompatible response"),
}
}
}
pub fn set_slave(&mut self, slave: u8) -> Result<(), Error> {
unsafe {
match ffi::modbus_set_slave(self.ctx, slave as c_int) {
-1 => Err(Error::Modbus {
msg: "set_slave".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
0 => Ok(()),
_ => panic!("libmodbus API incompatible response"),
}
}
}
pub fn get_slave(&self) -> Result<u8, Error> {
unsafe {
match ffi::modbus_get_slave(self.ctx) {
-1 => Err(Error::Modbus {
msg: "get_slave".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
num => Ok(num as u8),
}
}
}
pub fn set_debug(&mut self, flag: bool) -> Result<(), Error> {
unsafe {
match ffi::modbus_set_debug(self.ctx, flag as c_int) {
-1 => Err(Error::Modbus {
msg: "set_debug".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
0 => Ok(()),
_ => panic!("libmodbus API incompatible response"),
}
}
}
pub fn get_byte_timeout(&self) -> Result<Timeout, Error> {
let mut timeout = Timeout { sec: 0, usec: 0 };
unsafe {
match ffi::modbus_get_byte_timeout(self.ctx, &mut timeout.sec, &mut timeout.usec) {
-1 => Err(Error::Modbus {
msg: "get_byte_timeout".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
0 => Ok(timeout),
_ => panic!("libmodbus API incompatible response"),
}
}
}
pub fn set_byte_timeout(&mut self, timeout: Timeout) -> Result<(), Error> {
unsafe {
match ffi::modbus_set_byte_timeout(self.ctx, timeout.sec, timeout.usec) {
-1 => Err(Error::Modbus {
msg: "set_byte_timeout".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
0 => Ok(()),
_ => panic!("libmodbus API incompatible response"),
}
}
}
pub fn get_response_timeout(&self) -> Result<Timeout, Error> {
let mut timeout = Timeout { sec: 0, usec: 0 };
unsafe {
match ffi::modbus_get_response_timeout(self.ctx, &mut timeout.sec, &mut timeout.usec) {
-1 => Err(Error::Modbus {
msg: "get_response_timeout".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
0 => Ok(timeout),
_ => panic!("libmodbus API incompatible response"),
}
}
}
pub fn set_response_timeout(&mut self, timeout: Timeout) -> Result<(), Error> {
unsafe {
match ffi::modbus_set_response_timeout(self.ctx, timeout.sec, timeout.usec) {
-1 => Err(Error::Modbus {
msg: "set_response_timeout".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
0 => Ok(()),
_ => panic!("libmodbus API incompatible response"),
}
}
}
pub fn set_error_recovery(&mut self, flags: Option<&[ErrorRecoveryMode]>) -> Result<(), Error> {
let flags = flags.unwrap_or(&[]).iter().fold(
ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_NONE,
|acc, v| acc | v.as_raw(),
);
unsafe {
match ffi::modbus_set_error_recovery(self.ctx, flags) {
-1 => Err(Error::Modbus {
msg: "set_error_recovery".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
0 => Ok(()),
_ => panic!("libmodbus API incompatible response"),
}
}
}
pub fn set_socket(&mut self, socket: i32) -> Result<(), Error> {
unsafe {
match ffi::modbus_set_socket(self.ctx, socket) {
-1 => Err(Error::Modbus {
msg: "set_socket".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
0 => Ok(()),
_ => unreachable!(),
}
}
}
pub fn get_socket(&self) -> Result<i32, Error> {
unsafe {
match ffi::modbus_get_socket(self.ctx) {
-1 => Err(Error::Modbus {
msg: "get_socket".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
socket => Ok(socket),
}
}
}
pub fn get_header_length(&self) -> i32 {
unsafe { ffi::modbus_get_header_length(self.ctx) }
}
pub fn reply_exception(&self, request: &[u8], exception_code: Exception) -> Result<i32, Error> {
unsafe {
match ffi::modbus_reply_exception(self.ctx, request.as_ptr(), exception_code as c_uint)
{
-1 => Err(Error::Modbus {
msg: "reply_exception".to_owned(),
source: ::std::io::Error::last_os_error(),
}),
len => Ok(len),
}
}
}
pub fn strerror(errnum: i32) -> String {
let c_str = unsafe { ::std::ffi::CStr::from_ptr(ffi::modbus_strerror(errnum)) };
String::from_utf8_lossy(c_str.to_bytes()).into_owned()
}
pub fn close(&self) {
unsafe {
ffi::modbus_close(self.ctx);
}
}
pub fn free(&mut self) {
unsafe {
ffi::modbus_free(self.ctx);
self.ctx = std::ptr::null_mut();
}
}
}
pub fn set_bits_from_byte(dest: &mut [u8], index: u32, value: u8) {
unsafe { ffi::modbus_set_bits_from_byte(dest.as_mut_ptr(), index as c_int, value) }
}
pub fn set_bits_from_bytes(dest: &mut [u8], index: u16, num_bit: u16, bytes: &[u8]) {
unsafe {
ffi::modbus_set_bits_from_bytes(
dest.as_mut_ptr(),
index as c_int,
num_bit as c_uint,
bytes.as_ptr(),
)
}
}
pub fn get_byte_from_bits(src: &[u8], index: u8, num_bit: u16) -> u8 {
unsafe { ffi::modbus_get_byte_from_bits(src.as_ptr(), index as c_int, num_bit as c_uint) }
}
pub fn get_float_abcd(src: &[u16]) -> f32 {
unsafe { ffi::modbus_get_float_abcd(src.as_ptr()) }
}
pub fn set_float_abcd(src: f32, dest: &mut [u16]) {
unsafe { ffi::modbus_set_float_abcd(src, dest.as_mut_ptr()) }
}
pub fn get_float_badc(src: &[u16]) -> f32 {
unsafe { ffi::modbus_get_float_badc(src.as_ptr()) }
}
pub fn set_float_badc(src: f32, dest: &mut [u16]) {
unsafe { ffi::modbus_set_float_badc(src, dest.as_mut_ptr()) }
}
pub fn get_float_cdab(src: &[u16]) -> f32 {
unsafe { ffi::modbus_get_float_cdab(src.as_ptr()) }
}
pub fn set_float_cdab(src: f32, dest: &mut [u16]) {
unsafe { ffi::modbus_set_float_cdab(src, dest.as_mut_ptr()) }
}
pub fn get_float_dcba(src: &[u16]) -> f32 {
unsafe { ffi::modbus_get_float_dcba(src.as_ptr()) }
}
pub fn set_float_dcba(src: f32, dest: &mut [u16]) {
unsafe { ffi::modbus_set_float_dcba(src, dest.as_mut_ptr()) }
}
impl Drop for Modbus {
fn drop(&mut self) {
self.close();
self.free();
}
}