using DataService; using System; using System.Collections.Generic; using System.ComponentModel; using System.Net; using System.Net.Sockets; using System.Text; namespace OmronPlcDriver { [Description("Omron(CS/CJ) UDP协议")] public sealed class OmronCsCjUDPReader : IPLCDriver, IMultiReadWrite //IPLCDriver : IDriver, IReaderWriter IDriver : IDisposable { #region /****************************************/ //更新人: zjf //更新内容:新增_pdu,可以根据实际情况进行采集 //更新日期:20171205 //更新原因:根据现场进行参数调整以提高采集响应速度或者减小网络压力 /***************************************/ /// /// PDU的值,包大小上限 /// int _pdu; /// /// 获取PDU的值 /// public int PDU { get { return _pdu; } set { _pdu = value; } } /// /// 获取设备地址 /// /// /// 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(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; } dv.ByteOrder = ByteOrder.Network; 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 int Port { get { return _port; } set { _port = value; } } public string ServerName { get { return _ip; } set { _ip = value; } } /// /// 是否关闭 /// 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 OmronCsCjUDPReader(IDataServer server, short id, string name) { _id = id; _name = name; _server = server; } /// /// 连接 /// /// public bool Connect() { try { Console.WriteLine("开始连接"); if (udpSynCl != null) udpSynCl.Close(); //IPAddress ip = IPAddress.Parse(_ip); // ---------------------------------------------------------------- // Connect synchronous client udpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); udpSynCl.SendTimeout = _timeout; udpSynCl.ReceiveTimeout = _timeout; udpSynCl.Connect(_ip, _port); return true; } catch (SocketException error) { if (OnClose != null) OnClose(this, new ShutdownRequestEventArgs(error.Message)); return false; } } /// /// 生成读命令头 /// /// 电脑节点号,设置和PLC节点不一致即可 /// 读取的起始地址 /// 读取长度,2个字节为一个单位 /// /// PLC节点号,可为0 /// private byte[] CreateReadHeader(byte pcnode, int startAddress, ushort length, byte function, byte plcnode = 0) { //80 00 00 14 00 00 00 FD 00 00 01 01 00 00 00 00 00 01 //80 00 02 00 41 00 00 0B 00 00 01 01 82 00 64 00 00 14 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 if (function == 0x1) { data = new byte[(write_data[16] * 256 + write_data[17]) * 2]; Array.Copy(udpSynClBuffer, 14, data, 0, data.Length); } else { return null; } 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) { NetShortGroup grp = new NetShortGroup(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; //Console.WriteLine("OmronReader错误->" + GetErrorString(exception)); 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), (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); } public ItemData ReadUInt32(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((uint)IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0)), 0, QUALITIES.QUALITY_GOOD); } public ItemData ReadUInt16(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((ushort)IPAddress.HostToNetworkOrder(BitConverter.ToInt16(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(IPAddress.HostToNetworkOrder(value)), (byte)address.Area); return data == null ? -1 : 0; } public int WriteUInt16(DeviceAddress address, ushort value) { var data = WriteSingleRegister(PcNodeId, address.Start, (byte)address.DBNumber, BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)value)), (byte)address.Area); return data == null ? -1 : 0; } public int WriteUInt32(DeviceAddress address, uint value) { var data = WriteMultipleRegister(PcNodeId, address.Start, (byte)address.DBNumber, BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder((int)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(IPAddress.HostToNetworkOrder(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 960; } } 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); } } /// /// 欧姆龙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; } }