395 lines
13 KiB
C#
395 lines
13 KiB
C#
using Aurora.Api;
|
|
using Aurora.Api.Devices;
|
|
using Hermes;
|
|
using NModbus;
|
|
using NModbus.Serial;
|
|
using System;
|
|
using System.IO.Ports;
|
|
using System.Linq;
|
|
using System.Net.Sockets;
|
|
|
|
namespace AuroraDeviceCommon
|
|
{
|
|
public class ModbusConn : IModbus
|
|
{
|
|
private static readonly Logger LOG = new Logger("Modbus");
|
|
|
|
private SerialPort _SerialPort;
|
|
private TcpClient _TcpClient;
|
|
private IModbusMaster _ModbusMaster;
|
|
private int _Slave;
|
|
|
|
public ModbusConn()
|
|
{
|
|
|
|
}
|
|
|
|
public int Connect(IDeviceConfig config)
|
|
{
|
|
int err = E2.INVALID_ARGUMENT;
|
|
if (config.ConnConfig is ModbusTcpIpConnConfig tcpipConfig) {
|
|
_Slave = tcpipConfig.Slave;
|
|
err = ActiveTcpIp(tcpipConfig);
|
|
} else if (config.ConnConfig is ModbusSerialConnConfig serialConfig) {
|
|
_Slave = serialConfig.Slave;
|
|
err = ActiveSerial(serialConfig);
|
|
} else {
|
|
LOG.W($"Warning : wrong config {config}");
|
|
}
|
|
return err;
|
|
}
|
|
|
|
public int Disconnect()
|
|
{
|
|
_SerialPort?.Close();
|
|
_TcpClient?.Close();
|
|
_ModbusMaster?.Dispose();
|
|
|
|
return E1.OK;
|
|
}
|
|
|
|
SerialPort CreateSerialPort(SerialConnConfig config)
|
|
=> new SerialPort {
|
|
PortName = $"COM{config.PortNum}",
|
|
BaudRate = config.BaudRate,
|
|
Parity = config.Parity,
|
|
StopBits = config.StopBits,
|
|
DataBits = config.DataBits,
|
|
|
|
//RtsEnable = true,
|
|
ReadTimeout = config.ReadTimeout,
|
|
WriteTimeout = config.WriteTimeout,
|
|
};
|
|
|
|
int ConnectSerial(SerialPort serialPort)
|
|
{
|
|
try {
|
|
serialPort.Open();
|
|
return E1.OK;
|
|
} catch (Exception e) {
|
|
LOG.E(e.ToString());
|
|
return E1.NETWORK;
|
|
}
|
|
}
|
|
|
|
TcpClient ConnectTcpIp(ModbusTcpIpConnConfig config)
|
|
{
|
|
try {
|
|
var tcpClient = new TcpClient(config.Address.IpAddress
|
|
, config.Address.PortNumber);
|
|
return tcpClient;
|
|
} catch (Exception e) {
|
|
LOG.E(e.ToString());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal int ActiveSerial(ModbusSerialConnConfig config)
|
|
{
|
|
_Slave = config.Slave;
|
|
_SerialPort?.Close();
|
|
_SerialPort = CreateSerialPort(config);
|
|
int err = ConnectSerial(_SerialPort);
|
|
if (err != E1.OK)
|
|
return err;
|
|
var factory = new ModbusFactory();
|
|
_ModbusMaster = factory.CreateRtuMaster(_SerialPort);
|
|
if (_ModbusMaster == null) {
|
|
LOG.E($"ERROR: fail to init modbus ");
|
|
return E.C(E1.NETWORK, E2.NOT_CONNECTED);
|
|
}
|
|
return E1.OK;
|
|
}
|
|
|
|
internal int ActiveTcpIp(ModbusTcpIpConnConfig config)
|
|
{
|
|
_Slave = config.Slave;
|
|
_TcpClient?.Close();
|
|
_TcpClient = ConnectTcpIp(config);
|
|
if (_TcpClient == null)
|
|
return E1.NETWORK;
|
|
|
|
var factory = new ModbusFactory();
|
|
_ModbusMaster = factory.CreateMaster(_TcpClient);
|
|
if (_ModbusMaster == null) {
|
|
LOG.E($"ERROR: fail to init modbus ");
|
|
return E.C(E1.NETWORK, E2.NOT_CONNECTED);
|
|
}
|
|
return E1.OK;
|
|
}
|
|
|
|
public int WriteBit(ushort address, bool bit)
|
|
=> WriteBit_NOLOCK(address, bit);
|
|
|
|
public int WriteWord(ushort address, short value)
|
|
=> WriteSingle_NOLOCK(address, (ushort)value);
|
|
|
|
public int WriteDoubleWord(ushort address, int value)
|
|
=> WriteFloat_NOLOCK(address, value);
|
|
|
|
public int WriteReal(ushort address, float word)
|
|
=> WriteFloat_NOLOCK(address, word);
|
|
|
|
public int WriteBitBlock(ushort address, bool[] bits)
|
|
{
|
|
try {
|
|
_ModbusMaster.WriteMultipleCoils(
|
|
BitConverter.GetBytes(_Slave)[0],
|
|
address,
|
|
bits
|
|
);
|
|
return E1.OK;
|
|
} catch (Exception e) {
|
|
LOG.E($"ERROR: fail to write registers - address: 0x{address:X}\n{e}");
|
|
return E1.NETWORK;
|
|
}
|
|
}
|
|
|
|
public int WriteWordBlock(ushort address, short[] words)
|
|
{
|
|
try {
|
|
_ModbusMaster.WriteMultipleRegisters(
|
|
BitConverter.GetBytes(_Slave)[0],
|
|
address,
|
|
words?.Select(b => (ushort)b).ToArray()
|
|
);
|
|
return E1.OK;
|
|
} catch (Exception e) {
|
|
LOG.E($"ERROR: fail to write registers - address: 0x{address:X}\n{e}");
|
|
return E1.NETWORK;
|
|
}
|
|
}
|
|
|
|
public (int err, bool value) ReadBit(ushort address)
|
|
{
|
|
(int err, bool[] value) = ReadBit_NOLOCK(address);
|
|
return (err, value[0]);
|
|
}
|
|
|
|
public (int err, bool value) ReadInputBit(ushort address)
|
|
{
|
|
(int err, bool[] value) = ReadInputBit_NOLOCK(address);
|
|
return (err, value[0]);
|
|
}
|
|
|
|
public (int err, short value) ReadWord(ushort address)
|
|
{
|
|
(int err, ushort value) = ReadSingle_NOLOCK(address);
|
|
return (err, (short)value);
|
|
}
|
|
|
|
public (int err, short value) ReadInputWord(ushort address)
|
|
{
|
|
(int err, ushort value) = ReadSingleInput_NOLOCK(address);
|
|
return (err, (short)value);
|
|
}
|
|
|
|
public (int err, int value) ReadDoubleWord(ushort address)
|
|
{
|
|
(int err, float value) = ReadFloat_NOLOCK(address);
|
|
return (err, (int)value);
|
|
}
|
|
|
|
public (int err, float value) ReadReal(ushort address)
|
|
=> ReadFloat_NOLOCK(address);
|
|
|
|
public (int err, bool[] bits) ReadBitBlock(ushort address, int count)
|
|
=> ReadBit_NOLOCK(address, (ushort)count);
|
|
|
|
public (int err, bool[] bits) ReadInputBitBlock(ushort address, int count)
|
|
=> ReadInputBit_NOLOCK(address, (ushort)count);
|
|
|
|
public (int err, short[] words) ReadWordBlock(ushort address, int count)
|
|
{
|
|
(int err, ushort[] words) = ReadMulti_NOLOCK(address, (ushort)count);
|
|
return (err, words?.Select(b => (short)b).ToArray());
|
|
}
|
|
|
|
public (int err, short[] words) ReadInputWordBlock(ushort address, int count)
|
|
{
|
|
(int err, ushort[] words) = ReadMultiInput_NOLOCK(address, (ushort)count);
|
|
return (err, words?.Select(b => (short)b).ToArray());
|
|
}
|
|
|
|
internal (int err, ushort[] value) ReadMulti_NOLOCK(ushort address, ushort count)
|
|
{
|
|
var values = Read_NOLOCK(address, count);
|
|
return (
|
|
err: values != null ? E1.OK : E1.NETWORK,
|
|
value: values
|
|
);
|
|
}
|
|
|
|
internal (int err, ushort[] value) ReadMultiInput_NOLOCK(ushort address, ushort count)
|
|
{
|
|
var values = ReadInput_NOLOCK(address, count);
|
|
return (
|
|
err: values != null ? E1.OK : E1.NETWORK,
|
|
value: values
|
|
);
|
|
}
|
|
|
|
internal int WriteBit_NOLOCK(ushort address, bool value)
|
|
{
|
|
try {
|
|
_ModbusMaster.WriteSingleCoil(
|
|
BitConverter.GetBytes(_Slave)[0],
|
|
address,
|
|
value
|
|
);
|
|
return E1.OK;
|
|
} catch (Exception e) {
|
|
LOG.E($"ERROR: fail to write bit - address: 0x{address:X}\n{e.ToString()}");
|
|
return E1.NETWORK;
|
|
}
|
|
}
|
|
|
|
internal int WriteSingle_NOLOCK(ushort address, ushort value)
|
|
{
|
|
try {
|
|
_ModbusMaster.WriteSingleRegister(
|
|
BitConverter.GetBytes(_Slave)[0],
|
|
address,
|
|
value
|
|
);
|
|
return E1.OK;
|
|
} catch (Exception e) {
|
|
LOG.E($"ERROR: fail to write word - address: 0x{address:X}\n{e.ToString()}");
|
|
return E1.NETWORK;
|
|
}
|
|
}
|
|
|
|
internal int WriteFloat_NOLOCK(ushort address, float value)
|
|
{
|
|
var bytes = BitConverter.GetBytes(value);
|
|
var ushorts = new[] {
|
|
(ushort)(bytes[1] << 8 | bytes[0]),
|
|
(ushort)(bytes[3] << 8 | bytes[2]),
|
|
};
|
|
return WriteRegisters_NOLOCK(address, ushorts);
|
|
}
|
|
|
|
internal int WriteRegisters_NOLOCK(ushort address, params ushort[] values)
|
|
{
|
|
try {
|
|
_ModbusMaster.WriteMultipleRegisters(
|
|
BitConverter.GetBytes(_Slave)[0],
|
|
address,
|
|
values
|
|
);
|
|
return E1.OK;
|
|
} catch (Exception e) {
|
|
LOG.E($"ERROR: fail to write registers - address: 0x{address:X}\n{e.ToString()}");
|
|
return E1.NETWORK;
|
|
}
|
|
}
|
|
|
|
internal (int err, bool[] value) ReadBit_NOLOCK(ushort address, ushort count = 1)
|
|
{
|
|
bool[] values = null;
|
|
try {
|
|
values = _ModbusMaster.ReadCoils(
|
|
BitConverter.GetBytes(_Slave)[0],
|
|
address,
|
|
count
|
|
);
|
|
} catch (Exception) {
|
|
LOG.E($"ERROR: fail to read word - address: 0x{address:X}");
|
|
}
|
|
return (
|
|
err: values != null ? E1.OK : E1.NETWORK,
|
|
value: values
|
|
);
|
|
}
|
|
|
|
internal (int err, bool[] value) ReadInputBit_NOLOCK(ushort address, ushort count = 1)
|
|
{
|
|
bool[] values = null;
|
|
try {
|
|
values = _ModbusMaster.ReadInputs(
|
|
BitConverter.GetBytes(_Slave)[0],
|
|
address,
|
|
count
|
|
);
|
|
} catch (Exception) {
|
|
LOG.E($"ERROR: fail to read word - address: 0x{address:X}");
|
|
}
|
|
return (
|
|
err: values != null ? E1.OK : E1.NETWORK,
|
|
value: values
|
|
);
|
|
}
|
|
|
|
internal (int err, ushort value) ReadSingle_NOLOCK(ushort address)
|
|
{
|
|
var values = Read_NOLOCK(address, 1);
|
|
return (
|
|
err: values != null ? E1.OK : E1.NETWORK,
|
|
value: values[0]
|
|
);
|
|
}
|
|
|
|
internal (int err, ushort value) ReadSingleInput_NOLOCK(ushort address)
|
|
{
|
|
var values = Read_NOLOCK(address, 1);
|
|
return (
|
|
err: values != null ? E1.OK : E1.NETWORK,
|
|
value: values[0]
|
|
);
|
|
}
|
|
|
|
internal (int err, float value) ReadFloat_NOLOCK(ushort address)
|
|
{
|
|
var values = Read_NOLOCK(address, 2);
|
|
var bytes = new[] {
|
|
(byte)(values[0] & 0xFF),
|
|
(byte)(values[0] >> 8 & 0xFF),
|
|
(byte)(values[1] & 0xFF),
|
|
(byte)(values[1] >> 8 & 0xFF),
|
|
};
|
|
return (
|
|
err: values != null ? E1.OK : E1.NETWORK,
|
|
value: BitConverter.ToSingle(bytes, 0)
|
|
);
|
|
}
|
|
|
|
internal ushort[] Read_NOLOCK(ushort address, ushort count)
|
|
{
|
|
ushort[] values = null;
|
|
try {
|
|
values = _ModbusMaster.ReadHoldingRegisters(
|
|
BitConverter.GetBytes(_Slave)[0],
|
|
address,
|
|
count
|
|
);
|
|
if (values?.Length != count) {
|
|
LOG.E($"ERROR: fail to read word - address: 0x{address:X} count: {count}");
|
|
return null;
|
|
}
|
|
} catch (Exception e) {
|
|
LOG.E($"ERROR: fail to read word - address: 0x{address:X} count: {count}\n{e.ToString()}");
|
|
}
|
|
return values;
|
|
}
|
|
|
|
internal ushort[] ReadInput_NOLOCK(ushort address, ushort count)
|
|
{
|
|
ushort[] values = null;
|
|
try {
|
|
values = _ModbusMaster.ReadInputRegisters(
|
|
BitConverter.GetBytes(_Slave)[0],
|
|
address,
|
|
count
|
|
);
|
|
if (values?.Length != count) {
|
|
LOG.E($"ERROR: fail to read word - address: 0x{address:X} count: {count}");
|
|
return null;
|
|
}
|
|
} catch (Exception e) {
|
|
LOG.E($"ERROR: fail to read word - address: 0x{address:X} count: {count}\n{e.ToString()}");
|
|
}
|
|
return values;
|
|
}
|
|
}
|
|
}
|