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.
1219 lines
43 KiB
1219 lines
43 KiB
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;
|
|
|
|
/// <summary>Constant for exception illegal function.</summary>
|
|
public const byte excIllegalFunction = 1;
|
|
/// <summary>Constant for exception illegal data address.</summary>
|
|
public const byte excIllegalDataAdr = 2;
|
|
/// <summary>Constant for exception illegal data value.</summary>
|
|
public const byte excIllegalDataVal = 3;
|
|
/// <summary>Constant for exception slave device failure.</summary>
|
|
public const byte excSlaveDeviceFailure = 4;
|
|
/// <summary>Constant for exception acknowledge.</summary>
|
|
public const byte excAck = 5;
|
|
/// <summary>Constant for exception slave is busy/booting up.</summary>
|
|
public const byte excSlaveIsBusy = 6;
|
|
/// <summary>Constant for exception gate path unavailable.</summary>
|
|
public const byte excGatePathUnavailable = 10;
|
|
/// <summary>Constant for exception not connected.</summary>
|
|
public const byte excExceptionNotConnected = 253;
|
|
/// <summary>Constant for exception connection lost.</summary>
|
|
public const byte excExceptionConnectionLost = 254;
|
|
/// <summary>Constant for exception response timeout.</summary>
|
|
public const byte excExceptionTimeout = 255;
|
|
/// <summary>Constant for exception wrong offset.</summary>
|
|
private const byte excExceptionOffset = 128;
|
|
/// <summary>Constant for exception send failt.</summary>
|
|
private const byte excSendFailt = 100;
|
|
|
|
private static int _timeout;
|
|
|
|
private Socket tcpSynCl;
|
|
private byte[] tcpSynClBuffer = new byte[256];
|
|
|
|
/// <summary>Response data event. This event is called when new data arrives</summary>
|
|
public delegate void ResponseData(ushort id, byte function, byte[] data);
|
|
/// <summary>Exception data event. This event is called when the data is incorrect</summary>
|
|
public delegate void ExceptionData(int id, byte function, byte exception);
|
|
/// <summary>Exception data event. This event is called when the data is incorrect</summary>
|
|
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<IGroup> _grps = new List<IGroup>(20);
|
|
public IEnumerable<IGroup> 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<int> ReadInt32(DeviceAddress address)
|
|
{
|
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 4, (byte)address.Area));
|
|
if (data == null)
|
|
return new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD);
|
|
else
|
|
return new ItemData<int>(IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0)), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<short> ReadInt16(DeviceAddress address)
|
|
{
|
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 2, (byte)address.Area));
|
|
if (data == null)
|
|
return new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD);
|
|
else
|
|
return new ItemData<short>(IPAddress.HostToNetworkOrder(BitConverter.ToInt16(data, 0)), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<byte> ReadByte(DeviceAddress address)
|
|
{
|
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.Area));
|
|
if (data == null)
|
|
return new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD);
|
|
else
|
|
return new ItemData<byte>(data[0], 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<string> ReadString(DeviceAddress address, ushort size)
|
|
{
|
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, size, (byte)address.Area));
|
|
if (data == null)
|
|
return new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD);
|
|
else
|
|
return new ItemData<string>(Encoding.Default.GetString(data, 0, size), 0, QUALITIES.QUALITY_GOOD);//�Ƿ������ֽ������⣿
|
|
}
|
|
|
|
public unsafe ItemData<float> ReadFloat(DeviceAddress address)
|
|
{
|
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 4, (byte)address.Area));
|
|
if (data == null)
|
|
return new ItemData<float>(0.0f, 0, QUALITIES.QUALITY_BAD);
|
|
else
|
|
{
|
|
int value = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0));
|
|
return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public unsafe ItemData<bool> ReadBit(DeviceAddress address)
|
|
{
|
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.Area));
|
|
if (data == null)
|
|
return new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD);
|
|
else
|
|
{
|
|
fixed (byte* p = data)
|
|
{
|
|
int* p1 = (int*)p;
|
|
return new ItemData<bool>((*p1 & (1 << (address.Bit < 8 ? address.Bit + 8 : address.Bit - 8)))
|
|
!= 0, 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
}
|
|
|
|
public ItemData<object> 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<int>(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<int> ReadInt32(DeviceAddress address)
|
|
{
|
|
return new ItemData<int>(BitConverter.ToInt32(_cache, address.Start - _start.Start), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<bool> 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<bool>((*p1 & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public ItemData<short> ReadInt16(DeviceAddress address)
|
|
{
|
|
return new ItemData<short>(BitConverter.ToInt16(_cache, address.Start - _start.Start), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<byte> ReadByte(DeviceAddress address)
|
|
{
|
|
return new ItemData<byte>(_cache[address.Start - _start.Start], 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<string> ReadString(DeviceAddress address, ushort size = 64)
|
|
{
|
|
return new ItemData<string>(BitConverter.ToString(_cache, address.Start - _start.Start, size), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<float> ReadFloat(DeviceAddress address)
|
|
{
|
|
return new ItemData<float>(BitConverter.ToSingle(_cache, address.Start - _start.Start), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<object> 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<int> ReadInt32(DeviceAddress address)
|
|
{
|
|
return new ItemData<int>(BitConverter.ToInt32(_cache, GetOffset(address, _start))
|
|
, 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<bool> ReadBit(DeviceAddress address)
|
|
{
|
|
fixed (byte* p = _cache)
|
|
{
|
|
int* p1 = (int*)(p + GetOffset(address, _start));
|
|
return new ItemData<bool>((*p1 & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public ItemData<short> ReadInt16(DeviceAddress address)
|
|
{
|
|
return new ItemData<short>(BitConverter.ToInt16(_cache, GetOffset(address, _start)),
|
|
0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<byte> ReadByte(DeviceAddress address)
|
|
{
|
|
return new ItemData<byte>(_cache[GetOffset(address, _start)], 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<string> ReadString(DeviceAddress address, ushort size = 64)
|
|
{
|
|
return new ItemData<string>(BitConverter.ToString(_cache, GetOffset(address, _start), size), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<float> ReadFloat(DeviceAddress address)
|
|
{
|
|
int value = BitConverter.ToInt32(_cache, GetOffset(address, _start));
|
|
//value = IPAddress.HostToNetworkOrder(value);
|
|
return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<object> 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<int> ReadInt32(DeviceAddress address)
|
|
{
|
|
return new ItemData<int>(BitConverter.ToInt32(_cache, GetOffset(address, _start)), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<bool> ReadBit(DeviceAddress address)
|
|
{
|
|
//if (address.Area == MX_AREA.D || address.Area == MX_AREA.T)
|
|
// return new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD);
|
|
fixed (byte* p = _cache)
|
|
{
|
|
short* p1 = (short*)(p + GetOffset(address, _start));
|
|
return new ItemData<bool>((*p1 & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public ItemData<short> ReadInt16(DeviceAddress address)
|
|
{
|
|
return new ItemData<short>(BitConverter.ToInt16(_cache, GetOffset(address, _start)), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<byte> ReadByte(DeviceAddress address)
|
|
{
|
|
return new ItemData<byte>(_cache[GetOffset(address, _start)], 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<string> ReadString(DeviceAddress address, ushort size = 64)
|
|
{
|
|
byte[] bytes = new byte[size];
|
|
Array.Copy(_cache, GetOffset(address, _start), bytes, 0, size);
|
|
return new ItemData<string>(Encoding.Default.GetString(bytes).Trim((char)0), 0, QUALITIES.QUALITY_GOOD);
|
|
//return new ItemData<string>(BitConverter.ToString(_cache, GetOffset(address, _start), size>>3), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<float> ReadFloat(DeviceAddress address)
|
|
{
|
|
return new ItemData<float>(BitConverter.ToSingle(_cache, GetOffset(address, _start)), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<object> 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();
|
|
}
|
|
|
|
}
|
|
}
|
|
|