|
|
@@ -1,9 +1,12 @@
|
|
|
using System.Collections.Concurrent;
|
|
|
using System.Reflection;
|
|
|
+using System.Text;
|
|
|
using YSAI.Core.attribute;
|
|
|
using YSAI.Core.communication.net.tcp.client;
|
|
|
using YSAI.Core.data;
|
|
|
using YSAI.Core.@interface;
|
|
|
+using YSAI.Core.subscription;
|
|
|
+using YSAI.Core.virtualAddress;
|
|
|
using YSAI.Unility;
|
|
|
using static YSAI.Mitsubishi.MitsubishiData;
|
|
|
|
|
|
@@ -77,6 +80,11 @@ namespace YSAI.Mitsubishi
|
|
|
/// </summary>
|
|
|
private TcpClientOperate tcpClientOperate;
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 虚拟地址
|
|
|
+ /// </summary>
|
|
|
+ private VirtualAddressManage VAM = new VirtualAddressManage();
|
|
|
+
|
|
|
#region 基础
|
|
|
/// <summary>
|
|
|
/// Qna_3E地址解析
|
|
|
@@ -571,8 +579,7 @@ namespace YSAI.Mitsubishi
|
|
|
//读取失败返回
|
|
|
return new byte[] { };
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+ return bytes;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -635,7 +642,21 @@ namespace YSAI.Mitsubishi
|
|
|
|
|
|
public OperateResult Off()
|
|
|
{
|
|
|
- throw new NotImplementedException();
|
|
|
+ string SN = Depart("Off");
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (tcpClientOperate == null || !tcpClientOperate.GetStatus().State)
|
|
|
+ {
|
|
|
+ return Break(SN, false, "未连接");
|
|
|
+ }
|
|
|
+ tcpClientOperate.Dispose();
|
|
|
+ tcpClientOperate = null;
|
|
|
+ return Break(SN, true);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ return Break(SN, false, ex.Message, Exception: ex);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public Task<OperateResult> OffAsync()
|
|
|
@@ -688,17 +709,210 @@ namespace YSAI.Mitsubishi
|
|
|
|
|
|
public OperateResult Read(Address address)
|
|
|
{
|
|
|
- throw new NotImplementedException();
|
|
|
+ string SN = Depart("Read");
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (tcpClientOperate == null || !tcpClientOperate.GetStatus().State)
|
|
|
+ {
|
|
|
+ return Break(SN, false, "未连接");
|
|
|
+ }
|
|
|
+ //节点数据
|
|
|
+ ConcurrentDictionary<string, AddressValue> param = new ConcurrentDictionary<string, AddressValue>();
|
|
|
+ //循环添加项集合
|
|
|
+ foreach (var item in address.AddressArray)
|
|
|
+ {
|
|
|
+ if (!item.IsEnable) continue;
|
|
|
+ //是不是虚拟地址
|
|
|
+ bool IsVA = false;
|
|
|
+ //初始化虚拟地址
|
|
|
+ VAM.InitVirtualAddress(item, out IsVA);
|
|
|
+ //值
|
|
|
+ string? Value = string.Empty;
|
|
|
+
|
|
|
+ if (IsVA)
|
|
|
+ {
|
|
|
+ Value = VAM.Read(item);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ (string addressName, int count) addressData = (string.Empty, 0);
|
|
|
+ if (item.AddressName.Contains(","))
|
|
|
+ {
|
|
|
+ string[] strs = item.AddressName.Split(',');
|
|
|
+ addressData.addressName = strs[0];
|
|
|
+ addressData.count = int.Parse(strs[1]);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ addressData.addressName = item.AddressName;
|
|
|
+ }
|
|
|
+ if (addressData.count.Equals(0))
|
|
|
+ {
|
|
|
+ switch (item.AddressDataType)
|
|
|
+ {
|
|
|
+ case Core.@enum.DataType.Short:
|
|
|
+ Value = BitConverter.ToInt16(R(addressData.addressName, 2), 0).ToString();
|
|
|
+ break;
|
|
|
+ case Core.@enum.DataType.Bool:
|
|
|
+ Value = ((R(addressData.addressName, 1)[0] & 0b00010000) != 0).ToString();
|
|
|
+ break;
|
|
|
+ case Core.@enum.DataType.Ushort:
|
|
|
+ Value = BitConverter.ToUInt16(R(addressData.addressName, 2), 0).ToString();
|
|
|
+ break;
|
|
|
+ case Core.@enum.DataType.Int:
|
|
|
+ Value = BitConverter.ToInt32(R(addressData.addressName, 4), 0).ToString();
|
|
|
+ break;
|
|
|
+ case Core.@enum.DataType.Uint:
|
|
|
+ Value = BitConverter.ToUInt32(R(addressData.addressName, 4), 0).ToString();
|
|
|
+ break;
|
|
|
+ case Core.@enum.DataType.Long:
|
|
|
+ Value = BitConverter.ToInt64(R(addressData.addressName, 8), 0).ToString();
|
|
|
+ break;
|
|
|
+ case Core.@enum.DataType.Ulong:
|
|
|
+ Value = BitConverter.ToUInt64(R(addressData.addressName, 8), 0).ToString();
|
|
|
+ break;
|
|
|
+ case Core.@enum.DataType.Double:
|
|
|
+ Value = BitConverter.ToDouble(R(addressData.addressName, 8), 0).ToString();
|
|
|
+ break;
|
|
|
+ case Core.@enum.DataType.Float:
|
|
|
+ Value = BitConverter.ToSingle(R(addressData.addressName, 4), 0).ToString();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ Value = $"不支持{item.AddressDataType}类型读取";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //数据长度
|
|
|
+ int len = 0;
|
|
|
+ //返回的字节数
|
|
|
+ byte[] bytes;
|
|
|
+ //连续读取返回的数据
|
|
|
+ List<RetValue> DValue = new List<RetValue>();
|
|
|
+ //数据类型
|
|
|
+ string dbType = addressData.addressName.Substring(0, 1);
|
|
|
+ //数据块地址
|
|
|
+ int dbAddress = int.Parse(addressData.addressName.Substring(1));
|
|
|
+
|
|
|
+ switch (item.AddressDataType)
|
|
|
+ {
|
|
|
+ case Core.@enum.DataType.Short:
|
|
|
+ len = 2;
|
|
|
+ bytes = R(addressData.addressName, Convert.ToUInt16(len * addressData.count));
|
|
|
+ for (int i = 0; i < addressData.count; i++)
|
|
|
+ {
|
|
|
+ DValue.Add(new RetValue
|
|
|
+ {
|
|
|
+ Address = $"{dbType}{dbAddress + i * len}",
|
|
|
+ Value = BitConverter.ToInt16(bytes, (addressData.count - 1 - i) * len).ToString()
|
|
|
+ });
|
|
|
+ }
|
|
|
+ Value = DValue.ToJson();
|
|
|
+ break;
|
|
|
+ case Core.@enum.DataType.Bool:
|
|
|
+ len = 1;
|
|
|
+ bytes = R(addressData.addressName, Convert.ToUInt16(len * addressData.count));
|
|
|
+ for (int i = 0; i < addressData.count; i++)
|
|
|
+ {
|
|
|
+ var index = i / 2;
|
|
|
+ var isoffset = i % 2 == 0;
|
|
|
+ bool value;
|
|
|
+ if (isoffset)
|
|
|
+ {
|
|
|
+ value = (bytes[index] & 0b00010000) != 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ value = (bytes[index] & 0b00000001) != 0;
|
|
|
+ }
|
|
|
+ DValue.Add(new RetValue
|
|
|
+ {
|
|
|
+ Address = $"{dbType}{dbAddress + i * len}",
|
|
|
+ Value = value.ToString()
|
|
|
+ });
|
|
|
+ }
|
|
|
+ Value = DValue.ToJson();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ Value = $"不支持{item.AddressDataType}类型批量读取";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //数据处理
|
|
|
+ AddressValue addressValue = Core.handler.AddressHandler.ExecuteDispose(item, Value);
|
|
|
+
|
|
|
+ //数据添加
|
|
|
+ param.AddOrUpdate(item.AddressName, addressValue, (k, v) => addressValue);
|
|
|
+
|
|
|
+ }
|
|
|
+ if (param.Count > 0)
|
|
|
+ {
|
|
|
+ //返回读取的数据
|
|
|
+ return Break(SN, true, RData: param, RType: Core.@enum.ResultType.KeyValue);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return Break(SN, false, "读取失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ return Break(SN, false, ex.Message, Exception: ex);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public Task<OperateResult> ReadAsync(Address address)
|
|
|
{
|
|
|
return Task.Run(() => Read(address));
|
|
|
}
|
|
|
-
|
|
|
+ /// <summary>
|
|
|
+ /// 实现订阅功能
|
|
|
+ /// </summary>
|
|
|
+ private SubscribeOperate subscribeOperate;
|
|
|
public OperateResult Subscribe(Address address)
|
|
|
{
|
|
|
- throw new NotImplementedException();
|
|
|
+ string SN = Depart("Subscribe");
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (subscribeOperate == null)
|
|
|
+ {
|
|
|
+ subscribeOperate = SubscribeOperate.Instance(new SubscribeData.Basics()
|
|
|
+ {
|
|
|
+ Address = address,
|
|
|
+ ChangeOut = basics.ChangeOut,
|
|
|
+ Function = Read,
|
|
|
+ AllOut = basics.AllOut,
|
|
|
+ HandleInterval = basics.HandleInterval,
|
|
|
+ SN = basics.SN,
|
|
|
+ TaskHandleInterval = basics.TaskHandleInterval,
|
|
|
+ TaskNumber = basics.TaskNumber
|
|
|
+ });
|
|
|
+ subscribeOperate.OnEvent += SubscribeOperate_OnEvent;
|
|
|
+ OperateResult operateResult = subscribeOperate.On();
|
|
|
+ return Break(SN, operateResult.State, operateResult.Message);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ OperateResult operateResult = subscribeOperate.Subscribe(address);
|
|
|
+ return Break(SN, operateResult.State, operateResult.Message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ return Break(SN, false, ex.Message, Exception: ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 事件抛出
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="sender">自定义订阅对象</param>
|
|
|
+ /// <param name="e">返回的参数</param>
|
|
|
+ private void SubscribeOperate_OnEvent(object? sender, EventResult e)
|
|
|
+ {
|
|
|
+ OnEventHandler(this, e);
|
|
|
}
|
|
|
|
|
|
public Task<OperateResult> SubscribeAsync(Address address)
|
|
|
@@ -708,7 +922,23 @@ namespace YSAI.Mitsubishi
|
|
|
|
|
|
public OperateResult UnSubscribe(Address address)
|
|
|
{
|
|
|
- throw new NotImplementedException();
|
|
|
+ string SN = Depart("UnSubscribe");
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (subscribeOperate != null)
|
|
|
+ {
|
|
|
+ OperateResult operateResult = subscribeOperate.UnSubscribe(address);
|
|
|
+ return Break(SN, operateResult.State, operateResult.Message);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return Break(SN, false, "当前尚未订阅");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ return Break(SN, false, ex.Message, Exception: ex);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public Task<OperateResult> UnSubscribeAsync(Address address)
|
|
|
@@ -718,7 +948,94 @@ namespace YSAI.Mitsubishi
|
|
|
|
|
|
public OperateResult Write<V>(ConcurrentDictionary<string, V> Values)
|
|
|
{
|
|
|
- throw new NotImplementedException();
|
|
|
+ string SN = Depart("Write");
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (tcpClientOperate == null || !tcpClientOperate.GetStatus().State)
|
|
|
+ {
|
|
|
+ return Break(SN, false, "未连接");
|
|
|
+ }
|
|
|
+ //失败消息
|
|
|
+ List<string> FailMessage = new List<string>();
|
|
|
+
|
|
|
+ foreach (var item in Values)
|
|
|
+ {
|
|
|
+ KeyValuePair<string, V> Param = item;
|
|
|
+ //判断是不是虚拟点
|
|
|
+ if (VAM.IsVirtualAddress(Param.Key))
|
|
|
+ {
|
|
|
+ if (!VAM.Write(Param.Key, Param.Value.ToString()))
|
|
|
+ {
|
|
|
+ FailMessage.Add($"{Param.Key},写入失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ bool RState = false;
|
|
|
+ if (typeof(V).Name.Equals("String"))
|
|
|
+ {
|
|
|
+ var valueBytes = Encoding.ASCII.GetBytes(item.Value.ToString());
|
|
|
+ var bytes = new byte[valueBytes.Length + 1];
|
|
|
+ bytes[0] = (byte)valueBytes.Length;
|
|
|
+ valueBytes.CopyTo(bytes, 1);
|
|
|
+ Array.Reverse(bytes);
|
|
|
+ RState = W(item.Key, bytes);
|
|
|
+ }
|
|
|
+ else if (typeof(V).Name.Equals("Boolean"))
|
|
|
+ {
|
|
|
+ byte[] valueByte = new byte[1];
|
|
|
+ if (Convert.ToBoolean(item.Value)) valueByte[0] = 16;
|
|
|
+ RState = W(item.Key, valueByte, true);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ object obj = Param.Value;
|
|
|
+ if (obj is byte)
|
|
|
+ {
|
|
|
+ RState = W(Param.Key, BitConverter.GetBytes((byte)obj));
|
|
|
+ }
|
|
|
+ else if (obj is Int16)
|
|
|
+ {
|
|
|
+ RState = W(Param.Key, BitConverter.GetBytes((Int16)obj));
|
|
|
+ }
|
|
|
+ else if (obj is UInt16)
|
|
|
+ {
|
|
|
+ RState = W(Param.Key, BitConverter.GetBytes((UInt16)obj));
|
|
|
+ }
|
|
|
+ else if (obj is Int32)
|
|
|
+ {
|
|
|
+ RState = W(Param.Key, BitConverter.GetBytes((Int32)obj));
|
|
|
+ }
|
|
|
+ else if (obj is Int64)
|
|
|
+ {
|
|
|
+ RState = W(Param.Key, BitConverter.GetBytes((Int64)obj));
|
|
|
+ }
|
|
|
+ else if (obj is float)
|
|
|
+ {
|
|
|
+ RState = W(Param.Key, BitConverter.GetBytes((float)obj));
|
|
|
+ }
|
|
|
+ else if (obj is double)
|
|
|
+ {
|
|
|
+ RState = W(Param.Key, BitConverter.GetBytes((double)obj));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!RState)
|
|
|
+ {
|
|
|
+ FailMessage.Add($"{Param.Key},写入失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (FailMessage.Count > 0)
|
|
|
+ {
|
|
|
+ return Break("Write", false, FailMessage.ToJson());
|
|
|
+ }
|
|
|
+ return Break("Write", true, "写入成功");
|
|
|
+
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ return Break(SN, false, ex.Message, Exception: ex);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public Task<OperateResult> WriteAsync<V>(ConcurrentDictionary<string, V> Values)
|