Просмотр исходного кода

1. 底层通信读取等待结果新增分批读取
2. 新增三菱PLC数据采集
3. 细节优化
4. 版本更新

Shun 2 лет назад
Родитель
Сommit
b3d51c1bca

+ 7 - 1
README.md

@@ -396,4 +396,10 @@ while(true)
 6. 版本更新
 
 #### 2023-11-02
-1. 新增创建单例接口方法,为了在反射或获取参数的情况下使用所有功能
+1. 新增创建单例接口方法,为了在反射或获取参数的情况下使用所有功能
+
+#### 2023-11-03
+1. 底层通信读取等待结果新增分批读取
+2. 新增三菱PLC数据采集
+3. 细节优化
+4. 版本更新

+ 0 - 18
src/YSAI.DAQ/CodeMaid.config

@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
-    <configSections>
-        <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
-            <section name="SteveCadwallader.CodeMaid.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
-        </sectionGroup>
-    </configSections>
-    <userSettings>
-        <SteveCadwallader.CodeMaid.Properties.Settings>
-            <setting name="General_IconSet" serializeAs="String">
-                <value>3</value>
-            </setting>
-            <setting name="General_Theme" serializeAs="String">
-                <value>1</value>
-            </setting>
-        </SteveCadwallader.CodeMaid.Properties.Settings>
-    </userSettings>
-</configuration>

+ 1 - 1
src/YSAI.DAQ/YSAI.Core/YSAI.Core.csproj

@@ -5,7 +5,7 @@
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
     <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
-    <Version>1.0.0.79</Version>
+    <Version>1.0.0.80</Version>
     <Authors>Shun</Authors>
     <Company>YSAI</Company>
     <Product>SCADA</Product>

+ 32 - 15
src/YSAI.DAQ/YSAI.Core/communication/net/tcp/client/TcpClientOperate.cs

@@ -322,7 +322,7 @@ namespace YSAI.Core.communication.net.tcp.client
             return Task.Run(() => Send(Data));
         }
 
-        public OperateResult SendWait(byte[] Data)
+        public OperateResult SendWait(byte[] Data, int SumCount = 65536, int LotCount = 0)
         {
             Depart("SendWait");
             try
@@ -333,33 +333,49 @@ namespace YSAI.Core.communication.net.tcp.client
                     if (reverseBack.State)
                     {
                         CancellationTokenSource token = new CancellationTokenSource();
-                        byte[] Byte = new byte[65536];
+                        byte[] Byte = new byte[SumCount];
                         bool Status = Task.Run(() =>
                         {
-                            while (!token.IsCancellationRequested)
+                            if (LotCount != 0)
                             {
-                                int alen = Communication.Available;  //判断是不是有数据可以读取
-                                if (alen > 0)
+                                int offset = 0;
+                                while (offset < SumCount && !token.IsCancellationRequested)
                                 {
-                                    int bytes = Communication.GetStream().Read(Byte, 0, alen);  //读取数据
-                                    if (bytes > 0)
+                                    int count = (SumCount - offset) >= LotCount ? LotCount : (SumCount - offset);
+                                    int bytes = Communication.GetStream().Read(Byte, offset, count);  //读取数据
+                                    if (bytes == 0)
                                     {
+                                        Byte = new byte[] { };
                                         return;
                                     }
+                                    offset += bytes;
+                                }
+                            }
+                            else
+                            {
+                                while (!token.IsCancellationRequested)
+                                {
+                                    int alen = Communication.Available;  //判断是不是有数据可以读取
+                                    if (alen > 0)
+                                    {
+                                        int bytes = Communication.GetStream().Read(Byte, 0, alen);  //读取数据
+                                        if (bytes > 0)
+                                        {
+                                            return;
+                                        }
+                                        Thread.Sleep(SleepTime);
+                                    }
                                     else
                                     {
                                         Thread.Sleep(SleepTime);
                                     }
                                 }
-                                else
-                                {
-                                    Thread.Sleep(SleepTime);
-                                }
                             }
+
                         }, token.Token).Wait(basics.SendWaitInterval);
                         if (Status)
                         {
-                            if (Byte.Length > 0)
+                            if (Byte.TrimEnd().Length > 0)
                             {
                                 return Break("SendWait", true, RData: Byte.TrimEnd(), RType: @enum.ResultType.Byte);
                             }
@@ -372,8 +388,9 @@ namespace YSAI.Core.communication.net.tcp.client
                         {
                             return Break("SendWait", false, "接收数据超时");
                         }
+
                     }
-                    return Break("SendWait", false, "读取或写入失败");
+                    return Break("SendWait", false, "发送等待结果失败");
                 }
                 return Break("SendWait", false, "未连接");
             }
@@ -383,9 +400,9 @@ namespace YSAI.Core.communication.net.tcp.client
             }
         }
 
-        public Task<OperateResult> SendWaitAsync(byte[] Data)
+        public Task<OperateResult> SendWaitAsync(byte[] Data, int SumCount = 65536, int LotCount = 0)
         {
-            return Task.Run(() => SendWait(Data));
+            return Task.Run(() => SendWait(Data, SumCount, LotCount));
         }
 
         public OperateResult GetObject()

+ 8 - 11
src/YSAI.DAQ/YSAI.Core/communication/net/udp/UdpOperate.cs

@@ -296,7 +296,7 @@ namespace YSAI.Core.communication.net.udp
             return Task.Run(() => Send(Data, iPEndPoint));
         }
 
-        public OperateResult SendWait(byte[] Data)
+        public OperateResult SendWait(byte[] Data, int SumCount = 65536, int LotCount = 0)
         {
             Depart("SendWait");
             try
@@ -307,7 +307,7 @@ namespace YSAI.Core.communication.net.udp
                     if (reverseBack.State)
                     {
                         CancellationTokenSource token = new CancellationTokenSource();
-                        byte[] Byte = new byte[65536];
+                        byte[] Byte = new byte[SumCount];
                         bool Status = Task.Run(() =>
                         {
                             while (!token.IsCancellationRequested)
@@ -315,16 +315,13 @@ namespace YSAI.Core.communication.net.udp
                                 int alen = Communication.Available;  //判断是不是有数据可以读取
                                 if (alen > 0)
                                 {
-                                    UdpReceiveResult urr = Communication.ReceiveAsync().Result;  //读取数据
+                                    UdpReceiveResult urr = Communication.ReceiveAsync(token.Token).Result;  //读取数据
                                     if (urr.Buffer.Length > 0)
                                     {
                                         Byte = urr.Buffer;
                                         return;
                                     }
-                                    else
-                                    {
-                                        Thread.Sleep(SleepTime);
-                                    }
+                                    Thread.Sleep(SleepTime);
                                 }
                                 else
                                 {
@@ -334,7 +331,7 @@ namespace YSAI.Core.communication.net.udp
                         }, token.Token).Wait(basics.SendWaitInterval);
                         if (Status)
                         {
-                            if (Byte.Length > 0)
+                            if (Byte.TrimEnd().Length > 0)
                             {
                                 return Break("SendWait", true, RData: Byte.TrimEnd(), RType: @enum.ResultType.Byte);
                             }
@@ -358,9 +355,9 @@ namespace YSAI.Core.communication.net.udp
             }
         }
 
-        public Task<OperateResult> SendWaitAsync(byte[] Data)
+        public Task<OperateResult> SendWaitAsync(byte[] Data, int SumCount = 65536, int LotCount = 0)
         {
-            return Task.Run(() => SendWait(Data));
+            return Task.Run(() => SendWait(Data, SumCount, LotCount));
         }
 
         public OperateResult SendWait(byte[] Data, IPEndPoint iPEndPoint)
@@ -415,7 +412,7 @@ namespace YSAI.Core.communication.net.udp
                             return Break("SendWait", false, "接收数据超时");
                         }
                     }
-                    return Break("SendWait", false, "读取或写入失败");
+                    return Break("SendWait", false, "发送等待结果失败");
                 }
                 return Break("SendWait", false, "未连接");
             }

+ 8 - 11
src/YSAI.DAQ/YSAI.Core/communication/net/ws/client/WsClientOperate.cs

@@ -330,7 +330,7 @@ namespace YSAI.Core.communication.net.ws.client
             return Task.Run(() => Send(Data));
         }
 
-        public OperateResult SendWait(byte[] Data)
+        public OperateResult SendWait(byte[] Data, int SumCount = 65536, int LotCount = 0)
         {
             Depart("SendWait");
             try
@@ -341,27 +341,24 @@ namespace YSAI.Core.communication.net.ws.client
                     if (reverseBack.State)
                     {
                         CancellationTokenSource token = new CancellationTokenSource();
-                        byte[] Byte = new byte[65536];
+                        byte[] Byte = new byte[SumCount];
                         bool Status = Task.Run(() =>
                         {
                             while (!token.IsCancellationRequested)
                             {
-                                ArraySegment<byte> CacheBuffer = new ArraySegment<byte>(new byte[65536]);
+                                ArraySegment<byte> CacheBuffer = new ArraySegment<byte>(Byte);
                                 WebSocketReceiveResult result = Communication.ReceiveAsync(CacheBuffer, token.Token).Result;  //读取数据
                                 if (result.Count > 0)
                                 {
                                     Byte = CacheBuffer.Array;
                                     return;
                                 }
-                                else
-                                {
-                                    Thread.Sleep(SleepTime);
-                                }
+                                Thread.Sleep(SleepTime);
                             }
                         }, token.Token).Wait(basics.SendWaitInterval);
                         if (Status)
                         {
-                            if (Byte.Length > 0)
+                            if (Byte.TrimEnd().Length > 0)
                             {
                                 return Break("SendWait", true, RData: Byte.TrimEnd(), RType: @enum.ResultType.Byte);
                             }
@@ -375,7 +372,7 @@ namespace YSAI.Core.communication.net.ws.client
                             return Break("SendWait", false, "接收数据超时");
                         }
                     }
-                    return Break("SendWait", false, "读取或写入失败");
+                    return Break("SendWait", false, "发送等待结果失败");
                 }
                 return Break("SendWait", false, "未连接");
             }
@@ -385,9 +382,9 @@ namespace YSAI.Core.communication.net.ws.client
             }
         }
 
-        public Task<OperateResult> SendWaitAsync(byte[] Data)
+        public Task<OperateResult> SendWaitAsync(byte[] Data, int SumCount = 65536, int LotCount = 0)
         {
-            return Task.Run(() => SendWait(Data));
+            return Task.Run(() => SendWait(Data, SumCount, LotCount));
         }
 
         public OperateResult GetObject()

+ 32 - 14
src/YSAI.DAQ/YSAI.Core/communication/serial/SerialOperate.cs

@@ -256,7 +256,7 @@ namespace YSAI.Core.communication.serial
             return Task.Run(() => Send(Data));
         }
 
-        public OperateResult SendWait(byte[] Data)
+        public OperateResult SendWait(byte[] Data, int SumCount = 65536, int LotCount = 0)
         {
             Depart("SendWait");
             try
@@ -267,29 +267,47 @@ namespace YSAI.Core.communication.serial
                     if (reverseBack.State)
                     {
                         CancellationTokenSource token = new CancellationTokenSource();
-                        byte[] Byte = new byte[65536];
+                        byte[] Byte = new byte[SumCount];
                         bool Status = Task.Run(() =>
                         {
-                            while (!token.IsCancellationRequested)
+                            if (LotCount != 0)
                             {
-                                int BufSize = Communication.BytesToRead;
-                                if (BufSize > 0)
+                                int offset = 0;
+                                while (offset < SumCount && !token.IsCancellationRequested)
                                 {
-                                    Byte = new byte[BufSize];
-                                    if (Communication.Read(Byte, 0, BufSize) <= 0)
+                                    int count = (SumCount - offset) >= LotCount ? LotCount : (SumCount - offset);
+                                    int bytes = Communication.Read(Byte, offset, count);
+                                    if (bytes == 0)
                                     {
-                                        Thread.Sleep(SleepTime);
+                                        Byte = new byte[] { };
+                                        return;
                                     }
+                                    offset += bytes;
                                 }
-                                else
+                            }
+                            else
+                            {
+                                while (!token.IsCancellationRequested)
                                 {
-                                    Thread.Sleep(SleepTime);
+                                    int BufSize = Communication.BytesToRead;
+                                    if (BufSize > 0)
+                                    {
+                                        if (Communication.Read(Byte, 0, BufSize) > 0)
+                                        {
+                                            return;
+                                        }
+                                        Thread.Sleep(SleepTime);
+                                    }
+                                    else
+                                    {
+                                        Thread.Sleep(SleepTime);
+                                    }
                                 }
                             }
                         }, token.Token).Wait(basics.SendWaitInterval);
                         if (Status)
                         {
-                            if (Byte.Length > 0)
+                            if (Byte.TrimEnd().Length > 0)
                             {
                                 return Break("SendWait", true, RData: Byte.TrimEnd(), RType: @enum.ResultType.Byte);
                             }
@@ -303,7 +321,7 @@ namespace YSAI.Core.communication.serial
                             return Break("SendWait", false, "接收数据超时");
                         }
                     }
-                    return Break("SendWait", false, "读取或写入失败");
+                    return Break("SendWait", false, "发送等待结果失败");
                 }
                 return Break("SendWait", false, "未连接");
             }
@@ -313,9 +331,9 @@ namespace YSAI.Core.communication.serial
             }
         }
 
-        public Task<OperateResult> SendWaitAsync(byte[] Data)
+        public Task<OperateResult> SendWaitAsync(byte[] Data, int SumCount = 65536, int LotCount = 0)
         {
-            return Task.Run(() => SendWait(Data));
+            return Task.Run(() => SendWait(Data, SumCount, LotCount));
         }
 
         public OperateResult GetObject()

+ 10 - 4
src/YSAI.DAQ/YSAI.Core/interface/ISendWait.cs

@@ -8,17 +8,23 @@ namespace YSAI.Core.@interface
     public interface ISendWait
     {
         /// <summary>
-        /// 发送等待结果
+        /// 发送等待结果(支持[TcpClientOperate][SerialOperate]分批读取),
+        /// 分批读取时:SumCount 与 LotCount 结合使用
         /// </summary>
         /// <param name="Data">字节数据</param>
+        /// <param name="SumCount">接收数据的总长度 默认 65536,如果是分批读取则需要计算得到总数</param>
+        /// <param name="LotCount">一批读多少数据,如果为 0 则不使用分批读取</param>
         /// <returns>统一出参</returns>
-        OperateResult SendWait(byte[] Data);
+        OperateResult SendWait(byte[] Data, int SumCount = 65536, int LotCount = 0);
 
         /// <summary>
-        /// 发送等待结果
+        /// 发送等待结果(支持[TcpClientOperate][SerialOperate]分批读取),
+        /// 分批读取时:SumCount 与 LotCount 结合使用
         /// </summary>
         /// <param name="Data">字节数据</param>
+        /// <param name="SumCount">接收数据的总长度 默认 65536,如果是分批读取则需要计算得到总数</param>
+        /// <param name="LotCount">一批读多少数据,如果为 0 则不使用分批读取</param>
         /// <returns>统一出参</returns>
-        Task<OperateResult> SendWaitAsync(byte[] Data);
+        Task<OperateResult> SendWaitAsync(byte[] Data, int SumCount = 65536, int LotCount = 0);
     }
 }

+ 0 - 6
src/YSAI.DAQ/YSAI.Mitsubishi/Class1.cs

@@ -1,6 +0,0 @@
-namespace YSAI.Mitsubishi
-{
-    public class Class1
-    {
-    }
-}

+ 183 - 0
src/YSAI.DAQ/YSAI.Mitsubishi/MitsubishiData.cs

@@ -0,0 +1,183 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using System.ComponentModel;
+using YSAI.Core.attribute;
+using YSAI.Core.subscription;
+using YSAI.Unility;
+
+namespace YSAI.Mitsubishi
+{
+    public class MitsubishiData
+    {
+        /// <summary>
+        /// 基础数据
+        /// </summary>
+        public class Basics : SubscribeData.SCData
+        {
+            /// <summary>
+            /// 唯一标识符
+            /// </summary>
+            [Description("唯一标识符")]
+            public string? SN { get; set; } = Guid.NewGuid().ToUpperNString();
+
+            /// <summary>
+            /// ip地址
+            /// </summary>
+            [Description("IP")]
+            [Verify(@"^(25[0-4]|2[0-4]\\d]|[01]?\\d{2}|[1-9])\\.(25[0-5]|2[0-4]\\d]|[01]?\\d?\\d)\\.(25[0-5]|2[0-4]\\d]|[01]?\\d?\\d)\\.(25[0-4]|2[0-4]\\d]|[01]?\\d{2}|[1-9])$", "输入有误")]
+            [Display(true, true, true, Core.data.ParamStructure.dataCate.text)]
+            public string? Ip { get; set; }
+
+            /// <summary>
+            /// 端口
+            /// </summary>
+            [Description("端口")]
+            [Display(true, true, true, Core.data.ParamStructure.dataCate.unmber)]
+            public int Port { get; set; }
+
+            /// <summary>
+            /// 是否需要断开重新连接
+            /// </summary>
+            [Description("是否需要断开重新连接")]
+            [Display(true, true, true, Core.data.ParamStructure.dataCate.radio)]
+            public bool InterruptReconnection { get; set; } = true;
+
+            /// <summary>
+            /// 重连间隔(毫秒)
+            /// </summary>
+            [Description("重连间隔")]
+            [Unit("ms")]
+            [Display(true, true, true, Core.data.ParamStructure.dataCate.unmber)]
+            public int ReconnectionInterval { get; set; } = 2000;
+
+            /// <summary>
+            /// 超时时间
+            /// </summary>
+            [Description("超时时间")]
+            [Unit("ms")]
+            [Display(true, true, true, Core.data.ParamStructure.dataCate.unmber)]
+            public int Timeout { get; set; } = 1000;
+
+            /// <summary>
+            /// 设备类型
+            /// </summary>
+            [Description("设备类型")]
+            [JsonConverter(typeof(StringEnumConverter))]
+            [Display(true, true, true, Core.data.ParamStructure.dataCate.select)]
+            public DevType DType { get; set; }
+        }
+
+        /// <summary>
+        /// 设备类型
+        /// </summary>
+        public enum DevType
+        {
+            [Description("三菱MC.A1E帧")]
+            A1E,
+            [Description("三菱MC.QNA3E帧")]
+            QNA3E
+        }
+
+        /// <summary>
+        /// 数据类型
+        /// </summary>
+        public enum McDT
+        {
+            /// <summary>
+            /// 未定义
+            /// </summary>
+            [Description("未定义")]
+            None = 0,
+            /// <summary>
+            /// Bool
+            /// </summary>
+            [Description("Bool")]
+            Bool = 1,
+            /// <summary>
+            /// Byte
+            /// </summary>
+            [Description("Byte")]
+            Byte = 2,
+            /// <summary>
+            /// Int16
+            /// </summary>
+            [Description("Int16")]
+            Int16 = 3,
+            /// <summary>
+            /// UInt16
+            /// </summary>
+            [Description("UInt16")]
+            UInt16 = 4,
+            /// <summary>
+            /// Int32
+            /// </summary>
+            [Description("Int32")]
+            Int32 = 5,
+            /// <summary>
+            /// UInt32
+            /// </summary>
+            [Description("UInt32")]
+            UInt32 = 6,
+            /// <summary>
+            /// Int64
+            /// </summary>
+            [Description("Int64")]
+            Int64 = 7,
+            /// <summary>
+            /// UInt64
+            /// </summary>
+            [Description("UInt64")]
+            UInt64 = 8,
+            /// <summary>
+            /// Float
+            /// </summary>
+            [Description("Float")]
+            Float = 9,
+            /// <summary>
+            /// Double
+            /// </summary>
+            [Description("Double")]
+            Double = 10,
+            /// <summary>
+            /// String
+            /// </summary>
+            [Description("String")]
+            String = 11,
+        }
+        /// <summary>
+        /// 三菱解析后的地址信息
+        /// </summary>
+        public class McAddressDetails
+        {
+            /// <summary>
+            /// 开始地址
+            /// </summary>
+            public int BeginAddress { get; set; }
+
+            /// <summary>
+            /// 类型的代号
+            /// </summary>
+            public byte[] TypeCode { get; set; }
+
+            /// <summary>
+            /// 类型的代号
+            /// </summary>
+            public string TypeChar { get; set; }
+
+            /// <summary>
+            /// 数据的类型,0代表按字,1代表按位
+            /// </summary>
+            public byte BitType { get; set; }
+
+            /// <summary>
+            /// 指示地址是10进制,还是16进制的
+            /// </summary>
+            public int Format { get; set; }
+
+            /// <summary>
+            /// 数据类型
+            /// </summary>
+            public McDT DataType { get; set; }
+        }
+    }
+}

+ 872 - 0
src/YSAI.DAQ/YSAI.Mitsubishi/MitsubishiOperate.cs

@@ -0,0 +1,872 @@
+using System.Collections.Concurrent;
+using System.Reflection;
+using YSAI.Core.attribute;
+using YSAI.Core.communication.net.tcp.client;
+using YSAI.Core.data;
+using YSAI.Core.@interface;
+using YSAI.Unility;
+using static YSAI.Mitsubishi.MitsubishiData;
+
+namespace YSAI.Mitsubishi
+{
+    /// <summary>
+    /// 三菱PLC通信客户端
+    /// </summary>
+    public class MitsubishiOperate : IBaseAbstract, IDaq
+    {
+        protected override string TAG => "MitsubishiOperate";
+
+        /// <summary>
+        /// 锁
+        /// </summary>
+        private static readonly object Lock = new object();
+
+        /// <summary>
+        /// 自身对象集合
+        /// </summary>
+        private static List<MitsubishiOperate> ThisObjList = new List<MitsubishiOperate>();
+
+        /// <summary>
+        /// 单例模式
+        /// </summary>
+        /// <returns></returns>
+        public static MitsubishiOperate Instance(MitsubishiData.Basics basics)
+        {
+            if (ThisObjList.Count >= MaxInstanceCount)
+            {
+                throw new Exception(ExceedMaxInstanceCountTips);
+            }
+            MitsubishiOperate? exp = ThisObjList.FirstOrDefault(c => c.basics.Comparer(basics).result);
+            if (exp == null)
+            {
+                lock (Lock)
+                {
+                    if (ThisObjList.Count(c => c.basics.Comparer(basics).result) > 0)
+                    {
+                        return ThisObjList.First(c => c.basics.Comparer(basics).result);
+                    }
+                    else
+                    {
+                        MitsubishiOperate exp2 = new MitsubishiOperate(basics);
+                        ThisObjList.Add(exp2);
+                        return exp2;
+                    }
+                }
+            }
+            return exp;
+        }
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="param">参数</param>
+        public MitsubishiOperate(MitsubishiData.Basics basics)
+        {
+            this.basics = basics;
+        }
+        public MitsubishiOperate()
+        { }
+
+        /// <summary>
+        /// 基础数据
+        /// </summary>
+        private MitsubishiData.Basics basics;
+
+        /// <summary>
+        ///SOCKET TCP Client
+        /// </summary>
+        private TcpClientOperate tcpClientOperate;
+
+        #region 基础
+        /// <summary>
+        /// Qna_3E地址解析
+        /// </summary>
+        /// <param name="address">地址名</param>
+        /// <param name="toUpper">转换成大写</param>
+        /// <returns></returns>
+        private McAddressDetails ParseQNA3E(string address, McDT dataType = McDT.None, bool toUpper = true)
+        {
+            if (toUpper) address = address.ToUpper();
+            var addressInfo = new McAddressDetails()
+            {
+                DataType = dataType
+            };
+            switch (address[0])
+            {
+                case 'M'://M中间继电器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x90 };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 10;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'X':// X输入继电器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x9C };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 16;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'Y'://Y输出继电器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x9D };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 16;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'D'://D数据寄存器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0xA8 };
+                        addressInfo.BitType = 0x00;
+                        addressInfo.Format = 10;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'W'://W链接寄存器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0xB4 };
+                        addressInfo.BitType = 0x00;
+                        addressInfo.Format = 16;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'L'://L锁存继电器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x92 };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 10;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'F'://F报警器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x93 };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 10;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'V'://V边沿继电器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x94 };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 10;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'B'://B链接继电器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0xA0 };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 16;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'R'://R文件寄存器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0xAF };
+                        addressInfo.BitType = 0x00;
+                        addressInfo.Format = 10;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'S':
+                    {
+                        //累计定时器的线圈
+                        if (address[1] == 'C')
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xC6 };
+                            addressInfo.BitType = 0x01;
+                            addressInfo.Format = 10;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(2), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 2);
+                        }
+                        //累计定时器的触点
+                        else if (address[1] == 'S')
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xC7 };
+                            addressInfo.BitType = 0x01;
+                            addressInfo.Format = 10;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(2), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 2);
+                        }
+                        //累计定时器的当前值
+                        else if (address[1] == 'N')
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xC8 };
+                            addressInfo.BitType = 0x00;
+                            addressInfo.Format = 100;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(2), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 2);
+                        }
+                        // S步进继电器
+                        else
+                        {
+                            addressInfo.TypeCode = new byte[] { 0x98 };
+                            addressInfo.BitType = 0x01;
+                            addressInfo.Format = 10;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 1);
+                        }
+                        break;
+                    }
+                case 'Z':
+                    {
+                        //文件寄存器ZR区
+                        if (address[1] == 'R')
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xB0 };
+                            addressInfo.BitType = 0x00;
+                            addressInfo.Format = 16;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(2), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 2);
+                        }
+                        //变址寄存器
+                        else
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xCC };
+                            addressInfo.BitType = 0x00;
+                            addressInfo.Format = 10;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 1);
+                        }
+                        break;
+                    }
+                case 'T':
+                    {
+                        // 定时器的当前值
+                        if (address[1] == 'N')
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xC2 };
+                            addressInfo.BitType = 0x00;
+                            addressInfo.Format = 10;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(2), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 2);
+                        }
+                        //定时器的触点
+                        else if (address[1] == 'S')
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xC1 };
+                            addressInfo.BitType = 0x01;
+                            addressInfo.Format = 10;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(2), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 2);
+                        }
+                        //定时器的线圈
+                        else if (address[1] == 'C')
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xC0 };
+                            addressInfo.BitType = 0x01;
+                            addressInfo.Format = 10;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(2), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 2);
+                        }
+                        break;
+                    }
+                case 'C':
+                    {
+                        //计数器的当前值
+                        if (address[1] == 'N')
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xC5 };
+                            addressInfo.BitType = 0x00;
+                            addressInfo.Format = 10;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(2), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 2);
+                        }
+                        //计数器的触点
+                        else if (address[1] == 'S')
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xC4 };
+                            addressInfo.BitType = 0x01;
+                            addressInfo.Format = 10;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(2), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 2);
+                        }
+                        //计数器的线圈
+                        else if (address[1] == 'C')
+                        {
+                            addressInfo.TypeCode = new byte[] { 0xC3 };
+                            addressInfo.BitType = 0x01;
+                            addressInfo.Format = 10;
+                            addressInfo.BeginAddress = Convert.ToInt32(address.Substring(2), addressInfo.Format);
+                            addressInfo.TypeChar = address.Substring(0, 2);
+                        }
+                        break;
+                    }
+            }
+            return addressInfo;
+        }
+
+        /// <summary>
+        /// A_1E地址解析
+        /// </summary>
+        /// <param name="address">地址名</param>
+        /// <param name="toUpper">转换成大写</param>
+        /// <returns></returns>
+        private McAddressDetails ParseA1E(string address, bool toUpper = true)
+        {
+            if (toUpper) address = address.ToUpper();
+            var addressInfo = new McAddressDetails();
+            switch (address[0])
+            {
+                case 'X'://X输入寄存器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x58, 0x20 };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 8;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'Y'://Y输出寄存器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x59, 0x20 };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 8;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'M'://M中间寄存器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x4D, 0x20 };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 10;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'S'://S状态寄存器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x53, 0x20 };
+                        addressInfo.BitType = 0x01;
+                        addressInfo.Format = 10;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'D'://D数据寄存器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x44, 0x20 };
+                        addressInfo.BitType = 0x00;
+                        addressInfo.Format = 10;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+                case 'R'://R文件寄存器
+                    {
+                        addressInfo.TypeCode = new byte[] { 0x52, 0x20 };
+                        addressInfo.BitType = 0x00;
+                        addressInfo.Format = 10;
+                        addressInfo.BeginAddress = Convert.ToInt32(address.Substring(1), addressInfo.Format);
+                        addressInfo.TypeChar = address.Substring(0, 1);
+                    }
+                    break;
+            }
+            return addressInfo;
+        }
+
+        /// <summary>
+        /// QNA3E读取命令
+        /// </summary>
+        /// <param name="beginAddress">地址</param>
+        /// <param name="typeCode">类型代码</param>
+        /// <param name="length">长度</param>
+        /// <param name="isBit">是不是布尔类型</param>
+        /// <returns></returns>
+        private byte[] ReadCommand_QNA3E(int beginAddress, byte[] typeCode, ushort length, bool isBit)
+        {
+            if (!isBit) length = (ushort)(length / 2);
+
+            byte[] command = new byte[21];
+            command[0] = 0x50;
+            command[1] = 0x00; //副头部
+            command[2] = 0x00; //网络编号
+            command[3] = 0xFF; //PLC编号
+            command[4] = 0xFF;
+            command[5] = 0x03; //IO编号
+            command[6] = 0x00; //模块站号
+            command[7] = (byte)((command.Length - 9) % 256);
+            command[8] = (byte)((command.Length - 9) / 256); // 请求数据长度
+            command[9] = 0x0A;
+            command[10] = 0x00; //时钟
+            command[11] = 0x01;
+            command[12] = 0x04;//指令(0x01 0x04读 0x01 0x14写)
+            command[13] = isBit ? (byte)0x01 : (byte)0x00;//子指令(位 或 字节为单位)
+            command[14] = 0x00;
+            command[15] = BitConverter.GetBytes(beginAddress)[0];// 起始地址的地位
+            command[16] = BitConverter.GetBytes(beginAddress)[1];
+            command[17] = BitConverter.GetBytes(beginAddress)[2];
+            command[18] = typeCode[0]; //数据类型
+            command[19] = (byte)(length % 256);
+            command[20] = (byte)(length / 256); //长度
+            return command;
+        }
+
+        /// <summary>
+        /// A1E读取命令
+        /// </summary>
+        /// <param name="beginAddress">地址</param>
+        /// <param name="typeCode">类型代码</param>
+        /// <param name="length">长度</param>
+        /// <param name="isBit">是不是布尔类型</param>
+        /// <returns></returns>
+        private byte[] ReadCommand_A1E(int beginAddress, byte[] typeCode, ushort length, bool isBit)
+        {
+            if (!isBit)
+                length = (ushort)(length / 2);
+            byte[] command = new byte[12];
+            command[0] = isBit ? (byte)0x00 : (byte)0x01;//副头部
+            command[1] = 0xFF; //PLC编号
+            command[2] = 0x0A;
+            command[3] = 0x00;
+            command[4] = BitConverter.GetBytes(beginAddress)[0]; // 
+            command[5] = BitConverter.GetBytes(beginAddress)[1]; // 开始读取的地址
+            command[6] = 0x00;
+            command[7] = 0x00;
+            command[8] = typeCode[1];
+            command[9] = typeCode[0];
+            command[10] = (byte)(length % 256);//长度
+            command[11] = (byte)(length / 256);
+            return command;
+        }
+
+        /// <summary>
+        /// QNA3E写入命令
+        /// </summary>
+        /// <param name="beginAddress">地址</param>
+        /// <param name="typeCode">类型代码</param>
+        /// <param name="data">数据</param>
+        /// <param name="isBit">是不是布尔类型</param>
+        /// <returns></returns>
+        private byte[] WriteCommand_QNA3E(int beginAddress, byte[] typeCode, byte[] data, bool isBit)
+        {
+            var length = data.Length / 2;
+            if (isBit) length = 1;
+
+            byte[] command = new byte[21 + data.Length];
+            command[0] = 0x50;
+            command[1] = 0x00; //副头部
+            command[2] = 0x00; //网络编号
+            command[3] = 0xFF; //PLC编号
+            command[4] = 0xFF;
+            command[5] = 0x03; //IO编号
+            command[6] = 0x00; //模块站号
+            command[7] = (byte)((command.Length - 9) % 256);// 请求数据长度
+            command[8] = (byte)((command.Length - 9) / 256);
+            command[9] = 0x0A;
+            command[10] = 0x00; //时钟
+            command[11] = 0x01;
+            command[12] = 0x14;//指令(0x01 0x04读 0x01 0x14写)
+            command[13] = isBit ? (byte)0x01 : (byte)0x00;//子指令(位 或 字节为单位)
+            command[14] = 0x00;
+            command[15] = BitConverter.GetBytes(beginAddress)[0];// 起始地址的地位
+            command[16] = BitConverter.GetBytes(beginAddress)[1];
+            command[17] = BitConverter.GetBytes(beginAddress)[2];
+            command[18] = typeCode[0];//数据类型
+            command[19] = (byte)(length % 256);
+            command[20] = (byte)(length / 256); //长度
+            data.Reverse().ToArray().CopyTo(command, 21);
+            return command;
+        }
+
+        /// <summary>
+        /// A1E写入命令
+        /// </summary>
+        /// <param name="beginAddress">地址</param>
+        /// <param name="typeCode">类型代码</param>
+        /// <param name="data">数据</param>
+        /// <param name="isBit">是不是布尔类型</param>
+        /// <returns></returns>
+        private byte[] WriteCommand_A1E(int beginAddress, byte[] typeCode, byte[] data, bool isBit)
+        {
+            var length = data.Length / 2;
+            if (isBit) length = data.Length;
+
+            byte[] command = new byte[12 + data.Length];
+            command[0] = isBit ? (byte)0x02 : (byte)0x03;     //副标题
+            command[1] = 0xFF;                             // PLC号
+            command[2] = 0x0A;
+            command[3] = 0x00;
+            command[4] = BitConverter.GetBytes(beginAddress)[0];        //
+            command[5] = BitConverter.GetBytes(beginAddress)[1];        //起始地址的地位
+            command[6] = 0x00;
+            command[7] = 0x00;
+            command[8] = typeCode[1];        //
+            command[9] = typeCode[0];        //数据类型
+            command[10] = (byte)(length % 256);
+            command[11] = (byte)(length / 256);
+            data.Reverse().ToArray().CopyTo(command, 12);
+            return command;
+        }
+
+        /// <summary>
+        /// 读取数据
+        /// </summary>
+        /// <param name="address">地址</param>
+        /// <param name="length">长度</param>
+        /// <param name="isBit">是不是布尔类型</param>
+        /// <returns>统一返回(byte[])</returns>
+        private byte[] R(string address, ushort length, bool isBit = false)
+        {
+            //地址详情
+            McAddressDetails details = null;
+            //命令字节
+            byte[]? command = null;
+            //类型判断
+            switch (basics.DType)
+            {
+                case DevType.A1E:
+                    details = ParseA1E(address);
+                    command = ReadCommand_A1E(details.BeginAddress, details.TypeCode, length, isBit);
+                    break;
+                case DevType.QNA3E:
+                    details = ParseQNA3E(address);
+                    command = ReadCommand_QNA3E(details.BeginAddress, details.TypeCode, length, isBit);
+                    break;
+            }
+            //发送等待结果
+            OperateResult operateResult = null;
+            switch (basics.DType)
+            {
+                case DevType.A1E:
+                    var lenght = command[10] + command[11] * 256;
+                    if (isBit)
+                    {
+                        operateResult = tcpClientOperate.SendWait(command, (int)Math.Ceiling(lenght * 0.5) + 2, 4096);
+                    }
+                    else
+                    {
+                        operateResult = tcpClientOperate.SendWait(command);
+                    }
+                    break;
+                case DevType.QNA3E:
+                    operateResult = tcpClientOperate.SendWait(command);
+                    break;
+            }
+            if (!operateResult.State)
+            {
+                //读取失败返回
+                return new byte[] { };
+            }
+            //字节数组转换
+            byte[]? bytes = operateResult.GetRData<byte[]>();
+            if (bytes == null)
+            {
+                //读取失败返回
+                return new byte[] { };
+            }
+
+
+        }
+
+        /// <summary>
+        /// 写入数据
+        /// </summary>
+        /// <param name="address">地址</param>
+        /// <param name="data">数据</param>
+        /// <param name="isBit">是不是布尔类型</param>
+        /// <returns>统一返回(写入状态)</returns>
+        private bool W(string address, byte[] data, bool isBit = false)
+        {
+            //数据反转
+            Array.Reverse(data);
+            //地址详情
+            McAddressDetails details = null;
+            //命令字节
+            byte[]? command = null;
+            //类型判断
+            switch (basics.DType)
+            {
+                case DevType.A1E:
+                    details = ParseA1E(address);
+                    command = WriteCommand_A1E(details.BeginAddress, details.TypeCode, data, isBit);
+                    break;
+                case DevType.QNA3E:
+                    details = ParseQNA3E(address);
+                    command = WriteCommand_QNA3E(details.BeginAddress, details.TypeCode, data, isBit);
+                    break;
+            }
+            //发送等待结果,只要有响应说明写入成功
+            return tcpClientOperate.SendWait(command).State;
+        }
+        #endregion
+
+        public void Dispose()
+        {
+            Off();
+            GC.Collect();
+            GC.SuppressFinalize(this);
+            ThisObjList.Remove(this);
+        }
+
+        public OperateResult GetStatus()
+        {
+            string SN = Depart("GetStatus");
+            if (tcpClientOperate == null)
+            {
+                return Break(SN, false, "未连接");
+            }
+            else
+            {
+                return Break(SN, tcpClientOperate.GetStatus().State, tcpClientOperate.GetStatus().State ? "已连接" : "未连接");
+            }
+        }
+
+        public Task<OperateResult> GetStatusAsync()
+        {
+            return Task.Run(() => GetStatus());
+        }
+
+        public OperateResult Off()
+        {
+            throw new NotImplementedException();
+        }
+
+        public Task<OperateResult> OffAsync()
+        {
+            return Task.Run(() => Off());
+        }
+
+        public OperateResult On()
+        {
+            string SN = Depart("On");
+            try
+            {
+                if (tcpClientOperate != null && tcpClientOperate.GetStatus().State)
+                {
+                    return Break(SN, false, "已连接");
+                }
+                if (tcpClientOperate != null && !tcpClientOperate.GetStatus().State)
+                {
+                    tcpClientOperate.Dispose();
+                }
+                //先实例化底层 socket 通信
+                tcpClientOperate = TcpClientOperate.Instance(new TcpClientData.Basics
+                {
+                    InterruptReconnection = basics.InterruptReconnection,
+                    Ip = basics.Ip,
+                    Port = basics.Port,
+                    ReconnectionInterval = basics.ReconnectionInterval,
+                    SN = basics.SN,
+                    Timeout = basics.Timeout,
+                    SendWait = true,
+                    SendWaitInterval = basics.Timeout
+                });
+                OperateResult operateResult = tcpClientOperate.On();
+                if (operateResult.State)
+                {
+                    return Break(SN, true);
+                }
+                return Break(SN, false, operateResult.Message);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, Exception: ex);
+            }
+        }
+
+        public Task<OperateResult> OnAsync()
+        {
+            return Task.Run(() => On());
+        }
+
+        public OperateResult Read(Address address)
+        {
+            throw new NotImplementedException();
+        }
+
+        public Task<OperateResult> ReadAsync(Address address)
+        {
+            return Task.Run(() => Read(address));
+        }
+
+        public OperateResult Subscribe(Address address)
+        {
+            throw new NotImplementedException();
+        }
+
+        public Task<OperateResult> SubscribeAsync(Address address)
+        {
+            return Task.Run(() => Subscribe(address));
+        }
+
+        public OperateResult UnSubscribe(Address address)
+        {
+            throw new NotImplementedException();
+        }
+
+        public Task<OperateResult> UnSubscribeAsync(Address address)
+        {
+            return Task.Run(() => UnSubscribe(address));
+        }
+
+        public OperateResult Write<V>(ConcurrentDictionary<string, V> Values)
+        {
+            throw new NotImplementedException();
+        }
+
+        public Task<OperateResult> WriteAsync<V>(ConcurrentDictionary<string, V> Values)
+        {
+            return Task.Run(() => Write(Values));
+        }
+
+        public OperateResult GetParam()
+        {
+            string SN = Depart("GetParam");
+            try
+            {
+                //通过反射得到参数信息
+                List<ReflexTool.LibInstanceParam>? libInstanceParams = ReflexTool.GetClassAllPropertyData<MitsubishiData.Basics>();
+                //名称
+                string name = TAG.Replace("Operate", string.Empty).Replace("Client", string.Empty);
+                //命名空间
+                string nameSpace = "YSAI.Mitsubishi.MitsubishiOperate";
+                //对象实例
+                MitsubishiData.Basics basics = new MitsubishiData.Basics();
+                //参数结构体
+                ParamStructure? paramStructure = new ParamStructure()
+                {
+                    Name = name,
+                    Description = name,
+                    Subset = new List<ParamStructure.subset>
+                        {
+                            new ParamStructure.subset
+                            {
+                                Description = name,
+                                Name = name,
+                                Propertie = new List<ParamStructure.subset.propertie>()
+                            }
+                        }
+                };
+                paramStructure.Subset[0].Propertie.Add(new ParamStructure.subset.propertie
+                {
+                    PropertyName = "ServiceName",
+                    Description = "命名空间",
+                    Show = false,
+                    Use = false,
+                    Default = nameSpace,
+                    DataCate = null
+                });
+                foreach (var lib in libInstanceParams)
+                {
+                    //默认值
+                    string Default = ReflexTool.GetModelValue(lib.Name, basics);
+                    //前端展示特性
+                    DisplayAttribute? displayAttribute = typeof(MitsubishiData.Basics).GetProperty(lib.Name).GetCustomAttribute<DisplayAttribute>();
+                    //验证特性
+                    VerifyAttribute? verifyAttribute = typeof(MitsubishiData.Basics).GetProperty(lib.Name).GetCustomAttribute<VerifyAttribute>();
+                    //单位特性
+                    UnitAttribute? unitAttribute = typeof(MitsubishiData.Basics).GetProperty(lib.Name).GetCustomAttribute<UnitAttribute>();
+                    //描述上加单位
+                    string Describe = lib.Describe;
+                    if (unitAttribute != null && !string.IsNullOrWhiteSpace(unitAttribute.Unit))
+                    {
+                        Describe += $"({unitAttribute.Unit})";
+                    }
+
+                    ParamStructure.subset.propertie propertie = new ParamStructure.subset.propertie
+                    {
+                        PropertyName = lib.Name,
+                        Description = Describe,
+
+                        Show = displayAttribute?.Show ?? false,
+                        Use = displayAttribute?.Use ?? false,
+                        MustFillIn = displayAttribute?.MustFillIn ?? false,
+                        DataCate = displayAttribute?.DataCate ?? null,
+
+                        Regex = verifyAttribute?.Regex ?? null,
+                        FailTips = verifyAttribute?.FailTips ?? null,
+
+                        Default = Default
+                    };
+                    switch (displayAttribute?.DataCate)
+                    {
+                        case Core.data.ParamStructure.dataCate.select:
+                            propertie.Options = new List<ParamStructure.subset.propertie.options>();
+                            foreach (var val in lib.EnumArray as List<dynamic>)
+                            {
+                                string des = val.Describe;
+                                if (!string.IsNullOrEmpty(des))
+                                {
+                                    des = $"({val.Describe})";
+                                }
+                                propertie.Options.Add(new ParamStructure.subset.propertie.options
+                                {
+                                    Key = val.Name + des,
+                                    Value = val.Value,
+                                });
+                            }
+                            break;
+
+                        case Core.data.ParamStructure.dataCate.radio:
+                            propertie.Options = new List<ParamStructure.subset.propertie.options>();
+                            propertie.Options.Add(new ParamStructure.subset.propertie.options
+                            {
+                                Key = "是",
+                                Value = true,
+                            });
+                            propertie.Options.Add(new ParamStructure.subset.propertie.options
+                            {
+                                Key = "否",
+                                Value = false,
+                            });
+                            break;
+                    }
+                    paramStructure.Subset[0].Propertie.Add(propertie);
+                }
+                return Break(SN, true, paramStructure.ToJson().JsonFormatting(), paramStructure, Core.@enum.ResultType.Object);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, Exception: ex);
+            }
+        }
+
+        public Task<OperateResult> GetParamAsync()
+        {
+            return Task.Run(() => GetParam());
+        }
+
+        public OperateResult CreateInstance<T>(T Basics)
+        {
+            string SN = Depart("CreateInstance");
+            try
+            {
+                //先判断对象类型是否一致
+                if (typeof(T).FullName.Equals(typeof(MitsubishiData.Basics).FullName))
+                {
+                    return Break(SN, true, RData: Instance(Basics as MitsubishiData.Basics));
+                }
+                else
+                {
+                    return Break(SN, false, "对象类型错误,无法创建实例");
+                }
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, Exception: ex);
+            }
+        }
+
+        public Task<OperateResult> CreateInstanceAsync<T>(T Basics)
+        {
+            return Task.Run(() => CreateInstance(Basics));
+        }
+    }
+}

+ 4 - 0
src/YSAI.DAQ/YSAI.Mitsubishi/YSAI.Mitsubishi.csproj

@@ -6,4 +6,8 @@
     <Nullable>enable</Nullable>
   </PropertyGroup>
 
+  <ItemGroup>
+    <PackageReference Include="YSAI.Core" Version="1.0.0.79" />
+  </ItemGroup>
+
 </Project>

+ 0 - 1
src/YSAI.DAQ/YSAI.Opc/ua/client/OpcUaClientOperate.cs

@@ -2,7 +2,6 @@
 using Opc.Ua.Client;
 using Opc.Ua.Configuration;
 using System.Collections.Concurrent;
-using System.Dynamic;
 using System.Reflection;
 using System.Security.Cryptography.X509Certificates;
 using YSAI.Core.attribute;

+ 1 - 1
src/YSAI.DAQ/YSAI.S7/S7Operate.cs

@@ -327,7 +327,7 @@ namespace YSAI.S7
             }
             catch (Exception ex)
             {
-                return Break("Read", false, ex.Message);
+                return Break("Read", false, ex.Message, Exception: ex);
             }
         }