diff --git a/.gitignore b/.gitignore index 7e0ecf5..5424663 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ Desktop.ini !*.dll !*.bak /.vs +/SCADA/Program/packages/Newtonsoft.Json.11.0.2 diff --git a/SCADA/Program/.vs/DataExchange/v15/.suo b/SCADA/Program/.vs/DataExchange/v15/.suo index f095429..3886163 100644 Binary files a/SCADA/Program/.vs/DataExchange/v15/.suo and b/SCADA/Program/.vs/DataExchange/v15/.suo differ diff --git a/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide b/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide index f50e9df..a4b47f4 100644 Binary files a/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide and b/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide differ diff --git a/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide-shm b/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide-shm index cc5de54..afd6d05 100644 Binary files a/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide-shm and b/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide-shm differ diff --git a/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide-wal b/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide-wal index 541cc16..f14725d 100644 Binary files a/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide-wal and b/SCADA/Program/.vs/DataExchange/v15/Server/sqlite3/storage.ide-wal differ diff --git a/SCADA/Program/DataExchange.sln b/SCADA/Program/DataExchange.sln index 48ca860..27a960b 100644 --- a/SCADA/Program/DataExchange.sln +++ b/SCADA/Program/DataExchange.sln @@ -39,6 +39,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ABPLCDriver", "ABPLCReader\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DDEDriver", "DDEDriver\DDEDriver.csproj", "{14938033-7870-477D-925C-A447933A44E7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MelsecDriver", "MelsecDriver\MelsecDriver.csproj", "{AE7D9641-3F7D-4282-B752-2FA14619CFBF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HslCommunication_Net45", "..\..\..\HslCommunication\HslCommunication_Net45\HslCommunication_Net45.csproj", "{7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -359,6 +363,46 @@ Global {14938033-7870-477D-925C-A447933A44E7}.Release|x64.ActiveCfg = Release|x86 {14938033-7870-477D-925C-A447933A44E7}.Release|x86.ActiveCfg = Release|x86 {14938033-7870-477D-925C-A447933A44E7}.Release|x86.Build.0 = Release|x86 + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Debug|Itanium.ActiveCfg = Debug|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Debug|Itanium.Build.0 = Debug|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Debug|x64.ActiveCfg = Debug|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Debug|x64.Build.0 = Debug|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Debug|x86.ActiveCfg = Debug|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Debug|x86.Build.0 = Debug|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Release|Any CPU.Build.0 = Release|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Release|Itanium.ActiveCfg = Release|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Release|Itanium.Build.0 = Release|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Release|x64.ActiveCfg = Release|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Release|x64.Build.0 = Release|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Release|x86.ActiveCfg = Release|Any CPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF}.Release|x86.Build.0 = Release|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Debug|Itanium.ActiveCfg = Debug|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Debug|Itanium.Build.0 = Debug|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Debug|x64.ActiveCfg = Debug|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Debug|x64.Build.0 = Debug|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Debug|x86.ActiveCfg = Debug|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Debug|x86.Build.0 = Debug|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Release|Any CPU.Build.0 = Release|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Release|Itanium.ActiveCfg = Release|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Release|Itanium.Build.0 = Release|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Release|x64.ActiveCfg = Release|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Release|x64.Build.0 = Release|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Release|x86.ActiveCfg = Release|Any CPU + {7F8029DA-1B94-4B1D-B91F-5B21DBF2F3A2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SCADA/Program/DataHelper/MssqlFactory.cs b/SCADA/Program/DataHelper/MssqlFactory.cs index 45d5c1d..6541c70 100644 --- a/SCADA/Program/DataHelper/MssqlFactory.cs +++ b/SCADA/Program/DataHelper/MssqlFactory.cs @@ -12,6 +12,11 @@ namespace DatabaseLib DataHelper.AddErrorLog(new Exception(message)); } + public void CallException(Exception e) + { + DataHelper.AddErrorLog(e); + } + public bool ConnectionTest() { //创建连接对象 @@ -30,7 +35,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(e.Message); + CallException(e); } } //mySqlConnection is a SqlConnection object @@ -77,7 +82,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(SQL + " " + e.Message); + CallException(new Exception(SQL + " " + e.Message, e)); } } return ds; @@ -104,7 +109,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(SQL + " " + e.Message); + CallException(new Exception(SQL + " " + e.Message, e)); } } return ds; @@ -135,7 +140,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(SQLs + " " + e.Message); + CallException(new Exception(SQLs + " " + e.Message, e)); } } return ds; @@ -162,7 +167,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(SQL + " " + e.Message); + CallException(new Exception(SQL + " " + e.Message, e)); } } return dt; @@ -198,7 +203,7 @@ namespace DatabaseLib { if (sqlT != null) sqlT.Rollback(); - CallException(SQL + " " + e.Message); + CallException(new Exception(SQL + " " + e.Message, e)); return -1; } return res; @@ -235,7 +240,7 @@ namespace DatabaseLib { if (sqlT != null) sqlT.Rollback(); - CallException(SQLs + " " + e.Message); + CallException(new Exception(SQLs + " " + e.Message, e)); res = -1; } return res; @@ -278,7 +283,7 @@ namespace DatabaseLib { if (sqlT != null) sqlT.Rollback(); - CallException(SQLs + " " + e.Message); + CallException(new Exception(SQLs + " " + e.Message, e)); res = -1; } return res; @@ -308,7 +313,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(SQL + " " + e.Message); + CallException(new Exception(SQL + " " + e.Message, e)); } } @@ -345,7 +350,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(sSQL + " " + e.Message); + CallException(new Exception(sSQL + " " + e.Message, e)); return null; } } @@ -379,7 +384,7 @@ namespace DatabaseLib { if (sqlT != null) sqlT.Rollback(); - CallException(sSQL + " " + e.Message); + CallException(new Exception(sSQL + " " + e.Message, e)); return null; } } @@ -423,7 +428,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(ProName + " " + e.Message); + CallException(new Exception(ProName + " " + e.Message, e)); return -1; } } @@ -450,7 +455,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(ProName + " " + e.Message); + CallException(new Exception(ProName + " " + e.Message, e)); return false; } } @@ -489,7 +494,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(ProName + " " + e.Message); + CallException(new Exception(ProName + " " + e.Message, e)); return null; } } @@ -518,7 +523,7 @@ namespace DatabaseLib catch (Exception e) { - CallException(ProName + " " + e.Message); + CallException(new Exception(ProName + " " + e.Message, e)); return null; } } @@ -555,7 +560,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(ProName + " " + e.Message); + CallException(new Exception(ProName + " " + e.Message, e)); return null; } } @@ -586,7 +591,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(ProName + " " + e.Message); + CallException(new Exception(ProName + " " + e.Message, e)); return null; } } @@ -624,7 +629,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(ProName + " " + e.Message); + CallException(new Exception(ProName + " " + e.Message, e)); return null; } } @@ -662,7 +667,7 @@ namespace DatabaseLib } catch (Exception e) { - CallException(ProName + " " + e.Message); + CallException(new Exception(ProName + " " + e.Message, e)); return null; } } @@ -701,7 +706,44 @@ namespace DatabaseLib if (sqlT != null) sqlT.Rollback(); m_Conn.Close(); - CallException(e.Message); + CallException(e); + return false; + } + } + } + + public bool BulkCopy(DataTable reader, string tableName, string command = null, SqlBulkCopyOptions options = SqlBulkCopyOptions.Default) + { + using (SqlConnection m_Conn = new SqlConnection(DataHelper.ConnectString)) + { + SqlTransaction sqlT = null; + try + { + if (m_Conn.State == ConnectionState.Closed) + m_Conn.Open(); + sqlT = m_Conn.BeginTransaction(); + if (!string.IsNullOrEmpty(command)) + { + SqlCommand cmd = new SqlCommand(command, m_Conn); + cmd.Transaction = sqlT; + cmd.ExecuteNonQuery(); + } + SqlBulkCopy copy = new SqlBulkCopy(m_Conn, options, sqlT); + copy.DestinationTableName = tableName; + copy.BulkCopyTimeout = 100000; + //copy.BatchSize = _capacity; + copy.WriteToServer(reader);//如果写入失败,考虑不能无限增加线程数 + //Clear(); + sqlT.Commit(); + m_Conn.Close(); + return true; + } + catch (Exception e) + { + if (sqlT != null) + sqlT.Rollback(); + m_Conn.Close(); + CallException(e); return false; } } diff --git a/SCADA/Program/MelsecDriver/FxSerialReader.cs b/SCADA/Program/MelsecDriver/FxSerialReader.cs new file mode 100644 index 0000000..2daf132 --- /dev/null +++ b/SCADA/Program/MelsecDriver/FxSerialReader.cs @@ -0,0 +1,1405 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO.Ports; +using System.Linq; +using System.Net; +using System.Text; +using System.Timers; +using DataService; +using HslCommunication.Profinet.Melsec; + +namespace MelsecDriver +{ + [Description("三菱Fx系列串口")] + //ModbusRTUReader : IPLCDriver IPLCDriver : IDriver, IReaderWriter IDriver : IDisposable + public sealed class FxSerialReader : IPLCDriver + { + #region : IDisposable + + public void Dispose() + { + foreach (IGroup grp in _grps) + { + grp.Dispose(); + } + + _grps.Clear(); + _fxSerial.Close(); + } + + #endregion + + //从站地址 + short _id; + + public short ID + { + get { return _id; } + } + + string _name; + + public string Name + { + get { return _name; } + } + + private string _serverName = "unknown"; + + public string ServerName + { + get { return _serverName; } + set { _serverName = value; } + } + + public bool IsClosed + { + get { return _fxSerial == null || _fxSerial.IsOpen == false; } + } + + IDataServer _server; + + public IDataServer Parent + { + get { return _server; } + } + + + private string _port = "COM1"; + + [Category("串口设置"), Description("串口号")] + public string PortName + { + get { return _port; } + set { _port = value; } + } + + private int _timeOut = 3000; + + [Category("串口设置"), Description("通迅超时时间")] + public int TimeOut + { + get { return _timeOut; } + set { _timeOut = value; } + } + + private int _baudRate = 9600; + + [Category("串口设置"), Description("波特率")] + public int BaudRate + { + get { return _baudRate; } + set { _baudRate = value; } + } + + private int _dataBits = 8; + + [Category("串口设置"), Description("数据位")] + public int DataBits + { + get { return _dataBits; } + set { _dataBits = value; } + } + + private StopBits _stopBits = StopBits.One; + + [Category("串口设置"), Description("停止位")] + public StopBits StopBits + { + get { return _stopBits; } + set { _stopBits = value; } + } + + private Parity _parity = Parity.None; + + [Category("串口设置"), Description("奇偶校验")] + public Parity Parity + { + get { return _parity; } + set { _parity = value; } + } + + private MelsecFxSerial _fxSerial; + + public bool Connect() + { + _fxSerial = new MelsecFxSerial(); + try + { + _fxSerial.SerialPortInni(sp => + { + sp.PortName = _port; + sp.BaudRate = _baudRate; + sp.DataBits = _dataBits; + sp.StopBits = _stopBits; + sp.Parity = _parity; + }); + _fxSerial.Open(); + + return true; + } + catch (Exception ex) + { + if (OnError != null) + OnError(this, new IOErrorEventArgs(ex.Message)); + return false; + } + } + + List _grps = new List(); + + public event IOErrorEventHandler OnError; + + public IEnumerable Groups + { + get { return _grps; } + } + + public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0, bool active = false) + { + FxShortGroup grp = new FxShortGroup(id, name, updateRate, active, this); + _grps.Add(grp); + return grp; + } + + public bool RemoveGroup(IGroup grp) + { + grp.IsActive = false; + return _grps.Remove(grp); + } + + //自定义构造函数3 + public FxSerialReader(IDataServer server, short id, string name) + { + _id = id; + _name = name; + _server = server; + } + + object _async = new object(); + + + public byte[] ReadBytes(DeviceAddress address, ushort size) + { + string addr; + addr = GetAddress(address); + try + { + lock (_async) + { + if (address.DBNumber < 3) + { + var read = _fxSerial.ReadBool(addr, (ushort) (size * 16)); + if (read.IsSuccess) + { + byte[] retBytes = new byte[size * 2]; + Buffer.BlockCopy(read.Content,0,retBytes,0,size * 2); + return retBytes; + } + else + { + if (OnError != null) + OnError(this, new IOErrorEventArgs(read.Message)); + return null; + } + } + else + { + var read = _fxSerial.Read(addr, size); + if (read.IsSuccess) + { + return read.Content; + } + else + { + if (OnError != null) + OnError(this, new IOErrorEventArgs(read.Message)); + return null; + } + } + + } + } + catch (Exception e) + { + if (OnError != null) + OnError(this, new IOErrorEventArgs(e.Message)); + return null; + } + } + + public ItemData ReadUInt32(DeviceAddress address) + { + byte[] bit = ReadBytes(address, 2); + return bit == null ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) : + new ItemData(BitConverter.ToUInt32(bit, 0), 0, QUALITIES.QUALITY_GOOD); + } + + public ItemData ReadInt32(DeviceAddress address) + { + byte[] bit = ReadBytes(address, 2); + return bit == null ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) : + new ItemData(BitConverter.ToInt32(bit, 0), 0, QUALITIES.QUALITY_GOOD); + } + + public ItemData ReadUInt16(DeviceAddress address) + { + byte[] bit = ReadBytes(address, 1); + return bit == null ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) : + new ItemData(BitConverter.ToUInt16(bit, 0), 0, QUALITIES.QUALITY_GOOD); + } + + public ItemData ReadInt16(DeviceAddress address) + { + byte[] bit = ReadBytes(address, 1); + return bit == null ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) : + new ItemData(BitConverter.ToInt16(bit, 0), 0, QUALITIES.QUALITY_GOOD); + } + + public ItemData ReadByte(DeviceAddress address) + { + byte[] bit = ReadBytes(address, 1); + return bit == null ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) : + new ItemData(bit[0], 0, QUALITIES.QUALITY_GOOD); + } + + public ItemData ReadString(DeviceAddress address, ushort size) + { + byte[] bit = ReadBytes(address, size); + return bit == null ? new ItemData(string.Empty, 0, QUALITIES.QUALITY_BAD) : + new ItemData(Encoding.ASCII.GetString(bit), 0, QUALITIES.QUALITY_GOOD); + } + + public ItemData ReadFloat(DeviceAddress address) + { + byte[] bit = ReadBytes(address, 2); + return bit == null ? new ItemData(0f, 0, QUALITIES.QUALITY_BAD) : + new ItemData(BitConverter.ToSingle(bit, 0), 0, QUALITIES.QUALITY_GOOD); + } + + public ItemData ReadBit(DeviceAddress address) + { + byte[] bit = ReadBytes(address, 1); + return bit == null ? new ItemData(false, 0, QUALITIES.QUALITY_BAD) : + new ItemData((bit[0] & (1 << (address.Bit))) > 0, 0, QUALITIES.QUALITY_GOOD); + } + + public ItemData ReadValue(DeviceAddress address) + { + return this.ReadValueEx(address); + } + + public int WriteBytes(DeviceAddress address, byte[] bit) + { + return Writes(address, bit); + } + + public int WriteBit(DeviceAddress address, bool bit) + { + try + { + lock (_async) + { + + string addr = GetAddress(address); + var ret = _fxSerial.Write(addr, bit); + if (ret.IsSuccess) + { + return 0; + } + else + { + if (OnError != null) + { + OnError(this, new IOErrorEventArgs(ret.Message)); + } + + return -1; + } + } + } + catch (Exception e) + { + if (OnError != null) + { + OnError(this, new IOErrorEventArgs(e.Message)); + } + + return -1; + } + + } + + public int WriteBits(DeviceAddress address, byte bits) + { + return Writes(address, new byte[] {bits}); + } + + public int WriteInt16(DeviceAddress address, short value) + { + return Writes(address, BitConverter.GetBytes(value)); + } + + public int WriteUInt16(DeviceAddress address, ushort value) + { + return Writes(address, BitConverter.GetBytes(value)); + } + + public int WriteInt32(DeviceAddress address, int value) + { + return Writes(address, BitConverter.GetBytes(value)); + } + + public int WriteUInt32(DeviceAddress address, uint value) + { + return Writes(address, BitConverter.GetBytes(value)); + } + + public int WriteFloat(DeviceAddress address, float value) + { + return Writes(address, BitConverter.GetBytes(value)); + } + + public int WriteString(DeviceAddress address, string str) + { + return Writes(address, Encoding.ASCII.GetBytes(str)); + } + + private int Writes(DeviceAddress address, byte[] bytes) + { + try + { + lock (_async) + { + string addr = GetAddress(address); + var ret = _fxSerial.Write(addr, bytes); + if (ret.IsSuccess) + { + return 0; + } + else + { + if (OnError != null) + { + OnError(this, new IOErrorEventArgs(ret.Message)); + } + return -1; + } + } + } + catch (Exception e) + { + if (OnError != null) + { + OnError(this, new IOErrorEventArgs(e.Message)); + } + return -1; + } + + } + + + public int WriteValue(DeviceAddress address, object value) + { + return this.WriteValueEx(address, value); + } + + public int PDU { get; } + + //将三菱PLC的地址解析为modbus地址格式,参考bcnet的地址解析方式 + //T/C暂存器分为bool类型和word类型,采用地址的第二码区分,第二码A表示bool,第二码为B表示word, 默认则为读取word值 + private string ParseAddress(TagMetaData meta) + { + string address = meta.Address; + int m, n; + string newaddress = ""; + switch (address[0]) + { + case 'Y': + case 'y': + int.TryParse(address.Substring(1, address.Length - 2), out m); + int.TryParse(address.Substring(address.Length - 1), out n); + newaddress = '0' + (m * 8 + n).ToString().PadLeft(5, '0'); + break; + case 'M': + case 'm': + int.TryParse(address.Substring(1, address.Length - 1), out m); + if (m >= 8000) + { + newaddress = '0' + (5001 + (m - 8000)).ToString().PadLeft(5, '0'); + } + else + { + newaddress = '0' + (10001 + m).ToString().PadLeft(5, '0'); + } + break; + case 'T': + case 't': + if (meta.DataType == DataType.BOOL) + { + int.TryParse(address.Substring(1, address.Length - 1), out m); + newaddress = '0' + (6001 + m).ToString().PadLeft(5, '0'); + } + else + { + int.TryParse(address.Substring(1, address.Length - 1), out m); + newaddress = '4' + (1 + m).ToString().PadLeft(5, '0'); + } + break; + case 'C': + case 'c': + if (meta.DataType == DataType.BOOL) + { + int.TryParse(address.Substring(1, address.Length - 1), out m); + newaddress = '0' + (7001 + m).ToString().PadLeft(5, '0'); + } + else + { + int.TryParse(address.Substring(1, address.Length - 1), out m); + newaddress = '4' + (1001 + m).ToString().PadLeft(5, '0'); + } + break; + case 'S': + case 's': + int.TryParse(address.Substring(1, address.Length - 1), out m); + newaddress = '0' + (30001 + m).ToString().PadLeft(5, '0'); + break; + case 'X': + case 'x': + int.TryParse(address.Substring(1, address.Length - 2), out m); + int.TryParse(address.Substring(address.Length - 1), out n); + newaddress = '1' + (1 + m * 8 + n).ToString().PadLeft(5, '0'); + break; + case 'D': + case 'd': + int.TryParse(address.Substring(1, address.Length - 1), out m); + if (m >= 8000) + { + newaddress = '4' + (1301 + m - 8000).ToString().PadLeft(5, '0'); + } + else + { + newaddress = '4' + (2001 + m).ToString().PadLeft(5, '0'); + } + break; + + } + return newaddress; + } + + + public DeviceAddress GetDeviceAddress(string address) + { + DeviceAddress dv = DeviceAddress.Empty; + + if (string.IsNullOrEmpty(address)) + return dv; + + if (string.IsNullOrEmpty(address)) + return dv; + + switch (address[0]) + { + case '0': + { + dv.DBNumber = Fx.fctReadCoil; + int st; + int.TryParse(address, out st); + st--; + dv.Bit = (byte)(st % 16); + st /= 16; + dv.Start = st; + //dv.Bit--; + } + break; + case '1': + { + dv.DBNumber = Fx.fctReadDiscreteInputs; + int st; + int.TryParse(address.Substring(1), out st); + st--; + dv.Bit = (byte)(st % 16); + st /= 16; + dv.Start = st; + //dv.Bit--; + } + break; + case '4': + { + int index = address.IndexOf('.'); + dv.DBNumber = Fx.fctReadHoldingRegister; + if (index > 0) + { + dv.Start = int.Parse(address.Substring(1, index - 1)); + dv.Bit = byte.Parse(address.Substring(index + 1)); + } + else + dv.Start = int.Parse(address.Substring(1)); + dv.Start--; + dv.Bit--; + dv.ByteOrder = ByteOrder.BigEndian; + } + break; + case '3': + + break; + } + return dv; + } + + public DeviceAddress GetDeviceAddress(TagMetaData meta) + { + string newaddress = ParseAddress(meta); + return GetDeviceAddress(newaddress); + } + + public string GetAddress(DeviceAddress dv) + { + string address = "", newaddress = ""; + int addr; + switch (dv.DBNumber) + { + case Fx.fctReadCoil: + address = '0' + (dv.Start * 16 + dv.Bit + 1).ToString().PadLeft(5, '0'); + + int.TryParse(address, out addr); + if (addr >= 30001) + { + newaddress = "S" + (addr - 30001).ToString(); + } + else if (addr >= 10001) + { + newaddress = "M" + (addr - 10001).ToString(); + } + else if (addr >= 7001) + { + newaddress = "C" + (addr - 7001).ToString(); + } + else if (addr >= 6001) + { + newaddress = "T" + (addr - 6001).ToString(); + } + else if (addr >= 5001) + { + newaddress = "M8" + (addr - 5001).ToString().PadLeft(3, '0'); + } + else + { + newaddress = "Y" + Convert.ToString(addr - 1, 8); + } + break; + case Fx.fctReadDiscreteInputs: + address = "1" + (dv.Start * 16 + dv.Bit + 1).ToString().PadLeft(5, '0'); + int.TryParse(address, out addr); + newaddress = "X" + Convert.ToString(addr - 100001, 8); + break; + case Fx.fctReadHoldingRegister: + address = "4" + (dv.Start * 16 + 1).ToString().PadLeft(5, '0'); + int.TryParse(address, out addr); + if (addr >= 420001) + { + newaddress = "R" + (addr - 420001).ToString(); + } + else if (addr >= 402001) + { + newaddress = "D" + (addr - 402001).ToString(); + } + else if (addr >= 401301) + { + newaddress = "D8" + (addr - 401301).ToString().PadLeft(3, '0'); + } + else if (addr >= 401001) + { + newaddress = "C" + (addr - 401001).ToString(); + } + else if (addr >= 400001) + { + newaddress = "T" + (addr - 400001).ToString(); + } + break; + case Fx.fctReadInputRegister: + + break; + } + return newaddress; + } + } + + public sealed class Fx + { + public const byte fctReadCoil = 1; + public const byte fctReadDiscreteInputs = 2; + public const byte fctReadHoldingRegister = 3; + public const byte fctReadInputRegister = 4; + public const byte fctWriteSingleCoil = 5; + public const byte fctWriteSingleRegister = 6; + public const byte fctWriteMultipleCoils = 15; + public const byte fctWriteMultipleRegister = 16; + public const byte fctReadWriteMultipleRegister = 23; + } + + + public class FxShortGroup : IGroup + { + protected Timer _timer; + + protected bool _isActive; + public virtual bool IsActive + { + get + { + return _isActive; + } + set + { + _isActive = value; + if (value) + { + if (_updateRate <= 0) _updateRate = 100; + _timer.Interval = _updateRate; + _timer.Elapsed += new ElapsedEventHandler(timer_Timer); + _timer.Start(); + } + else + { + _timer.Elapsed -= new ElapsedEventHandler(timer_Timer); + _timer.Stop(); + } + } + } + + protected short _id; + public short ID + { + get + { + return _id; + } + } + + protected int _updateRate; + public int UpdateRate + { + get + { + return _updateRate; + } + set + { + _updateRate = value; + } + } + + protected string _name; + public string Name + { + get + { + return _name; + } + set + { + _name = value; + } + } + + protected float _deadband; + public float DeadBand + { + get + { + return _deadband; + } + set + { + _deadband = value; + } + } + + + protected ICache _cacheReader; + + protected IPLCDriver _plcReader; + public IDriver Parent + { + get + { + return _plcReader; + } + } + + protected List _changedList; + public List ChangedList + { + get + { + return _changedList; + } + } + + + protected List _items; + public IEnumerable Items + { + get { return _items; } + } + + protected IDataServer _server; + public IDataServer Server + { + get + { + return _server; + } + } + + protected List _rangeList = new List(); + + protected FxShortGroup() + { + } + + public FxShortGroup(short id, string name, int updateRate, bool active, IPLCDriver plcReader) + { + this._id = id; + this._name = name; + this._updateRate = updateRate; + this._isActive = active; + this._plcReader = plcReader; + this._server = _plcReader.Parent; + this._timer = new Timer(); + this._changedList = new List(); + this._cacheReader = new ShortCacheReader(); + } + + public bool AddItems(IList items) + { + FxSerialReader fxReader = _plcReader as FxSerialReader; + if (fxReader == null) return false; + + + int count = items.Count; + if (_items == null) _items = new List(count); + lock (_server.SyncRoot) + { + for (int i = 0; i < count; i++) + { + ITag dataItem = null; + TagMetaData meta = items[i]; + if (meta.GroupID == this._id) + { + DeviceAddress addr = fxReader.GetDeviceAddress(meta); + if (addr.DataSize == 0) addr.DataSize = meta.Size; + if (addr.VarType == DataType.NONE) addr.VarType = meta.DataType; + if (addr.VarType != DataType.BOOL) addr.Bit = 0; + switch (meta.DataType) + { + case DataType.BOOL: + dataItem = new BoolTag(meta.ID, addr, this); + break; + case DataType.BYTE: + dataItem = new ByteTag(meta.ID, addr, this); + break; + case DataType.WORD: + dataItem = new UShortTag(meta.ID, addr, this); + break; + case DataType.SHORT: + dataItem = new ShortTag(meta.ID, addr, this); + break; + case DataType.DWORD: + dataItem = new UIntTag(meta.ID, addr, this); + break; + case DataType.INT: + dataItem = new IntTag(meta.ID, addr, this); + break; + case DataType.FLOAT: + dataItem = new FloatTag(meta.ID, addr, this); + break; + case DataType.STR: + dataItem = new StringTag(meta.ID, addr, this); + break; + } + if (dataItem != null) + { + //dataItem.Active = meta.Active; + _items.Add(dataItem); + _server.AddItemIndex(meta.Name, dataItem); + } + } + } + } + _items.TrimExcess(); + _items.Sort((x, y) => x.Address.CompareTo(y.Address)); + UpdatePDUArea(); + return true; + } + + public bool AddTags(IEnumerable tags) + { + if (_items == null) + { + _items = new List(); + } + foreach (ITag tag in tags) + { + if (tag != null) + { + _items.Add(tag); + } + } + _items.TrimExcess(); + _items.Sort((x, y) => x.Address.CompareTo(y.Address)); + UpdatePDUArea(); + return true; + } + + public bool RemoveItems(params ITag[] items) + { + foreach (var item in items) + { + _server.RemoveItemIndex(item.GetTagName()); + _items.Remove(item); + } + UpdatePDUArea(); + return true; + } + + protected void UpdatePDUArea() + { + int count = _items.Count; + if (count > 0) + { + DeviceAddress _start = _items[0].Address; + _start.Bit = 0; + int bitCount = _cacheReader.ByteCount; + if (count > 1) + { + int cacheLength = 0;//缓冲区的大小 + int cacheIndexStart = 0; + int startIndex = 0; + DeviceAddress segmentEnd = DeviceAddress.Empty; + DeviceAddress tagAddress = DeviceAddress.Empty; + DeviceAddress segmentStart = _start; + for (int j = 1, i = 1; i < count; i++, j++) + { + tagAddress = _items[i].Address;//当前变量地址 + int offset1 = _cacheReader.GetOffset(tagAddress, segmentStart); + if (offset1 > (_plcReader.PDU - tagAddress.DataSize) / bitCount) + { + segmentEnd = _items[i - 1].Address; + int len = _cacheReader.GetOffset(segmentEnd, segmentStart); + len += segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount; + tagAddress.CacheIndex = (ushort)(cacheIndexStart + len); + _items[i].Address = tagAddress; + _rangeList.Add(new PDUArea(segmentStart, len, startIndex, j)); + startIndex += j; j = 0; + cacheLength += len;//更新缓存长度 + cacheIndexStart = cacheLength; + segmentStart = tagAddress;//更新数据片段的起始地址 + } + else + { + tagAddress.CacheIndex = (ushort)(cacheIndexStart + offset1); + _items[i].Address = tagAddress; + } + if (i == count - 1) + { + segmentEnd = _items[i].Address; + int segmentLength = _cacheReader.GetOffset(segmentEnd, segmentStart); + if (segmentLength > (_plcReader.PDU - segmentEnd.DataSize) / bitCount) + { + segmentEnd = _items[i - 1].Address; + segmentLength = segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount; + } + tagAddress.CacheIndex = (ushort)(cacheIndexStart + segmentLength); + _items[i].Address = tagAddress; + segmentLength += segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount; + _rangeList.Add(new PDUArea(segmentStart, segmentLength, startIndex, j + 1)); + cacheLength += segmentLength; + } + } + _cacheReader.Size = cacheLength; + } + else + { + var size = _start.DataSize <= bitCount ? 1 : _start.DataSize / bitCount; + _rangeList.Add(new PDUArea(_start, size, 0, 1)); + _cacheReader.Size = size;//改变Cache的Size属性值将创建Cache的内存区域 + } + } + } + + public ITag FindItemByAddress(DeviceAddress addr) + { + int index = _items.BinarySearch(new BoolTag(0, addr, null)); + return index < 0 ? null : _items[index]; + } + + public bool SetActiveState(bool active, params short[] items) + { + return false; + } + + object sync = new object(); + + public void Flush() + { + lock (sync) + { + _changedList.Clear(); + Poll(); + if (_changedList.Count > 0) + Update(); + } + } + + protected void timer_Timer(object sender, EventArgs e) + { + //Modified Kevin + if (_isActive && !_plcReader.IsClosed) + { + lock (sync) + { + _changedList.Clear(); + Poll(); + if (_changedList.Count > 0) + Update(); + } + } + else + return; + } + + protected virtual unsafe void Poll() + { + short[] cache = (short[])_cacheReader.Cache; + int k = 0; + foreach (PDUArea area in _rangeList) + { + byte[] rcvBytes = _plcReader.ReadBytes(area.Start, (ushort)area.Len);//从PLC读取数据 + if (rcvBytes == null) + { + k += (area.Len + 1) / 2; + continue; + } + else + { + int len = rcvBytes.Length / 2; + fixed (byte* p1 = rcvBytes) + { + short* prcv = (short*)p1; + int index = area.StartIndex;//index指向_items中的Tag元数据 + int count = index + area.Count; + while (index < count) + { + DeviceAddress addr = _items[index].Address; + int iShort = addr.CacheIndex; + int iShort1 = iShort - k; + if (addr.VarType == DataType.BOOL) + { + int tmp = prcv[iShort1] ^ cache[iShort]; + DeviceAddress next = addr; + if (tmp != 0) + { + while (addr.Start == next.Start) + { + if ((tmp & (1 << next.Bit)) > 0) _changedList.Add(index); + if (++index < count) + next = _items[index].Address; + else + break; + } + } + else + { + while (addr.Start == next.Start && ++index < count) + { + next = _items[index].Address; + } + } + } + else + { + if (addr.ByteOrder.HasFlag(ByteOrder.BigEndian)) + { + for (int i = 0; i < addr.DataSize / 2; i++) + { + prcv[iShort1 + i] = IPAddress.HostToNetworkOrder(prcv[iShort1 + i]); + } + } + if (addr.DataSize <= 2) + { + if (prcv[iShort1] != cache[iShort]) _changedList.Add(index); + } + else + { + int size = addr.DataSize / 2; + for (int i = 0; i < size; i++) + { + if (prcv[iShort1 + i] != cache[iShort + i]) + { + _changedList.Add(index); + break; + } + } + } + index++; + } + } + short[] prcvShorts = new short[len]; + for (int i = 0; i < len; i++) + { + prcvShorts[i] = prcv[i]; + } + + //改成Array.Copy 由于线程安全的问题 + Array.Copy(prcvShorts, 0, cache, k, len); + /* + for (int j = 0; j < len; j++) + { + cache[j + offset] = prcv[j]; + }//将PLC读取的数据写入到CacheReader中 + */ + } + k += len; + } + } + + } + + protected void Update() + { + DateTime dt = DateTime.Now; + if (DataChange != null) + { + HistoryData[] values = new HistoryData[_changedList.Count]; + int i = 0; + foreach (int index in _changedList) + { + ITag item = _items[index]; + var itemData = item.Read(); + if (item.Active) + item.Update(itemData, dt, QUALITIES.QUALITY_GOOD); + if (_deadband == 0 || (item.Address.VarType == DataType.FLOAT && + (Math.Abs(itemData.Single / item.Value.Single - 1) > _deadband))) + { + values[i].ID = item.ID; + values[i].Quality = item.Quality; + values[i].Value = itemData; + values[i].TimeStamp = item.TimeStamp; + i++; + } + } + foreach (DataChangeEventHandler deleg in DataChange.GetInvocationList()) + { + deleg.BeginInvoke(this, new DataChangeEventArgs(1, values), null, null); + } + } + else + { + foreach (int index in _changedList) + { + ITag item = _items[index]; + if (item.Active) + item.Update(item.Read(), dt, QUALITIES.QUALITY_GOOD); + } + } + } + + public void Dispose() + { + if (_timer != null) + _timer.Dispose(); + //if (_items != null) + // _items.Clear(); + } + + public virtual HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray) + { + int len = itemArray.Length; + HistoryData[] values = new HistoryData[len]; + if (source == DataSource.Device) + { + IMultiReadWrite multi = _plcReader as IMultiReadWrite; + if (multi != null) + { + Array.Sort(itemArray); + var itemArr = multi.ReadMultiple(Array.ConvertAll(itemArray, x => x.Address)); + for (int i = 0; i < len; i++) + { + values[i].ID = itemArray[i].ID; + values[i].Value = itemArr[i].Value; + values[i].TimeStamp = itemArr[i].TimeStamp.ToDateTime(); + itemArray[i].Update(itemArr[i].Value, values[i].TimeStamp, itemArr[i].Quality); + } + } + else + { + for (int i = 0; i < len; i++) + { + itemArray[i].Refresh(source); + values[i].ID = itemArray[i].ID; + values[i].Value = itemArray[i].Value; + values[i].Quality = itemArray[i].Quality; + values[i].TimeStamp = itemArray[i].TimeStamp; + } + } + } + else + { + for (int i = 0; i < len; i++) + { + values[i].ID = itemArray[i].ID; + values[i].Value = itemArray[i].Value; + values[i].Quality = itemArray[i].Quality; + values[i].TimeStamp = itemArray[i].TimeStamp; + } + } + return values; + } + + public virtual int BatchWrite(SortedDictionary items, bool isSync = true) + { + int len = items.Count; + int rev = 0; + IMultiReadWrite multi = _plcReader as IMultiReadWrite; + if (multi != null) + { + DeviceAddress[] addrs = new DeviceAddress[len]; + object[] objs = new object[len]; + int i = 0; + foreach (var item in items) + { + addrs[i] = item.Key.Address; + objs[i] = item.Value; + i++; + } + rev = multi.WriteMultiple(addrs, objs); + } + else + { + foreach (var tag in items) + { + if (tag.Key.Write(tag.Value) < 0) + rev = -1; + } + } + if (DataChange != null && rev >= 0) + { + HistoryData[] data = new HistoryData[len]; + int i = 0; + foreach (var item in items) + { + ITag tag = item.Key; + data[i].ID = tag.ID; + data[i].TimeStamp = tag.TimeStamp; + data[i].Quality = tag.Quality; + data[i].Value = item.Key.ToStorage(item.Value); + i++; + } + foreach (DataChangeEventHandler deleg in DataChange.GetInvocationList()) + { + deleg.BeginInvoke(this, new DataChangeEventArgs(1, data), null, null); + } + } + return rev; + } + + public ItemData ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache) + { + return source == DataSource.Cache ? _cacheReader.ReadInt32(address) : _plcReader.ReadInt32(address); + } + + public ItemData ReadUInt32(DeviceAddress address, DataSource source = DataSource.Cache) + { + return source == DataSource.Cache ? _cacheReader.ReadUInt32(address) : _plcReader.ReadUInt32(address); + } + + public ItemData ReadUInt16(DeviceAddress address, DataSource source = DataSource.Cache) + { + return source == DataSource.Cache ? _cacheReader.ReadUInt16(address) : _plcReader.ReadUInt16(address); + } + + public ItemData ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache) + { + return source == DataSource.Cache ? _cacheReader.ReadInt16(address) : _plcReader.ReadInt16(address); + } + + public ItemData ReadByte(DeviceAddress address, DataSource source = DataSource.Cache) + { + return source == DataSource.Cache ? _cacheReader.ReadByte(address) : _plcReader.ReadByte(address); + } + + public ItemData ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache) + { + return source == DataSource.Cache ? _cacheReader.ReadFloat(address) : _plcReader.ReadFloat(address); + } + + public ItemData ReadBool(DeviceAddress address, DataSource source = DataSource.Cache) + { + return source == DataSource.Cache ? _cacheReader.ReadBit(address) : _plcReader.ReadBit(address); + } + + public ItemData ReadString(DeviceAddress address, DataSource source = DataSource.Cache) + { + ushort siz = address.DataSize; + return source == DataSource.Cache ? _cacheReader.ReadString(address, siz) : + _plcReader.ReadString(address, siz); + } + + public int WriteInt32(DeviceAddress address, int value) + { + int rs = _plcReader.WriteInt32(address, value); + if (rs >= 0) + { + if (DataChange != null) + { + ITag tag = GetTagByAddress(address); + if (tag != null) + DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] + { + new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Int32=value}, DateTime.Now) + })); + } + } + return rs; + } + + public int WriteUInt32(DeviceAddress address, uint value) + { + int rs = _plcReader.WriteUInt32(address, value); + if (rs >= 0) + { + if (DataChange != null) + { + ITag tag = GetTagByAddress(address); + if (tag != null) + DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] + { + new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{DWord=value}, DateTime.Now) + })); + } + } + return rs; + } + + public int WriteUInt16(DeviceAddress address, ushort value) + { + int rs = _plcReader.WriteUInt16(address, value); + if (rs >= 0) + { + if (DataChange != null) + { + ITag tag = GetTagByAddress(address); + if (tag != null) + DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] + { + new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Word=value}, DateTime.Now) + })); + } + } + return rs; + } + + public int WriteInt16(DeviceAddress address, short value) + { + int rs = _plcReader.WriteInt16(address, value); + if (rs >= 0) + { + if (DataChange != null) + { + ITag tag = GetTagByAddress(address); + if (tag != null) + DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] + { + new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Int16=value}, DateTime.Now) + })); + } + } + return rs; + } + + public int WriteFloat(DeviceAddress address, float value) + { + int rs = _plcReader.WriteFloat(address, value); + if (rs >= 0) + { + if (DataChange != null) + { + ITag tag = GetTagByAddress(address); + if (tag != null) + DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] + { + new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Single=value}, DateTime.Now) + })); + } + } + return rs; + } + + public int WriteString(DeviceAddress address, string value) + { + int rs = _plcReader.WriteString(address, value); + if (rs >= 0) + { + if (DataChange != null) + { + ITag tag = GetTagByAddress(address); + if (tag != null) + DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] + { + new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,Storage.Empty, DateTime.Now) + })); + } + } + return rs; + } + + public int WriteBit(DeviceAddress address, bool value) + { + int rs = _plcReader.WriteBit(address, value); + if (rs >= 0) + { + if (DataChange != null) + { + ITag tag = GetTagByAddress(address); + if (tag != null) + DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] + { + new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Boolean=value}, DateTime.Now) + })); + } + } + return rs; + } + + public int WriteBits(DeviceAddress address, byte value) + { + int rs = _plcReader.WriteBits(address, value); + if (rs >= 0) + { + if (DataChange != null) + { + ITag tag = GetTagByAddress(address); + if (tag != null) + DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] + { + new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Byte=value}, DateTime.Now) + })); + } + } + return rs; + } + + private ITag GetTagByAddress(DeviceAddress addr) + { + int index = _items.BinarySearch(new BoolTag(0, addr, null)); + return index < 0 ? null : _items[index]; + } + + public event DataChangeEventHandler DataChange; + } + + +} diff --git a/SCADA/Program/MelsecDriver/MelsecDriver.csproj b/SCADA/Program/MelsecDriver/MelsecDriver.csproj new file mode 100644 index 0000000..a85e5fe --- /dev/null +++ b/SCADA/Program/MelsecDriver/MelsecDriver.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {AE7D9641-3F7D-4282-B752-2FA14619CFBF} + Library + Properties + MelsecDriver + MelsecDriver + v4.5.2 + 512 + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + {7f8029da-1b94-4b1d-b91f-5b21dbf2f3a2} + HslCommunication_Net45 + + + {8965E389-6466-4B30-BD43-83C909044637} + DataService + + + + \ No newline at end of file diff --git a/SCADA/Program/MelsecDriver/Properties/AssemblyInfo.cs b/SCADA/Program/MelsecDriver/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c5f561d --- /dev/null +++ b/SCADA/Program/MelsecDriver/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("MelsecDriver")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MelsecDriver")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("ae7d9641-3f7d-4282-b752-2fa14619cfbf")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]