You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
518 lines
17 KiB
518 lines
17 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Timers;
|
|
using DataService;
|
|
using Tuxeip;
|
|
|
|
namespace ABPLCDriver
|
|
{
|
|
[Description("EtherNet IP协议")]
|
|
public unsafe class ABEtherNetReader : IPLCDriver
|
|
{
|
|
Eip_Connection* connection;
|
|
Eip_Session* session;
|
|
byte _rack = 1;
|
|
byte _slot;
|
|
string _ip;
|
|
|
|
short _id;
|
|
public short ID
|
|
{
|
|
get
|
|
{
|
|
return _id;
|
|
}
|
|
}
|
|
|
|
public bool IsClosed
|
|
{
|
|
get
|
|
{
|
|
return connection == null || session == null;
|
|
}
|
|
}
|
|
|
|
public int PDU
|
|
{
|
|
get
|
|
{
|
|
return 492;
|
|
}
|
|
}
|
|
|
|
int _timeOut = 1000;
|
|
public int TimeOut
|
|
{
|
|
get
|
|
{
|
|
return _timeOut;
|
|
}
|
|
set
|
|
{
|
|
_timeOut = value;
|
|
}
|
|
}
|
|
|
|
string _name;
|
|
public string Name
|
|
{
|
|
get
|
|
{
|
|
return _name;
|
|
}
|
|
}
|
|
|
|
public string ServerName
|
|
{
|
|
get
|
|
{
|
|
return _ip;
|
|
}
|
|
set
|
|
{
|
|
_ip = value;
|
|
}
|
|
}
|
|
|
|
|
|
public byte Rack
|
|
{
|
|
get
|
|
{
|
|
return _rack;
|
|
}
|
|
set
|
|
{
|
|
_rack = value;
|
|
}
|
|
}
|
|
|
|
public byte Slot
|
|
{
|
|
get
|
|
{
|
|
return _slot;
|
|
}
|
|
set
|
|
{
|
|
_slot = value;
|
|
}
|
|
}
|
|
|
|
public ABEtherNetReader(IDataServer server, short id, string name)
|
|
{
|
|
_id = id;
|
|
_server = server;
|
|
_name = name;
|
|
}
|
|
|
|
public bool Connect()
|
|
{
|
|
try
|
|
{
|
|
if (session != null) Dispose();
|
|
session = Tuxeip_Class.OpenSession(_ip);
|
|
byte[] path = new byte[] { _rack, _slot };
|
|
if (session != null)
|
|
{
|
|
int res = Tuxeip_Class._RegisterSession(session);
|
|
connection = Tuxeip_Class.ConnectPLCOverCNET(session, Plc_Type.LGX, path);
|
|
}
|
|
return (session != null && connection != null);
|
|
}
|
|
catch (Exception error)
|
|
{
|
|
if (OnClose != null)
|
|
{
|
|
OnClose(this, new ShutdownRequestEventArgs(error.Message));
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
List<IGroup> _groups = new List<IGroup>();
|
|
public IEnumerable<IGroup> Groups
|
|
{
|
|
get { return _groups; }
|
|
}
|
|
|
|
IDataServer _server;
|
|
public IDataServer Parent
|
|
{
|
|
get { return _server; }
|
|
}
|
|
|
|
public IGroup AddGroup(string name, short id, int updateRate, float deadBand, bool active)
|
|
{
|
|
ABPLCGroup grp = new ABPLCGroup(id, name, updateRate, active, this);
|
|
_groups.Add(grp);
|
|
return grp;
|
|
}
|
|
|
|
public bool RemoveGroup(IGroup grp)
|
|
{
|
|
grp.IsActive = false;
|
|
return _groups.Remove(grp);
|
|
}
|
|
|
|
public event ShutdownRequestEventHandler OnClose;
|
|
|
|
public void Dispose()
|
|
{
|
|
if (connection != null)
|
|
Tuxeip_Class._Forward_Close(connection);
|
|
if (session != null)
|
|
Tuxeip_Class._UnRegisterSession(session);
|
|
Tuxeip_Class.CloseSession(session);
|
|
Marshal.FreeCoTaskMem((IntPtr)connection);
|
|
Marshal.FreeCoTaskMem((IntPtr)session);
|
|
connection = null;
|
|
session = null;
|
|
}
|
|
|
|
public string GetAddress(DeviceAddress address)
|
|
{
|
|
return string.Concat(address.Area == ABAREA.N ? "N" : "F", address.DBNumber.ToString(), "[", address.Start.ToString(), "]",
|
|
address.VarType == DataType.BOOL ? "." + address.Bit.ToString() : "");//考虑对localData I/O信号的解析
|
|
}
|
|
|
|
public DeviceAddress GetDeviceAddress(string address)
|
|
{
|
|
DeviceAddress addr = DeviceAddress.Empty;//考虑对localData I/O信号的解析
|
|
if (!string.IsNullOrEmpty(address))
|
|
{
|
|
addr.Area = address[0] == 'N' ? ABAREA.N : ABAREA.F;
|
|
int index = address.IndexOf('[');
|
|
if (index > 0)
|
|
{
|
|
ushort db;
|
|
ushort.TryParse(address.Substring(1, index - 1), out db);
|
|
addr.DBNumber = db;
|
|
int ind2 = address.IndexOf(']');
|
|
if (ind2 > 0)
|
|
{
|
|
int start;
|
|
int.TryParse(address.Substring(index + 1, ind2 - index - 1), out start);
|
|
addr.Start = start;
|
|
}
|
|
int dig = address.IndexOf('.');
|
|
if (dig > 0)
|
|
{
|
|
byte bit;
|
|
byte.TryParse(address.Substring(dig + 1), out bit);
|
|
addr.Bit = bit;
|
|
addr.VarType = DataType.BOOL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ushort db;
|
|
ushort.TryParse(address.Substring(1), out db);
|
|
addr.DBNumber = db;
|
|
}
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
|
|
public byte[] ReadBytes(DeviceAddress address, ushort size)
|
|
{
|
|
byte[] result = new byte[size];
|
|
float[] buffer = ReadFloatArray(address, (ushort)(size / 4));
|
|
if (buffer != null)
|
|
{
|
|
Buffer.BlockCopy(buffer, 0, result, 0, size);
|
|
return result;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public float[] ReadFloatArray(DeviceAddress address, ushort size)
|
|
{
|
|
if (IsClosed)
|
|
return null;
|
|
else
|
|
{
|
|
float[] buffer = new float[size]; address.VarType = DataType.FLOAT; var addr = GetAddress(address);
|
|
LGX_Read* data = Tuxeip_Class._ReadLgxData(session, connection, addr, size);
|
|
if (data != null && data->Varcount > 0)
|
|
{
|
|
for (int i = 0; i < data->Varcount; i++)
|
|
{
|
|
buffer[i] = Tuxeip_Class._GetLGXValueAsFloat(data, i);
|
|
}
|
|
data = null;
|
|
return buffer;
|
|
}
|
|
connection = null;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public ItemData<int> ReadInt32(DeviceAddress address)
|
|
{
|
|
if (IsClosed)
|
|
return new ItemData<int>(0, 0, QUALITIES.QUALITY_NOT_CONNECTED);
|
|
else
|
|
{
|
|
LGX_Read* data = Tuxeip_Class._ReadLgxData(session, connection, GetAddress(address), 1);
|
|
return new ItemData<int>(Tuxeip_Class._GetLGXValueAsInteger(data, 0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public ItemData<short> ReadInt16(DeviceAddress address)
|
|
{
|
|
if (IsClosed)
|
|
return new ItemData<short>(0, 0, QUALITIES.QUALITY_NOT_CONNECTED);
|
|
else
|
|
{
|
|
LGX_Read* data = Tuxeip_Class._ReadLgxData(session, connection, GetAddress(address), 1);
|
|
return new ItemData<short>((short)Tuxeip_Class._GetLGXValueAsInteger(data, 0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public ItemData<byte> ReadByte(DeviceAddress address)
|
|
{
|
|
if (IsClosed)
|
|
return new ItemData<byte>(0, 0, QUALITIES.QUALITY_NOT_CONNECTED);
|
|
else
|
|
{
|
|
LGX_Read* data = Tuxeip_Class._ReadLgxData(session, connection, GetAddress(address), 1);
|
|
return new ItemData<byte>((byte)Tuxeip_Class._GetLGXValueAsInteger(data, 0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public ItemData<string> ReadString(DeviceAddress address, ushort size)
|
|
{
|
|
if (IsClosed)
|
|
return new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_NOT_CONNECTED);
|
|
else
|
|
{
|
|
byte[] buffer = ReadBytes(address, size);
|
|
if (buffer != null)
|
|
{
|
|
return new ItemData<string>(Encoding.ASCII.GetString(buffer).Trim(), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
return new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD);
|
|
}
|
|
}
|
|
|
|
public ItemData<float> ReadFloat(DeviceAddress address)
|
|
{
|
|
if (IsClosed)
|
|
return new ItemData<float>(0, 0, QUALITIES.QUALITY_NOT_CONNECTED);
|
|
else
|
|
{
|
|
LGX_Read* data = Tuxeip_Class._ReadLgxData(session, connection, GetAddress(address), 1);
|
|
return new ItemData<float>(Tuxeip_Class._GetLGXValueAsFloat(data, 0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public ItemData<bool> ReadBit(DeviceAddress address)
|
|
{
|
|
if (IsClosed)
|
|
return new ItemData<bool>(false, 0, QUALITIES.QUALITY_NOT_CONNECTED);
|
|
else
|
|
{
|
|
LGX_Read* data = Tuxeip_Class._ReadLgxData(session, connection, GetAddress(address), 1);
|
|
return new ItemData<bool>(Tuxeip_Class._GetLGXValueAsInteger(data, 0) > 0, 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public ItemData<object> ReadValue(DeviceAddress address)
|
|
{
|
|
return this.ReadValueEx(address);
|
|
}
|
|
|
|
public unsafe int WriteBytes(DeviceAddress address, byte[] bit)
|
|
{
|
|
if (IsClosed) return -1;
|
|
fixed (void* b = bit)
|
|
{
|
|
return Tuxeip_Class._WriteLgxData(session, connection, GetAddress(address), LGX_Data_Type.LGX_BITARRAY, b, 1);
|
|
}
|
|
}
|
|
|
|
public int WriteBit(DeviceAddress address, bool bit)
|
|
{
|
|
if (IsClosed) return -1;
|
|
return Tuxeip_Class._WriteLgxData(session, connection, GetAddress(address), LGX_Data_Type.LGX_BOOL, &bit, 1);
|
|
}
|
|
|
|
public int WriteBits(DeviceAddress address, byte bits)
|
|
{
|
|
if (IsClosed) return -1;
|
|
return Tuxeip_Class._WriteLgxData(session, connection, GetAddress(address), LGX_Data_Type.LGX_SINT, &bits, 1);
|
|
}
|
|
|
|
public int WriteInt16(DeviceAddress address, short value)
|
|
{
|
|
if (IsClosed) return -1;
|
|
return Tuxeip_Class._WriteLgxData(session, connection, GetAddress(address), LGX_Data_Type.LGX_INT, &value, 1);
|
|
}
|
|
|
|
public int WriteInt32(DeviceAddress address, int value)
|
|
{
|
|
if (IsClosed) return -1;
|
|
return Tuxeip_Class._WriteLgxData(session, connection, GetAddress(address), LGX_Data_Type.LGX_DINT, &value, 1);
|
|
}
|
|
|
|
public int WriteFloat(DeviceAddress address, float value)
|
|
{
|
|
if (IsClosed) return -1;
|
|
return Tuxeip_Class._WriteLgxData(session, connection, GetAddress(address), LGX_Data_Type.LGX_REAL, &value, 1);
|
|
}
|
|
|
|
public int WriteString(DeviceAddress address, string str)
|
|
{
|
|
byte[] b = Encoding.ASCII.GetBytes(str);
|
|
return WriteBytes(address, b);
|
|
}
|
|
|
|
public int WriteValue(DeviceAddress address, object value)
|
|
{
|
|
return this.WriteValueEx(address, value);
|
|
}
|
|
|
|
public ItemData<uint> ReadUInt32(DeviceAddress address)
|
|
{
|
|
if (IsClosed)
|
|
return new ItemData<uint>(0, 0, QUALITIES.QUALITY_NOT_CONNECTED);
|
|
else
|
|
{
|
|
LGX_Read* data = Tuxeip_Class._ReadLgxData(session, connection, GetAddress(address), 1);
|
|
return new ItemData<uint>((uint)Tuxeip_Class._GetLGXValueAsInteger(data, 0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public ItemData<ushort> ReadUInt16(DeviceAddress address)
|
|
{
|
|
if (IsClosed)
|
|
return new ItemData<ushort>(0, 0, QUALITIES.QUALITY_NOT_CONNECTED);
|
|
else
|
|
{
|
|
LGX_Read* data = Tuxeip_Class._ReadLgxData(session, connection, GetAddress(address), 1);
|
|
return new ItemData<ushort>((ushort)Tuxeip_Class._GetLGXValueAsInteger(data, 0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public int WriteUInt16(DeviceAddress address, ushort value)
|
|
{
|
|
if (IsClosed) return -1;
|
|
return Tuxeip_Class._WriteLgxData(session, connection, GetAddress(address), LGX_Data_Type.LGX_INT, &value, 1);
|
|
}
|
|
|
|
public int WriteUInt32(DeviceAddress address, uint value)
|
|
{
|
|
if (IsClosed) return -1;
|
|
return Tuxeip_Class._WriteLgxData(session, connection, GetAddress(address), LGX_Data_Type.LGX_DINT, &value, 1);
|
|
}
|
|
|
|
public int Limit
|
|
{
|
|
get { return 122; }
|
|
}
|
|
|
|
}
|
|
|
|
public sealed class ABPLCGroup : PLCGroup
|
|
{
|
|
public ABPLCGroup(short id, string name, int updateRate, bool active, ABEtherNetReader plcReader)
|
|
{
|
|
this._id = id;
|
|
this._name = name;
|
|
this._updateRate = updateRate;
|
|
this._isActive = active;
|
|
this._plcReader = plcReader;
|
|
this._server = _plcReader.Parent;
|
|
this._changedList = new List<int>();
|
|
this._timer = new Timer();
|
|
this._cacheReader = new FloatCacheReader();
|
|
}
|
|
|
|
protected override void Poll()
|
|
{
|
|
float[] cache = (float[])_cacheReader.Cache;
|
|
int offset = 0;
|
|
foreach (PDUArea area in _rangeList)
|
|
{
|
|
float[] prcv = ((ABEtherNetReader)_plcReader).ReadFloatArray(area.Start, (ushort)area.Len);//从PLC读取数据
|
|
if (prcv == null)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
int len = prcv.Length;
|
|
int index = area.StartIndex;//index指向_items中的Tag元数据
|
|
int count = index + area.Count;
|
|
while (index < count)
|
|
{
|
|
DeviceAddress addr = _items[index].Address;
|
|
int iInt = addr.CacheIndex;
|
|
int iInt1 = iInt - offset;
|
|
if (addr.VarType == DataType.BOOL)
|
|
{
|
|
int tmp = (int)prcv[iInt1] ^ (int)cache[iInt];
|
|
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 <= 4)
|
|
{
|
|
if (prcv[iInt1] != cache[iInt]) _changedList.Add(index);
|
|
}
|
|
else
|
|
{
|
|
int size = addr.DataSize / 4;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
if (prcv[iInt1 + i] != cache[iInt + i])
|
|
{
|
|
_changedList.Add(index);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
for (int j = 0; j < len; j++)
|
|
{
|
|
cache[j + offset] = prcv[j];
|
|
}//将PLC读取的数据写入到CacheReader中
|
|
offset += len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ABAREA
|
|
{
|
|
public const int N = 0;
|
|
public const int F = 1;
|
|
public const int I = 2;
|
|
public const int O = 3;
|
|
}
|
|
}
|
|
|