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; } } }