diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocal.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocal.cs index f75400c..bf93d71 100644 --- a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocal.cs +++ b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocal.cs @@ -15,7 +15,7 @@ namespace Modbus.Net.Modbus public ModbusAsciiProtocal(string com, byte slaveAddress, byte masterAddress, Endian endian) : base(slaveAddress, masterAddress, endian) { - ProtocalLinker = new ModbusAsciiProtocalLinker(com); + ProtocalLinker = new ModbusAsciiProtocalLinker(com, slaveAddress); } } } \ No newline at end of file diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocalLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocalLinker.cs index 8952917..f314fca 100644 --- a/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocalLinker.cs +++ b/Modbus.Net/Modbus.Net.Modbus/ModbusAsciiProtocalLinker.cs @@ -8,7 +8,7 @@ namespace Modbus.Net.Modbus /// public class ModbusAsciiProtocalLinker : ComProtocalLinker { - public ModbusAsciiProtocalLinker(string com) : base(com, 9600, Parity.None, StopBits.One, 8) + public ModbusAsciiProtocalLinker(string com, int slaveAddress) : base(com, 9600, Parity.None, StopBits.One, 8, slaveAddress) { } diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocal.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocal.cs index 53fab41..aace433 100644 --- a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocal.cs +++ b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocal.cs @@ -15,7 +15,7 @@ namespace Modbus.Net.Modbus public ModbusRtuProtocal(string com, byte slaveAddress, byte masterAddress, Endian endian) : base(slaveAddress, masterAddress, endian) { - ProtocalLinker = new ModbusRtuProtocalLinker(com); + ProtocalLinker = new ModbusRtuProtocalLinker(com, slaveAddress); } } } \ No newline at end of file diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocalLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocalLinker.cs index a5a089d..0d91f6b 100644 --- a/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocalLinker.cs +++ b/Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocalLinker.cs @@ -7,7 +7,7 @@ namespace Modbus.Net.Modbus /// public class ModbusRtuProtocalLinker : ComProtocalLinker { - public ModbusRtuProtocalLinker(string com) : base(com, 9600, Parity.None, StopBits.One, 8) + public ModbusRtuProtocalLinker(string com, int slaveAddress) : base(com, 9600, Parity.None, StopBits.One, 8, slaveAddress) { } diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocalLinker.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocalLinker.cs index f49667c..87a8a22 100644 --- a/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocalLinker.cs +++ b/Modbus.Net/Modbus.Net.Modbus/ModbusTcpProtocalLinker.cs @@ -7,7 +7,7 @@ namespace Modbus.Net.Modbus /// public class ModbusTcpProtocalLinker : TcpProtocalLinker { - public ModbusTcpProtocalLinker(string ip) : base(ip, int.Parse(ConfigurationManager.AppSettings["ModbusPort"])) + public ModbusTcpProtocalLinker(string ip) : base(ip, int.Parse(ConfigurationManager.AppSettings["ModbusPort"] ?? "502")) { } diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocal.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocal.cs index 1585154..d91aa63 100644 --- a/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocal.cs +++ b/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocal.cs @@ -47,7 +47,7 @@ namespace Modbus.Net.Siemens public override async Task ConnectAsync() { - ProtocalLinker = new SiemensPpiProtocalLinker(_com); + ProtocalLinker = new SiemensPpiProtocalLinker(_com, SlaveAddress); var inputStruct = new ComCreateReferenceSiemensInputStruct(SlaveAddress, MasterAddress); var outputStruct = await await diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocalLinker.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocalLinker.cs index 0ddb7ad..b8d93f1 100644 --- a/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocalLinker.cs +++ b/Modbus.Net/Modbus.Net.Siemens/SiemensPpiProtocalLinker.cs @@ -9,8 +9,8 @@ namespace Modbus.Net.Siemens /// public class SiemensPpiProtocalLinker : ComProtocalLinker { - public SiemensPpiProtocalLinker(string com) - : base(com, 9600, Parity.Even, StopBits.One, 8) + public SiemensPpiProtocalLinker(string com, int slaveAddress) + : base(com, 9600, Parity.Even, StopBits.One, 8, slaveAddress) { } diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocal.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocal.cs index cc44acd..3150fa6 100644 --- a/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocal.cs +++ b/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocal.cs @@ -24,7 +24,7 @@ namespace Modbus.Net.Siemens } public SiemensTcpProtocal(byte tdpuSize, ushort tsapSrc, ushort tsapDst, ushort maxCalling, ushort maxCalled, - ushort maxPdu, string ip) : this(tdpuSize, tsapSrc, tsapDst, maxCalling, maxCalled, maxPdu, ip, 0) + ushort maxPdu, string ip) : this(tdpuSize, tsapSrc, tsapDst, maxCalling, maxCalled, maxPdu, ip, int.Parse(ConfigurationManager.AppSettings["SiemensPort"])) { } @@ -85,7 +85,7 @@ namespace Modbus.Net.Siemens public override async Task ConnectAsync() { _connectTryCount++; - ProtocalLinker = _port == 0 ? new SiemensTcpProtocalLinker(_ip) : new SiemensTcpProtocalLinker(_ip, _port); + ProtocalLinker = new SiemensTcpProtocalLinker(_ip, _port); if (!await ProtocalLinker.ConnectAsync()) return false; _connectTryCount = 0; var inputStruct = new CreateReferenceSiemensInputStruct(_tdpuSize, _taspSrc, _tsapDst); diff --git a/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocalLinker.cs b/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocalLinker.cs index 23f4b62..ffbeabf 100644 --- a/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocalLinker.cs +++ b/Modbus.Net/Modbus.Net.Siemens/SiemensTcpProtocalLinker.cs @@ -9,7 +9,7 @@ namespace Modbus.Net.Siemens public class SiemensTcpProtocalLinker : TcpProtocalLinker { public SiemensTcpProtocalLinker(string ip) - : base(ip, int.Parse(ConfigurationManager.AppSettings["SiemensPort"])) + : this(ip, int.Parse(ConfigurationManager.AppSettings["SiemensPort"] ?? "102")) { } diff --git a/Modbus.Net/Modbus.Net/ComConnector.cs b/Modbus.Net/Modbus.Net/ComConnector.cs index f0efd88..257289f 100644 --- a/Modbus.Net/Modbus.Net/ComConnector.cs +++ b/Modbus.Net/Modbus.Net/ComConnector.cs @@ -1,12 +1,19 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.IO.Ports; +using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Modbus.Net { + public class SerialPortLock : SerialPort + { + public object Lock { get; set; } = new object(); + } + /// /// 串口通讯类 /// @@ -14,6 +21,9 @@ namespace Modbus.Net { public delegate byte[] GetDate(byte[] bts); + private static Dictionary Connectors { get; } = new Dictionary(); + private static Dictionary Linkers { get; } = new Dictionary(); + private readonly int _baudRate; //private GetDate mygetDate; @@ -22,19 +32,19 @@ namespace Modbus.Net private readonly Parity _parity; private readonly StopBits _stopBits; private readonly int _timeoutTime; + private readonly string _slave; - private SerialPort _serialPort1; - - private bool m_disposed = true; + private bool m_disposed = false; public ComConnector(string com, int baudRate, Parity parity, StopBits stopBits, int dataBits, int timeoutTime) { - _com = com; + _com = com.Split(':')[0]; _timeoutTime = timeoutTime; _baudRate = baudRate; _parity = parity; _stopBits = stopBits; _dataBits = dataBits; + _slave = com.Split(':')[1]; //端口号 //读超时 @@ -42,27 +52,18 @@ namespace Modbus.Net //奇偶校验 //停止位 //数据位 + //从站号标识 } - public override string ConnectionToken => _com; + public override string ConnectionToken => _slave + ":" + _com; - private SerialPort SerialPort1 + private SerialPortLock SerialPort { get { - if (_serialPort1 == null) - { - _serialPort1 = new SerialPort - { - PortName = _com, - BaudRate = _baudRate, - Parity = _parity, - StopBits = _stopBits, - DataBits = _dataBits, - ReadTimeout = _timeoutTime - }; - } - return _serialPort1; + if (Connectors.ContainsKey(_com)) + return Connectors[_com]; + return null; } } @@ -71,7 +72,7 @@ namespace Modbus.Net /// public void Dispose() { - Dispose(true); + Dispose(true); //.NET Framework 类库 // GC..::.SuppressFinalize 方法 //请求系统不要调用指定对象的终结器。 @@ -93,17 +94,17 @@ namespace Modbus.Net Array.Clear(readBuf, 0, readBuf.Length); int nReadLen, nBytelen; - if (SerialPort1.IsOpen == false) + if (SerialPort.IsOpen == false) return -1; nBytelen = 0; - SerialPort1.ReadTimeout = HowTime; + SerialPort.ReadTimeout = HowTime; try { - while (SerialPort1.BytesToRead > 0) + while (SerialPort.BytesToRead > 0) { - readBuf[nBytelen] = (byte) SerialPort1.ReadByte(); + readBuf[nBytelen] = (byte) SerialPort.ReadByte(); var bTmp = new byte[bufRoom]; Array.Clear(bTmp, 0, bTmp.Length); @@ -139,16 +140,16 @@ namespace Modbus.Net sbyte nBytelen; //long nByteRead; - if (SerialPort1.IsOpen == false) + if (SerialPort.IsOpen == false) return 0; nBytelen = 0; - SerialPort1.ReadTimeout = ByteTime; + SerialPort.ReadTimeout = ByteTime; - while (nBytelen < ReadRoom - 1 && SerialPort1.BytesToRead > 0) + while (nBytelen < ReadRoom - 1 && SerialPort.BytesToRead > 0) { try { - ReadBuf[nBytelen] = (byte) SerialPort1.ReadByte(); + ReadBuf[nBytelen] = (byte) SerialPort.ReadByte(); nBytelen++; // add one } catch (Exception ex) @@ -251,18 +252,23 @@ namespace Modbus.Net // Release managed resources } // Release unmanaged resources - if (_serialPort1 != null) + if (SerialPort != null) { - try + if (Linkers.Values.Count(p => p == _com) <= 1) { - _serialPort1.Close(); + try + { + SerialPort.Close(); + } + catch (Exception) + { + //ignore + } + SerialPort.Dispose(); + Connectors[_com] = null; + Connectors.Remove(_com); } - catch (Exception) - { - //ignore - } - _serialPort1.Dispose(); - _serialPort1 = null; + Linkers.Remove(_slave); } m_disposed = true; } @@ -283,12 +289,11 @@ namespace Modbus.Net { get { - if (_serialPort1 != null && !SerialPort1.IsOpen) + if (SerialPort != null && !SerialPort.IsOpen) { - _serialPort1.Dispose(); - _serialPort1 = null; + SerialPort.Dispose(); } - return _serialPort1 != null && _serialPort1.IsOpen; + return SerialPort != null && SerialPort.IsOpen && Linkers.ContainsKey(_slave); } } @@ -296,7 +301,23 @@ namespace Modbus.Net { try { - SerialPort1.Open(); + if (!Connectors.ContainsKey(_com)) + { + Connectors.Add(_com, new SerialPortLock + { + PortName = _com, + BaudRate = _baudRate, + Parity = _parity, + StopBits = _stopBits, + DataBits = _dataBits, + ReadTimeout = _timeoutTime + }); + } + if (!Linkers.ContainsKey(_slave)) + { + Linkers.Add(_slave, _com); + } + SerialPort.Open(); return true; } catch (Exception e) @@ -312,7 +333,7 @@ namespace Modbus.Net public override bool Disconnect() { - if (SerialPort1 != null) + if (Linkers.ContainsKey(_slave) && Connectors.ContainsKey(_com)) { try { @@ -343,19 +364,22 @@ namespace Modbus.Net { try { - if (!SerialPort1.IsOpen) + if (!SerialPort.IsOpen) { try { - SerialPort1.Open(); + SerialPort.Open(); } catch (Exception) { Dispose(); - SerialPort1.Open(); + SerialPort.Open(); } } - SerialPort1.Write(sendbytes, 0, sendbytes.Length); + lock (SerialPort.Lock) + { + SerialPort.Write(sendbytes, 0, sendbytes.Length); + } return ReadMsg(); } catch @@ -374,7 +398,22 @@ namespace Modbus.Net { try { - SerialPort1.Write(sendbytes, 0, sendbytes.Length); + if (!SerialPort.IsOpen) + { + try + { + SerialPort.Open(); + } + catch (Exception) + { + Dispose(); + SerialPort.Open(); + } + } + lock (SerialPort.Lock) + { + SerialPort.Write(sendbytes, 0, sendbytes.Length); + } return true; } catch (Exception) @@ -397,9 +436,9 @@ namespace Modbus.Net { try { - if (!SerialPort1.IsOpen) + if (!SerialPort.IsOpen) { - SerialPort1.Open(); + SerialPort.Open(); } byte[] data; diff --git a/Modbus.Net/Modbus.Net/ComProtocalLinker.cs b/Modbus.Net/Modbus.Net/ComProtocalLinker.cs index 95e61a5..13d4461 100644 --- a/Modbus.Net/Modbus.Net/ComProtocalLinker.cs +++ b/Modbus.Net/Modbus.Net/ComProtocalLinker.cs @@ -15,8 +15,8 @@ namespace Modbus.Net /// 校验位 /// 停止位 /// 数据位 - protected ComProtocalLinker(int baudRate, Parity parity, StopBits stopBits, int dataBits) - : this(ConfigurationManager.AppSettings["COM"], baudRate, parity, stopBits, dataBits) + protected ComProtocalLinker(int baudRate, Parity parity, StopBits stopBits, int dataBits, int slaveAddress) + : this(ConfigurationManager.AppSettings["COM"], baudRate, parity, stopBits, dataBits, slaveAddress) { } @@ -28,10 +28,27 @@ namespace Modbus.Net /// 校验位 /// 停止位 /// 数据位 - protected ComProtocalLinker(string com, int baudRate, Parity parity, StopBits stopBits, int dataBits) + protected ComProtocalLinker(string com, int baudRate, Parity parity, StopBits stopBits, int dataBits, + int slaveAddress) + : this( + com, baudRate, parity, stopBits, dataBits, + int.Parse(ConfigurationManager.AppSettings["ComConnectionTimeout"] ?? "3000"), slaveAddress) { - //初始化连对象 - BaseConnector = new ComConnector(com, baudRate, parity, stopBits, dataBits, int.Parse(ConfigurationManager.AppSettings["ComConnectionTimeout"])); + } + + /// + /// 构造器 + /// + /// 串口端口号 + /// 波特率 + /// 校验位 + /// 停止位 + /// 数据位 + /// 超时时间 + protected ComProtocalLinker(string com, int baudRate, Parity parity, StopBits stopBits, int dataBits, + int connectionTimeout, int slaveAddress) + { + BaseConnector = new ComConnector(com + ":" + slaveAddress, baudRate, parity, stopBits, dataBits, connectionTimeout); } } } \ No newline at end of file diff --git a/Modbus.Net/src/Base.Common/TcpProtocalLinker.cs b/Modbus.Net/src/Base.Common/TcpProtocalLinker.cs index 92cb4b0..b353cd8 100644 --- a/Modbus.Net/src/Base.Common/TcpProtocalLinker.cs +++ b/Modbus.Net/src/Base.Common/TcpProtocalLinker.cs @@ -12,7 +12,8 @@ namespace Modbus.Net /// /// 构造器 /// - protected TcpProtocalLinker() : this(ConfigurationManager.AppSettings["IP"], int.Parse(ConfigurationManager.AppSettings["ModbusPort"])) + protected TcpProtocalLinker(int port) + : this(ConfigurationManager.AppSettings["IP"], port) { } @@ -22,9 +23,20 @@ namespace Modbus.Net /// Ip地址 /// 端口 protected TcpProtocalLinker(string ip, int port) + : this(ip, port, int.Parse(ConfigurationManager.AppSettings["IPConnectionTimeout"] ?? "5000")) + { + } + + /// + /// 构造器 + /// + /// Ip地址 + /// 端口 + /// 超时时间 + protected TcpProtocalLinker(string ip, int port, int connectionTimeout) { //初始化连接对象 - BaseConnector = new TcpConnector(ip, port, int.Parse(ConfigurationManager.AppSettings["IPConnectionTimeout"])); + BaseConnector = new TcpConnector(ip, port, connectionTimeout); } } } \ No newline at end of file diff --git a/Tests/Modbus.Net.Tests/Modbus.Net.Tests.csproj b/Tests/Modbus.Net.Tests/Modbus.Net.Tests.csproj index aaf4bb9..a3c5572 100644 --- a/Tests/Modbus.Net.Tests/Modbus.Net.Tests.csproj +++ b/Tests/Modbus.Net.Tests/Modbus.Net.Tests.csproj @@ -58,6 +58,7 @@ + diff --git a/Tests/Modbus.Net.Tests/ModbusMultiStationTest.cs b/Tests/Modbus.Net.Tests/ModbusMultiStationTest.cs new file mode 100644 index 0000000..f86fac3 --- /dev/null +++ b/Tests/Modbus.Net.Tests/ModbusMultiStationTest.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Modbus.Net.Modbus; + +namespace Modbus.Net.Tests +{ + [TestClass] + public class ModbusMultiStationTest + { + private BaseMachine _modbusRtuMachine1; + + private BaseMachine _modbusRtuMachine2; + + [TestInitialize] + public void Init() + { + _modbusRtuMachine1 = new ModbusMachine(ModbusType.Rtu, "COM1", null, true, 1, 0); + _modbusRtuMachine2 = new ModbusMachine(ModbusType.Rtu, "COM1", null, true, 2, 0); + } + + [TestMethod] + public async Task Get() + { + var addresses = new List + { + new AddressUnit + { + Id = "0", + Area = "4X", + Address = 2, + SubAddress = 0, + CommunicationTag = "A1", + DataType = typeof(ushort) + }, + new AddressUnit + { + Id = "1", + Area = "4X", + Address = 3, + SubAddress = 0, + CommunicationTag = "A2", + DataType = typeof(ushort) + }, + new AddressUnit + { + Id = "2", + Area = "4X", + Address = 4, + SubAddress = 0, + CommunicationTag = "A3", + DataType = typeof(ushort) + }, + new AddressUnit + { + Id = "3", + Area = "4X", + Address = 5, + SubAddress = 0, + CommunicationTag = "A4", + DataType = typeof(ushort) + }, + new AddressUnit + { + Id = "4", + Area = "4X", + Address = 6, + SubAddress = 0, + CommunicationTag = "A5", + DataType = typeof(uint) + }, + new AddressUnit + { + Id = "5", + Area = "4X", + Address = 8, + SubAddress = 0, + CommunicationTag = "A6", + DataType = typeof(uint) + } + }; + + _modbusRtuMachine1.GetAddresses = addresses.ToList(); + _modbusRtuMachine2.GetAddresses = addresses.ToList(); + + await _modbusRtuMachine1.SetDatasAsync(MachineSetDataType.CommunicationTag, new Dictionary() + { + { + "A1", 70 + }, + { + "A2", 71 + }, + { + "A3", 72 + }, + { + "A4", 73 + }, + { + "A5", 717870 + }, + { + "A6", 717871 + }, + }); + await _modbusRtuMachine2.SetDatasAsync(MachineSetDataType.CommunicationTag, new Dictionary() + { + { + "A1", 74 + }, + { + "A2", 75 + }, + { + "A3", 76 + }, + { + "A4", 77 + }, + { + "A5", 717873 + }, + { + "A6", 717874 + }, + }); + var ans = await _modbusRtuMachine1.GetDatasAsync(MachineGetDataType.CommunicationTag); + var ans2 = await _modbusRtuMachine2.GetDatasAsync(MachineGetDataType.CommunicationTag); + + _modbusRtuMachine1.Disconnect(); + _modbusRtuMachine2.Disconnect(); + + Assert.AreEqual(ans["A1"].PlcValue, 70); + Assert.AreEqual(ans2["A1"].PlcValue, 74); + } + } +}