ソースを参照

1. 新增 NETMQ 传输协议(不保证消息质量模式)

Shun 2 年 前
コミット
3497cee4b3

+ 9 - 2
src/YSAI.DAQ/YSAI.DAQ.sln

@@ -103,9 +103,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YSAI.Beckhoff.Pack", "YSAI.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YSAI.Omron", "YSAI.Omron\YSAI.Omron.csproj", "{0C46F8C9-A44A-4AAD-A0B7-D0FB387A0B00}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YSAI.Omron.Pack", "YSAI.Omron.Pack\YSAI.Omron.Pack.csproj", "{5EB13A98-97E5-4848-A5F3-D90974AA76A8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YSAI.Omron.Pack", "YSAI.Omron.Pack\YSAI.Omron.Pack.csproj", "{5EB13A98-97E5-4848-A5F3-D90974AA76A8}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YSAI.Mitsubishi.Pack", "YSAI.Mitsubishi.Pack\YSAI.Mitsubishi.Pack.csproj", "{3573D496-3EE5-48B2-9060-E5306B5E3A94}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YSAI.Mitsubishi.Pack", "YSAI.Mitsubishi.Pack\YSAI.Mitsubishi.Pack.csproj", "{3573D496-3EE5-48B2-9060-E5306B5E3A94}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YSAI.NetMQ", "YSAI.NetMQ\YSAI.NetMQ.csproj", "{B703E19E-A1A7-4D7B-908A-C28B66512A8B}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -285,6 +287,10 @@ Global
 		{3573D496-3EE5-48B2-9060-E5306B5E3A94}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{3573D496-3EE5-48B2-9060-E5306B5E3A94}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{3573D496-3EE5-48B2-9060-E5306B5E3A94}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B703E19E-A1A7-4D7B-908A-C28B66512A8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B703E19E-A1A7-4D7B-908A-C28B66512A8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B703E19E-A1A7-4D7B-908A-C28B66512A8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B703E19E-A1A7-4D7B-908A-C28B66512A8B}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -337,6 +343,7 @@ Global
 		{0C46F8C9-A44A-4AAD-A0B7-D0FB387A0B00} = {0A264424-1AD7-49FA-B813-D96498066479}
 		{5EB13A98-97E5-4848-A5F3-D90974AA76A8} = {1EBA4FD7-DF52-49A0-8AA4-9E61ABC614F5}
 		{3573D496-3EE5-48B2-9060-E5306B5E3A94} = {1EBA4FD7-DF52-49A0-8AA4-9E61ABC614F5}
+		{B703E19E-A1A7-4D7B-908A-C28B66512A8B} = {9D8EDBBA-7A97-4D84-9B12-7FCC2F834046}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {5D5D3927-6714-40C0-84EA-44C5BA4C5E87}

+ 48 - 0
src/YSAI.DAQ/YSAI.NetMQ/NetMQData.cs

@@ -0,0 +1,48 @@
+using System.ComponentModel;
+using YSAI.Core.attribute;
+
+namespace YSAI.NetMQ
+{
+    public class NetMQData
+    {
+        /// <summary>
+        /// 基础数据
+        /// </summary>
+        public class Basics
+        {
+            /// <summary>
+            /// 订阅地址;
+            /// 客户端;
+            /// 当订阅地址与发布地址一致时,此实例又是服务端又是客户端
+            /// </summary>
+            [Description("订阅地址")]
+            [Display(true, true, false, Core.data.ParamStructure.dataCate.text)]
+            public string? SAddress { get; set; } = "tcp://127.0.0.1:8866";
+
+            /// <summary>
+            /// 发布地址
+            /// 服务端;
+            /// 当订阅地址与发布地址一致时,此实例又是服务端又是客户端
+            /// </summary>
+            [Description("发布地址")]
+            [Display(true, true, false, Core.data.ParamStructure.dataCate.text)]
+            public string? PAddress { get; set; } = "tcp://127.0.0.1:8866";
+
+            /// <summary>
+            /// 队列大小
+            /// </summary>
+            [Description("队列大小")]
+            [Display(true, true, true, Core.data.ParamStructure.dataCate.unmber)]
+            public int ReceiveHighWatermark { get; set; } = 1;
+
+            /// <summary>
+            /// 超时时间
+            /// </summary>
+            [Description("超时时间")]
+            [Unit("ms")]
+            [Display(true, true, true, Core.data.ParamStructure.dataCate.unmber)]
+            public int TimeOut { get; set; } = 1;
+        }
+
+    }
+}

+ 545 - 0
src/YSAI.DAQ/YSAI.NetMQ/NetMQOperate.cs

@@ -0,0 +1,545 @@
+using NetMQ;
+using NetMQ.Sockets;
+using System.Collections.Concurrent;
+using System.Reflection;
+using YSAI.Core.attribute;
+using YSAI.Core.data;
+using YSAI.Core.@interface;
+using YSAI.Unility;
+
+namespace YSAI.NetMQ
+{
+    /// <summary>
+    /// NetMQ 是轻量级消息传递库 ZeroMQ 的 100% 原生 C# 端
+    /// </summary>
+    public class NetMQOperate : IBaseAbstract, IRelay
+    {
+        protected override string TAG => "NetMQClientOperate";
+
+        /// <summary>
+        /// 锁
+        /// </summary>
+        private static readonly object Lock = new object();
+
+        /// <summary>
+        /// 自身对象集合
+        /// </summary>
+        private static List<NetMQOperate> ThisObjList = new List<NetMQOperate>();
+
+        /// <summary>
+        /// 单例模式
+        /// </summary>
+        /// <returns></returns>
+        public static NetMQOperate Instance(NetMQData.Basics basics)
+        {
+            if (ThisObjList.Count >= MaxInstanceCount)
+            {
+                throw new Exception(ExceedMaxInstanceCountTips);
+            }
+            NetMQOperate? 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
+                    {
+                        NetMQOperate exp2 = new NetMQOperate(basics);
+                        ThisObjList.Add(exp2);
+                        return exp2;
+                    }
+                }
+            }
+            return exp;
+        }
+
+        /// <summary>
+        /// 基础数据
+        /// </summary>
+        private NetMQData.Basics basics;
+
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        public NetMQOperate(NetMQData.Basics basics)
+        {
+            this.basics = basics;
+        }
+
+        public NetMQOperate()
+        { }
+
+        /// <summary>
+        /// 发布者;
+        /// 服务端
+        /// </summary>
+        private PublisherSocket pSocket;
+
+        /// <summary>
+        /// 订阅对象IOC
+        /// </summary>
+        private ConcurrentDictionary<string, SubObj> SubIoc;
+
+
+        class SubObj
+        {
+            /// <summary>
+            /// 订阅对象
+            /// </summary>
+            public SubscriberSocket socket { get; set; }
+            /// <summary>
+            /// 任务对象
+            /// </summary>
+            public Task task { get; set; }
+            /// <summary>
+            /// 令牌
+            /// </summary>
+            public CancellationTokenSource token { get; set; }
+        }
+
+        /// <summary>
+        /// 获取启动类型
+        /// 2:发布模式
+        /// 3:又订阅又发布
+        /// </summary>
+        /// <returns></returns>
+        private int GType()
+        {
+            if (!string.IsNullOrWhiteSpace(basics.SAddress) && !string.IsNullOrWhiteSpace(basics.PAddress))
+            {
+                return 3;
+            }
+            if (string.IsNullOrWhiteSpace(basics.SAddress) && !string.IsNullOrWhiteSpace(basics.PAddress))
+            {
+                return 2;
+            }
+            return 0;
+        }
+
+        /// <summary>
+        /// 释放
+        /// </summary>
+        /// <exception cref="NotImplementedException"></exception>
+        public void Dispose()
+        {
+            Off();
+            GC.Collect();
+            GC.SuppressFinalize(this);
+            ThisObjList.Remove(this);
+        }
+
+        public OperateResult On()
+        {
+            string SN = Depart("On");
+            try
+            {
+                int type = GType();
+                switch (type)
+                {
+                    case 2:  //发布模式(服务端)
+                    case 3:  //订阅发布模式(客户端服务端)
+                        if (pSocket != null)
+                        {
+                            return Break(SN, false, type == 3 ? "发布订阅模式已打开" : "发布模式已打开");
+                        }
+                        //先启动服务端
+                        pSocket = new PublisherSocket();
+                        pSocket.Options.ReceiveHighWatermark = basics.ReceiveHighWatermark;
+                        pSocket.Bind(basics.PAddress);
+                        break;
+                }
+
+                return Break(SN, true);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, Exception: ex);
+            }
+        }
+
+        public Task<OperateResult> OnAsync()
+        {
+            return Task.Run(() => On());
+        }
+
+        public OperateResult Off()
+        {
+            string SN = Depart("Off");
+            try
+            {
+                if (SubIoc != null)
+                {
+                    foreach (var item in SubIoc)
+                    {
+                        item.Value.token.Cancel();
+                        item.Value.task.Wait();
+                        item.Value.task.Dispose();
+                        item.Value.socket.Close();
+                        item.Value.socket.Dispose();
+                    }
+                    SubIoc.Clear();
+                    SubIoc = null;
+                }
+                int type = GType();
+                switch (type)
+                {
+                    case 2:  //发布模式(服务端)
+                    case 3:  //订阅发布模式(客户端服务端)
+                        if (pSocket == null)
+                        {
+                            return Break(SN, false, type == 3 ? "发布订阅模式未打开" : "发布模式未打开");
+                        }
+                        pSocket.Close();
+                        pSocket.Dispose();
+                        pSocket = null;
+                        break;
+                }
+
+                return Break(SN, true);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, Exception: ex);
+            }
+        }
+
+        public Task<OperateResult> OffAsync()
+        {
+            return Task.Run(() => Off());
+        }
+
+        public OperateResult Produce(string Topic, string Content)
+        {
+            string SN = Depart("Produce");
+            try
+            {
+                if (pSocket == null)
+                {
+                    return Break(SN, false, "发布模式未打开");
+                }
+                pSocket.SendMoreFrame(Topic).SendFrame(Content);
+                return Break(SN, true);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, Exception: ex);
+            }
+        }
+
+        public Task<OperateResult> ProduceAsync(string Topic, string Content)
+        {
+            return Task.Run(() => Produce(Topic, Content));
+        }
+
+        /// <summary>
+        /// 订阅消息处理
+        /// </summary>
+        /// <param name="source"></param>
+        /// <returns></returns>
+        Task SubMessageHandle(CancellationTokenSource source, SubscriberSocket sSocket, int timeout)
+        {
+            return Task.Factory.StartNew(() =>
+            {
+                while (!source.IsCancellationRequested)
+                {
+                    string Content = string.Empty;
+                    if (sSocket.TryReceiveFrameString(TimeSpan.FromSeconds(timeout), out string Topic, out bool moreFrames))
+                    {
+                        if (moreFrames)
+                        {
+                            Content = sSocket.ReceiveFrameString();
+                            if (Content.IsJson())
+                            {
+                                OnEventHandler(this, new EventResult(true, $"{TAG} 接收到主题 ( {Topic} ) 内容 ( {Content} )", "{" + $"\"Topic\":\"{Topic}\",\"Content\":{Content}" + "}", Core.@enum.ResultType.Dynamic));
+                            }
+                            else
+                            {
+                                OnEventHandler(this, new EventResult(true, $"{TAG} 接收到主题 ( {Topic} ) 内容 ( {Content} )", "{" + $"\"Topic\":\"{Topic}\",\"Content\":\"{Content}\"" + "}", Core.@enum.ResultType.Dynamic));
+                            }
+                        }
+                    }
+                }
+            }, source.Token);
+        }
+
+        public OperateResult Subscribe(string Topic)
+        {
+            string SN = Depart("Subscribe");
+            try
+            {
+                if (SubIoc == null)
+                {
+                    SubIoc = new ConcurrentDictionary<string, SubObj>();
+                }
+                //判断是否有此主题的订阅
+                if (!SubIoc.ContainsKey(Topic))
+                {
+                    //初始对象
+                    SubscriberSocket socket = new SubscriberSocket();
+                    socket.Options.ReceiveHighWatermark = basics.ReceiveHighWatermark;
+                    socket.Connect(basics.SAddress);
+                    //订阅主题
+                    socket.Subscribe(Topic);
+                    //初始令牌
+                    CancellationTokenSource token = new CancellationTokenSource();
+                    //启动任务
+                    Task task = SubMessageHandle(token, socket, basics.TimeOut);
+                    //设置新的对象
+                    SubObj subObj = new SubObj()
+                    {
+                        socket = socket,
+                        task = task,
+                        token = token
+                    };
+                    //更新至容器
+                    SubIoc.AddOrUpdate(Topic, subObj, (k, v) => subObj);
+                }
+                else
+                {
+                    return Break(SN, false, "此主题已订阅");
+                }
+                return Break(SN, true);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, Exception: ex);
+            }
+        }
+
+        public Task<OperateResult> SubscribeAsync(string Topic)
+        {
+            return Task.Run(() => Subscribe(Topic));
+        }
+
+        public OperateResult UnSubscribe(string Topic)
+        {
+            string SN = Depart("UnSubscribe");
+            try
+            {
+                if (SubIoc == null)
+                {
+                    return Break(SN, false, "尚未存在订阅数据");
+                }
+                //判断是否有此主题的订阅
+                if (SubIoc.ContainsKey(Topic))
+                {
+                    if (SubIoc.Remove(Topic, out SubObj subObj))
+                    {
+                        subObj.token.Cancel();
+                        subObj.task.Wait();
+                        subObj.task.Dispose();
+                        subObj.socket.Close();
+                        subObj.socket.Dispose();
+                    }
+                    else
+                    {
+                        return Break(SN, false, "取消订阅失败");
+                    }
+                }
+                else
+                {
+                    return Break(SN, false, "此主题尚未订阅");
+                }
+                return Break(SN, true);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, Exception: ex);
+            }
+        }
+
+        public Task<OperateResult> UnSubscribeAsync(string Topic)
+        {
+            return Task.Run(() => UnSubscribe(Topic));
+        }
+
+        public OperateResult GetStatus()
+        {
+            string SN = Depart("GetStatus");
+            try
+            {
+                switch (GType())
+                {
+                    case 2:  //发布模式(服务端)
+                        if (pSocket == null)
+                        {
+                            return Break(SN, false, "发布模式未打开");
+                        }
+                        break;
+                    case 3:  //订阅发布模式(客户端服务端)
+                        if (pSocket == null)
+                        {
+                            return Break(SN, false, "发布订阅模式未打开");
+                        }
+                        break;
+                }
+                return Break(SN, true);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, Exception: ex);
+            }
+        }
+
+        public Task<OperateResult> GetStatusAsync()
+        {
+            return Task.Run(() => GetStatus());
+        }
+
+        public OperateResult GetParam()
+        {
+            string SN = Depart("GetParam");
+            try
+            {
+                //通过反射得到参数信息
+                List<ReflexTool.LibInstanceParam>? libInstanceParams = ReflexTool.GetClassAllPropertyData<NetMQData.Basics>();
+                //名称
+                string name = TAG.Replace("Operate", string.Empty).Replace("Client", string.Empty);
+                //命名空间
+                string nameSpace = "YSAI.Netty.NettyClientOperate";
+                //对象实例
+                NetMQData.Basics basics = new NetMQData.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
+                });
+                paramStructure.Subset[0].Propertie.Add(new ParamStructure.subset.propertie
+                {
+                    PropertyName = "Topic",
+                    Description = "主题",
+                    Show = true,
+                    Use = true,
+                    Default = "test",
+                    DataCate = ParamStructure.dataCate.text
+                });
+                foreach (var lib in libInstanceParams)
+                {
+                    //默认值
+                    string Default = ReflexTool.GetModelValue(lib.Name, basics);
+                    //前端展示特性
+                    DisplayAttribute? displayAttribute = typeof(NetMQData.Basics).GetProperty(lib.Name).GetCustomAttribute<DisplayAttribute>();
+                    //验证特性
+                    VerifyAttribute? verifyAttribute = typeof(NetMQData.Basics).GetProperty(lib.Name).GetCustomAttribute<VerifyAttribute>();
+                    //单位特性
+                    UnitAttribute? unitAttribute = typeof(NetMQData.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 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 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(NetMQData.Basics).FullName))
+                {
+                    return Break(SN, true, RData: Instance(Basics as NetMQData.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));
+        }
+    }
+}

+ 14 - 0
src/YSAI.DAQ/YSAI.NetMQ/YSAI.NetMQ.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="NetMQ" Version="4.0.1.13" />
+    <PackageReference Include="YSAI.Core" Version="1.0.0.82" />
+  </ItemGroup>
+
+</Project>

+ 116 - 44
src/YSAI.DAQ/YSAI.Test.All/Program.cs

@@ -1,4 +1,76 @@
 
+
+//using NetMQ;
+//using NetMQ.Sockets;
+
+//SubscriberSocket subscriberSocket = new SubscriberSocket();
+//subscriberSocket.Connect("tcp://localhost:12345");
+//subscriberSocket.Subscribe("Topic1");
+//subscriberSocket.Options.ReceiveHighWatermark = 1000;
+
+
+
+//while (true)
+//{
+//    string content = string.Empty;
+//    if (subscriberSocket.TryReceiveFrameString(TimeSpan.FromSeconds(1), out string topic, out bool moreFrames))
+//    {
+//        if (moreFrames)
+//        {
+//            content = subscriberSocket.ReceiveFrameString();
+//        }
+//    }
+
+
+//    Console.WriteLine(topic);
+//    Console.WriteLine(content);
+//    Console.WriteLine("■■■■■");
+//}
+
+
+
+
+using YSAI.NetMQ;
+using YSAI.Unility;
+string topic = "666";
+string topic1 = "777";
+int count = 1;
+
+NetMQOperate netMQClientOperate = NetMQOperate.Instance(new NetMQData.Basics());
+Console.WriteLine(netMQClientOperate.On().ToJson().JsonFormatting());
+
+netMQClientOperate.OnEvent += NetMQClientOperate_OnEvent;
+
+Console.WriteLine(netMQClientOperate.Subscribe(topic).ToJson().JsonFormatting());
+Console.WriteLine(netMQClientOperate.Subscribe(topic1).ToJson().JsonFormatting());
+
+while (true)
+{
+    Console.ReadLine();
+
+    netMQClientOperate.Produce(topic, new Random().NextDouble().ToString());
+    netMQClientOperate.Produce(topic1, new Random().NextDouble().ToString());
+    count++;
+
+
+    if (count == 10)
+    {
+        Console.WriteLine(netMQClientOperate.UnSubscribe(topic).State);
+        Console.WriteLine(netMQClientOperate.UnSubscribe(topic1).State);
+        break;
+    }
+}
+
+Console.ReadKey();
+
+
+void NetMQClientOperate_OnEvent(object? sender, YSAI.Core.data.EventResult e)
+{
+    Console.WriteLine(e.ToJson());
+}
+
+
+
 //using YSAI.Core.data;
 //using YSAI.Omron;
 //using YSAI.Unility;
@@ -96,61 +168,61 @@
 
 
 
-using YSAI.Beckhoff;
-using YSAI.Core.@interface;
+//using YSAI.Beckhoff;
+//using YSAI.Core.@interface;
 
-//第一种方式
-BeckhoffOperate beckhoffOperate1 = new BeckhoffOperate();
+////第一种方式
+//BeckhoffOperate beckhoffOperate1 = new BeckhoffOperate();
 
-//第二种方式(单例模式)
-BeckhoffOperate? beckhoffOperate2 = new BeckhoffOperate().CreateInstance(new BeckhoffData.Basics()).GetRData<BeckhoffOperate>();
+////第二种方式(单例模式)
+//BeckhoffOperate? beckhoffOperate2 = new BeckhoffOperate().CreateInstance(new BeckhoffData.Basics()).GetRData<BeckhoffOperate>();
 
-//第三种方式(单例模式)
-BeckhoffOperate beckhoffOperate3 = BeckhoffOperate.Instance(new BeckhoffData.Basics());
+////第三种方式(单例模式)
+//BeckhoffOperate beckhoffOperate3 = BeckhoffOperate.Instance(new BeckhoffData.Basics());
 
-//第四种方式
-IDaq daq1 = new BeckhoffOperate(new BeckhoffData.Basics());
+////第四种方式
+//IDaq daq1 = new BeckhoffOperate(new BeckhoffData.Basics());
 
-//第五种方式
-IDaq daq2 = BeckhoffOperate.Instance(new BeckhoffData.Basics());
+////第五种方式
+//IDaq daq2 = BeckhoffOperate.Instance(new BeckhoffData.Basics());
 
-//第六种方式
-using (BeckhoffOperate beckhoffOperate4 = new BeckhoffOperate())
-{
-    //使用完直接释放
-}
+////第六种方式
+//using (BeckhoffOperate beckhoffOperate4 = new BeckhoffOperate())
+//{
+//    //使用完直接释放
+//}
 
-//第七种方式
-using (BeckhoffOperate beckhoffOperate5 = BeckhoffOperate.Instance(new BeckhoffData.Basics()))
-{
-    //使用完直接释放这个单例
-}
+////第七种方式
+//using (BeckhoffOperate beckhoffOperate5 = BeckhoffOperate.Instance(new BeckhoffData.Basics()))
+//{
+//    //使用完直接释放这个单例
+//}
 
-//第八种方式
-using (BeckhoffOperate? beckhoffOperate6 = new BeckhoffOperate().CreateInstance(new BeckhoffData.Basics()).GetRData<BeckhoffOperate>())
-{
-    //使用完直接释放这个单例
-}
+////第八种方式
+//using (BeckhoffOperate? beckhoffOperate6 = new BeckhoffOperate().CreateInstance(new BeckhoffData.Basics()).GetRData<BeckhoffOperate>())
+//{
+//    //使用完直接释放这个单例
+//}
 
-//第九种方式
-using (IDaq daq3 = new BeckhoffOperate(new BeckhoffData.Basics()))
-{
-    //使用完直接释放这个单例
-}
+////第九种方式
+//using (IDaq daq3 = new BeckhoffOperate(new BeckhoffData.Basics()))
+//{
+//    //使用完直接释放这个单例
+//}
 
-//第十种方式
-using (IDaq daq4 = BeckhoffOperate.Instance(new BeckhoffData.Basics()))
-{
-    //使用完直接释放这个单例
-}
+////第十种方式
+//using (IDaq daq4 = BeckhoffOperate.Instance(new BeckhoffData.Basics()))
+//{
+//    //使用完直接释放这个单例
+//}
 
-//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
-//第一种的应用场景是获取库参数信息时当这个对象已经实例化了,但是实际功能无法使用,就可以直接使用 CreateInstance 创建一个单例模式
-//先获取参数
-beckhoffOperate1.GetParam();
-//在生成单例后赋值,这样当前实例就已经可以使用所有功能,并且是单例的存在
-beckhoffOperate1 = beckhoffOperate1.CreateInstance(new BeckhoffData.Basics()).GetRData<BeckhoffOperate>();
-//第二种与第三种就是直接创建单例模式,但第二种是为了在反射的情况下使用单例模式,第二种也是为第一种情况而产生
+////■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
+////第一种的应用场景是获取库参数信息时当这个对象已经实例化了,但是实际功能无法使用,就可以直接使用 CreateInstance 创建一个单例模式
+////先获取参数
+//beckhoffOperate1.GetParam();
+////在生成单例后赋值,这样当前实例就已经可以使用所有功能,并且是单例的存在
+//beckhoffOperate1 = beckhoffOperate1.CreateInstance(new BeckhoffData.Basics()).GetRData<BeckhoffOperate>();
+////第二种与第三种就是直接创建单例模式,但第二种是为了在反射的情况下使用单例模式,第二种也是为第一种情况而产生
 
 
 

+ 2 - 0
src/YSAI.DAQ/YSAI.Test.All/YSAI.Test.All.csproj

@@ -15,6 +15,8 @@
   <ItemGroup>
     <ProjectReference Include="..\YSAI.Beckhoff\YSAI.Beckhoff.csproj" />
     <ProjectReference Include="..\YSAI.Mitsubishi\YSAI.Mitsubishi.csproj" />
+    <ProjectReference Include="..\YSAI.NetMQ\YSAI.NetMQ.csproj" />
+    <ProjectReference Include="..\YSAI.Netty\YSAI.Netty.csproj" />
     <ProjectReference Include="..\YSAI.Omron\YSAI.Omron.csproj" />
   </ItemGroup>