C# SCADA
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.

607 lines
20 KiB

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
namespace DataService
{
public class ClientReader : IDevice
{
string _ip;
public string ServerName
{
get { return _ip; }
}
internal Socket tcpSynCl;
internal Socket tcpASynCl;
public bool IsClosed
{
get
{
//return tcpASynCl.Poll(-1, SelectMode.SelectRead);
return !tcpSynCl.Connected || !tcpASynCl.Connected;
}
}
private ushort _timeout = 0;
public int TimeOut
{
get { return _timeout; }
}
List<ClientGroup> _grps = new List<ClientGroup>(1);
public IEnumerable<IGroup> Groups
{
get { return _grps; }
}
IDataServer _server;
public IDataServer Parent
{
get { return _server; }
}
public ClientReader(IDataServer server, string ip)
{
_server = server;
_ip = ip;
}
public bool Connect()
{
try
{
int port = 1000;
IPAddress ip = IPAddress.Parse(_ip);
tcpASynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpASynCl.Connect(new IPEndPoint(ip, port));
tcpASynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout);
tcpASynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout);
tcpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpSynCl.Connect(new IPEndPoint(ip, port));
tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout);
tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout);
return true;
}
catch (SocketException error)
{
if (OnClose != null)
OnClose(this, new ShutdownRequestEventArgs(error.Message));
return false;
}
}
public IGroup AddGroup(string name, ushort id, int updateRate, int timeOut = 0, float deadBand = 0f, bool active = false)
{
ClientGroup grp = new ClientGroup(id, name, updateRate, active, this);
_grps.Add(grp);
return grp;
}
public int RemoveAllGroup()
{
foreach (IGroup grp in _grps)
{
grp.Dispose();
}
_grps.Clear();
return 1;
}
public event ShutdownRequestEventHandler OnClose;
public void Dispose()
{
if (tcpSynCl != null)
{
if (tcpSynCl.Connected)
{
try
{
tcpASynCl.Shutdown(SocketShutdown.Both);
tcpSynCl.Shutdown(SocketShutdown.Both);
}
catch { }
if (OnClose != null)
OnClose(this, new ShutdownRequestEventArgs("SHUTDOWN"));
tcpSynCl.Close();
tcpASynCl.Close();
}
tcpSynCl = null;
tcpASynCl = null;
}
RemoveAllGroup();
}
}
public class ClientGroup : IGroup
{
public const byte fctHead = 0xAB;
public const byte fctReadSingle = 1;
public const byte fctReadMultiple = 2;
public const byte fctWriteSingle = 5;
public const byte fctWriteMultiple = 15;
bool _active = false;
public bool IsActive
{
get
{
return _active;
}
set
{
_active = value;
if (value)
{
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(ReciveData), _tcpASynCl);
}
}
}
protected ushort _id;
public ushort 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;
}
}
public int Size
{
get
{
return _items == null ? 0 : _items.Length;
}
}
protected string _name;
public string Name
{
get
{
return _name;
}
}
protected float _deadband;
public float DeadBand
{
get
{
return _deadband;
}
set
{
_deadband = value;
}
}
protected ClientReader _plcReader;
public IDevice Parent
{
get
{
return _plcReader;
}
}
protected ITag[] _items;
public IEnumerable<ITag> Items
{
get { return _items; }
}
IDataServer _server;
Socket _tcpSynCl, _tcpASynCl;
byte[] tcpSynClBuffer;
public ClientGroup(ushort 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;
this._tcpASynCl = plcReader.tcpASynCl;
this._tcpSynCl = plcReader.tcpASynCl;
tcpSynClBuffer = new byte[_tcpASynCl.ReceiveBufferSize];
}
private byte[] ReadSingleData(DeviceAddress address, DataSource source = DataSource.Cache)
{
short ID = (short)address.Start;
byte type = (byte)address.VarType;
byte[] idbits = BitConverter.GetBytes(ID);
byte[] write_data = new byte[6] { fctHead, fctReadSingle,
source == DataSource.Cache?(byte)0:(byte)1, idbits[0], idbits[1], type };
byte[] data = new byte[type < 4 ? 1 : type < 6 ? 2 : 4];
SocketError error;
_tcpSynCl.Send(write_data, 0, 6, SocketFlags.None, out error);
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, data.Length + 3, SocketFlags.None, out error);
Array.Copy(tcpSynClBuffer, 3, data, 0, data.Length);
if (error == SocketError.Success)
return data;
else
{
throw new SocketException((int)error);
}
}
private int WriteSingleData(DeviceAddress address, byte[] value)
{
short ID = (short)address.Start;
byte type = (byte)address.VarType;
byte[] idbits = BitConverter.GetBytes(ID);
byte[] write_data = new byte[6] { fctHead, fctWriteSingle, 1, idbits[0], idbits[1], type };
byte[] data = new byte[6 + value.Length];
write_data.CopyTo(data, 0);
value.CopyTo(data, 6);
SocketError error;
_tcpSynCl.Send(data, 0, data.Length, SocketFlags.None, out error);
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, 2, SocketFlags.None, out error);
if (error == SocketError.Success)
return tcpSynClBuffer[1];
else
{
throw new SocketException((int)error);
}
}
public void Init()
{
if (_items != null)
{
for (int i = 0; i < _items.Length; i++)
{
_items[i].Value = _items[i].Read(DataSource.Cache);//DataSource.Device
}
}
}
private void ReciveData(object state)
{
if (state == null || !_active) return;
byte[] bytes = new byte[_tcpASynCl.ReceiveBufferSize];
int result = 0;
SocketError error;
do
{
result = _tcpASynCl.Receive(bytes, 0, bytes.Length, SocketFlags.None, out error);
if (result > 5 && bytes[0] == 0xAB)
{
short len = BitConverter.ToInt16(bytes, 1);
short count = BitConverter.ToInt16(bytes, 3);
int j = 5;
DateTime time = DateTime.UtcNow;
Storage value = Storage.Empty;
for (int i = 0; i < count; i++)
{
short id = BitConverter.ToInt16(bytes, j);
j += 2;
ITag tag = GetItemByID(id);
if (tag != null)
{
DataType type = (DataType)bytes[j++];
switch (type)
{
case DataType.BOOL:
value.Boolean = BitConverter.ToBoolean(bytes, j++);
break;
case DataType.BYTE:
value.Byte = bytes[j++];
break;
case DataType.SHORT:
value.Int16 = BitConverter.ToInt16(bytes, j);
j += 2;
break;
case DataType.INT:
value.Int32 = BitConverter.ToInt32(bytes, j);
j += 4;
break;
case DataType.FLOAT:
value.Single = BitConverter.ToSingle(bytes, j);
j += 4;
break;
}
tag.Update(value, time, QUALITIES.QUALITY_GOOD);
}
else
{
byte type = bytes[j];
j += (type < 4 ? 2 : type < 6 ? 3 : 5);
}
}
//Array.Clear(bytes, 0, count);
}
}
while (result > 0);
}
public bool AddItems(ItemMetaData[] items)
{
int count = items.Length;
if (_items == null) _items = new ITag[count];
for (int i = 0; i < count; i++)
{
ITag dataItem = null;
ItemMetaData meta = items[i];
DeviceAddress addr = new DeviceAddress(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:
case DataType.SHORT:
dataItem = new ShortTag(meta.ID, addr, this);
break;
case DataType.TIME:
case DataType.INT:
dataItem = new IntTag(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[i] = dataItem;
_server.AddItemIndex(meta.Name, dataItem);
}
Array.Sort<ITag>(_items);
Init();
return true;
}
public bool RemoveAll()
{
Array.Clear(_items, 0, _items.Length);
return true;
}
public bool SetActiveState(bool active, params short[] items)
{
return true;
}
public int FindItemByAddress(DeviceAddress addr)
{
return Array.BinarySearch<ITag>(_items, new BoolTag(0, addr, null));
}
public ITag GetItemByID(short id)
{
return _server[id];
}
public int BatchRead(DataSource source, bool isSync, params ITag[] itemArray)
{
if (itemArray == null) return -1;
int len = itemArray.Length;
byte[] bt = new byte[4];
byte[] data = new byte[3 + len * 2];
int j=0;
data[j++] = fctHead;
data[j++] = fctReadMultiple;
data[j++] = source == DataSource.Cache ? (byte)0 : (byte)1;
bt = BitConverter.GetBytes(itemArray.Length);
data[j++] = bt[0];
data[j++] = bt[1];
data[j++] = bt[2];
data[j++] = bt[3];
for (int i = 0; i < len; i++)
{
ITag tag = itemArray[i];
bt = BitConverter.GetBytes(tag.ID);
data[j++] = bt[0];
data[j++] = bt[1];
data[j++] = (byte)(tag.Address.DataSize >> 3);
}
SocketError error;
_tcpSynCl.Send(data, 0, data.Length, SocketFlags.None, out error);
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, tcpSynClBuffer.Length, SocketFlags.None, out error);
j = 2;
if (error == SocketError.Success)
{
DateTime time=DateTime.UtcNow;
Storage value=Storage.Empty;
for (int i = 0; i < len; i++)
{
ITag tag = itemArray[i];
switch (tag.Address.VarType)
{
case DataType.BOOL:
value.Boolean = BitConverter.ToBoolean(tcpSynClBuffer, j++);
break;
case DataType.BYTE:
value.Byte = tcpSynClBuffer[j++];
break;
case DataType.SHORT:
value.Int16 = BitConverter.ToInt16(tcpSynClBuffer, j);
j += 2;
break;
case DataType.INT:
value.Int32 = BitConverter.ToInt32(tcpSynClBuffer, j);
j += 4;
break;
case DataType.FLOAT:
value.Single = BitConverter.ToSingle(tcpSynClBuffer, j);
j += 4;
break;
}
tag.Update(value, time, QUALITIES.QUALITY_GOOD);
}
return 0;
}
else
{
throw new SocketException((int)error);
}
}
public int BatchWrite(IDictionary<ITag, object> items, bool isSync = true)
{
List<byte> list = new List<byte>(new byte[] { fctHead, fctWriteMultiple });
list.AddRange(BitConverter.GetBytes(items.Count));
foreach (var item in items)
{
ITag tag = item.Key;
list.AddRange(BitConverter.GetBytes(tag.ID));
switch (tag.Address.VarType)
{
case DataType.BOOL:
list.Add((bool)item.Value ? (byte)1 : (byte)0);
break;
case DataType.BYTE:
list.Add((byte)item.Value);
break;
case DataType.SHORT:
list.AddRange(BitConverter.GetBytes((short)item.Value));
break;
case DataType.INT:
list.AddRange(BitConverter.GetBytes((int)item.Value));
break;
case DataType.FLOAT:
list.AddRange(BitConverter.GetBytes((float)item.Value));
break;
}
}
SocketError error;
_tcpSynCl.Send(list.ToArray(), 0, list.Count, SocketFlags.None, out error);
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, 2, SocketFlags.None, out error);
if (error == SocketError.Success)
return tcpSynClBuffer[1];
else
{
throw new SocketException((int)error);
}
}
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<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.Default.GetString(data, 0, address.DataSize), 0, QUALITIES.QUALITY_GOOD);
}
public int WriteInt32(DeviceAddress address, int 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()
{
RemoveAll();
_items = null;
}
}
}