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.
1257 lines
51 KiB
1257 lines
51 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using DataService;
|
|
|
|
namespace ClientDriver
|
|
{
|
|
[Description("客户端驱动")]
|
|
public class ClientReader : IDriver//客户端存在对TLV数据的字节序转换问题,需测试
|
|
{
|
|
short _id;
|
|
public short ID
|
|
{
|
|
get
|
|
{
|
|
return _id;
|
|
}
|
|
}
|
|
|
|
private int _timeout = 0;
|
|
public int TimeOut
|
|
{
|
|
get { return _timeout; }
|
|
set { _timeout = value; }
|
|
}
|
|
|
|
string _name;
|
|
public string Name
|
|
{
|
|
get
|
|
{
|
|
return _name;
|
|
}
|
|
}
|
|
|
|
string _ip;
|
|
public string ServerName
|
|
{
|
|
get { return _ip; }
|
|
set { _ip = value; }
|
|
}
|
|
|
|
internal Socket tcpSend;
|
|
internal Socket tcpRecive;
|
|
|
|
public bool IsClosed
|
|
{
|
|
get
|
|
{
|
|
//return tcpASynCl.Poll(-1, SelectMode.SelectRead);
|
|
return tcpSend == null || tcpRecive == null || !tcpSend.Connected || !tcpRecive.Connected;
|
|
}
|
|
}
|
|
|
|
List<IGroup> _grps = new List<IGroup>(1);
|
|
public IEnumerable<IGroup> Groups
|
|
{
|
|
get { return _grps; }
|
|
}
|
|
|
|
IDataServer _server;
|
|
public IDataServer Parent
|
|
{
|
|
get { return _server; }
|
|
}
|
|
|
|
public ClientReader(IDataServer server, short id, string name)
|
|
{
|
|
_id = id;
|
|
_server = server;
|
|
_name = name;
|
|
}
|
|
|
|
public bool Connect()
|
|
{
|
|
int port = 6543;
|
|
lock (this)
|
|
{
|
|
try
|
|
{
|
|
if (tcpRecive != null)
|
|
{
|
|
tcpRecive.Dispose();
|
|
}
|
|
if (tcpSend != null)
|
|
{
|
|
tcpSend.Dispose();
|
|
}
|
|
tcpRecive = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
tcpRecive.Connect(_ip, port);
|
|
tcpRecive.SendTimeout = _timeout;
|
|
tcpRecive.ReceiveTimeout = -1;
|
|
|
|
tcpSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
tcpSend.Connect(_ip, port);
|
|
tcpSend.SendTimeout = _timeout;
|
|
tcpSend.ReceiveTimeout = _timeout;
|
|
return true;
|
|
}
|
|
catch (SocketException error)
|
|
{
|
|
if (OnError != null)
|
|
OnError(this, new IOErrorEventArgs(error.Message));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false)
|
|
{
|
|
ClientGroup grp = new ClientGroup(id, name, updateRate, active, this);
|
|
_grps.Add(grp);
|
|
return grp;
|
|
}
|
|
|
|
public bool RemoveGroup(IGroup grp)
|
|
{
|
|
grp.IsActive = false;
|
|
return _grps.Remove(grp);
|
|
}
|
|
|
|
public event IOErrorEventHandler OnError;
|
|
|
|
public void Dispose()
|
|
{
|
|
foreach (IGroup grp in _grps)
|
|
{
|
|
grp.Dispose();
|
|
}
|
|
_grps.Clear();
|
|
try
|
|
{
|
|
if (tcpRecive != null)
|
|
{
|
|
if (tcpRecive.Connected)
|
|
tcpRecive.Shutdown(SocketShutdown.Both);
|
|
tcpRecive.Dispose();
|
|
}
|
|
if (tcpSend != null)
|
|
{
|
|
if (tcpSend.Connected)
|
|
tcpSend.Shutdown(SocketShutdown.Both);
|
|
tcpSend.Dispose();
|
|
}
|
|
}
|
|
catch (SocketException err)
|
|
{
|
|
if (OnError != null)
|
|
OnError(this, new IOErrorEventArgs(err.Message));
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ClientGroup : IGroup
|
|
{
|
|
bool _active = false;
|
|
public bool IsActive
|
|
{
|
|
get
|
|
{
|
|
return _active;
|
|
}
|
|
set
|
|
{
|
|
_active = value;
|
|
if (value)
|
|
{
|
|
if (_plcReader != null && _plcReader.tcpSend != null)
|
|
{
|
|
this._tcpRecive = _plcReader.tcpRecive;
|
|
this._tcpSend = _plcReader.tcpSend;
|
|
try
|
|
{
|
|
_addr = (_tcpSend.RemoteEndPoint as IPEndPoint).Address;
|
|
}
|
|
catch { }
|
|
Thread workItem = new Thread(new ThreadStart(ReciveData));
|
|
workItem.Priority = ThreadPriority.Highest;
|
|
workItem.Start();
|
|
if (_updateRate > 0)
|
|
ThreadPool.QueueUserWorkItem(new WaitCallback(this.OnUpdate));
|
|
else
|
|
Init();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected short _id;
|
|
public short ID
|
|
{
|
|
get
|
|
{
|
|
return _id;
|
|
}
|
|
}
|
|
|
|
protected int _updateRate;
|
|
public int UpdateRate
|
|
{
|
|
get
|
|
{
|
|
return _updateRate;
|
|
}
|
|
set
|
|
{
|
|
_updateRate = value;
|
|
}
|
|
}
|
|
|
|
protected DeviceAddress _start;
|
|
public DeviceAddress Start
|
|
{
|
|
get
|
|
{
|
|
return _start;
|
|
}
|
|
}
|
|
|
|
protected string _name;
|
|
public string Name
|
|
{
|
|
get
|
|
{
|
|
return _name;
|
|
}
|
|
set
|
|
{
|
|
_name = value;
|
|
}
|
|
}
|
|
|
|
protected float _deadband;
|
|
public float DeadBand
|
|
{
|
|
get
|
|
{
|
|
return _deadband;
|
|
}
|
|
set
|
|
{
|
|
_deadband = value;
|
|
}
|
|
}
|
|
|
|
protected ClientReader _plcReader;
|
|
public IDriver Parent
|
|
{
|
|
get
|
|
{
|
|
return _plcReader;
|
|
}
|
|
}
|
|
|
|
protected Dictionary<short, ITag> _items;
|
|
public IEnumerable<ITag> Items
|
|
{
|
|
get { return _items.Values; }
|
|
}
|
|
|
|
IDataServer _server;
|
|
public IDataServer Server
|
|
{
|
|
get
|
|
{
|
|
return _server;
|
|
}
|
|
}
|
|
|
|
Socket _tcpSend, _tcpRecive;
|
|
byte[] tcpBuffer;
|
|
|
|
IPAddress _addr;
|
|
public IPAddress RemoteAddress
|
|
{
|
|
get { return _addr; }
|
|
}
|
|
|
|
public ClientGroup(short id, string name, int updateRate, bool active, ClientReader plcReader)
|
|
{
|
|
this._id = id;
|
|
this._name = name;
|
|
this._updateRate = updateRate;
|
|
this._active = active;
|
|
this._plcReader = plcReader;
|
|
this._server = plcReader.Parent;
|
|
tcpBuffer = new byte[8192];
|
|
}
|
|
|
|
object sendasync = new object();
|
|
private byte[] ReadSingleData(DeviceAddress address, DataSource source = DataSource.Cache)
|
|
{
|
|
if (_tcpSend == null) return null;
|
|
short ID = (short)address.Start;
|
|
byte type = (byte)address.VarType;
|
|
byte len = (byte)(address.DataSize);
|
|
byte[] idbits = BitConverter.GetBytes(ID);
|
|
byte[] write_data = new byte[6] { FCTCOMMAND.fctHead, FCTCOMMAND.fctReadSingle,
|
|
source == DataSource.Cache?(byte)0:(byte)1, idbits[0], idbits[1] ,len};
|
|
byte[] data = new byte[len];
|
|
SocketError error;
|
|
lock (sendasync)
|
|
{
|
|
_tcpSend.Send(write_data, 0, 6, SocketFlags.None, out error);
|
|
int result = _tcpSend.Receive(tcpBuffer, 0, data.Length + 5, SocketFlags.None, out error);
|
|
}
|
|
Array.Copy(tcpBuffer, 5, data, 0, data.Length);
|
|
if (error == SocketError.Success)
|
|
return data;
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private int WriteSingleData(DeviceAddress address, byte[] value)
|
|
{
|
|
if (_tcpSend == null) return -1;
|
|
short ID = (short)address.Start;
|
|
byte type = (byte)address.VarType;
|
|
byte[] idbits = BitConverter.GetBytes(ID);
|
|
byte[] write_data = new byte[6] { FCTCOMMAND.fctHead, FCTCOMMAND.fctWriteSingle, 1, idbits[0], idbits[1], (byte)(value.Length) };
|
|
byte[] data = new byte[6 + value.Length];
|
|
write_data.CopyTo(data, 0);
|
|
value.CopyTo(data, 6);
|
|
SocketError error;
|
|
lock (sendasync)
|
|
{
|
|
_tcpSend.Send(data, 0, data.Length, SocketFlags.None, out error);
|
|
_tcpSend.Receive(tcpBuffer, 0, 2, SocketFlags.None, out error);
|
|
}
|
|
if (error == SocketError.Success)
|
|
return tcpBuffer[1];//可在此处返回一个错误号
|
|
else
|
|
{
|
|
return (int)error;
|
|
}
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
if (_items != null && _tcpSend != null)
|
|
{
|
|
int len = 0;
|
|
List<ITag> tags = new List<ITag>();
|
|
foreach (var tag in _items.Values)
|
|
{
|
|
len += (3 + tag.Address.DataSize);
|
|
if (len >= tcpBuffer.Length - 10)
|
|
{
|
|
len = 0;
|
|
BatchRead(DataSource.Cache, true, tags.ToArray());
|
|
tags.Clear();
|
|
}
|
|
tags.Add(tag);
|
|
}
|
|
BatchRead(DataSource.Cache, true, tags.ToArray());
|
|
}
|
|
}
|
|
|
|
private void ReciveData()
|
|
{
|
|
if (!_active || _plcReader.tcpRecive == null) return;
|
|
List<HistoryData> historys = null;
|
|
byte[] bytes = new byte[ushort.MaxValue];
|
|
byte[] temp = new byte[_tcpRecive.ReceiveBufferSize];
|
|
Storage value = Storage.Empty;
|
|
int result = 0;
|
|
int start = 0;
|
|
SocketError error;
|
|
do
|
|
{
|
|
if (!_tcpRecive.Connected) return;
|
|
result = _tcpRecive.Receive(bytes, 0, bytes.Length, SocketFlags.None, out error);
|
|
if (error == SocketError.Success)
|
|
{
|
|
if (DataChange != null)
|
|
historys = new List<HistoryData>();
|
|
//DateTime time = DateTime.Now;//当前时间戳
|
|
if (start != 0 && temp[0] == FCTCOMMAND.fctHead)
|
|
{
|
|
int j = 3;
|
|
if (start < 0)
|
|
{
|
|
Array.Copy(bytes, 0, temp, -start, 5 + start);
|
|
}
|
|
short tc = BitConverter.ToInt16(temp, j);//总字节数
|
|
if (start < 0)
|
|
start += tc;
|
|
Array.Copy(bytes, 0, temp, tc - start, start);
|
|
j += 2;
|
|
while (j < tc)
|
|
{
|
|
short id = BitConverter.ToInt16(temp, j);//标签ID、数据长度、数据值(T,L,V)
|
|
j += 2;
|
|
byte length = temp[j++];
|
|
ITag tag;
|
|
if (_items.TryGetValue(id, out tag))
|
|
{
|
|
//数据类型
|
|
switch (tag.Address.VarType)
|
|
{
|
|
case DataType.BOOL:
|
|
value.Boolean = BitConverter.ToBoolean(temp, j);
|
|
break;
|
|
case DataType.BYTE:
|
|
value.Byte = temp[j];
|
|
break;
|
|
case DataType.WORD:
|
|
value.Word = BitConverter.ToUInt16(temp, j);//需测试
|
|
break;
|
|
case DataType.SHORT:
|
|
value.Int16 = BitConverter.ToInt16(temp, j);//需测试
|
|
break;
|
|
case DataType.DWORD:
|
|
value.DWord = BitConverter.ToUInt32(temp, j);//需测试
|
|
break;
|
|
case DataType.INT:
|
|
value.Int32 = BitConverter.ToInt32(temp, j);//需测试
|
|
break;
|
|
case DataType.FLOAT:
|
|
value.Single = BitConverter.ToSingle(temp, j);
|
|
break;
|
|
case DataType.STR:
|
|
StringTag strTag = tag as StringTag;
|
|
if (strTag != null)
|
|
{
|
|
strTag.String = Encoding.ASCII.GetString(temp, j, length).Trim((char)0);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
j += length;
|
|
DateTime time = DateTime.FromFileTime(BitConverter.ToInt64(temp, j));
|
|
j += 8;
|
|
tag.Update(value, time, QUALITIES.QUALITY_GOOD);
|
|
if (historys != null)
|
|
historys.Add(new HistoryData(id, QUALITIES.QUALITY_GOOD, value, time));
|
|
}
|
|
else
|
|
j += length + 8;
|
|
}
|
|
}
|
|
byte head = bytes[start];
|
|
int count = start;
|
|
while (head == FCTCOMMAND.fctHead && result > count)
|
|
{
|
|
if (count + 5 > bytes.Length)
|
|
{
|
|
start = count - bytes.Length;
|
|
Array.Copy(bytes, count, temp, 0, -start);
|
|
break;
|
|
}
|
|
int j = count + 3;
|
|
short tc = BitConverter.ToInt16(bytes, j);//总标签数
|
|
count += tc;
|
|
if (count >= bytes.Length)
|
|
{
|
|
start = count - bytes.Length;
|
|
Array.Copy(bytes, count - tc, temp, 0, tc - start);
|
|
break;
|
|
}
|
|
else start = 0;
|
|
j += 2;
|
|
while (j < count)
|
|
{
|
|
short id = BitConverter.ToInt16(bytes, j);//标签ID、数据长度、数据值(T,L,V)
|
|
j += 2;
|
|
byte length = bytes[j++];
|
|
ITag tag;
|
|
if (_items.TryGetValue(id, out tag))
|
|
{
|
|
//数据类型
|
|
switch (tag.Address.VarType)
|
|
{
|
|
case DataType.BOOL:
|
|
value.Boolean = BitConverter.ToBoolean(bytes, j);
|
|
break;
|
|
case DataType.BYTE:
|
|
value.Byte = bytes[j];
|
|
break;
|
|
case DataType.WORD:
|
|
value.Word = BitConverter.ToUInt16(bytes, j);//需测试
|
|
break;
|
|
case DataType.SHORT:
|
|
value.Int16 = BitConverter.ToInt16(bytes, j);//需测试
|
|
break;
|
|
case DataType.DWORD:
|
|
value.DWord = BitConverter.ToUInt32(bytes, j);//需测试
|
|
break;
|
|
case DataType.INT:
|
|
value.Int32 = BitConverter.ToInt32(bytes, j);//需测试
|
|
break;
|
|
case DataType.FLOAT:
|
|
value.Single = BitConverter.ToSingle(bytes, j);
|
|
break;
|
|
case DataType.STR:
|
|
StringTag strTag = tag as StringTag;
|
|
if (strTag != null)
|
|
{
|
|
strTag.String = Encoding.ASCII.GetString(bytes, j, length).Trim((char)0);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
j += length;
|
|
DateTime time = DateTime.FromFileTime(BitConverter.ToInt64(bytes, j));
|
|
j += 8;
|
|
tag.Update(value, time, QUALITIES.QUALITY_GOOD);
|
|
if (historys != null)
|
|
historys.Add(new HistoryData(id, QUALITIES.QUALITY_GOOD, value, time));
|
|
}
|
|
else
|
|
j += length + 8;
|
|
}
|
|
head = bytes[count];
|
|
}
|
|
if (DataChange != null && historys.Count > 0)
|
|
DataChange(this, new DataChangeEventArgs(1, historys));
|
|
|
|
}
|
|
else if (error == SocketError.ConnectionReset || error == SocketError.Interrupted
|
|
|| error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
|
|
{
|
|
_tcpRecive.Dispose();
|
|
_active = false;
|
|
return;
|
|
}
|
|
}
|
|
while (result > 0);
|
|
}
|
|
|
|
public void OnUpdate(object stateInfo)
|
|
{
|
|
while (true)
|
|
{
|
|
Thread.Sleep(_updateRate);
|
|
lock (this)
|
|
{
|
|
if (!_active)
|
|
{
|
|
return;
|
|
}
|
|
Init();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool AddItems(IList<TagMetaData> items)
|
|
{
|
|
int count = items.Count;
|
|
if (_items == null) _items = new Dictionary<short, ITag>(count);
|
|
lock (_server.SyncRoot)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
ITag dataItem = null;
|
|
TagMetaData meta = items[i];
|
|
DeviceAddress addr = new DeviceAddress(0, 0, 0, meta.ID, meta.Size, 0, meta.DataType);
|
|
switch (meta.DataType)
|
|
{
|
|
case DataType.BOOL:
|
|
dataItem = new BoolTag(meta.ID, addr, this);
|
|
break;
|
|
case DataType.BYTE:
|
|
dataItem = new ByteTag(meta.ID, addr, this);
|
|
break;
|
|
case DataType.WORD:
|
|
dataItem = new UShortTag(meta.ID, addr, this);
|
|
break;
|
|
case DataType.SHORT:
|
|
dataItem = new ShortTag(meta.ID, addr, this);
|
|
break;
|
|
case DataType.INT:
|
|
dataItem = new IntTag(meta.ID, addr, this);
|
|
break;
|
|
case DataType.DWORD:
|
|
dataItem = new UIntTag(meta.ID, addr, this);
|
|
break;
|
|
case DataType.FLOAT:
|
|
dataItem = new FloatTag(meta.ID, addr, this);
|
|
break;
|
|
case DataType.STR:
|
|
dataItem = new StringTag(meta.ID, addr, this);
|
|
break;
|
|
default:
|
|
dataItem = new BoolTag(meta.ID, addr, this);
|
|
break;
|
|
}
|
|
_items.Add(meta.ID, dataItem);
|
|
_server.AddItemIndex(meta.Name, dataItem);
|
|
}
|
|
}
|
|
//Init();
|
|
return true;
|
|
}
|
|
|
|
public bool AddTags(IEnumerable<ITag> tags)
|
|
{
|
|
if (_items == null)
|
|
{
|
|
_items = new Dictionary<short, ITag>();
|
|
}
|
|
foreach (ITag tag in tags)
|
|
{
|
|
if (tag != null)
|
|
{
|
|
_items.Add(tag.ID, tag);
|
|
}
|
|
}
|
|
//Init();
|
|
return true;
|
|
}
|
|
|
|
public bool RemoveItems(params ITag[] items)
|
|
{
|
|
foreach (var item in items)
|
|
{
|
|
_server.RemoveItemIndex(item.GetTagName());
|
|
_items.Remove(item.ID);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool SetActiveState(bool active, params short[] items)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public ITag FindItemByAddress(DeviceAddress addr)
|
|
{
|
|
ITag tag;
|
|
if (_items.TryGetValue((short)addr.Start, out tag))
|
|
return tag;
|
|
return _server[(short)addr.Start];
|
|
}
|
|
|
|
public HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray)
|
|
{
|
|
if (itemArray.Length == 0 || _tcpSend == null || !_tcpSend.Connected) return null;
|
|
short len = (short)itemArray.Length;
|
|
byte[] bt = new byte[2];
|
|
byte[] data = new byte[5 + len * 2];
|
|
int j = 0;
|
|
data[j++] = FCTCOMMAND.fctHead;
|
|
data[j++] = FCTCOMMAND.fctReadMultiple;
|
|
data[j++] = source == DataSource.Cache ? (byte)0 : (byte)1;
|
|
bt = BitConverter.GetBytes(len);
|
|
data[j++] = bt[0];
|
|
data[j++] = bt[1];
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
bt = BitConverter.GetBytes(itemArray[i].ID);
|
|
data[j++] = bt[0];
|
|
data[j++] = bt[1];
|
|
}
|
|
SocketError error;
|
|
lock (sendasync)
|
|
{
|
|
_tcpSend.Send(data, 0, data.Length, SocketFlags.None, out error);
|
|
int result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
|
|
}
|
|
//!!!!此处的协议应注意,如批量读入的数据量超过缓存,必须分批合并;在协议头应加入总字节数,以和result比较是否需要循环读入。
|
|
j = 5;
|
|
if (error == SocketError.Success)
|
|
{
|
|
DateTime time = DateTime.Now;
|
|
HistoryData[] values = new HistoryData[len];
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
short id = BitConverter.ToInt16(tcpBuffer, j);
|
|
j += 2;
|
|
ITag tag = _server[id];
|
|
byte length = tcpBuffer[j++];
|
|
if (tag != null && length > 0)
|
|
{
|
|
switch (tag.Address.VarType)
|
|
{
|
|
case DataType.BOOL:
|
|
values[i].Value.Boolean = BitConverter.ToBoolean(tcpBuffer, j);
|
|
break;
|
|
case DataType.BYTE:
|
|
values[i].Value.Byte = tcpBuffer[j];
|
|
break;
|
|
case DataType.WORD:
|
|
values[i].Value.Word = BitConverter.ToUInt16(tcpBuffer, j);
|
|
break;
|
|
case DataType.SHORT:
|
|
values[i].Value.Int16 = BitConverter.ToInt16(tcpBuffer, j);
|
|
break;
|
|
case DataType.DWORD:
|
|
values[i].Value.DWord = BitConverter.ToUInt32(tcpBuffer, j);
|
|
break;
|
|
case DataType.INT:
|
|
values[i].Value.Int32 = BitConverter.ToInt32(tcpBuffer, j);
|
|
break;
|
|
case DataType.FLOAT:
|
|
values[i].Value.Single = BitConverter.ToSingle(tcpBuffer, j);
|
|
break;
|
|
case DataType.STR:
|
|
StringTag strTag = tag as StringTag;
|
|
if (strTag != null)
|
|
{
|
|
strTag.String = Encoding.ASCII.GetString(tcpBuffer, j, length).Trim();
|
|
}
|
|
break;
|
|
}
|
|
if (values[i].Value != tag.Value)
|
|
tag.Update(values[i].Value, time, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
j += length;
|
|
}
|
|
return values;
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
|
|
public int BatchWrite(SortedDictionary<ITag, object> items, bool isSync = true)
|
|
{
|
|
if (_tcpSend == null || !_tcpSend.Connected) return -1;
|
|
List<byte> list = new List<byte>(new byte[] { FCTCOMMAND.fctHead, FCTCOMMAND.fctWriteMultiple });
|
|
list.AddRange(BitConverter.GetBytes((short)items.Count));
|
|
foreach (var item in items)
|
|
{
|
|
ITag tag = item.Key;
|
|
list.AddRange(BitConverter.GetBytes(tag.ID));
|
|
var addr = tag.Address;
|
|
if (addr.VarType != DataType.STR)
|
|
list.Add((byte)(addr.DataSize));//此处存疑
|
|
switch (addr.VarType)
|
|
{
|
|
case DataType.BOOL:
|
|
list.Add(Convert.ToBoolean(item.Value) ? (byte)1 : (byte)0);
|
|
break;
|
|
case DataType.BYTE:
|
|
list.Add(Convert.ToByte(item.Value));
|
|
break;
|
|
case DataType.WORD:
|
|
list.AddRange(BitConverter.GetBytes(Convert.ToUInt16(item.Value)));
|
|
break;
|
|
case DataType.SHORT:
|
|
list.AddRange(BitConverter.GetBytes(Convert.ToInt16(item.Value)));
|
|
break;
|
|
case DataType.DWORD:
|
|
list.AddRange(BitConverter.GetBytes(Convert.ToUInt32(item.Value)));
|
|
break;
|
|
case DataType.INT:
|
|
list.AddRange(BitConverter.GetBytes(Convert.ToInt32(item.Value)));
|
|
break;
|
|
case DataType.FLOAT:
|
|
list.AddRange(BitConverter.GetBytes(Convert.ToSingle(item.Value)));
|
|
break;
|
|
case DataType.STR:
|
|
var bts = Encoding.ASCII.GetBytes(Convert.ToString(item.Value));
|
|
list.Add((byte)bts.Length);
|
|
list.AddRange(bts);
|
|
break;
|
|
}
|
|
}
|
|
SocketError error;
|
|
lock (sendasync)
|
|
{
|
|
_tcpSend.Send(list.ToArray(), 0, list.Count, SocketFlags.None, out error);
|
|
_tcpSend.Receive(tcpBuffer, 0, 2, SocketFlags.None, out error);
|
|
}
|
|
if (error == SocketError.Success)
|
|
return tcpBuffer[1];
|
|
else
|
|
{
|
|
return (int)error;
|
|
}
|
|
}
|
|
|
|
public IEnumerable<HistoryData> SendHdaRequest(DateTime start, DateTime end)
|
|
{
|
|
if (_tcpSend == null || !_tcpSend.Connected) yield break;
|
|
byte[] hdaReq = new byte[18];
|
|
hdaReq[0] = FCTCOMMAND.fctHead;
|
|
hdaReq[1] = FCTCOMMAND.fctHdaRequest;
|
|
byte[] startbuffer = BitConverter.GetBytes(start.ToFileTime());
|
|
startbuffer.CopyTo(hdaReq, 2);
|
|
byte[] endbuffer = BitConverter.GetBytes(end.ToFileTime());
|
|
endbuffer.CopyTo(hdaReq, 10);
|
|
SocketError error;
|
|
HistoryData data = HistoryData.Empty;
|
|
short tempid = short.MinValue;
|
|
byte[] temp = new byte[14];
|
|
ITag tag = null;
|
|
int index = 0;
|
|
int size = 0;
|
|
int result = 0;
|
|
short id = 0;
|
|
lock (sendasync)
|
|
{
|
|
_tcpSend.Send(hdaReq);
|
|
do
|
|
{
|
|
result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
|
|
if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
|
|
{
|
|
_tcpSend.Dispose();
|
|
yield break;
|
|
}
|
|
if (index != 0)
|
|
{
|
|
Array.Copy(tcpBuffer, 0, temp, 14 - index, index);
|
|
id = BitConverter.ToInt16(temp, 0);
|
|
tempid = id;
|
|
tag = _server[id];
|
|
if (tag == null) yield break;
|
|
data.ID = id;
|
|
size = tag.Address.DataSize;
|
|
index -= (4 - size);
|
|
switch (tag.Address.VarType)
|
|
{
|
|
case DataType.BOOL:
|
|
data.Value.Boolean = BitConverter.ToBoolean(temp, 2);
|
|
break;
|
|
case DataType.BYTE:
|
|
data.Value.Byte = tcpBuffer[index];
|
|
break;
|
|
case DataType.WORD:
|
|
data.Value.Word = BitConverter.ToUInt16(temp, 2);
|
|
break;
|
|
case DataType.SHORT:
|
|
data.Value.Int16 = BitConverter.ToInt16(temp, 2);
|
|
break;
|
|
case DataType.DWORD:
|
|
data.Value.DWord = BitConverter.ToUInt32(temp, 2);
|
|
break;
|
|
case DataType.INT:
|
|
data.Value.Int32 = BitConverter.ToInt32(temp, 2);
|
|
break;
|
|
case DataType.FLOAT:
|
|
data.Value.Single = BitConverter.ToSingle(temp, 2);
|
|
break;
|
|
}
|
|
long fileTime = BitConverter.ToInt64(temp, 2 + size);
|
|
if (fileTime == -1) yield break;
|
|
data.TimeStamp = DateTime.FromFileTime(fileTime);
|
|
yield return data;
|
|
}
|
|
while (result >= index + 2)
|
|
{
|
|
id = BitConverter.ToInt16(tcpBuffer, index);
|
|
if (tempid != id)
|
|
{
|
|
tempid = id;
|
|
tag = _server[id];
|
|
if (tag == null) yield break;
|
|
size = tag.Address.DataSize;
|
|
}
|
|
if (index + 10 + size > result)
|
|
break;
|
|
data.ID = id;
|
|
index += 2;
|
|
switch (tag.Address.VarType)
|
|
{
|
|
case DataType.BOOL:
|
|
data.Value.Boolean = BitConverter.ToBoolean(tcpBuffer, index);
|
|
break;
|
|
case DataType.BYTE:
|
|
data.Value.Byte = tcpBuffer[index];
|
|
break;
|
|
case DataType.WORD:
|
|
data.Value.Word = BitConverter.ToUInt16(tcpBuffer, index);
|
|
break;
|
|
case DataType.SHORT:
|
|
data.Value.Int16 = BitConverter.ToInt16(tcpBuffer, index);
|
|
break;
|
|
case DataType.DWORD:
|
|
data.Value.DWord = BitConverter.ToUInt32(tcpBuffer, index);
|
|
break;
|
|
case DataType.INT:
|
|
data.Value.Int32 = BitConverter.ToInt32(tcpBuffer, index);
|
|
break;
|
|
case DataType.FLOAT:
|
|
data.Value.Single = BitConverter.ToSingle(tcpBuffer, index);
|
|
break;
|
|
}
|
|
index += size;
|
|
long fileTime = BitConverter.ToInt64(tcpBuffer, index);
|
|
if (fileTime == -1) yield break;
|
|
data.TimeStamp = DateTime.FromFileTime(fileTime);
|
|
index += 8;
|
|
yield return data;
|
|
}
|
|
if (index == result)
|
|
index = 0;
|
|
else
|
|
{
|
|
Array.Copy(tcpBuffer, index, temp, 0, result - index);
|
|
index += 14 - result;
|
|
}
|
|
} while (result > 0);
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
public IEnumerable<HistoryData> SendHdaRequest(DateTime start, DateTime end, short id)
|
|
{
|
|
if (_tcpSend == null || !_tcpSend.Connected) yield break;
|
|
ITag tag = _server[id];
|
|
if (tag == null) yield break;
|
|
byte[] hdaReq = new byte[20];
|
|
hdaReq[0] = FCTCOMMAND.fctHead;
|
|
hdaReq[1] = FCTCOMMAND.fctHdaIdRequest;
|
|
byte[] startbuffer = BitConverter.GetBytes(start.ToFileTime());
|
|
startbuffer.CopyTo(hdaReq, 2);
|
|
byte[] endbuffer = BitConverter.GetBytes(end.ToFileTime());
|
|
endbuffer.CopyTo(hdaReq, 10);
|
|
byte[] idbuffer = BitConverter.GetBytes(id);
|
|
idbuffer.CopyTo(hdaReq, 18);
|
|
SocketError error;
|
|
int index = 0;
|
|
HistoryData data = HistoryData.Empty;
|
|
data.ID = id;
|
|
int result = 0;
|
|
lock (sendasync)
|
|
{
|
|
_tcpSend.Send(hdaReq);
|
|
switch (tag.Address.VarType)
|
|
{
|
|
case DataType.FLOAT:
|
|
do
|
|
{
|
|
result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
|
|
if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
|
|
{
|
|
_tcpSend.Dispose();
|
|
yield break;
|
|
}
|
|
while (index + 12 <= result)
|
|
{
|
|
data.Value.Single = BitConverter.ToSingle(tcpBuffer, index);//未来可考虑量程转换和其他数据类型
|
|
index += 4;
|
|
long fileTime = BitConverter.ToInt64(tcpBuffer, index);
|
|
if (fileTime == -1) yield break;
|
|
data.TimeStamp = DateTime.FromFileTime(fileTime);
|
|
index += 8;
|
|
yield return data;
|
|
}
|
|
if (index == result)
|
|
index = 0;
|
|
else
|
|
index += 12 - result;//丢弃一个值
|
|
} while (result > 0);
|
|
break;
|
|
case DataType.SHORT:
|
|
do
|
|
{
|
|
result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
|
|
if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
|
|
{
|
|
_tcpSend.Dispose();
|
|
yield break;
|
|
}
|
|
while (index + 10 <= result)
|
|
{
|
|
data.Value.Int16 = BitConverter.ToInt16(tcpBuffer, index);//未来可考虑量程转换和其他数据类型
|
|
index += 2;
|
|
long fileTime = BitConverter.ToInt64(tcpBuffer, index);
|
|
if (fileTime == -1) yield break;
|
|
data.TimeStamp = DateTime.FromFileTime(fileTime);
|
|
index += 8;
|
|
yield return data;
|
|
}
|
|
if (index == result)
|
|
index = 0;
|
|
else
|
|
index += 10 - result;//丢弃一个值
|
|
} while (result > 0);
|
|
break;
|
|
case DataType.WORD:
|
|
do
|
|
{
|
|
result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
|
|
if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
|
|
{
|
|
_tcpSend.Dispose();
|
|
yield break;
|
|
}
|
|
while (index + 10 <= result)
|
|
{
|
|
data.Value.Word = BitConverter.ToUInt16(tcpBuffer, index);//未来可考虑量程转换和其他数据类型
|
|
index += 2;
|
|
long fileTime = BitConverter.ToInt64(tcpBuffer, index);
|
|
if (fileTime == -1) yield break;
|
|
data.TimeStamp = DateTime.FromFileTime(fileTime);
|
|
index += 8;
|
|
yield return data;
|
|
}
|
|
if (index == result)
|
|
index = 0;
|
|
else
|
|
index += 10 - result;//丢弃一个值
|
|
} while (result > 0);
|
|
break;
|
|
case DataType.DWORD:
|
|
do
|
|
{
|
|
result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
|
|
if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
|
|
{
|
|
_tcpSend.Dispose();
|
|
yield break;
|
|
}
|
|
while (index + 12 <= result)
|
|
{
|
|
data.Value.DWord = BitConverter.ToUInt32(tcpBuffer, index);//未来可考虑量程转换和其他数据类型
|
|
index += 4;
|
|
long fileTime = BitConverter.ToInt64(tcpBuffer, index);
|
|
if (fileTime == -1) yield break;
|
|
data.TimeStamp = DateTime.FromFileTime(fileTime);
|
|
index += 8;
|
|
yield return data;
|
|
}
|
|
if (index == result)
|
|
index = 0;
|
|
else
|
|
index += 12 - result;//丢弃一个值
|
|
} while (result > 0);
|
|
break;
|
|
case DataType.INT:
|
|
do
|
|
{
|
|
result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
|
|
if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
|
|
{
|
|
_tcpSend.Dispose();
|
|
yield break;
|
|
}
|
|
while (index + 12 <= result)
|
|
{
|
|
data.Value.Int32 = BitConverter.ToInt32(tcpBuffer, index);//未来可考虑量程转换和其他数据类型
|
|
index += 4;
|
|
long fileTime = BitConverter.ToInt64(tcpBuffer, index);
|
|
if (fileTime == -1) yield break;
|
|
data.TimeStamp = DateTime.FromFileTime(fileTime);
|
|
index += 8;
|
|
yield return data;
|
|
}
|
|
if (index == result)
|
|
index = 0;
|
|
else
|
|
index += 12 - result;//丢弃一个值
|
|
} while (result > 0);
|
|
break;
|
|
case DataType.BOOL://布尔量一个都不能少
|
|
{
|
|
byte[] temp = new byte[9];
|
|
do
|
|
{
|
|
result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
|
|
if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
|
|
{
|
|
_tcpSend.Dispose();
|
|
yield break;
|
|
}
|
|
if (index != 0)
|
|
{
|
|
Array.Copy(tcpBuffer, 0, temp, 9 - index, index);
|
|
data.Value.Boolean = BitConverter.ToBoolean(temp, 0);
|
|
long fileTime = BitConverter.ToInt64(temp, 1);
|
|
if (fileTime == -1) yield break;
|
|
data.TimeStamp = DateTime.FromFileTime(fileTime);
|
|
yield return data;
|
|
}
|
|
while (index + 9 <= result)
|
|
{
|
|
data.Value.Boolean = BitConverter.ToBoolean(tcpBuffer, index);
|
|
index += 1;
|
|
long fileTime = BitConverter.ToInt64(tcpBuffer, index);
|
|
if (fileTime == -1) yield break;
|
|
data.TimeStamp = DateTime.FromFileTime(fileTime);
|
|
index += 8;
|
|
yield return data;
|
|
}
|
|
if (index == result)
|
|
index = 0;
|
|
else
|
|
{
|
|
Array.Copy(tcpBuffer, index, temp, 0, result - index);
|
|
index += 9 - result;
|
|
}
|
|
} while (result > 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int SendResetRequest()
|
|
{
|
|
if (_tcpSend != null && _tcpSend.Connected)
|
|
{
|
|
var ipaddr = (_tcpSend.LocalEndPoint as IPEndPoint).Address;
|
|
byte[] resetReq = new byte[6];
|
|
resetReq[0] = FCTCOMMAND.fctHead;
|
|
resetReq[1] = FCTCOMMAND.fctReset;
|
|
ipaddr.GetAddressBytes().CopyTo(resetReq, 2);
|
|
lock (sendasync)
|
|
{
|
|
return _tcpSend.Send(resetReq);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public int SendAlarmRequest(DateTime? start, DateTime? end)
|
|
{
|
|
if (_tcpSend != null && _tcpSend.Connected)
|
|
{
|
|
byte[] alarmReq = new byte[18];
|
|
alarmReq[0] = FCTCOMMAND.fctHead;
|
|
alarmReq[1] = FCTCOMMAND.fctAlarmRequest;
|
|
if (start.HasValue)
|
|
{
|
|
byte[] startbuffer = BitConverter.GetBytes(start.Value.ToFileTime());
|
|
startbuffer.CopyTo(alarmReq, 2);
|
|
}
|
|
if (end.HasValue)
|
|
{
|
|
byte[] endbuffer = BitConverter.GetBytes(end.Value.ToFileTime());
|
|
endbuffer.CopyTo(alarmReq, 10);
|
|
}
|
|
SocketError error;
|
|
lock (sendasync)
|
|
{
|
|
_tcpSend.Send(alarmReq);
|
|
_tcpSend.Receive(tcpBuffer, 0, 2, SocketFlags.None, out error);
|
|
}
|
|
return (int)error;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache)
|
|
{
|
|
var data = ReadSingleData(address, source);
|
|
return data == null ? new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD) :
|
|
new ItemData<int>(BitConverter.ToInt32(data, 0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<uint> ReadUInt32(DeviceAddress address, DataSource source = DataSource.Cache)
|
|
{
|
|
var data = ReadSingleData(address, source);
|
|
return data == null ? new ItemData<uint>(0, 0, QUALITIES.QUALITY_BAD) :
|
|
new ItemData<uint>(BitConverter.ToUInt32(data, 0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<ushort> ReadUInt16(DeviceAddress address, DataSource source = DataSource.Cache)
|
|
{
|
|
var data = ReadSingleData(address, source);
|
|
return data == null ? new ItemData<ushort>(0, 0, QUALITIES.QUALITY_BAD) :
|
|
new ItemData<ushort>(BitConverter.ToUInt16(data, 0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache)
|
|
{
|
|
var data = ReadSingleData(address, source);
|
|
return data == null ? new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD) :
|
|
new ItemData<short>(BitConverter.ToInt16(data, 0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache)
|
|
{
|
|
var data = ReadSingleData(address, source);
|
|
return data == null ? new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD) :
|
|
new ItemData<byte>(data[0], 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public unsafe ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache)
|
|
{
|
|
var data = ReadSingleData(address, source);
|
|
if (data == null)
|
|
return new ItemData<float>(0.0f, 0, QUALITIES.QUALITY_BAD);
|
|
else
|
|
{
|
|
int value = BitConverter.ToInt32(data, 0);
|
|
return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
}
|
|
|
|
public ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache)
|
|
{
|
|
var data = ReadSingleData(address, source);
|
|
return data == null ? new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD) :
|
|
new ItemData<bool>(BitConverter.ToBoolean(data, address.Bit), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache)
|
|
{
|
|
var data = ReadSingleData(address, source);
|
|
return data == null ? new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD) :
|
|
new ItemData<string>(Encoding.ASCII.GetString(data, 0, Math.Min((int)address.DataSize, 254)).Trim((char)0), 0, QUALITIES.QUALITY_GOOD);
|
|
}
|
|
|
|
public int WriteInt32(DeviceAddress address, int value)
|
|
{
|
|
return WriteSingleData(address, BitConverter.GetBytes(value));
|
|
}
|
|
|
|
public int WriteUInt32(DeviceAddress address, uint value)
|
|
{
|
|
return WriteSingleData(address, BitConverter.GetBytes(value));
|
|
}
|
|
|
|
public int WriteUInt16(DeviceAddress address, ushort value)
|
|
{
|
|
return WriteSingleData(address, BitConverter.GetBytes(value));
|
|
}
|
|
|
|
public int WriteInt16(DeviceAddress address, short value)
|
|
{
|
|
return WriteSingleData(address, BitConverter.GetBytes(value));
|
|
}
|
|
|
|
public int WriteFloat(DeviceAddress address, float value)
|
|
{
|
|
return WriteSingleData(address, BitConverter.GetBytes(value));
|
|
}
|
|
|
|
public int WriteString(DeviceAddress address, string value)
|
|
{
|
|
return WriteSingleData(address, Encoding.ASCII.GetBytes(value));
|
|
}
|
|
|
|
public int WriteBit(DeviceAddress address, bool value)
|
|
{
|
|
return WriteSingleData(address, new byte[] { (byte)(value ? 1 : 0) });
|
|
}
|
|
|
|
public int WriteBits(DeviceAddress address, byte value)
|
|
{
|
|
return WriteSingleData(address, new byte[] { value });
|
|
}
|
|
|
|
public event DataChangeEventHandler DataChange;
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_items != null)
|
|
{
|
|
_items.Clear();
|
|
}
|
|
_items = null;
|
|
}
|
|
}
|
|
}
|
|
|