From cef7455e641ce5167e2410c24d821664d0835042 Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 9 Nov 2017 17:39:11 +0800 Subject: [PATCH] fix bug of modbustcp --- .../x86/Debug/CoreTest_MarkupCompile.i.lref | 8 + SCADA/Program/DataService/ClientReader.cs | 607 -------- SCADA/Program/DataService/FCTCOMMAND.cs | 4 - SCADA/Program/DataService/IModbusReader.cs | 180 --- SCADA/Program/DataService/Route.cs | 219 --- SCADA/Program/DataService/TagListGroup.cs | 385 ------ SCADA/Program/DataService/WpfApplication1.sln | 30 - .../DataService/bin/Debug/DataService.dll | Bin 84992 -> 84992 bytes SCADA/Program/DataService/codeback.txt | 1219 ----------------- .../DataService.csproj.FileListAbsolute.txt | 1 + .../DataService/obj/Debug/DataService.dll | Bin 84992 -> 84992 bytes SCADA/Program/ModbusDriver/ModbusTCPDriver.cs | 63 +- .../ModbusDriver/bin/Debug/DataService.dll | Bin 84992 -> 84992 bytes .../ModbusDriver/bin/Debug/ModbusDriver.dll | Bin 28160 -> 28160 bytes .../ModbusDriver/obj/Debug/ModbusDriver.dll | Bin 28160 -> 28160 bytes SCADA/dll/ModbusDriver.dll | Bin 26112 -> 28160 bytes 16 files changed, 45 insertions(+), 2671 deletions(-) create mode 100644 SCADA/Program/CoreTest/obj/x86/Debug/CoreTest_MarkupCompile.i.lref delete mode 100644 SCADA/Program/DataService/ClientReader.cs delete mode 100644 SCADA/Program/DataService/FCTCOMMAND.cs delete mode 100644 SCADA/Program/DataService/IModbusReader.cs delete mode 100644 SCADA/Program/DataService/Route.cs delete mode 100644 SCADA/Program/DataService/TagListGroup.cs delete mode 100644 SCADA/Program/DataService/WpfApplication1.sln delete mode 100644 SCADA/Program/DataService/codeback.txt diff --git a/SCADA/Program/CoreTest/obj/x86/Debug/CoreTest_MarkupCompile.i.lref b/SCADA/Program/CoreTest/obj/x86/Debug/CoreTest_MarkupCompile.i.lref new file mode 100644 index 0000000..5e01469 --- /dev/null +++ b/SCADA/Program/CoreTest/obj/x86/Debug/CoreTest_MarkupCompile.i.lref @@ -0,0 +1,8 @@ +C:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\obj\x86\Debug\GeneratedInternalTypeHelper.g.i.cs + +FC:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\AlarmSet.xaml;; +FC:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\Guage.xaml;; +FC:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\MainWindow.xaml;; +FC:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\TagMonitor.xaml;; +FC:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\Trend.xaml;; + diff --git a/SCADA/Program/DataService/ClientReader.cs b/SCADA/Program/DataService/ClientReader.cs deleted file mode 100644 index 1b1c7bb..0000000 --- a/SCADA/Program/DataService/ClientReader.cs +++ /dev/null @@ -1,607 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Sockets; -using System.Net; -using System.Text; -using System.Threading; - -namespace DataService -{ - public class ClientReader : IDevice - { - string _ip; - public string ServerName - { - get { return _ip; } - } - - internal Socket tcpSynCl; - internal Socket tcpASynCl; - - public bool IsClosed - { - get - { - //return tcpASynCl.Poll(-1, SelectMode.SelectRead); - return !tcpSynCl.Connected || !tcpASynCl.Connected; - } - } - - private ushort _timeout = 0; - public int TimeOut - { - get { return _timeout; } - } - - - List _grps = new List(1); - public IEnumerable Groups - { - get { return _grps; } - } - - IDataServer _server; - public IDataServer Parent - { - get { return _server; } - } - - public ClientReader(IDataServer server, string ip) - { - _server = server; - _ip = ip; - } - - public bool Connect() - { - try - { - int port = 1000; - IPAddress ip = IPAddress.Parse(_ip); - tcpASynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - tcpASynCl.Connect(new IPEndPoint(ip, port)); - tcpASynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout); - tcpASynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout); - - tcpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - tcpSynCl.Connect(new IPEndPoint(ip, port)); - tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout); - tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout); - return true; - } - catch (SocketException error) - { - if (OnClose != null) - OnClose(this, new ShutdownRequestEventArgs(error.Message)); - return false; - } - } - - public IGroup AddGroup(string name, ushort id, int updateRate, int timeOut = 0, float deadBand = 0f, bool active = false) - { - ClientGroup grp = new ClientGroup(id, name, updateRate, active, this); - _grps.Add(grp); - return grp; - } - - public int RemoveAllGroup() - { - foreach (IGroup grp in _grps) - { - grp.Dispose(); - } - _grps.Clear(); - return 1; - } - - public event ShutdownRequestEventHandler OnClose; - - public void Dispose() - { - if (tcpSynCl != null) - { - if (tcpSynCl.Connected) - { - try - { - tcpASynCl.Shutdown(SocketShutdown.Both); - tcpSynCl.Shutdown(SocketShutdown.Both); - } - catch { } - if (OnClose != null) - OnClose(this, new ShutdownRequestEventArgs("SHUTDOWN")); - tcpSynCl.Close(); - tcpASynCl.Close(); - } - tcpSynCl = null; - tcpASynCl = null; - } - RemoveAllGroup(); - } - } - - public class ClientGroup : IGroup - { - public const byte fctHead = 0xAB; - public const byte fctReadSingle = 1; - public const byte fctReadMultiple = 2; - public const byte fctWriteSingle = 5; - public const byte fctWriteMultiple = 15; - - bool _active = false; - public bool IsActive - { - get - { - return _active; - } - set - { - _active = value; - if (value) - { - ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(ReciveData), _tcpASynCl); - } - } - } - - protected ushort _id; - public ushort ID - { - get - { - return _id; - } - } - - protected int _updateRate; - public int UpdateRate - { - get - { - return _updateRate; - } - set - { - _updateRate = value; - } - } - - protected DeviceAddress _start; - public DeviceAddress Start - { - get - { - return _start; - } - } - - public int Size - { - get - { - return _items == null ? 0 : _items.Length; - } - } - - protected string _name; - public string Name - { - get - { - return _name; - } - } - - protected float _deadband; - public float DeadBand - { - get - { - return _deadband; - } - set - { - _deadband = value; - } - } - - protected ClientReader _plcReader; - public IDevice Parent - { - get - { - return _plcReader; - } - } - - protected ITag[] _items; - public IEnumerable Items - { - get { return _items; } - } - - IDataServer _server; - Socket _tcpSynCl, _tcpASynCl; - - byte[] tcpSynClBuffer; - - public ClientGroup(ushort id, string name, int updateRate, bool active, ClientReader plcReader) - { - this._id = id; - this._name = name; - this._updateRate = updateRate; - this._active = active; - this._plcReader = plcReader; - this._server = plcReader.Parent; - this._tcpASynCl = plcReader.tcpASynCl; - this._tcpSynCl = plcReader.tcpASynCl; - tcpSynClBuffer = new byte[_tcpASynCl.ReceiveBufferSize]; - } - - private byte[] ReadSingleData(DeviceAddress address, DataSource source = DataSource.Cache) - { - short ID = (short)address.Start; - byte type = (byte)address.VarType; - byte[] idbits = BitConverter.GetBytes(ID); - byte[] write_data = new byte[6] { fctHead, fctReadSingle, - source == DataSource.Cache?(byte)0:(byte)1, idbits[0], idbits[1], type }; - byte[] data = new byte[type < 4 ? 1 : type < 6 ? 2 : 4]; - SocketError error; - _tcpSynCl.Send(write_data, 0, 6, SocketFlags.None, out error); - int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, data.Length + 3, SocketFlags.None, out error); - Array.Copy(tcpSynClBuffer, 3, data, 0, data.Length); - if (error == SocketError.Success) - return data; - else - { - throw new SocketException((int)error); - } - } - - - private int WriteSingleData(DeviceAddress address, byte[] value) - { - short ID = (short)address.Start; - byte type = (byte)address.VarType; - byte[] idbits = BitConverter.GetBytes(ID); - byte[] write_data = new byte[6] { fctHead, fctWriteSingle, 1, idbits[0], idbits[1], type }; - byte[] data = new byte[6 + value.Length]; - write_data.CopyTo(data, 0); - value.CopyTo(data, 6); - SocketError error; - _tcpSynCl.Send(data, 0, data.Length, SocketFlags.None, out error); - int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, 2, SocketFlags.None, out error); - if (error == SocketError.Success) - return tcpSynClBuffer[1]; - else - { - throw new SocketException((int)error); - } - } - - - public void Init() - { - if (_items != null) - { - for (int i = 0; i < _items.Length; i++) - { - _items[i].Value = _items[i].Read(DataSource.Cache);//DataSource.Device - } - } - } - - private void ReciveData(object state) - { - if (state == null || !_active) return; - byte[] bytes = new byte[_tcpASynCl.ReceiveBufferSize]; - int result = 0; - SocketError error; - do - { - result = _tcpASynCl.Receive(bytes, 0, bytes.Length, SocketFlags.None, out error); - if (result > 5 && bytes[0] == 0xAB) - { - short len = BitConverter.ToInt16(bytes, 1); - short count = BitConverter.ToInt16(bytes, 3); - int j = 5; - DateTime time = DateTime.UtcNow; - Storage value = Storage.Empty; - for (int i = 0; i < count; i++) - { - short id = BitConverter.ToInt16(bytes, j); - j += 2; - ITag tag = GetItemByID(id); - if (tag != null) - { - DataType type = (DataType)bytes[j++]; - switch (type) - { - case DataType.BOOL: - value.Boolean = BitConverter.ToBoolean(bytes, j++); - break; - case DataType.BYTE: - value.Byte = bytes[j++]; - break; - case DataType.SHORT: - value.Int16 = BitConverter.ToInt16(bytes, j); - j += 2; - break; - case DataType.INT: - value.Int32 = BitConverter.ToInt32(bytes, j); - j += 4; - break; - case DataType.FLOAT: - value.Single = BitConverter.ToSingle(bytes, j); - j += 4; - break; - } - tag.Update(value, time, QUALITIES.QUALITY_GOOD); - } - else - { - byte type = bytes[j]; - j += (type < 4 ? 2 : type < 6 ? 3 : 5); - } - } - //Array.Clear(bytes, 0, count); - } - } - while (result > 0); - } - - public bool AddItems(ItemMetaData[] items) - { - int count = items.Length; - if (_items == null) _items = new ITag[count]; - for (int i = 0; i < count; i++) - { - ITag dataItem = null; - ItemMetaData meta = items[i]; - DeviceAddress addr = new DeviceAddress(0, 0, meta.ID, meta.Size, 0, meta.DataType); - 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: - case DataType.SHORT: - dataItem = new ShortTag(meta.ID, addr, this); - break; - case DataType.TIME: - 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; - default: - dataItem = new BoolTag(meta.ID, addr, this); - break; - } - _items[i] = dataItem; - _server.AddItemIndex(meta.Name, dataItem); - } - Array.Sort(_items); - Init(); - return true; - } - - public bool RemoveAll() - { - Array.Clear(_items, 0, _items.Length); - return true; - } - - public bool SetActiveState(bool active, params short[] items) - { - return true; - } - - public int FindItemByAddress(DeviceAddress addr) - { - return Array.BinarySearch(_items, new BoolTag(0, addr, null)); - } - - public ITag GetItemByID(short id) - { - return _server[id]; - } - - public int BatchRead(DataSource source, bool isSync, params ITag[] itemArray) - { - if (itemArray == null) return -1; - int len = itemArray.Length; - byte[] bt = new byte[4]; - byte[] data = new byte[3 + len * 2]; - int j=0; - data[j++] = fctHead; - data[j++] = fctReadMultiple; - data[j++] = source == DataSource.Cache ? (byte)0 : (byte)1; - bt = BitConverter.GetBytes(itemArray.Length); - data[j++] = bt[0]; - data[j++] = bt[1]; - data[j++] = bt[2]; - data[j++] = bt[3]; - for (int i = 0; i < len; i++) - { - ITag tag = itemArray[i]; - bt = BitConverter.GetBytes(tag.ID); - data[j++] = bt[0]; - data[j++] = bt[1]; - data[j++] = (byte)(tag.Address.DataSize >> 3); - } - SocketError error; - _tcpSynCl.Send(data, 0, data.Length, SocketFlags.None, out error); - int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, tcpSynClBuffer.Length, SocketFlags.None, out error); - j = 2; - if (error == SocketError.Success) - { - DateTime time=DateTime.UtcNow; - Storage value=Storage.Empty; - for (int i = 0; i < len; i++) - { - ITag tag = itemArray[i]; - switch (tag.Address.VarType) - { - case DataType.BOOL: - value.Boolean = BitConverter.ToBoolean(tcpSynClBuffer, j++); - break; - case DataType.BYTE: - value.Byte = tcpSynClBuffer[j++]; - break; - case DataType.SHORT: - value.Int16 = BitConverter.ToInt16(tcpSynClBuffer, j); - j += 2; - break; - case DataType.INT: - value.Int32 = BitConverter.ToInt32(tcpSynClBuffer, j); - j += 4; - break; - case DataType.FLOAT: - value.Single = BitConverter.ToSingle(tcpSynClBuffer, j); - j += 4; - break; - } - tag.Update(value, time, QUALITIES.QUALITY_GOOD); - } - return 0; - } - else - { - throw new SocketException((int)error); - } - } - - public int BatchWrite(IDictionary items, bool isSync = true) - { - List list = new List(new byte[] { fctHead, fctWriteMultiple }); - list.AddRange(BitConverter.GetBytes(items.Count)); - foreach (var item in items) - { - ITag tag = item.Key; - list.AddRange(BitConverter.GetBytes(tag.ID)); - switch (tag.Address.VarType) - { - case DataType.BOOL: - list.Add((bool)item.Value ? (byte)1 : (byte)0); - break; - case DataType.BYTE: - list.Add((byte)item.Value); - break; - case DataType.SHORT: - list.AddRange(BitConverter.GetBytes((short)item.Value)); - break; - case DataType.INT: - list.AddRange(BitConverter.GetBytes((int)item.Value)); - break; - case DataType.FLOAT: - list.AddRange(BitConverter.GetBytes((float)item.Value)); - break; - } - } - SocketError error; - _tcpSynCl.Send(list.ToArray(), 0, list.Count, SocketFlags.None, out error); - int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, 2, SocketFlags.None, out error); - if (error == SocketError.Success) - return tcpSynClBuffer[1]; - else - { - throw new SocketException((int)error); - } - } - - - public ItemData ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache) - { - var data = ReadSingleData(address, source); - return data == null ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) : - new ItemData(BitConverter.ToInt32(data, 0), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache) - { - var data = ReadSingleData(address, source); - return data == null ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) : - new ItemData(BitConverter.ToInt16(data, 0), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadByte(DeviceAddress address, DataSource source = DataSource.Cache) - { - var data = ReadSingleData(address, source); - return data == null ? new ItemData(0, 0, QUALITIES.QUALITY_BAD) : - new ItemData(data[0], 0, QUALITIES.QUALITY_GOOD); - } - - public unsafe ItemData ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache) - { - var data = ReadSingleData(address, source); - if (data == null) - return new ItemData(0.0f, 0, QUALITIES.QUALITY_BAD); - else - { - int value = BitConverter.ToInt32(data, 0); - return new ItemData(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD); - } - } - - public ItemData ReadBool(DeviceAddress address, DataSource source = DataSource.Cache) - { - var data = ReadSingleData(address, source); - return data == null ? new ItemData(false, 0, QUALITIES.QUALITY_BAD) : - new ItemData(BitConverter.ToBoolean(data, address.Bit), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadString(DeviceAddress address, DataSource source = DataSource.Cache) - { - var data = ReadSingleData(address, source); - return data == null ? new ItemData(string.Empty, 0, QUALITIES.QUALITY_BAD) : - new ItemData(Encoding.Default.GetString(data, 0, address.DataSize), 0, QUALITIES.QUALITY_GOOD); - } - - public int WriteInt32(DeviceAddress address, int value) - { - return WriteSingleData(address, BitConverter.GetBytes(value)); - } - - public int WriteInt16(DeviceAddress address, short value) - { - return WriteSingleData(address, BitConverter.GetBytes(value)); - } - - public int WriteFloat(DeviceAddress address, float value) - { - return WriteSingleData(address, BitConverter.GetBytes(value)); - } - - public int WriteString(DeviceAddress address, string value) - { - return WriteSingleData(address, Encoding.ASCII.GetBytes(value)); - } - - public int WriteBit(DeviceAddress address, bool value) - { - return WriteSingleData(address, new byte[] { (byte)(value ? 1 : 0) }); - } - - public int WriteBits(DeviceAddress address, byte value) - { - return WriteSingleData(address, new byte[] { value }); - } - - public event DataChangeEventHandler DataChange; - - public void Dispose() - { - RemoveAll(); - _items = null; - } - } -} diff --git a/SCADA/Program/DataService/FCTCOMMAND.cs b/SCADA/Program/DataService/FCTCOMMAND.cs deleted file mode 100644 index 8d128e8..0000000 --- a/SCADA/Program/DataService/FCTCOMMAND.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace DataService -{ - -} diff --git a/SCADA/Program/DataService/IModbusReader.cs b/SCADA/Program/DataService/IModbusReader.cs deleted file mode 100644 index 93333b3..0000000 --- a/SCADA/Program/DataService/IModbusReader.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System.Collections.Generic; - -namespace DataService -{ - public abstract class IModbusReader: IPLCDevice - { - 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 int PDU - { - get { return 0xFF; } - } - - public int GetBlockSize(int area) - { - return area > 2 ? PDU / 2 : PDU; - } - - public DeviceAddress GetDeviceAddress(string address) - { - DeviceAddress dv = DeviceAddress.Empty; - if (address == null || address.Length < 3) - return dv; - else - { - int index = address.IndexOf('.'); - switch (address[0]) - { - case '0': - dv.Area = fctReadCoil; - if (index > 0) - { - dv.Start = 64 * int.Parse(address.Substring(1, index - 1)); - dv.Bit = byte.Parse(address.Substring(index + 1)); - } - break; - case '1': - dv.Area = fctReadDiscreteInputs; - if (index > 0) - { - dv.Start = 64 * int.Parse(address.Substring(1, index - 1)); - dv.Bit = byte.Parse(address.Substring(index + 1)); - } - break; - case '4': - dv.Area = 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.Mid(1)); - break; - case '3': - dv.Area = fctReadInputRegister; - 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.Mid(1)); - break; - } - } - return dv; - } - - public string GetAddress(DeviceAddress address) - { - return string.Empty; - } - - - short _id; - public short ID - { - get - { - return _id; - } - } - - string _server; - public string ServerName - { - get { return _server; } - set { _server = value; } - } - - public abstract DeviceDriver Driver - { - get ; - } - - public abstract bool IsClosed - { - get; - } - - private int _timeout; - public int TimeOut - { - get { return _timeout; } - set { _timeout = value; } - } - - List _grps = new List(20); - public IEnumerable Groups - { - get { return _grps; } - } - - IDataServer _parent; - public IDataServer Parent - { - get { return _parent; } - } - - public abstract bool Connect(); - - public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false) - { - ModbusGroup grp = new ModbusGroup(id, name, updateRate, active, this); - _grps.Add(grp); - return grp; - } - - public bool RemoveGroup(IGroup grp) - { - grp.IsActive = false; - return _grps.Remove(grp); - } - - public event ShutdownRequestEventHandler OnClose; - - public abstract void Dispose(); - - public abstract byte[] ReadBytes(DeviceAddress address, ushort size); - - public abstract ItemData ReadInt32(DeviceAddress address); - - public abstract ItemData ReadInt16(DeviceAddress address); - - public abstract ItemData ReadByte(DeviceAddress address); - - public abstract ItemData ReadString(DeviceAddress address, ushort size); - - public abstract ItemData ReadFloat(DeviceAddress address); - - public abstract ItemData ReadBit(DeviceAddress address); - - public abstract ItemData ReadValue(DeviceAddress address); - - public abstract int WriteBytes(DeviceAddress address, byte[] bit); - - public abstract int WriteBit(DeviceAddress address, bool bit); - - public abstract int WriteBits(DeviceAddress address, byte bits); - - public abstract int WriteInt16(DeviceAddress address, short value); - - public abstract int WriteInt32(DeviceAddress address, int value); - - public abstract int WriteFloat(DeviceAddress address, float value); - - public abstract int WriteString(DeviceAddress address, string str); - - public abstract int WriteValue(DeviceAddress address, object value); - } -} diff --git a/SCADA/Program/DataService/Route.cs b/SCADA/Program/DataService/Route.cs deleted file mode 100644 index ea54d71..0000000 --- a/SCADA/Program/DataService/Route.cs +++ /dev/null @@ -1,219 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace DataService -{ - [Flags] - public enum DevStatus - { - None = 0, - RUN = 0x0001, - STOP = 0x0002, - OLS = 0x0004, - CLS = 0x0008, - PAUSE = 0x0010, - READY = 0x0020, - DUMP = 0x0040, - COMPLETE = 0x0080, - ALARM = 0x0100, - } - - [Flags] - public enum DevCommand - { - START = 0x0001, - STOP = 0x0002, - ABORT = 0x0004, - PAUSE = 0x0008, - HOLD = 0x0010, - WET = 0x0020, - DISCH = 0x0040, - DUMP = 0x0080, - } - - public interface IDevice : IComparable - { - string Name { get; } - string Description { get; } - DeviceTypes DeviceType { get; } - int ExtStatus { get; } - DevStatus Status { get; } - int Execute(DevCommand command); - } - - //可以根据订单的源仓目标仓,二分法搜索到仓的列表。得到仓的参数和状态;部分设备是互斥的(如闸门,分配器,三通),马达等是共用的。 - - - public sealed class RouteVex : IComparable, IEqualityComparer - { - string _source; - public string Source - { - get { return _source; } - } - - string _dest; - public string Dest - { - get { return _dest; } - } - - RouteVex _front; - public RouteVex Front - { - get { return _front; } - set { _front = value; } - } - - Func _func; - Func _funcWrite; - public bool IsRun - { - get { return _func == null ? true : _func(); } - } - - public RouteVex(string source, string dest = "") - { - _source = source; - _dest = dest; - } - - public RouteVex(string source, string dest, string startSignal, string runSignal, IDataServer srv) - { - _source = source; - _dest = dest; - var eval = srv.Eval; - if (!string.IsNullOrEmpty(runSignal)) - { - _func = eval.Eval(runSignal) as Func; - } - if (!string.IsNullOrEmpty(startSignal)) - { - _funcWrite = eval.WriteEval(startSignal) as Func; - } - } - - public int Start() - { - return _funcWrite == null ? 0 : _funcWrite(); - } - - public int CompareTo(RouteVex other) - { - if (this._source == null) return 0; - if (other._dest == null) return 1; - int comp = this._source.CompareTo(other._source); - return comp == 0 ? this._dest.CompareTo(other._dest) : comp; - } - - public override string ToString() - { - return string.Concat(_source, ",", _dest); - } - - public bool Equals(RouteVex x, RouteVex y) - { - if (x == null || y == null) return false; - return ((x.Source == null && y.Source == null) || (x.Source == y.Source)) && - ((x.Dest == null && y.Dest == null) || (x.Dest == y.Dest)); - } - - public int GetHashCode(RouteVex obj) - { - return obj.Source.GetHashCode() ^ obj.Dest.GetHashCode(); - } - } - - //正在执行的路径,保存到一个XML文件,包含当前执行的节点 - public sealed class Route : LinkedList, ICloneable - { - LinkedListNode _current; - public LinkedListNode Current - { - get { return _current; } - set { _current = value; } - } - - string _start; - public string Start - { - get { return _start; } - set { _start = value; } - } - - string _end; - public string End - { - get { return _end; } - set { _end = value; } - } - - RouteState _state; - public RouteState State - { - get { return _state; } - set { _state = value; } - } - - string[] _options; - public string[] Options - { - get { return _options; } - set { _options = value; } - } - - - public Route(string source, string dest, string[] options = null) - { - _start = source; - _end = dest; - _options = options; - } - - public object Clone() - { - Route rt = new Route(_start, _end, _options); - foreach (var routeVex in this) - { - rt.AddLast(routeVex); - } - return rt; - } - } - - public enum DeviceTypes - { - Misc, - Motor = 1, - Valve = 2, - Gate = 3, - Divert = 4, - FourWays = 5, - Distribute = 6, - Bin = 7, - Analog = 8, - Scale = 9, - Conveyor = 10, - Elevator = 11, - Mixer = 12, - Grind = 13, - Pellet = 14, - Extruder = 15, - CheckScale = 16, - Liquid = 17, - HandAdd = 18, - Sifter = 19, - Cyclone = 20, - AirHammer = 21 - } - - public enum RouteState - { - Idle, - Run, - Stop, - Suspend, - Complete, - Abort, - } -} diff --git a/SCADA/Program/DataService/TagListGroup.cs b/SCADA/Program/DataService/TagListGroup.cs deleted file mode 100644 index f456dbe..0000000 --- a/SCADA/Program/DataService/TagListGroup.cs +++ /dev/null @@ -1,385 +0,0 @@ -using System.Collections.Generic; -using System.Threading; - -namespace DataService -{ - public class TagListGroup : IGroup - { - bool _isActive, _isDisposed; - public bool IsActive - { - get - { - return _isActive; - } - set - { - _isActive = value; - if (value) - { - ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(this.OnUpdate), 1); - } - } - } - - short _id; - public short ID - { - get - { - return _id; - } - } - - int _updateRate; - public int UpdateRate - { - get - { - return _updateRate; - } - set - { - _updateRate = value; - } - } - - string _name; - public string Name - { - get - { - return _name; - } - set - { - _name = value; - } - } - - float deadband; - public float DeadBand - { - get - { - return deadband; - } - set - { - deadband = value; - } - } - - IPLCDriver _reader; - public IDriver Parent - { - get { return _reader; } - } - - List _items; - public IEnumerable Items - { - get { return _items; } - } - - IDataServer _server; - public IDataServer Server - { - get - { - return _server; - } - } - - HashSet _activeList; - CompareItemByID rc = new CompareItemByID(); - - public TagListGroup(short id, string name, int updateRate, int size, bool active, IPLCDriver plcReader) - { - this._id = id; - this._updateRate = updateRate; - this._isActive = active; - this._reader = plcReader; - this._name = name; - this._server = _reader.Parent; - } - - - public bool AddItems(IList items) - { - int count = items.Count; - if (_items == null) - { - _items = new List(count); - _activeList = new HashSet(); - } - lock (_server.SyncRoot) - { - for (int i = 0; i < count; i++) - { - ITag dataItem = null; - TagMetaData meta = items[i]; - if (meta.GroupID == this._id) - { - switch (meta.DataType) - { - case DataType.BOOL: - dataItem = new BoolTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); - break; - case DataType.BYTE: - dataItem = new ByteTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); - break; - case DataType.WORD: - case DataType.SHORT: - dataItem = new ShortTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); - break; - case DataType.TIME: - case DataType.INT: - dataItem = new IntTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); - break; - case DataType.FLOAT: - dataItem = new FloatTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); - break; - case DataType.STR: - dataItem = new StringTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); - break; - } - if (dataItem != null) - { - _items.Add(dataItem); - _server.AddItemIndex(meta.Name, dataItem); - _activeList.Add(meta.ID); - } - } - } - } - _items.Sort(rc); - _items.TrimExcess(); - return true; - } - - public bool AddTags(IEnumerable tags) - { - if (_items == null) - { - _items = new List(); - } - foreach (ITag tag in tags) - { - if (tag != null) - { - _items.Add(tag); - if (tag.Active) - { - _activeList.Add(tag.ID); - } - } - } - _items.Sort(rc); - _items.TrimExcess(); - return true; - } - - public bool RemoveItems(params ITag[] items) - { - foreach (var item in items) - { - _server.RemoveItemIndex(item.GetTagName()); - _activeList.Remove(item.ID); - _items.Remove(item); - } - return true; - } - - - public bool SetActiveState(bool active, params short[] items) - { - if (active) - { - lock (_activeList) - { - _activeList.UnionWith(items); - }; - } - else - { - lock (_activeList) - { - _activeList.ExceptWith(items); - }; - } - return true; - } - - public ITag FindItemByAddress(DeviceAddress addr) - { - for (int i = 0; i < _items.Count; i++) - { - DeviceAddress address = _items[i].Address; - if (addr.Area == address.Area - && addr.DBNumber == address.DBNumber - && addr.Start == address.Start - && addr.Bit == address.Bit) - return _items[i]; - } - return null; - } - - public HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray) - { - return ExtMethods.BatchRead(source, itemArray); - } - - public int BatchWrite(SortedDictionary items, bool isSync = true) - { - int rev = ExtMethods.BatchWrite(items); - if (DataChange != null && rev >= 0) - { - int len = items.Count; - 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 _reader.ReadInt32(address); - } - - public ItemData ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache) - { - return _reader.ReadInt16(address); - } - - public ItemData ReadByte(DeviceAddress address, DataSource source = DataSource.Cache) - { - return _reader.ReadByte(address); - } - - public ItemData ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache) - { - return _reader.ReadFloat(address); - } - - public ItemData ReadBool(DeviceAddress address, DataSource source = DataSource.Cache) - { - return _reader.ReadBit(address); - } - - public ItemData ReadString(DeviceAddress address, DataSource source = DataSource.Cache) - { - return new ItemData(string.Empty, 0, QUALITIES.QUALITY_UNCERTAIN); - } - - public int WriteInt32(DeviceAddress address, int value) - { - return _reader.WriteInt32(address, value); - } - - public int WriteInt16(DeviceAddress address, short value) - { - return _reader.WriteInt16(address, value); - } - - public int WriteFloat(DeviceAddress address, float value) - { - return _reader.WriteFloat(address, value); - } - - public int WriteString(DeviceAddress address, string value) - { - return _reader.WriteString(address, value); - } - - public int WriteBit(DeviceAddress address, bool value) - { - return _reader.WriteBit(address, value); - } - - public int WriteBits(DeviceAddress address, byte value) - { - return _reader.WriteBits(address, value); - } - - public void Dispose() - { - _items.Clear(); - _activeList.Clear(); - _isDisposed = true; - //Dictionary dict = new Dictionary(); - } - - - public void OnUpdate(object stateInfo) - { - while (true) - { - Thread.Sleep(_updateRate); - lock (this) - { - if (!_isActive || _isDisposed) - { - return; - } - List hdalist = new List(); - foreach (short id in _activeList) - { - ITag item = _server[id]; - if (item != null) - { - if (item.Refresh() && DataChange != null) - hdalist.Add(new HistoryData(item.ID, item.Quality, item.Value, item.TimeStamp)); - } - if (DataChange != null) - DataChange.BeginInvoke(this, new DataChangeEventArgs(1, hdalist), null, null); - } - } - } - } - - public event DataChangeEventHandler DataChange; - } - - public class CompareItemByID : IComparer - { - public int Compare(ITag x, ITag y) - { - if (x == null) - { - if (y == null) - { - return 0; - } - else - { - return -1; - } - } - else - { - if (y == null) - { - return 1; - } - else - { - return x.ID.CompareTo(y.ID); - } - } - } - } -} diff --git a/SCADA/Program/DataService/WpfApplication1.sln b/SCADA/Program/DataService/WpfApplication1.sln deleted file mode 100644 index f594be4..0000000 --- a/SCADA/Program/DataService/WpfApplication1.sln +++ /dev/null @@ -1,30 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataService", "DataService.csproj", "{8965E389-6466-4B30-BD43-83C909044637}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8965E389-6466-4B30-BD43-83C909044637}.Debug|Any CPU.ActiveCfg = Debug|x86 - {8965E389-6466-4B30-BD43-83C909044637}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {8965E389-6466-4B30-BD43-83C909044637}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {8965E389-6466-4B30-BD43-83C909044637}.Debug|x86.ActiveCfg = Debug|x86 - {8965E389-6466-4B30-BD43-83C909044637}.Debug|x86.Build.0 = Debug|x86 - {8965E389-6466-4B30-BD43-83C909044637}.Release|Any CPU.ActiveCfg = Release|x86 - {8965E389-6466-4B30-BD43-83C909044637}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {8965E389-6466-4B30-BD43-83C909044637}.Release|Mixed Platforms.Build.0 = Release|x86 - {8965E389-6466-4B30-BD43-83C909044637}.Release|x86.ActiveCfg = Release|x86 - {8965E389-6466-4B30-BD43-83C909044637}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/SCADA/Program/DataService/bin/Debug/DataService.dll b/SCADA/Program/DataService/bin/Debug/DataService.dll index c158fd8a33cb0319b10a36117db02bf900598c3d..ee1ce68bd3a717c4b0e8f62a3da4a233ae9087ac 100644 GIT binary patch delta 94 zcmZpe!P+o`bwUS=rV>k3W6#ze#tt=s3nwk4;$zPF&t^KYzguvo!*&&Q#yyfcASDby vzy!oHj0_Av;uslzI508<1-k@`-Rc&2?6%uuZ+{|o+k;cSZ|8Aj{K5qQ2^Ax( diff --git a/SCADA/Program/DataService/codeback.txt b/SCADA/Program/DataService/codeback.txt deleted file mode 100644 index 6e0881a..0000000 --- a/SCADA/Program/DataService/codeback.txt +++ /dev/null @@ -1,1219 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Sockets; -using System.Net; -using System.Text; -using System.Diagnostics; - -namespace DataService -{ - public class ModbusTCPReader : IPLCDevice - { - 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; - - /// Constant for exception illegal function. - public const byte excIllegalFunction = 1; - /// Constant for exception illegal data address. - public const byte excIllegalDataAdr = 2; - /// Constant for exception illegal data value. - public const byte excIllegalDataVal = 3; - /// Constant for exception slave device failure. - public const byte excSlaveDeviceFailure = 4; - /// Constant for exception acknowledge. - public const byte excAck = 5; - /// Constant for exception slave is busy/booting up. - public const byte excSlaveIsBusy = 6; - /// Constant for exception gate path unavailable. - public const byte excGatePathUnavailable = 10; - /// Constant for exception not connected. - public const byte excExceptionNotConnected = 253; - /// Constant for exception connection lost. - public const byte excExceptionConnectionLost = 254; - /// Constant for exception response timeout. - public const byte excExceptionTimeout = 255; - /// Constant for exception wrong offset. - private const byte excExceptionOffset = 128; - /// Constant for exception send failt. - private const byte excSendFailt = 100; - - private static int _timeout; - - private Socket tcpSynCl; - private byte[] tcpSynClBuffer = new byte[256]; - - /// Response data event. This event is called when new data arrives - public delegate void ResponseData(ushort id, byte function, byte[] data); - /// Exception data event. This event is called when the data is incorrect - public delegate void ExceptionData(int id, byte function, byte exception); - /// Exception data event. This event is called when the data is incorrect - short _id; - public short ID - { - get - { - return _id; - } - } - - string _ip; - public string ServerName - { - get { return _ip; } - set { _ip = value; } - } - - public bool IsClosed - { - get - { - return tcpSynCl == null || tcpSynCl.Connected == false; - } - } - - public int TimeOut - { - get { return _timeout; } - set{ _timeout = value; } - } - - public DeviceDriver Driver - { - get - { - return DeviceDriver.ModbusTcp; - } - } - - List _grps = new List(20); - public IEnumerable Groups - { - get { return _grps; } - } - - IDataServer _server; - public IDataServer Parent - { - get { return _server; } - } - - public ModbusTCPReader(IDataServer server,short id, string ip, int timeOut = 500) - { - _id = id; - _server = server; - _ip = ip; - _timeout = timeOut; - } - - public bool Connect() - { - try - { - int port = 502; - //IPAddress ip = IPAddress.Parse(_ip); - // ---------------------------------------------------------------- - // Connect synchronous client - tcpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - tcpSynCl.Connect(_ip, port); - tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout); - tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout); - tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1); - return true; - } - catch (SocketException error) - { - if (OnClose != null) - OnClose(this, new ShutdownRequestEventArgs(error.Message)); - return false; - } - } - - private byte[] CreateReadHeader(int id, int startAddress, ushort length, byte function) - { - byte[] data = new byte[12]; - - byte[] _id = BitConverter.GetBytes((short)id); - data[0] = _id[0]; // Slave id high byte - data[1] = _id[1]; // Slave id low byte - data[5] = 6; // Message size - data[6] = 0; // Slave address - data[7] = function; // Function code - byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress)); - data[8] = _adr[0]; // Start address - data[9] = _adr[1]; // Start address - byte[] _length = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)length)); - data[10] = _length[0]; // Number of data to read - data[11] = _length[1]; // Number of data to read - return data; - } - - private byte[] CreateWriteHeader(int id, int startAddress, ushort numData, ushort numBytes, byte function) - { - byte[] data = new byte[numBytes + 11]; - - byte[] _id = BitConverter.GetBytes(id); - data[0] = _id[0]; // Slave id high byte - data[1] = _id[1]; // Slave id low byte+ - byte[] _size = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)(5 + numBytes))); - data[4] = _size[0]; // Complete message size in bytes - data[5] = _size[1]; // Complete message size in bytes - data[6] = 0; // Slave address - data[7] = function; // Function code - byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress)); - data[8] = _adr[0]; // Start address - data[9] = _adr[1]; // Start address - if (function >= fctWriteMultipleCoils) - { - byte[] _cnt = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)numData)); - data[10] = _cnt[0]; // Number of bytes - data[11] = _cnt[1]; // Number of bytes - data[12] = (byte)(numBytes - 2); - } - return data; - } - - private byte[] WriteSyncData(byte[] write_data) - { - short id = BitConverter.ToInt16(write_data, 0); - if (IsClosed) CallException(id, write_data[7], excExceptionConnectionLost); - else - { - try - { - tcpSynCl.Send(write_data, 0, write_data.Length, SocketFlags.None); - int result = tcpSynCl.Receive(tcpSynClBuffer, 0, tcpSynClBuffer.Length, SocketFlags.None); - - byte function = tcpSynClBuffer[7]; - byte[] data; - - if (result == 0) CallException(id, write_data[7], excExceptionConnectionLost); - - // ------------------------------------------------------------ - // Response data is slave exception - if (function > excExceptionOffset) - { - function -= excExceptionOffset; - CallException(id, function, tcpSynClBuffer[8]); - return null; - } - // ------------------------------------------------------------ - // Write response data - else if ((function >= fctWriteSingleCoil) && (function != fctReadWriteMultipleRegister)) - { - data = new byte[2]; - Array.Copy(tcpSynClBuffer, 10, data, 0, 2); - } - // ------------------------------------------------------------ - // Read response data - else - { - data = new byte[tcpSynClBuffer[8]]; - Array.Copy(tcpSynClBuffer, 9, data, 0, tcpSynClBuffer[8]); - } - return data; - } - catch (SocketException) - { - CallException(id, write_data[7], excExceptionConnectionLost); - } - } - return null; - } - - public byte[] WriteSingleCoils(int id, int startAddress, bool OnOff) - { - byte[] data; - data = CreateWriteHeader(id, startAddress, 1, 1, fctWriteSingleCoil); - if (OnOff == true) data[10] = 255; - else data[10] = 0; - return WriteSyncData(data); - } - - public byte[] WriteMultipleCoils(int id, int startAddress, ushort numBits, byte[] values) - { - byte numBytes = Convert.ToByte(values.Length); - byte[] data; - data = CreateWriteHeader(id, startAddress, numBits, (byte)(numBytes + 2), fctWriteMultipleCoils); - Array.Copy(values, 0, data, 13, numBytes); - return WriteSyncData(data); - } - - public byte[] WriteSingleRegister(int id, int startAddress, byte[] values) - { - byte[] data; - data = CreateWriteHeader(id, startAddress, 1, 1, fctWriteSingleRegister); - data[10] = values[0]; - data[11] = values[1]; - return WriteSyncData(data); - } - - public byte[] WriteMultipleRegister(int id, int startAddress, byte[] values) - { - ushort numBytes = Convert.ToUInt16(values.Length); - if (numBytes % 2 > 0) numBytes++; - byte[] data; - - data = CreateWriteHeader(id, startAddress, Convert.ToUInt16(numBytes / 2), Convert.ToUInt16(numBytes + 2), fctWriteMultipleRegister); - Array.Copy(values, 0, data, 13, values.Length); - return WriteSyncData(data); - } - - public int PDU - { - get { return 256; } - } - - public int GetBlockSize(int area) - { - return area > 2 ? PDU / 2 : PDU; - } - - public DeviceAddress GetDeviceAddress(string address) - { - DeviceAddress dv = DeviceAddress.Empty; - if (address == null || address.Length < 3) - return dv; - else - { - int index = address.IndexOf('.'); - switch (address[0]) - { - case '0': - dv.Area = fctReadCoil; - if (index > 0) - { - dv.Start = 64 * int.Parse(address.Substring(1, index - 1)); - dv.Bit = byte.Parse(address.Substring(index + 1)); - } - break; - case '1': - dv.Area = fctReadDiscreteInputs; - if (index > 0) - { - dv.Start = 64 * int.Parse(address.Substring(1, index - 1)); - dv.Bit = byte.Parse(address.Substring(index + 1)); - } - break; - case '4': - dv.Area = 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.Mid(1)); - break; - case '3': - dv.Area = fctReadInputRegister; - 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.Mid(1)); - break; - } - } - return dv; - } - - public string GetAddress(DeviceAddress address) - { - return string.Empty; - } - - - public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false) - { - ModbusGroup grp = new ModbusGroup(id, name, updateRate, active, this); - _grps.Add(grp); - return grp; - } - - public bool RemoveGroup(IGroup grp) - { - grp.IsActive = false; - return _grps.Remove(grp); - } - - public void Dispose() - { - if (tcpSynCl != null) - { - if (tcpSynCl.Connected) - { - try { tcpSynCl.Shutdown(SocketShutdown.Both); } - catch { } - tcpSynCl.Close(); - } - tcpSynCl = null; - } - foreach (IGroup grp in _grps) - { - grp.Dispose(); - } - _grps.Clear(); - } - - internal string GetErrorString(byte exception) - { - switch (exception) - { - case excIllegalFunction: - return "Constant for exception illegal function."; - case excIllegalDataAdr: - return "Constant for exception illegal data address."; - case excIllegalDataVal: - return "Constant for exception illegal data value."; - case excSlaveDeviceFailure: - return "Constant for exception slave device failure."; - case excAck: - return "Constant for exception acknowledge."; - case excSlaveIsBusy: - return "Constant for exception slave is busy/booting up."; - case excGatePathUnavailable: - return "Constant for exception gate path unavailable."; - case excExceptionNotConnected: - return "Constant for exception not connected."; - case excExceptionConnectionLost: - return "Constant for exception connection lost."; - case excExceptionTimeout: - return "Constant for exception response timeout."; - case excExceptionOffset: - return "Constant for exception wrong offset."; - case excSendFailt: - return "Constant for exception send failt."; - } - return string.Empty; - } - - internal void CallException(int id, byte function, byte exception) - { - if (tcpSynCl == null) return; - if (exception == excExceptionConnectionLost && IsClosed == false) - { - if (OnClose != null) - OnClose(this, new ShutdownRequestEventArgs(GetErrorString(exception))); - } - } - - public byte[] ReadBytes(DeviceAddress address, ushort size) - { - return WriteSyncData(CreateReadHeader(address.Area, address.Start, size, (byte)address.Area)); - } - - public ItemData ReadInt32(DeviceAddress address) - { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 4, (byte)address.Area)); - if (data == null) - return new ItemData(0, 0, QUALITIES.QUALITY_BAD); - else - return new ItemData(IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0)), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadInt16(DeviceAddress address) - { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 2, (byte)address.Area)); - if (data == null) - return new ItemData(0, 0, QUALITIES.QUALITY_BAD); - else - return new ItemData(IPAddress.HostToNetworkOrder(BitConverter.ToInt16(data, 0)), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadByte(DeviceAddress address) - { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.Area)); - if (data == null) - return new ItemData(0, 0, QUALITIES.QUALITY_BAD); - else - return new ItemData(data[0], 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadString(DeviceAddress address, ushort size) - { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, size, (byte)address.Area)); - if (data == null) - return new ItemData(string.Empty, 0, QUALITIES.QUALITY_BAD); - else - return new ItemData(Encoding.Default.GetString(data, 0, size), 0, QUALITIES.QUALITY_GOOD);//Ƿֽ⣿ - } - - public unsafe ItemData ReadFloat(DeviceAddress address) - { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 4, (byte)address.Area)); - if (data == null) - return new ItemData(0.0f, 0, QUALITIES.QUALITY_BAD); - else - { - int value = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0)); - return new ItemData(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD); - } - } - - public unsafe ItemData ReadBit(DeviceAddress address) - { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.Area)); - if (data == null) - return new ItemData(false, 0, QUALITIES.QUALITY_BAD); - else - { - fixed (byte* p = data) - { - int* p1 = (int*)p; - return new ItemData((*p1 & (1 << (address.Bit < 8 ? address.Bit + 8 : address.Bit - 8))) - != 0, 0, QUALITIES.QUALITY_GOOD); - } - } - } - - public ItemData ReadValue(DeviceAddress address) - { - throw new NotImplementedException(); - } - - public int WriteBytes(DeviceAddress address, byte[] bit) - { - var data = WriteMultipleRegister(address.Area, address.Start, bit); - return data == null ? -1 : 0; - } - - public int WriteBit(DeviceAddress address, bool bit) - { - var data = WriteSingleCoils(address.Area, address.Start + address.Bit, bit); - return data == null ? -1 : 0; - } - - public int WriteBits(DeviceAddress address, byte bits) - { - var data = WriteSingleRegister(address.Area, address.Start, new byte[] { bits }); - return data == null ? -1 : 0; - } - - public int WriteInt16(DeviceAddress address, short value) - { - var data = WriteSingleRegister(address.Area, address.Start, BitConverter.GetBytes(value)); - return data == null ? -1 : 0; - } - - public int WriteInt32(DeviceAddress address, int value) - { - var data = WriteSingleRegister(address.Area, address.Start, BitConverter.GetBytes(value)); - return data == null ? -1 : 0; - } - - public int WriteFloat(DeviceAddress address, float value) - { - var data = WriteSingleRegister(address.Area, address.Start, BitConverter.GetBytes(value)); - return data == null ? -1 : 0; - } - - public int WriteString(DeviceAddress address, string str) - { - var data = WriteSingleRegister(address.Area, address.Start, Encoding.ASCII.GetBytes(str)); - return data == null ? -1 : 0; - } - - public int WriteValue(DeviceAddress address, object value) - { - throw new NotImplementedException(); - } - - public override string ToString() - { - return "MODBUS̫"; - } - - public event ShutdownRequestEventHandler OnClose; - } - - public class ModbusGroup : PLCGroup - { - public ModbusGroup(short id, string name, int updateRate, bool active, IPLCDevice plcReader) - { - this._id = id; - this._name = name; - this._updateRate = updateRate; - this._isActive = active; - this._plcReader = plcReader; - this._server = _plcReader.Parent; - this._timer = new MMTimer(); - this._changedList = new List(100); - this._cacheReader = new ModbusCacheReader(); - } - - protected override unsafe int StartScan(DeviceAddress start, ushort len) - { - if (_start.Area > 2) - { - byte[] b1 = _plcReader.ReadBytes(start, len); - if (b1 == null) - return -1; - fixed (byte* p3 = b1) - { - short* p2 = (short*)p3; - int address = start.Start; - fixed (byte* numPtr = _cacheReader.Cache) - { - short* p1 = (short*)numPtr + address - _start.Start; - int i = 0; - ITag other = new BoolTag(0, DeviceAddress.Empty, null); - while (i < len) - { - //start.Start += i; - p2[i] = IPAddress.HostToNetworkOrder(p2[i]); - if (p2[i] != p1[i]) - { - start.Start = address + i; - other.Address = start; - int index = _items.BinarySearch(other); - if (index >= 0) - { - ITag item1 = _items[index]; - int size = item1.Address.DataSize; - if (size >= 16) - { - _changedList.Add(index); - i += (size / 16); - } - else - { - DeviceAddress addr = _items[index].Address; - int tmp = p2[i] ^ p1[i]; - while (addr.Start == start.Start) - { - if ((tmp & (1 << addr.Bit)) > 0) - _changedList.Add(index); - if (++index < _items.Count) - addr = _items[index].Address; - else - break; - } - i++; - } - } - else - { - index = ~index; - if (index < _items.Count) - { - DeviceAddress addr = _items[index].Address; - if (addr.Start == start.Start) - { - int tmp = p2[i] ^ p1[i]; - while (addr.Start == start.Start) - { - if ((tmp & (1 << addr.Bit)) > 0) - _changedList.Add(index); - if (++index < _items.Count) - addr = _items[index].Address; - else - break; - } - } - else - { - if (addr.Start > start.Start && index > 0) - addr = _items[--index].Address; - int size = addr.DataSize / 16; - if (size > start.Start - addr.Start) - { - _changedList.Add(index); - i += size + addr.Start - start.Start; - continue; - } - } - } - i++; - } - } - else - i++; - } - for (int j = 0; j < len; j++) - { - *(p1++) = p2[j]; - } - return 1; - } - } - } - else - { - byte[] b1 = _plcReader.ReadBytes(start, len); - if (b1 == null || _cacheReader.Size < 1) - return -1; - fixed (byte* p3 = b1) - { - long* p2 = (long*)p3; - int address = start.Start; - fixed (byte* numPtr = _cacheReader.Cache) - { - long* p1 = (long*)numPtr + address - _start.Start; - int i = 0; - len <<= 3; - ITag other = new BoolTag(0, DeviceAddress.Empty, null); - while (i < len) - { - p2[i] = IPAddress.HostToNetworkOrder(p2[i]); - if (p2[i] != p1[i]) - { - start.Start = address + i << 6; - other.Address = start; - int index = _items.BinarySearch(other); - if (index > 0) - { - DeviceAddress addr = _items[index].Address; - long tmp = p2[i] ^ p1[i]; - while (addr.Start == start.Start) - { - if ((tmp & (1 << addr.Bit)) > 0) - _changedList.Add(index); - if (++index < _items.Count) - addr = _items[index].Address; - else - break; - } - } - else - { - index = ~index; - if (index < _items.Count) - { - DeviceAddress addr = _items[index].Address; - if (addr.Start == start.Start) - { - long tmp = p2[i] ^ p1[i]; - while (addr.Start == start.Start) - { - if ((tmp & (1 << addr.Bit)) > 0) - _changedList.Add(index); - if (++index < _items.Count) - addr = _items[index].Address; - else - break; - } - } - } - } - p1[i] = p2[i]; - i++; - } - else - i++; - } - return 1; - } - } - } - } - - } -} - - - - -using System; -using System.Net; -using System.Text; - -namespace DataService -{ - public unsafe class CacheReader : ICache - { - byte[] _cache; - public byte[] Cache - { - get - { - return _cache; - } - } - - DeviceAddress _start; - public DeviceAddress Start - { - get - { - return _start; - } - set - { - _start = value; - } - } - - int _size; - public int Size - { - get - { - return _size; - } - set - { - _size = value; - this._cache = new byte[_size]; - } - } - - public CacheReader() - { - } - - public CacheReader(DeviceAddress start, int size) - { - this.Start = start; - this.Size = size; - } - - public int GetOffset(DeviceAddress start, DeviceAddress end) - { - return start.Start - end.Start; - } - - public ItemData ReadInt32(DeviceAddress address) - { - return new ItemData(BitConverter.ToInt32(_cache, address.Start - _start.Start), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadBit(DeviceAddress address) - { - //byte b = _cache[address.Start - _start.Start]; - fixed (byte* p = _cache) - { - int* p1 = (int*)(p + address.Start - _start.Start); - return new ItemData((*p1 & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD); - } - } - - public ItemData ReadInt16(DeviceAddress address) - { - return new ItemData(BitConverter.ToInt16(_cache, address.Start - _start.Start), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadByte(DeviceAddress address) - { - return new ItemData(_cache[address.Start - _start.Start], 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadString(DeviceAddress address, ushort size = 64) - { - return new ItemData(BitConverter.ToString(_cache, address.Start - _start.Start, size), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadFloat(DeviceAddress address) - { - return new ItemData(BitConverter.ToSingle(_cache, address.Start - _start.Start), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadValue(DeviceAddress address) - { - throw new NotImplementedException(); - } - - public int WriteBit(DeviceAddress address, bool bit) - { - byte b = _cache[address.Start - _start.Start]; - b |= (byte)(1 << address.Bit); - return 0; - } - - public int WriteBits(DeviceAddress address, byte bits) - { - _cache[address.Start - _start.Start] = bits; - return 0; - } - - public int WriteInt16(DeviceAddress address, short value) - { - int index = address.Start - _start.Start; - fixed (byte* p1 = _cache) - { - byte* numPtr = p1 + index; - if ((((int)numPtr) & 1) == 0) - { - *((short*)numPtr) = value; - } - else - { - byte* numPtr2 = (byte*)&value; - numPtr[0] = numPtr2[0]; - numPtr[1] = numPtr2[1]; - } - } - return 0; - } - - public int WriteInt32(DeviceAddress address, int value) - { - int index = address.Start - _start.Start; - fixed (byte* p1 = _cache) - { - byte* numPtr = p1 + index; - if ((((int)numPtr) & 3) == 0) - { - *((int*)numPtr) = value; - } - else - { - byte* numPtr2 = (byte*)&value; - numPtr[0] = numPtr2[0]; - numPtr[1] = numPtr2[1]; - numPtr[2] = numPtr2[2]; - numPtr[3] = numPtr2[3]; - } - } - return 0; - } - - public int WriteFloat(DeviceAddress address, float value) - { - return 0; - } - - public int WriteString(DeviceAddress address, string str) - { - byte[] b = System.Text.Encoding.Default.GetBytes(str); - int index = address.Start - _start.Start; - Array.Copy(_cache, index, b, 0, 64); - return 0; - } - - public int WriteValue(DeviceAddress address, object value) - { - throw new NotImplementedException(); - } - - public byte[] ReadBytes(DeviceAddress address, ushort size) - { - byte[] bytes = new byte[size]; - Array.Copy(_cache, address.Start - _start.Start, bytes, 0, size); - return bytes; - } - - public int WriteBytes(DeviceAddress address, byte[] bit) - { - if (bit != null && bit.Length > 0) - { - Array.Copy(bit, 0, _cache, address.Start - _start.Start, bit.Length); - return 1; - } - return -1; - } - - } - - public unsafe class ModbusCacheReader : ICache - { - byte[] _cache; - public byte[] Cache - { - get - { - return _cache; - } - } - - DeviceAddress _start; - public DeviceAddress Start - { - get - { - return _start; - } - set - { - _start = value; - } - } - - int _size; - public int Size - { - get - { - return _size; - } - set - { - _size = value; - this._cache = new byte[_start.Area > 2 ? 2 * _size : _size]; - } - } - - public ModbusCacheReader() - { - } - - public ModbusCacheReader(DeviceAddress start, int size) - { - this.Start = start; - this.Size = size; - } - - public int GetOffset(DeviceAddress start, DeviceAddress end) - { - return start.Area > 2 ? (start.Start - end.Start) << 1 : start.Start - end.Start; - } - - public ItemData ReadInt32(DeviceAddress address) - { - return new ItemData(BitConverter.ToInt32(_cache, GetOffset(address, _start)) - , 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadBit(DeviceAddress address) - { - fixed (byte* p = _cache) - { - int* p1 = (int*)(p + GetOffset(address, _start)); - return new ItemData((*p1 & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD); - } - } - - public ItemData ReadInt16(DeviceAddress address) - { - return new ItemData(BitConverter.ToInt16(_cache, GetOffset(address, _start)), - 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadByte(DeviceAddress address) - { - return new ItemData(_cache[GetOffset(address, _start)], 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadString(DeviceAddress address, ushort size = 64) - { - return new ItemData(BitConverter.ToString(_cache, GetOffset(address, _start), size), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadFloat(DeviceAddress address) - { - int value = BitConverter.ToInt32(_cache, GetOffset(address, _start)); - //value = IPAddress.HostToNetworkOrder(value); - return new ItemData(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadValue(DeviceAddress address) - { - throw new NotImplementedException(); - } - - public int WriteBit(DeviceAddress address, bool bit) - { - throw new NotImplementedException(); - } - - public int WriteBits(DeviceAddress address, byte bits) - { - throw new NotImplementedException(); - } - - public int WriteInt16(DeviceAddress address, short value) - { - throw new NotImplementedException(); - } - - public int WriteInt32(DeviceAddress address, int value) - { - throw new NotImplementedException(); - } - - public int WriteFloat(DeviceAddress address, float value) - { - throw new NotImplementedException(); - } - - public int WriteString(DeviceAddress address, string str) - { - throw new NotImplementedException(); - } - - public int WriteValue(DeviceAddress address, object value) - { - throw new NotImplementedException(); - } - - public byte[] ReadBytes(DeviceAddress address, ushort size) - { - throw new NotImplementedException(); - } - - public int WriteBytes(DeviceAddress address, byte[] bit) - { - throw new NotImplementedException(); - } - } - - - public unsafe class MXCacheReader : ICache - { - byte[] _cache; - public byte[] Cache - { - get - { - return _cache; - } - } - - DeviceAddress _start; - public DeviceAddress Start - { - get - { - return _start; - } - set - { - _start = value; - } - } - - int _size; - public int Size - { - get - { - return _size; - } - set - { - _size = value; - this._cache = new byte[_size]; - } - } - - public MXCacheReader() - { - } - - public MXCacheReader(DeviceAddress start, int size) - { - this.Start = start; - this.Size = size; - } - - static int GetOffset(DeviceAddress addr1) - { - switch (addr1.Area) - { - case MX_AREA.T: - case MX_AREA.D: - return addr1.Area + 2 * addr1.Start; - case MX_AREA.S: - case MX_AREA.M: - case MX_AREA.X: - case MX_AREA.Y: - return addr1.Area + (addr1.Start >> 3); - default: - return 0; - } - } - - public int GetOffset(DeviceAddress start, DeviceAddress end) - { - return GetOffset(start) - GetOffset(end); - } - - public ItemData ReadInt32(DeviceAddress address) - { - return new ItemData(BitConverter.ToInt32(_cache, GetOffset(address, _start)), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadBit(DeviceAddress address) - { - //if (address.Area == MX_AREA.D || address.Area == MX_AREA.T) - // return new ItemData(false, 0, QUALITIES.QUALITY_BAD); - fixed (byte* p = _cache) - { - short* p1 = (short*)(p + GetOffset(address, _start)); - return new ItemData((*p1 & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD); - } - } - - public ItemData ReadInt16(DeviceAddress address) - { - return new ItemData(BitConverter.ToInt16(_cache, GetOffset(address, _start)), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadByte(DeviceAddress address) - { - return new ItemData(_cache[GetOffset(address, _start)], 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadString(DeviceAddress address, ushort size = 64) - { - byte[] bytes = new byte[size]; - Array.Copy(_cache, GetOffset(address, _start), bytes, 0, size); - return new ItemData(Encoding.Default.GetString(bytes).Trim((char)0), 0, QUALITIES.QUALITY_GOOD); - //return new ItemData(BitConverter.ToString(_cache, GetOffset(address, _start), size>>3), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadFloat(DeviceAddress address) - { - return new ItemData(BitConverter.ToSingle(_cache, GetOffset(address, _start)), 0, QUALITIES.QUALITY_GOOD); - } - - public ItemData ReadValue(DeviceAddress address) - { - throw new NotImplementedException(); - } - - public int WriteBit(DeviceAddress address, bool bit) - { - throw new NotImplementedException(); - } - - public int WriteBits(DeviceAddress address, byte bits) - { - throw new NotImplementedException(); - } - - public int WriteInt16(DeviceAddress address, short value) - { - throw new NotImplementedException(); - } - - public int WriteInt32(DeviceAddress address, int value) - { - throw new NotImplementedException(); - } - - public int WriteFloat(DeviceAddress address, float value) - { - throw new NotImplementedException(); - } - - public int WriteString(DeviceAddress address, string str) - { - throw new NotImplementedException(); - } - - public int WriteValue(DeviceAddress address, object value) - { - throw new NotImplementedException(); - } - - public byte[] ReadBytes(DeviceAddress address, ushort size) - { - throw new NotImplementedException(); - } - - public int WriteBytes(DeviceAddress address, byte[] bit) - { - throw new NotImplementedException(); - } - - } -} diff --git a/SCADA/Program/DataService/obj/Debug/DataService.csproj.FileListAbsolute.txt b/SCADA/Program/DataService/obj/Debug/DataService.csproj.FileListAbsolute.txt index 00493a7..b499287 100644 --- a/SCADA/Program/DataService/obj/Debug/DataService.csproj.FileListAbsolute.txt +++ b/SCADA/Program/DataService/obj/Debug/DataService.csproj.FileListAbsolute.txt @@ -22,3 +22,4 @@ C:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\DataService\bin\Debug\D C:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\DataService\bin\Debug\DataService.pdb C:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\DataService\obj\Debug\DataService.dll C:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\DataService\obj\Debug\DataService.pdb +C:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\DataService\obj\Debug\DataService.csprojResolveAssemblyReference.cache diff --git a/SCADA/Program/DataService/obj/Debug/DataService.dll b/SCADA/Program/DataService/obj/Debug/DataService.dll index c158fd8a33cb0319b10a36117db02bf900598c3d..ee1ce68bd3a717c4b0e8f62a3da4a233ae9087ac 100644 GIT binary patch delta 94 zcmZpe!P+o`bwUS=rV>k3W6#ze#tt=s3nwk4;$zPF&t^KYzguvo!*&&Q#yyfcASDby vzy!oHj0_Av;uslzI508<1-k@`-Rc&2?6%uuZ+{|o+k;cSZ|8Aj{K5qQ2^Ax( diff --git a/SCADA/Program/ModbusDriver/ModbusTCPDriver.cs b/SCADA/Program/ModbusDriver/ModbusTCPDriver.cs index 5cdb07b..9f9a234 100644 --- a/SCADA/Program/ModbusDriver/ModbusTCPDriver.cs +++ b/SCADA/Program/ModbusDriver/ModbusTCPDriver.cs @@ -56,6 +56,16 @@ namespace ModbusDriver set { _timeout = value; } } + byte _slaveId;//设备ID 单元号 字节号 + /// + /// 设备ID 单元号 字节号 + /// + public byte SlaveId + { + get { return _slaveId; } + set { _slaveId = value; } + } + List _grps = new List(20); public IEnumerable Groups { @@ -75,6 +85,7 @@ namespace ModbusDriver _server = server; _ip = ip; _timeout = timeOut; + byte.TryParse(spare1, out _slaveId); } public bool Connect() @@ -105,12 +116,10 @@ namespace ModbusDriver private byte[] CreateReadHeader(int id, int startAddress, ushort length, byte function) { byte[] data = new byte[12]; - - byte[] _id = BitConverter.GetBytes((short)id); - data[0] = _id[0]; // Slave id high byte - data[1] = _id[1]; // Slave id low byte + data[0] = 0; // Slave id high byte + data[1] = 0; // Slave id low byte data[5] = 6; // Message size - data[6] = 0; // Slave address + data[6] = (byte)id; // Slave address data[7] = function; // Function code byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress)); data[8] = _adr[0]; // Start address @@ -124,14 +133,12 @@ namespace ModbusDriver private byte[] CreateWriteHeader(int id, int startAddress, ushort numData, ushort numBytes, byte function) { byte[] data = new byte[numBytes + 11]; - - byte[] _id = BitConverter.GetBytes(id); - data[0] = _id[0]; // Slave id high byte - data[1] = _id[1]; // Slave id low byte+ + data[0] = 0; // Slave id high byte + data[1] = 0; // Slave id low byte+ byte[] _size = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)(5 + numBytes))); data[4] = _size[0]; // Complete message size in bytes data[5] = _size[1]; // Complete message size in bytes - data[6] = 0; // Slave address + data[6] = (byte)id; // Slave address data[7] = function; // Function code byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress)); data[8] = _adr[0]; // Start address @@ -243,11 +250,12 @@ namespace ModbusDriver DeviceAddress dv = DeviceAddress.Empty; if (string.IsNullOrEmpty(address)) return dv; + dv.Area = _slaveId; switch (address[0]) { case '0': { - dv.Area = Modbus.fctReadCoil; + dv.DBNumber = Modbus.fctReadCoil; int st; int.TryParse(address, out st); //dv.Start = (st / 16) * 16;//??????????????????? @@ -258,7 +266,7 @@ namespace ModbusDriver break; case '1': { - dv.Area = Modbus.fctReadDiscreteInputs; + dv.DBNumber = Modbus.fctReadDiscreteInputs; int st; int.TryParse(address.Substring(1), out st); //dv.Start = (st / 16) * 16;//??????????????????? @@ -270,7 +278,7 @@ namespace ModbusDriver case '4': { int index = address.IndexOf('.'); - dv.Area = Modbus.fctReadHoldingRegister; + dv.DBNumber = Modbus.fctReadHoldingRegister; if (index > 0) { dv.Start = int.Parse(address.Substring(1, index - 1)); @@ -284,7 +292,7 @@ namespace ModbusDriver case '3': { int index = address.IndexOf('.'); - dv.Area = Modbus.fctReadInputRegister; + dv.DBNumber = Modbus.fctReadInputRegister; if (index > 0) { dv.Start = int.Parse(address.Substring(1, index - 1)); @@ -381,14 +389,14 @@ namespace ModbusDriver public byte[] ReadBytes(DeviceAddress address, ushort size) { - int area = address.Area; - return area < 2 ? WriteSyncData(CreateReadHeader(area, address.Start * 16, (ushort)(16 * size), (byte)area)) - : WriteSyncData(CreateReadHeader(area, address.Start, size, (byte)area)); + int area = address.DBNumber; + return area < 2 ? WriteSyncData(CreateReadHeader(address.Area, address.Start * 16, (ushort)(16 * size), (byte)area)) + : WriteSyncData(CreateReadHeader(address.Area, address.Start, size, (byte)area)); } public ItemData ReadInt32(DeviceAddress address) { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 2, (byte)address.Area)); + byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 2, (byte)address.DBNumber)); if (data == null) return new ItemData(0, 0, QUALITIES.QUALITY_BAD); else @@ -397,7 +405,7 @@ namespace ModbusDriver public ItemData ReadInt16(DeviceAddress address) { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.Area)); + byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.DBNumber)); if (data == null) return new ItemData(0, 0, QUALITIES.QUALITY_BAD); else @@ -406,7 +414,7 @@ namespace ModbusDriver public ItemData ReadByte(DeviceAddress address) { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.Area)); + byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.DBNumber)); if (data == null) return new ItemData(0, 0, QUALITIES.QUALITY_BAD); else @@ -415,7 +423,7 @@ namespace ModbusDriver public ItemData ReadString(DeviceAddress address, ushort size) { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, size, (byte)address.Area)); + byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, size, (byte)address.DBNumber)); if (data == null) return new ItemData(string.Empty, 0, QUALITIES.QUALITY_BAD); else @@ -424,7 +432,7 @@ namespace ModbusDriver public unsafe ItemData ReadFloat(DeviceAddress address) { - byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 2, (byte)address.Area)); + byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 2, (byte)address.DBNumber)); if (data == null) return new ItemData(0.0f, 0, QUALITIES.QUALITY_BAD); else @@ -436,10 +444,11 @@ namespace ModbusDriver public ItemData ReadBit(DeviceAddress address) { - byte[] data = address.Area > 2 ? WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.Area)) : - WriteSyncData(CreateReadHeader(address.Area, address.Start + address.Bit, 1, (byte)address.Area)); + byte[] data = address.DBNumber > 2 ? WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.DBNumber)) : + WriteSyncData(CreateReadHeader(address.Area, address.Start * 16 + address.Bit, 1, (byte)address.DBNumber)); if (data == null) return new ItemData(false, 0, QUALITIES.QUALITY_BAD); + if (data.Length == 1) return new ItemData(data[0] > 0, 0, QUALITIES.QUALITY_GOOD); unsafe { fixed (byte* p = data) @@ -458,14 +467,14 @@ namespace ModbusDriver public int WriteBytes(DeviceAddress address, byte[] bit) { - var data = address.Area > 2 ? WriteMultipleRegister(address.Area, address.Start, bit) + var data = address.DBNumber > 2 ? WriteMultipleRegister(address.Area, address.Start, bit) : WriteMultipleCoils(address.Area, address.Start, (ushort)(8 * bit.Length), bit);//应考虑到 return data == null ? -1 : 0; } public int WriteBit(DeviceAddress address, bool bit) { - if (address.Area < 3) + if (address.DBNumber < 3) { var data = WriteSingleCoils(address.Area, address.Start + address.Bit, bit); return data == null ? -1 : 0; @@ -574,7 +583,7 @@ namespace ModbusDriver { while (addr.Start == next.Start) { - if ((tmp & (1 << next.Bit.BitSwap())) > 0) _changedList.Add(index); + if ((tmp & (1 << next.Bit)) > 0) _changedList.Add(index); if (++index < count) next = _items[index].Address; else diff --git a/SCADA/Program/ModbusDriver/bin/Debug/DataService.dll b/SCADA/Program/ModbusDriver/bin/Debug/DataService.dll index c158fd8a33cb0319b10a36117db02bf900598c3d..e33eb42ae88794ab81f1f62664d48554ec190964 100644 GIT binary patch delta 94 zcmZpe!P+o`bwUTr0tJ?+#-6P`j2&tM8~-ee_iexIG|o+k;cSZ|8Aj{K5qQ2^Ax( diff --git a/SCADA/Program/ModbusDriver/bin/Debug/ModbusDriver.dll b/SCADA/Program/ModbusDriver/bin/Debug/ModbusDriver.dll index 4848de0bc7f9ae90e121e301ed472a5311bf347d..c22e7130a13bc4bea0595332e6db385c254bba31 100644 GIT binary patch delta 7725 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 delta 7551 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 Z+sj?n)e%|zt!cCm>quSpThUq;_&@R*IZFTl diff --git a/SCADA/Program/ModbusDriver/obj/Debug/ModbusDriver.dll b/SCADA/Program/ModbusDriver/obj/Debug/ModbusDriver.dll index 4848de0bc7f9ae90e121e301ed472a5311bf347d..c22e7130a13bc4bea0595332e6db385c254bba31 100644 GIT binary patch delta 7725 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 delta 7551 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 Z+sj?n)e%|zt!cCm>quSpThUq;_&@R*IZFTl diff --git a/SCADA/dll/ModbusDriver.dll b/SCADA/dll/ModbusDriver.dll index 87f64a8a315663af393d7db583833d752b9c85ae..c22e7130a13bc4bea0595332e6db385c254bba31 100644 GIT binary patch literal 28160 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 Hf;8}7`hCh- literal 26112 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 C3DJoF