16 changed files with 45 additions and 2671 deletions
@ -0,0 +1,8 @@ |
|||
C:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\obj\x86\Debug\GeneratedInternalTypeHelper.g.i.cs |
|||
|
|||
FC:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\AlarmSet.xaml;; |
|||
FC:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\Guage.xaml;; |
|||
FC:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\MainWindow.xaml;; |
|||
FC:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\TagMonitor.xaml;; |
|||
FC:\Users\Yinan\Documents\Github\SharpSCADA\SCADA\Program\CoreTest\Trend.xaml;; |
|||
|
|||
@ -1,607 +0,0 @@ |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -1,4 +0,0 @@ |
|||
namespace DataService |
|||
{ |
|||
|
|||
} |
|||
@ -1,180 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace DataService |
|||
{ |
|||
public abstract class IModbusReader: 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; |
|||
|
|||
public int PDU |
|||
{ |
|||
get { return 0xFF; } |
|||
} |
|||
|
|||
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; |
|||
} |
|||
|
|||
|
|||
short _id; |
|||
public short ID |
|||
{ |
|||
get |
|||
{ |
|||
return _id; |
|||
} |
|||
} |
|||
|
|||
string _server; |
|||
public string ServerName |
|||
{ |
|||
get { return _server; } |
|||
set { _server = value; } |
|||
} |
|||
|
|||
public abstract DeviceDriver Driver |
|||
{ |
|||
get ; |
|||
} |
|||
|
|||
public abstract bool IsClosed |
|||
{ |
|||
get; |
|||
} |
|||
|
|||
private int _timeout; |
|||
public int TimeOut |
|||
{ |
|||
get { return _timeout; } |
|||
set { _timeout = value; } |
|||
} |
|||
|
|||
List<IGroup> _grps = new List<IGroup>(20); |
|||
public IEnumerable<IGroup> Groups |
|||
{ |
|||
get { return _grps; } |
|||
} |
|||
|
|||
IDataServer _parent; |
|||
public IDataServer Parent |
|||
{ |
|||
get { return _parent; } |
|||
} |
|||
|
|||
public abstract bool Connect(); |
|||
|
|||
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 event ShutdownRequestEventHandler OnClose; |
|||
|
|||
public abstract void Dispose(); |
|||
|
|||
public abstract byte[] ReadBytes(DeviceAddress address, ushort size); |
|||
|
|||
public abstract ItemData<int> ReadInt32(DeviceAddress address); |
|||
|
|||
public abstract ItemData<short> ReadInt16(DeviceAddress address); |
|||
|
|||
public abstract ItemData<byte> ReadByte(DeviceAddress address); |
|||
|
|||
public abstract ItemData<string> ReadString(DeviceAddress address, ushort size); |
|||
|
|||
public abstract ItemData<float> ReadFloat(DeviceAddress address); |
|||
|
|||
public abstract ItemData<bool> ReadBit(DeviceAddress address); |
|||
|
|||
public abstract ItemData<object> ReadValue(DeviceAddress address); |
|||
|
|||
public abstract int WriteBytes(DeviceAddress address, byte[] bit); |
|||
|
|||
public abstract int WriteBit(DeviceAddress address, bool bit); |
|||
|
|||
public abstract int WriteBits(DeviceAddress address, byte bits); |
|||
|
|||
public abstract int WriteInt16(DeviceAddress address, short value); |
|||
|
|||
public abstract int WriteInt32(DeviceAddress address, int value); |
|||
|
|||
public abstract int WriteFloat(DeviceAddress address, float value); |
|||
|
|||
public abstract int WriteString(DeviceAddress address, string str); |
|||
|
|||
public abstract int WriteValue(DeviceAddress address, object value); |
|||
} |
|||
} |
|||
@ -1,219 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace DataService |
|||
{ |
|||
[Flags] |
|||
public enum DevStatus |
|||
{ |
|||
None = 0, |
|||
RUN = 0x0001, |
|||
STOP = 0x0002, |
|||
OLS = 0x0004, |
|||
CLS = 0x0008, |
|||
PAUSE = 0x0010, |
|||
READY = 0x0020, |
|||
DUMP = 0x0040, |
|||
COMPLETE = 0x0080, |
|||
ALARM = 0x0100, |
|||
} |
|||
|
|||
[Flags] |
|||
public enum DevCommand |
|||
{ |
|||
START = 0x0001, |
|||
STOP = 0x0002, |
|||
ABORT = 0x0004, |
|||
PAUSE = 0x0008, |
|||
HOLD = 0x0010, |
|||
WET = 0x0020, |
|||
DISCH = 0x0040, |
|||
DUMP = 0x0080, |
|||
} |
|||
|
|||
public interface IDevice : IComparable<IDevice> |
|||
{ |
|||
string Name { get; } |
|||
string Description { get; } |
|||
DeviceTypes DeviceType { get; } |
|||
int ExtStatus { get; } |
|||
DevStatus Status { get; } |
|||
int Execute(DevCommand command); |
|||
} |
|||
|
|||
//可以根据订单的源仓目标仓,二分法搜索到仓的列表。得到仓的参数和状态;部分设备是互斥的(如闸门,分配器,三通),马达等是共用的。
|
|||
|
|||
|
|||
public sealed class RouteVex : IComparable<RouteVex>, IEqualityComparer<RouteVex> |
|||
{ |
|||
string _source; |
|||
public string Source |
|||
{ |
|||
get { return _source; } |
|||
} |
|||
|
|||
string _dest; |
|||
public string Dest |
|||
{ |
|||
get { return _dest; } |
|||
} |
|||
|
|||
RouteVex _front; |
|||
public RouteVex Front |
|||
{ |
|||
get { return _front; } |
|||
set { _front = value; } |
|||
} |
|||
|
|||
Func<bool> _func; |
|||
Func<int> _funcWrite; |
|||
public bool IsRun |
|||
{ |
|||
get { return _func == null ? true : _func(); } |
|||
} |
|||
|
|||
public RouteVex(string source, string dest = "") |
|||
{ |
|||
_source = source; |
|||
_dest = dest; |
|||
} |
|||
|
|||
public RouteVex(string source, string dest, string startSignal, string runSignal, IDataServer srv) |
|||
{ |
|||
_source = source; |
|||
_dest = dest; |
|||
var eval = srv.Eval; |
|||
if (!string.IsNullOrEmpty(runSignal)) |
|||
{ |
|||
_func = eval.Eval(runSignal) as Func<bool>; |
|||
} |
|||
if (!string.IsNullOrEmpty(startSignal)) |
|||
{ |
|||
_funcWrite = eval.WriteEval(startSignal) as Func<int>; |
|||
} |
|||
} |
|||
|
|||
public int Start() |
|||
{ |
|||
return _funcWrite == null ? 0 : _funcWrite(); |
|||
} |
|||
|
|||
public int CompareTo(RouteVex other) |
|||
{ |
|||
if (this._source == null) return 0; |
|||
if (other._dest == null) return 1; |
|||
int comp = this._source.CompareTo(other._source); |
|||
return comp == 0 ? this._dest.CompareTo(other._dest) : comp; |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return string.Concat(_source, ",", _dest); |
|||
} |
|||
|
|||
public bool Equals(RouteVex x, RouteVex y) |
|||
{ |
|||
if (x == null || y == null) return false; |
|||
return ((x.Source == null && y.Source == null) || (x.Source == y.Source)) && |
|||
((x.Dest == null && y.Dest == null) || (x.Dest == y.Dest)); |
|||
} |
|||
|
|||
public int GetHashCode(RouteVex obj) |
|||
{ |
|||
return obj.Source.GetHashCode() ^ obj.Dest.GetHashCode(); |
|||
} |
|||
} |
|||
|
|||
//正在执行的路径,保存到一个XML文件,包含当前执行的节点
|
|||
public sealed class Route : LinkedList<RouteVex>, ICloneable |
|||
{ |
|||
LinkedListNode<RouteVex> _current; |
|||
public LinkedListNode<RouteVex> Current |
|||
{ |
|||
get { return _current; } |
|||
set { _current = value; } |
|||
} |
|||
|
|||
string _start; |
|||
public string Start |
|||
{ |
|||
get { return _start; } |
|||
set { _start = value; } |
|||
} |
|||
|
|||
string _end; |
|||
public string End |
|||
{ |
|||
get { return _end; } |
|||
set { _end = value; } |
|||
} |
|||
|
|||
RouteState _state; |
|||
public RouteState State |
|||
{ |
|||
get { return _state; } |
|||
set { _state = value; } |
|||
} |
|||
|
|||
string[] _options; |
|||
public string[] Options |
|||
{ |
|||
get { return _options; } |
|||
set { _options = value; } |
|||
} |
|||
|
|||
|
|||
public Route(string source, string dest, string[] options = null) |
|||
{ |
|||
_start = source; |
|||
_end = dest; |
|||
_options = options; |
|||
} |
|||
|
|||
public object Clone() |
|||
{ |
|||
Route rt = new Route(_start, _end, _options); |
|||
foreach (var routeVex in this) |
|||
{ |
|||
rt.AddLast(routeVex); |
|||
} |
|||
return rt; |
|||
} |
|||
} |
|||
|
|||
public enum DeviceTypes |
|||
{ |
|||
Misc, |
|||
Motor = 1, |
|||
Valve = 2, |
|||
Gate = 3, |
|||
Divert = 4, |
|||
FourWays = 5, |
|||
Distribute = 6, |
|||
Bin = 7, |
|||
Analog = 8, |
|||
Scale = 9, |
|||
Conveyor = 10, |
|||
Elevator = 11, |
|||
Mixer = 12, |
|||
Grind = 13, |
|||
Pellet = 14, |
|||
Extruder = 15, |
|||
CheckScale = 16, |
|||
Liquid = 17, |
|||
HandAdd = 18, |
|||
Sifter = 19, |
|||
Cyclone = 20, |
|||
AirHammer = 21 |
|||
} |
|||
|
|||
public enum RouteState |
|||
{ |
|||
Idle, |
|||
Run, |
|||
Stop, |
|||
Suspend, |
|||
Complete, |
|||
Abort, |
|||
} |
|||
} |
|||
@ -1,385 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
|
|||
namespace DataService |
|||
{ |
|||
public class TagListGroup : IGroup |
|||
{ |
|||
bool _isActive, _isDisposed; |
|||
public bool IsActive |
|||
{ |
|||
get |
|||
{ |
|||
return _isActive; |
|||
} |
|||
set |
|||
{ |
|||
_isActive = value; |
|||
if (value) |
|||
{ |
|||
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(this.OnUpdate), 1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
short _id; |
|||
public short ID |
|||
{ |
|||
get |
|||
{ |
|||
return _id; |
|||
} |
|||
} |
|||
|
|||
int _updateRate; |
|||
public int UpdateRate |
|||
{ |
|||
get |
|||
{ |
|||
return _updateRate; |
|||
} |
|||
set |
|||
{ |
|||
_updateRate = value; |
|||
} |
|||
} |
|||
|
|||
string _name; |
|||
public string Name |
|||
{ |
|||
get |
|||
{ |
|||
return _name; |
|||
} |
|||
set |
|||
{ |
|||
_name = value; |
|||
} |
|||
} |
|||
|
|||
float deadband; |
|||
public float DeadBand |
|||
{ |
|||
get |
|||
{ |
|||
return deadband; |
|||
} |
|||
set |
|||
{ |
|||
deadband = value; |
|||
} |
|||
} |
|||
|
|||
IPLCDriver _reader; |
|||
public IDriver Parent |
|||
{ |
|||
get { return _reader; } |
|||
} |
|||
|
|||
List<ITag> _items; |
|||
public IEnumerable<ITag> Items |
|||
{ |
|||
get { return _items; } |
|||
} |
|||
|
|||
IDataServer _server; |
|||
public IDataServer Server |
|||
{ |
|||
get |
|||
{ |
|||
return _server; |
|||
} |
|||
} |
|||
|
|||
HashSet<short> _activeList; |
|||
CompareItemByID rc = new CompareItemByID(); |
|||
|
|||
public TagListGroup(short id, string name, int updateRate, int size, bool active, IPLCDriver plcReader) |
|||
{ |
|||
this._id = id; |
|||
this._updateRate = updateRate; |
|||
this._isActive = active; |
|||
this._reader = plcReader; |
|||
this._name = name; |
|||
this._server = _reader.Parent; |
|||
} |
|||
|
|||
|
|||
public bool AddItems(IList<TagMetaData> items) |
|||
{ |
|||
int count = items.Count; |
|||
if (_items == null) |
|||
{ |
|||
_items = new List<ITag>(count); |
|||
_activeList = new HashSet<short>(); |
|||
} |
|||
lock (_server.SyncRoot) |
|||
{ |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
ITag dataItem = null; |
|||
TagMetaData meta = items[i]; |
|||
if (meta.GroupID == this._id) |
|||
{ |
|||
switch (meta.DataType) |
|||
{ |
|||
case DataType.BOOL: |
|||
dataItem = new BoolTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); |
|||
break; |
|||
case DataType.BYTE: |
|||
dataItem = new ByteTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); |
|||
break; |
|||
case DataType.WORD: |
|||
case DataType.SHORT: |
|||
dataItem = new ShortTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); |
|||
break; |
|||
case DataType.TIME: |
|||
case DataType.INT: |
|||
dataItem = new IntTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); |
|||
break; |
|||
case DataType.FLOAT: |
|||
dataItem = new FloatTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); |
|||
break; |
|||
case DataType.STR: |
|||
dataItem = new StringTag(meta.ID, _reader.GetDeviceAddress(meta.Address), this); |
|||
break; |
|||
} |
|||
if (dataItem != null) |
|||
{ |
|||
_items.Add(dataItem); |
|||
_server.AddItemIndex(meta.Name, dataItem); |
|||
_activeList.Add(meta.ID); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
_items.Sort(rc); |
|||
_items.TrimExcess(); |
|||
return true; |
|||
} |
|||
|
|||
public bool AddTags(IEnumerable<ITag> tags) |
|||
{ |
|||
if (_items == null) |
|||
{ |
|||
_items = new List<ITag>(); |
|||
} |
|||
foreach (ITag tag in tags) |
|||
{ |
|||
if (tag != null) |
|||
{ |
|||
_items.Add(tag); |
|||
if (tag.Active) |
|||
{ |
|||
_activeList.Add(tag.ID); |
|||
} |
|||
} |
|||
} |
|||
_items.Sort(rc); |
|||
_items.TrimExcess(); |
|||
return true; |
|||
} |
|||
|
|||
public bool RemoveItems(params ITag[] items) |
|||
{ |
|||
foreach (var item in items) |
|||
{ |
|||
_server.RemoveItemIndex(item.GetTagName()); |
|||
_activeList.Remove(item.ID); |
|||
_items.Remove(item); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
|
|||
public bool SetActiveState(bool active, params short[] items) |
|||
{ |
|||
if (active) |
|||
{ |
|||
lock (_activeList) |
|||
{ |
|||
_activeList.UnionWith(items); |
|||
}; |
|||
} |
|||
else |
|||
{ |
|||
lock (_activeList) |
|||
{ |
|||
_activeList.ExceptWith(items); |
|||
}; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
public ITag FindItemByAddress(DeviceAddress addr) |
|||
{ |
|||
for (int i = 0; i < _items.Count; i++) |
|||
{ |
|||
DeviceAddress address = _items[i].Address; |
|||
if (addr.Area == address.Area |
|||
&& addr.DBNumber == address.DBNumber |
|||
&& addr.Start == address.Start |
|||
&& addr.Bit == address.Bit) |
|||
return _items[i]; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray) |
|||
{ |
|||
return ExtMethods.BatchRead(source, itemArray); |
|||
} |
|||
|
|||
public int BatchWrite(SortedDictionary<ITag, object> items, bool isSync = true) |
|||
{ |
|||
int rev = ExtMethods.BatchWrite(items); |
|||
if (DataChange != null && rev >= 0) |
|||
{ |
|||
int len = items.Count; |
|||
HistoryData[] data = new HistoryData[len]; |
|||
int i = 0; |
|||
foreach (var item in items) |
|||
{ |
|||
ITag tag = item.Key; |
|||
data[i].ID = tag.ID; |
|||
data[i].TimeStamp = tag.TimeStamp; |
|||
data[i].Quality = tag.Quality; |
|||
data[i].Value = item.Key.ToStorage(item.Value); |
|||
i++; |
|||
} |
|||
foreach (DataChangeEventHandler deleg in DataChange.GetInvocationList()) |
|||
{ |
|||
deleg.BeginInvoke(this, new DataChangeEventArgs(1, data), null, null); |
|||
} |
|||
} |
|||
return rev; |
|||
} |
|||
|
|||
public ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache) |
|||
{ |
|||
return _reader.ReadInt32(address); |
|||
} |
|||
|
|||
public ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache) |
|||
{ |
|||
return _reader.ReadInt16(address); |
|||
} |
|||
|
|||
public ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache) |
|||
{ |
|||
return _reader.ReadByte(address); |
|||
} |
|||
|
|||
public ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache) |
|||
{ |
|||
return _reader.ReadFloat(address); |
|||
} |
|||
|
|||
public ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache) |
|||
{ |
|||
return _reader.ReadBit(address); |
|||
} |
|||
|
|||
public ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache) |
|||
{ |
|||
return new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_UNCERTAIN); |
|||
} |
|||
|
|||
public int WriteInt32(DeviceAddress address, int value) |
|||
{ |
|||
return _reader.WriteInt32(address, value); |
|||
} |
|||
|
|||
public int WriteInt16(DeviceAddress address, short value) |
|||
{ |
|||
return _reader.WriteInt16(address, value); |
|||
} |
|||
|
|||
public int WriteFloat(DeviceAddress address, float value) |
|||
{ |
|||
return _reader.WriteFloat(address, value); |
|||
} |
|||
|
|||
public int WriteString(DeviceAddress address, string value) |
|||
{ |
|||
return _reader.WriteString(address, value); |
|||
} |
|||
|
|||
public int WriteBit(DeviceAddress address, bool value) |
|||
{ |
|||
return _reader.WriteBit(address, value); |
|||
} |
|||
|
|||
public int WriteBits(DeviceAddress address, byte value) |
|||
{ |
|||
return _reader.WriteBits(address, value); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_items.Clear(); |
|||
_activeList.Clear(); |
|||
_isDisposed = true; |
|||
//Dictionary<int, string> dict = new Dictionary<int, string>();
|
|||
} |
|||
|
|||
|
|||
public void OnUpdate(object stateInfo) |
|||
{ |
|||
while (true) |
|||
{ |
|||
Thread.Sleep(_updateRate); |
|||
lock (this) |
|||
{ |
|||
if (!_isActive || _isDisposed) |
|||
{ |
|||
return; |
|||
} |
|||
List<HistoryData> hdalist = new List<HistoryData>(); |
|||
foreach (short id in _activeList) |
|||
{ |
|||
ITag item = _server[id]; |
|||
if (item != null) |
|||
{ |
|||
if (item.Refresh() && DataChange != null) |
|||
hdalist.Add(new HistoryData(item.ID, item.Quality, item.Value, item.TimeStamp)); |
|||
} |
|||
if (DataChange != null) |
|||
DataChange.BeginInvoke(this, new DataChangeEventArgs(1, hdalist), null, null); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public event DataChangeEventHandler DataChange; |
|||
} |
|||
|
|||
public class CompareItemByID : IComparer<ITag> |
|||
{ |
|||
public int Compare(ITag x, ITag y) |
|||
{ |
|||
if (x == null) |
|||
{ |
|||
if (y == null) |
|||
{ |
|||
return 0; |
|||
} |
|||
else |
|||
{ |
|||
return -1; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (y == null) |
|||
{ |
|||
return 1; |
|||
} |
|||
else |
|||
{ |
|||
return x.ID.CompareTo(y.ID); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 11.00 |
|||
# Visual Studio 2010 |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataService", "DataService.csproj", "{8965E389-6466-4B30-BD43-83C909044637}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|Any CPU = Debug|Any CPU |
|||
Debug|Mixed Platforms = Debug|Mixed Platforms |
|||
Debug|x86 = Debug|x86 |
|||
Release|Any CPU = Release|Any CPU |
|||
Release|Mixed Platforms = Release|Mixed Platforms |
|||
Release|x86 = Release|x86 |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{8965E389-6466-4B30-BD43-83C909044637}.Debug|Any CPU.ActiveCfg = Debug|x86 |
|||
{8965E389-6466-4B30-BD43-83C909044637}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 |
|||
{8965E389-6466-4B30-BD43-83C909044637}.Debug|Mixed Platforms.Build.0 = Debug|x86 |
|||
{8965E389-6466-4B30-BD43-83C909044637}.Debug|x86.ActiveCfg = Debug|x86 |
|||
{8965E389-6466-4B30-BD43-83C909044637}.Debug|x86.Build.0 = Debug|x86 |
|||
{8965E389-6466-4B30-BD43-83C909044637}.Release|Any CPU.ActiveCfg = Release|x86 |
|||
{8965E389-6466-4B30-BD43-83C909044637}.Release|Mixed Platforms.ActiveCfg = Release|x86 |
|||
{8965E389-6466-4B30-BD43-83C909044637}.Release|Mixed Platforms.Build.0 = Release|x86 |
|||
{8965E389-6466-4B30-BD43-83C909044637}.Release|x86.ActiveCfg = Release|x86 |
|||
{8965E389-6466-4B30-BD43-83C909044637}.Release|x86.Build.0 = Release|x86 |
|||
EndGlobalSection |
|||
GlobalSection(SolutionProperties) = preSolution |
|||
HideSolutionNode = FALSE |
|||
EndGlobalSection |
|||
EndGlobal |
|||
Binary file not shown.
File diff suppressed because it is too large
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue