diff --git a/SCADA/Program/DataExchange.sln b/SCADA/Program/DataExchange.sln index 492bf4f..3a0ccad 100644 --- a/SCADA/Program/DataExchange.sln +++ b/SCADA/Program/DataExchange.sln @@ -33,6 +33,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagConfig", "TagConfig\TagC EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PanasonicDriver", "PanasonicDriver\PanasonicDriver.csproj", "{0D1C943D-594F-4E07-AA39-90CEC4E37C8E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OmronPlcDriver", "OmronPlcDriver\OmronPlcDriver.csproj", "{BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -299,6 +301,26 @@ Global {0D1C943D-594F-4E07-AA39-90CEC4E37C8E}.Release|x64.Build.0 = Release|Any CPU {0D1C943D-594F-4E07-AA39-90CEC4E37C8E}.Release|x86.ActiveCfg = Release|Any CPU {0D1C943D-594F-4E07-AA39-90CEC4E37C8E}.Release|x86.Build.0 = Release|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Debug|Itanium.ActiveCfg = Debug|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Debug|Itanium.Build.0 = Debug|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Debug|x64.Build.0 = Debug|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Debug|x86.Build.0 = Debug|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Release|Any CPU.Build.0 = Release|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Release|Itanium.ActiveCfg = Release|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Release|Itanium.Build.0 = Release|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Release|x64.ActiveCfg = Release|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Release|x64.Build.0 = Release|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Release|x86.ActiveCfg = Release|Any CPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SCADA/Program/ModbusDriver/ModbusTCPDriver.cs b/SCADA/Program/ModbusDriver/ModbusTCPDriver.cs index a4f4d6f..febbb3e 100644 --- a/SCADA/Program/ModbusDriver/ModbusTCPDriver.cs +++ b/SCADA/Program/ModbusDriver/ModbusTCPDriver.cs @@ -236,49 +236,53 @@ namespace ModbusDriver return data; } + object _async = new object(); private byte[] WriteSyncData(byte[] write_data) { short id = BitConverter.ToInt16(write_data, 0); if (IsClosed) CallException(id, write_data[7], Modbus.excExceptionConnectionLost); else { - try + lock (_async) { - tcpSynCl.Send(write_data, 0, write_data.Length, SocketFlags.None);//是否存在lock的问题? - int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None); + try + { + tcpSynCl.Send(write_data, 0, write_data.Length, SocketFlags.None);//是否存在lock的问题? + int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None); - byte function = tcpSynClBuffer[7]; - byte[] data; + byte function = tcpSynClBuffer[7]; + byte[] data; - if (result == 0) CallException(id, write_data[7], Modbus.excExceptionConnectionLost); + if (result == 0) CallException(id, write_data[7], Modbus.excExceptionConnectionLost); - // ------------------------------------------------------------ - // Response data is slave ModbusModbus.exception - if (function > Modbus.excExceptionOffset) - { - function -= Modbus.excExceptionOffset; - CallException(id, function, tcpSynClBuffer[8]); - return null; - } - // ------------------------------------------------------------ - // Write response data - else if ((function >= Modbus.fctWriteSingleCoil) && (function != Modbus.fctReadWriteMultipleRegister)) - { - data = new byte[2]; - Array.Copy(tcpSynClBuffer, 10, data, 0, 2); + // ------------------------------------------------------------ + // Response data is slave ModbusModbus.exception + if (function > Modbus.excExceptionOffset) + { + function -= Modbus.excExceptionOffset; + CallException(id, function, tcpSynClBuffer[8]); + return null; + } + // ------------------------------------------------------------ + // Write response data + else if ((function >= Modbus.fctWriteSingleCoil) && (function != Modbus.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; } - // ------------------------------------------------------------ - // Read response data - else + catch (SocketException) { - data = new byte[tcpSynClBuffer[8]]; - Array.Copy(tcpSynClBuffer, 9, data, 0, tcpSynClBuffer[8]); + CallException(id, write_data[7], Modbus.excExceptionConnectionLost); } - return data; - } - catch (SocketException) - { - CallException(id, write_data[7], Modbus.excExceptionConnectionLost); } } return null; @@ -568,7 +572,7 @@ namespace ModbusDriver byte[] rcvBytes = _plcReader.ReadBytes(area.Start, (ushort)area.Len);//从PLC读取数据 if (rcvBytes == null || rcvBytes.Length == 0) { - offset += area.Len / 2; + //offset += area.Len / 2; //_plcReader.Connect(); continue; } diff --git a/SCADA/Program/OmronPlcDriver/OmronPlcDriver.csproj b/SCADA/Program/OmronPlcDriver/OmronPlcDriver.csproj new file mode 100644 index 0000000..ae3c4d3 --- /dev/null +++ b/SCADA/Program/OmronPlcDriver/OmronPlcDriver.csproj @@ -0,0 +1,55 @@ + + + + + Debug + AnyCPU + {BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1} + Library + Properties + OmronPlcDriver + OmronPlcDriver + v4.0 + 512 + Client + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + {8965e389-6466-4b30-bd43-83c909044637} + DataService + + + + + \ No newline at end of file diff --git a/SCADA/Program/OmronPlcDriver/OmronUdpReader.cs b/SCADA/Program/OmronPlcDriver/OmronUdpReader.cs new file mode 100644 index 0000000..7778e61 --- /dev/null +++ b/SCADA/Program/OmronPlcDriver/OmronUdpReader.cs @@ -0,0 +1,875 @@ +using DataService; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Timers; + +namespace OmronPlcDriver +{ + [Description("Omron(CS/CJ) UDP协议")] + public sealed class OmronUdpReader : IPLCDriver, IMultiReadWrite + { + #region + + /****************************************/ + //更新人:新增_pdu,可以根据实际情况进行采集 + //更新日期:20171205 + //更新原因:根据现场进行参数调整以提供采集响应速度 + /***************************************/ + /// + /// PDU的值 + /// + int _pdu; + /// + /// 获取PDU的值 + /// + public int PDU + { + get { return _pdu; } + } + /// + /// 获取设备地址 + /// + /// + /// + public DeviceAddress GetDeviceAddress(string address) + { + DeviceAddress dv = DeviceAddress.Empty; + if (string.IsNullOrEmpty(address)) + return dv; + dv.Area = _plcNodeId; + switch (address[0]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + int index = address.IndexOf('.'); + dv.DBNumber = OmronCSCJ.fctCIO; + if (index > 0) + { + dv.Start = int.Parse(address.Substring(0, index - 1)); + dv.Bit = byte.Parse(address.Substring(index + 1)); + } + else + dv.Start = int.Parse(address); + } + break; + case 'D'://DM区 + { + int index = address.IndexOf('.'); + dv.DBNumber = OmronCSCJ.fctDM; + 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(index + 1)); + } + break; + case 'H'://HR区 + { + int index = address.IndexOf('.'); + dv.DBNumber = OmronCSCJ.fctHR; + 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)); + } + break; + case 'A'://AR区 + { + int index = address.IndexOf('.'); + dv.DBNumber = OmronCSCJ.fctA; + 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)); + } + break; + } + return dv; + } + + public string GetAddress(DeviceAddress address) + { + return string.Empty; + } + + #endregion + private int _timeout;//超时数据 + + private Socket udpSynCl; + private byte[] udpSynClBuffer = new byte[1024]; + + short _id;//驱动id + //驱动id + public short ID + { + get + { + return _id; + } + } + + string _name;//驱动名称 + /// + /// 驱动名称 + /// + public string Name + { + get + { + return _name; + } + } + + string _ip;//服务ip + int _port = 9600; //服务端口 + public string ServerName + { + get { return _ip + ":" + _port; } + set + { + if (!string.IsNullOrEmpty(value)) + { + int index = value.IndexOf(':'); + _ip = value.Substring(0, index - 1);//ip地址 + _port = int.Parse(value.Substring(index + 1));//端口号 + } + } + } + /// + /// 是否关闭 + /// + public bool IsClosed + { + get + { + return udpSynCl == null || udpSynCl.Connected == false; + } + } + /// + /// 超时时间 + /// + + public int TimeOut + { + get { return _timeout; } + set { _timeout = value; } + } + + byte _plcNodeId;//plc节点号 + /// + /// plc节点号,从para1参数读取 + /// + public byte PlcNodeId + { + get { return _pcNodeId; } + set { _pcNodeId = value; } + } + + byte _pcNodeId;//电脑节点号 + /// + /// 电脑节点号,从para2参数读取 + /// + public byte PcNodeId + { + get { return _pcNodeId; } + set { _pcNodeId = value; } + } + + List _grps = new List(20); + public IEnumerable Groups + { + get { return _grps; } + } + + IDataServer _server; + public IDataServer Parent + { + get { return _server; } + } + + public OmronUdpReader(IDataServer server, short id, string name, string servername, int timeOut = 500, string spare1 = null, string spare2 = null) + { + _id = id; + _name = name; + _server = server; + if (!string.IsNullOrEmpty(servername)) + { + int index = servername.IndexOf(':'); + _ip = servername.Substring(0, index - 1);//ip地址 + _port = int.Parse(servername.Substring(index + 1));//端口号 + } + _timeout = timeOut; + byte.TryParse(spare1, out _plcNodeId); + byte.TryParse(spare2, out _pcNodeId); + } + + /// + /// 连接 + /// + /// + public bool Connect() + { + try + { + if (udpSynCl != null) + udpSynCl.Close(); + //IPAddress ip = IPAddress.Parse(_ip); + // ---------------------------------------------------------------- + // Connect synchronous client + udpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Udp); + udpSynCl.SendTimeout = _timeout; + udpSynCl.ReceiveTimeout = _timeout; + udpSynCl.NoDelay = true; + udpSynCl.Connect(_ip, _port); + return true; + } + catch (SocketException error) + { + if (OnClose != null) + OnClose(this, new ShutdownRequestEventArgs(error.Message)); + return false; + } + } + /// + /// 生成读命令头 + /// + /// 电脑节点号,设置和PLC节点不一致即可 + /// 读取的起始地址 + /// 读取长度 + /// + /// PLC节点号,可为0 + /// + private byte[] CreateReadHeader(byte pcnode, int startAddress, ushort length, byte function, byte plcnode = 0) + { + byte[] data = new byte[18]; + data[0] = 0x80; + data[1] = 0; + data[2] = 0; //80 00 02 固定帧头 + data[2] = 0; + data[3] = plcnode; + data[5] = 0; //设备的网络号,节点号,单元号 + data[6] = 0; + data[7] = pcnode; + data[8] = 0; //PC的网络号,节点号,单元号 + data[9] = 0; + data[10] = 1; + data[11] = 1; //SID+MRC+SRC + data[12] = function; //数据区代码 + byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress)); + data[13] = _adr[0]; // 首地址高字节 + data[14] = _adr[1]; // 首地址低字节 + data[15] = 0; // 固定0 + byte[] _length = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)length)); + data[16] = _length[0]; // 读取数量高字节 + data[17] = _length[1]; // 读取数量低字节 + return data; + } + /// + /// 生成写命令头 + /// + /// + /// + /// + /// + /// + /// + private byte[] CreateWriteHeader(byte pcnode, int startAddress, ushort numData, byte function, byte plcnode = 0) + { + byte[] data = new byte[numData + 18]; + data[0] = 0x80; + data[1] = 0; + data[2] = 0; //80 00 02 固定帧头 + data[3] = 0; + data[4] = plcnode; + data[5] = 0; //设备的网络号,节点号,单元号 + data[6] = 0; + data[7] = pcnode; + data[8] = 0; //PC的网络号,节点号,单元号 + data[9] = 0; + data[10] = 1; + data[11] = 2; //SID+MRC+SRC + data[12] = function; //数据区代码 + byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress)); + data[13] = _adr[0]; // 首地址高字节 + data[14] = _adr[1]; // 首地址低字节 + data[15] = 0; // 固定0 + byte[] length = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)numData / 2)); + data[16] = length[0]; // 写取数量高字节 + data[17] = length[1]; // 写取数量低字节 + return data; + } + /// + /// 同步写数据到udp + /// + /// 写的字节数组 + /// + private byte[] WriteSyncData(byte[] write_data) + { + short id = BitConverter.ToInt16(write_data, 4); + if (IsClosed) CallException(id, write_data[12], OmronCSCJ.excExceptionConnectionLost); + else + { + try + { + udpSynCl.Send(write_data, 0, write_data.Length, SocketFlags.None);//是否存在lock的问题? + int result = udpSynCl.Receive(udpSynClBuffer, 0, 1024, SocketFlags.None); + + byte function = udpSynClBuffer[11];//读写功能 + byte[] data; + + int err = udpSynClBuffer[12] * 256 + udpSynClBuffer[13]; + + if (result == 0) CallException(id, write_data[12], OmronCSCJ.excExceptionConnectionLost); + + // ------------------------------------------------------------ + // Response data is slave ModbusModbus.exception + if (err != 0) + { + CallException(id, function, 4); + return null; + } + // ------------------------------------------------------------ + // Write response data + else if (function == 0x2) + { + data = new byte[2]; + Array.Copy(udpSynClBuffer, 10, data, 0, 2); + } + // ------------------------------------------------------------ + // Read response data + else + { + data = new byte[(write_data[16] * 256 + write_data[17]) * 2]; + Array.Copy(udpSynClBuffer, 14, data, 0, data.Length); + } + return data; + } + catch (SocketException) + { + CallException(id, write_data[12], OmronCSCJ.excExceptionConnectionLost); + } + } + return null; + } + + /// + /// 写单个寄存器 + /// + /// 电脑节点号 + /// 起始地址 + /// 内存区功能吗 + /// 写值数组 + /// plc节点号 + /// + public byte[] WriteSingleRegister(byte pcnode, int startAddress, byte function, byte[] values, byte plcnode = 0) + { + byte[] data; + data = CreateWriteHeader(pcnode, startAddress, 2, function, plcnode); + data[19] = values[0]; + data[20] = values[1]; + return WriteSyncData(data); + } + /// + /// 写多个寄存器 + /// + /// 电脑节点号 + /// 起始地址 + /// 内存区功能号 + /// 写值数组 + /// plc节点号 + /// + public byte[] WriteMultipleRegister(byte pcnode, int startAddress, byte function, byte[] values, byte plcnode = 0) + { + ushort numBytes = Convert.ToUInt16(values.Length); + if (numBytes % 2 > 0) numBytes++; + byte[] data; + + data = CreateWriteHeader(pcnode, startAddress, numBytes, function, plcnode); + Array.Copy(values, 0, data, 19, values.Length); + return WriteSyncData(data); + } + + public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false) + { + OmronUdpGroup grp = new OmronUdpGroup(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 (udpSynCl != null) + { + try { udpSynCl.Shutdown(SocketShutdown.Both); } + catch { } + udpSynCl.Close(); + + udpSynCl = null; + } + foreach (IGroup grp in _grps) + { + grp.Dispose(); + } + _grps.Clear(); + } + + internal string GetErrorString(byte exception) + { + switch (exception) + { + case OmronCSCJ.excIllegalFunction: + return "Constant for OmronCSCJ.exception illegal function."; + case OmronCSCJ.excIllegalDataAdr: + return "Constant for OmronCSCJ.exception illegal data address."; + case OmronCSCJ.excIllegalDataVal: + return "Constant for OmronCSCJ.exception illegal data value."; + case OmronCSCJ.excSlaveDeviceFailure: + return "Constant for OmronCSCJ.exception slave device failure."; + case OmronCSCJ.excAck: + return "Constant for OmronCSCJ.exception acknowledge."; + case OmronCSCJ.excSlaveIsBusy: + return "Constant for OmronCSCJ.exception slave is busy/booting up."; + case OmronCSCJ.excGatePathUnavailable: + return "Constant for OmronCSCJ.exception gate path unavailable."; + case OmronCSCJ.excExceptionNotConnected: + return "Constant for OmronCSCJ.exception not connected."; + case OmronCSCJ.excExceptionConnectionLost: + return "Constant for OmronCSCJ.exception connection lost."; + case OmronCSCJ.excExceptionTimeout: + return "Constant for OmronCSCJ.exception response timeout."; + case OmronCSCJ.excExceptionOffset: + return "Constant for OmronCSCJ.exception wrong offset."; + case OmronCSCJ.excSendFailt: + return "Constant for OmronCSCJ.exception send failt."; + } + return string.Empty; + } + + internal void CallException(int id, byte function, byte exception) + { + if (udpSynCl == null) return; + if (exception == OmronCSCJ.excExceptionConnectionLost && IsClosed == false) + { + if (OnClose != null) + OnClose(this, new ShutdownRequestEventArgs(GetErrorString(exception))); + } + } + + /// + /// 读取字节数组 + /// + /// 标签变量地址结构 + /// 长度 + /// + public byte[] ReadBytes(DeviceAddress address, ushort size) + { + ushort len = size; + if (len % 2 != 0) + { + len++; + } + return WriteSyncData(CreateReadHeader(PcNodeId, address.Start, (ushort)(len / 2), (byte)address.DBNumber, (byte)address.Area)); + } + /// + /// 读取32位整数 + /// + /// 标签变量地址结构 + /// + public ItemData ReadInt32(DeviceAddress address) + { + byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, 2, (byte)address.DBNumber, (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); + } + /// + /// 读取16位整数 + /// + /// 标签变量地址结构 + /// + public ItemData ReadInt16(DeviceAddress address) + { + byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, 1, (byte)address.DBNumber, (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); + } + /// + /// 读取1字节 + /// + /// 标签变量地址结构 + /// + public ItemData ReadByte(DeviceAddress address) + { + byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, 1, (byte)address.DBNumber, (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(PcNodeId, address.Start, size, (byte)address.DBNumber, (byte)address.Area)); + if (data == null) + return new ItemData(string.Empty, 0, QUALITIES.QUALITY_BAD); + else + return new ItemData(Encoding.ASCII.GetString(data, 0, data.Length), 0, QUALITIES.QUALITY_GOOD);//是否考虑字节序问题? + } + /// + /// 读取32位浮点数 + /// + /// 标签变量地址结构 + /// + public unsafe ItemData ReadFloat(DeviceAddress address) + { + byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, 2, (byte)address.DBNumber, (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); + } + } + /// + /// 读取1位 + /// + /// 标签变量地址结构体 + /// + public ItemData ReadBit(DeviceAddress address) + { + byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, 1, (byte)address.DBNumber, (byte)address.Area)); + 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) + { + short* p1 = (short*)p; + return new ItemData((*p1 & (1 << address.Bit.BitSwap())) + != 0, 0, QUALITIES.QUALITY_GOOD); + } + } + } + /// + /// 读object类型 + /// + /// + /// + public ItemData ReadValue(DeviceAddress address) + { + return this.ReadValueEx(address); + } + /// + /// 写字节数组到设备 + /// + /// 标签变量地址结构 + /// 需写的字节数组 + /// + public int WriteBytes(DeviceAddress address, byte[] bits) + { + var data = WriteMultipleRegister(PcNodeId, address.Start, (byte)address.DBNumber, bits, (byte)address.Area); + return data == null ? -1 : 0; + } + + public int WriteBit(DeviceAddress address, bool bit) + { + ItemData item = ReadInt16(address); + short value = (short)((bit ? 1 : 0) << (int)address.Bit); + short result = (short)(item.Value | value); + var data = WriteSingleRegister(PcNodeId, address.Start, (byte)address.DBNumber, BitConverter.GetBytes(result), (byte)address.Area); + return data == null ? -1 : 0; + } + + public int WriteBits(DeviceAddress address, byte bits) + { + var data = WriteSingleRegister(PcNodeId, address.Start, (byte)address.DBNumber, new byte[] { bits }, (byte)address.Area); + return data == null ? -1 : 0; + } + + public int WriteInt16(DeviceAddress address, short value) + { + var data = WriteSingleRegister(PcNodeId, address.Start, (byte)address.DBNumber, BitConverter.GetBytes(value), (byte)address.Area); + return data == null ? -1 : 0; + } + + public int WriteInt32(DeviceAddress address, int value) + { + var data = WriteMultipleRegister(PcNodeId, address.Start, (byte)address.DBNumber, BitConverter.GetBytes(value), (byte)address.Area); + return data == null ? -1 : 0; + } + + public int WriteFloat(DeviceAddress address, float value) + { + var data = WriteMultipleRegister(PcNodeId, address.Start, (byte)address.DBNumber, BitConverter.GetBytes(value), (byte)address.Area); + return data == null ? -1 : 0; + } + + public int WriteString(DeviceAddress address, string str) + { + var data = WriteMultipleRegister(PcNodeId, address.Start, (byte)address.DBNumber, Encoding.ASCII.GetBytes(str), (byte)address.Area); + return data == null ? -1 : 0; + } + + public int WriteValue(DeviceAddress address, object value) + { + return this.WriteValueEx(address, value); + } + + public event ShutdownRequestEventHandler OnClose; + + public int Limit + { + get { return 60; } + } + + public ItemData[] ReadMultiple(DeviceAddress[] addrsArr) + { + return this.PLCReadMultiple(new NetShortCacheReader(), addrsArr); + } + + public int WriteMultiple(DeviceAddress[] addrArr, object[] buffer) + { + return this.PLCWriteMultiple(new NetShortCacheReader(), addrArr, buffer, Limit); + } + } + + public sealed class OmronUdpGroup : PLCGroup + { + public OmronUdpGroup(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 NetShortCacheReader(); + } + + protected override unsafe void Poll() + { + short[] cache = (short[])_cacheReader.Cache; + int offset = 0; + foreach (PDUArea area in _rangeList) + { + byte[] rcvBytes = _plcReader.ReadBytes(area.Start, (ushort)area.Len);//从PLC读取数据 + if (rcvBytes == null || rcvBytes.Length == 0) + { + //_plcReader.Connect(); + 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 - offset; + 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.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++; + } + } + for (int j = 0; j < len; j++) + { + cache[j + offset] = prcv[j]; + }//将PLC读取的数据写入到CacheReader中 + } + offset += len; + } + } + } + } + + /// + /// 欧姆龙Omron CS/CJ系列PLC数据区功能号和错误代码常数定义类 + /// + public sealed class OmronCSCJ + { + /*****************************************************/ + //OMRON PLC CS/CJ系列数据区功能号 + /****************************************************/ + /// + /// CIO 0000 to CIO 6143 + /// + public const byte fctCIO = 0xB0;//CIO 0000 to CIO 6143 + /// + /// H000 to H511 + /// + public const byte fctHR = 0xB2;//H000 to H511 + /// + /// A448 to A959 + /// + public const byte fctA = 0xB3;//A448 to A959 + /// + /// //D00000 to D32767 + /// + public const byte fctDM = 0x82;//D00000 to D32767 + /// + /// E0_E00000 to E0_E32765 + /// + public const byte fctE0 = 0xA0;//E0_E00000 to E0_E32765 + /// + /// E1_E00000 to E1_E32765 + /// + public const byte fctE1 = 0xA1;//E1_E00000 to E1_E32765 + /// + /// E2_E00000 to E2_E32765 + /// + public const byte fctE2 = 0xA2;//E2_E00000 to E2_E32765 + /// + /// E3_E00000 to E3_E32765 + /// + public const byte fctE3 = 0xA3; + /// + /// E4_E00000 to E4_E32765 + /// + public const byte fctE4 = 0xA4;//E4_E00000 to E4_E32765 + /// + /// E5_E00000 to E5_E32765 + /// + public const byte fctE5 = 0xA5;//E5_E00000 to E5_E32765 + /// + /// E6_E00000 to E6_E32765 + /// + public const byte fctE6 = 0xA6;//E6_E00000 to E6_E32765 + /// + /// E7_E00000 to E7_E32765 + /// + public const byte fctE7 = 0xA7;//E7_E00000 to E7_E32765 + /// + /// E8_E00000 to E8_E32765 + /// + public const byte fctE8 = 0xA8;//E8_E00000 to E8_E32765 + /// + /// E9_E00000 to E9_E32765 + /// + public const byte fctE9 = 0xA9;//E9_E00000 to E9_E32765 + /// + /// EA_E00000 to EA_E32765 + /// + public const byte fctEA = 0xAA;//EA_E00000 to EA_E32765 + /// + /// EB_E00000 to EB_E32765 + /// + public const byte fctEB = 0xAB;//EB_E00000 to EB_E32765 + /// + /// EC_E00000 to EC_E32765 + /// + public const byte fctEC = 0xAC;//EC_E00000 to EC_E32765 + /// + /// E00000 to E32765 + /// + public const byte fctCurrentbank = 98;//E00000 to E32765 + + + /// 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. + public const byte excExceptionOffset = 128; + /// Constant for exception send failt. + public const byte excSendFailt = 100; + } +} diff --git a/SCADA/Program/OmronPlcDriver/Properties/AssemblyInfo.cs b/SCADA/Program/OmronPlcDriver/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5483ec6 --- /dev/null +++ b/SCADA/Program/OmronPlcDriver/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OmronPlcDriver")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("OmronPlcDriver")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("baf8082b-8dd8-4e7c-9c5b-d9213e08c3b1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SCADA/dll/ModbusDriver.dll b/SCADA/dll/ModbusDriver.dll index 5acf584..18131be 100644 Binary files a/SCADA/dll/ModbusDriver.dll and b/SCADA/dll/ModbusDriver.dll differ diff --git a/SCADA/dll/OmronPlcDriver.dll b/SCADA/dll/OmronPlcDriver.dll new file mode 100644 index 0000000..c3ccb6e Binary files /dev/null and b/SCADA/dll/OmronPlcDriver.dll differ