Browse Source

1. 新增基于DotNetty实现的RPC(远程过程调用) 支持一对多,支持身份验证
2. RabbitMQ 依赖库更新

Shun 2 years ago
parent
commit
166dafe5be

+ 8 - 1
README.md

@@ -34,6 +34,9 @@
 ## YSAI.Unility
 1. 公共快捷方便方法集合	[字节、枚举、文件、字符串、验证、比对、转换、反射、Json、Xml、Ftp、System ...]
 
+## 扩展工具
+1. [ YSAI.Rpc ]基于DotNetty实现的RPC(远程过程调用) 支持一对多,支持身份验证
+
 ## 采集协议
 1. OpcUa
 2. OpcDa
@@ -477,4 +480,8 @@ while(true)
 #### 2023-11-16
 1. RPC远程过程调用工具编写
 2. 底层SOCKET WEBSOCKET 修改断线重连规则
-3. 版本更新
+3. 版本更新
+
+#### 2023-11-21
+1. 新增基于DotNetty实现的RPC(远程过程调用) 支持一对多,支持身份验证
+2. RabbitMQ 依赖库更新

+ 15 - 1
src/YSAI.DAQ.sln

@@ -125,7 +125,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YSAI.VT", "YSAI.VT\YSAI.VT.
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "rpc", "rpc", "{F23C3553-3FE7-4ECC-9BBA-8C498C3B4398}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YSAI.Rpc", "YSAI.Rpc\YSAI.Rpc.csproj", "{87606DDA-82DA-4BCD-87B3-E7CEFB05EADC}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YSAI.Rpc", "YSAI.Rpc\YSAI.Rpc.csproj", "{87606DDA-82DA-4BCD-87B3-E7CEFB05EADC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YSAI.Test.RPC.Server", "YSAI.Test.RPC.Server\YSAI.Test.RPC.Server.csproj", "{30AB5928-8D59-4D35-8866-D42EF3273433}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YSAI.Test.RPC.Client", "YSAI.Test.RPC.Client\YSAI.Test.RPC.Client.csproj", "{E3F6DC4A-57F4-436F-86C5-6B2F782AA2C9}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -341,6 +345,14 @@ Global
 		{87606DDA-82DA-4BCD-87B3-E7CEFB05EADC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{87606DDA-82DA-4BCD-87B3-E7CEFB05EADC}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{87606DDA-82DA-4BCD-87B3-E7CEFB05EADC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{30AB5928-8D59-4D35-8866-D42EF3273433}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{30AB5928-8D59-4D35-8866-D42EF3273433}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{30AB5928-8D59-4D35-8866-D42EF3273433}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{30AB5928-8D59-4D35-8866-D42EF3273433}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E3F6DC4A-57F4-436F-86C5-6B2F782AA2C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E3F6DC4A-57F4-436F-86C5-6B2F782AA2C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E3F6DC4A-57F4-436F-86C5-6B2F782AA2C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E3F6DC4A-57F4-436F-86C5-6B2F782AA2C9}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -403,6 +415,8 @@ Global
 		{18BC89C3-3247-413C-9199-934741507CA0} = {1856E9E1-33C4-45C1-832C-854F9BE1ACC4}
 		{F23C3553-3FE7-4ECC-9BBA-8C498C3B4398} = {7EB5153B-7702-4D7B-8592-FE6D992682AB}
 		{87606DDA-82DA-4BCD-87B3-E7CEFB05EADC} = {F23C3553-3FE7-4ECC-9BBA-8C498C3B4398}
+		{30AB5928-8D59-4D35-8866-D42EF3273433} = {12CB0510-7B1E-4518-AA3B-412A4D323D42}
+		{E3F6DC4A-57F4-436F-86C5-6B2F782AA2C9} = {12CB0510-7B1E-4518-AA3B-412A4D323D42}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {5D5D3927-6714-40C0-84EA-44C5BA4C5E87}

+ 2 - 2
src/YSAI.RabbitMQ/YSAI.RabbitMQ.csproj

@@ -3,7 +3,7 @@
     <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
-    <Version>23.320.35050</Version>
+    <Version>23.325.22917</Version>
     <PackageOutputPath Condition="'$(Configuration)' == 'Release'">../YSAI.Publish/Release</PackageOutputPath>
     <PackageOutputPath Condition="'$(Configuration)' == 'Debug'">../YSAI.Publish/Debug</PackageOutputPath>
     <Authors>Shun</Authors>
@@ -16,7 +16,7 @@
     <Description>$(DescriptionType):$(DescriptionName) ( $(DescriptionDetails) )</Description>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="RabbitMQ.Client" Version="6.6.0" />
+    <PackageReference Include="RabbitMQ.Client" Version="6.7.0" />
     <PackageReference Include="YSAI.Core" Version="23.320.34643" />
   </ItemGroup>
   <!--<ItemGroup>

+ 1 - 1
src/YSAI.RelayManage/YSAI.RelayManage.csproj

@@ -7,7 +7,7 @@
   </PropertyGroup>
 
 	<ItemGroup>
-		<PackageReference Include="RabbitMQ.Client" Version="6.6.0" />
+		<PackageReference Include="RabbitMQ.Client" Version="6.7.0" />
 		<PackageReference Include="Confluent.Kafka" Version="2.3.0" />
 		<PackageReference Include="YSAI.Core" Version="23.320.34643" />
 		<FrameworkReference Include="Microsoft.AspNetCore.App" />

+ 2 - 2
src/YSAI.Rpc/YSAI.Rpc.csproj

@@ -3,7 +3,7 @@
     <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
-    <Version>23.320.7384</Version>
+    <Version>23.325.22917</Version>
     <PackageOutputPath Condition="'$(Configuration)' == 'Release'">../YSAI.Publish/Release</PackageOutputPath>
     <PackageOutputPath Condition="'$(Configuration)' == 'Debug'">../YSAI.Publish/Debug</PackageOutputPath>
     <Authors>Shun</Authors>
@@ -19,6 +19,6 @@
     <PackageReference Include="DotNetty.Codecs" Version="0.7.5" />
     <PackageReference Include="ImpromptuInterface" Version="8.0.4" />
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
-	<PackageReference Include="YSAI.Core" Version="23.320.34643" />
+    <PackageReference Include="YSAI.Core" Version="23.320.34643" />
   </ItemGroup>
 </Project>

+ 216 - 12
src/YSAI.Rpc/client/RpcClient.cs

@@ -1,7 +1,20 @@
-using DotNetty.Transport.Channels;
+using DotNetty.Buffers;
+using DotNetty.Codecs;
+using DotNetty.Transport.Bootstrapping;
+using DotNetty.Transport.Channels;
+using DotNetty.Transport.Channels.Sockets;
+using ImpromptuInterface;
+using Newtonsoft.Json;
+using System.Collections.Concurrent;
+using System.Net;
+using System.Reflection;
+using System.Text;
 using YSAI.Core.data;
 using YSAI.Core.@interface;
+using YSAI.Log;
+using YSAI.Rpc.data;
 using YSAI.Rpc.@interface;
+using YSAI.Rpc.unility;
 using YSAI.Unility;
 
 namespace YSAI.Rpc.client
@@ -27,7 +40,7 @@ namespace YSAI.Rpc.client
         /// 单例模式
         /// </summary>
         /// <returns></returns>
-        public static RpcClient Instance(data.Basics basics)
+        public static RpcClient Instance(Client.Basics basics)
         {
             if (ThisObjList.Count >= MaxInstanceCount)
             {
@@ -57,7 +70,7 @@ namespace YSAI.Rpc.client
         /// 构造函数
         /// </summary>
         /// <param name="param">参数</param>
-        public RpcClient(data.Basics basics)
+        public RpcClient(Client.Basics basics)
         {
             this.basics = basics;
         }
@@ -65,36 +78,227 @@ namespace YSAI.Rpc.client
         /// <summary>
         /// 基础数据
         /// </summary>
-        private data.Basics basics;
+        private Client.Basics basics;
+        /// <summary>
+        /// Netty 客户端
+        /// </summary>
+        private Bootstrap client;
+        /// <summary>
+        /// 客户端组
+        /// </summary>
+        private MultithreadEventLoopGroup ClientGroup;
+        /// <summary>
+        /// 通道
+        /// </summary>
+        private IChannel Channel;
+        /// <summary>
+        /// 创建
+        /// </summary>
+        private ConcurrentDictionary<string, object> creates { get; } = new ConcurrentDictionary<string, object>();
+        /// <summary>
+        /// 注册
+        /// </summary>
+        private Dictionary<string, System.Type> iRegister { get; set; } = new Dictionary<string, System.Type>();
+        /// <summary>
+        /// 任务等待
+        /// </summary>
+        private Await Await = new Await();
+        /// <summary>
+        /// 认证标识
+        /// </summary>
+        private string AuthenticationTag = Guid.NewGuid().ToUpperNString();
 
         public OperateResult Open()
         {
-            throw new NotImplementedException();
+            string SN = Depart("Open");
+            try
+            {
+                if (client != null)
+                {
+                    return Break(SN, false, "已打开");
+                }
+                client = new Bootstrap()
+               .Group(ClientGroup = new MultithreadEventLoopGroup())
+               .Channel<TcpSocketChannel>()
+               .Option(ChannelOption.TcpNodelay, true)
+               .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
+               {
+                   IChannelPipeline pipeline = channel.Pipeline;
+                   pipeline.AddLast("framing-enc", new LengthFieldPrepender(8));
+                   pipeline.AddLast("framing-dec", new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 8, 0, 8));
+                   pipeline.AddLast(new RpcClientHandler(this));
+               }));
+                Channel = client.ConnectAsync(new IPEndPoint(IPAddress.Parse(basics.Ip), basics.Port)).Result;
+                //发送身份认证
+                Await.Start(AuthenticationTag);
+                Channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(new Authentication()
+                {
+                    UserName = basics.UserName,
+                    Password = basics.Password,
+                    ISn = basics.ISn,
+                    INs = basics.INs
+                }.ToJson())));
+                //消息转换转换
+                Message? response = JsonConvert.DeserializeObject<Message>(Await.Wait(AuthenticationTag).RData);
+                if (response == null)
+                {
+                    return Break(SN, false, "服务器未响应认证信息");
+                }
+                return Break(SN, response.State, response.Info);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, ex);
+            }
         }
 
         public OperateResult Close()
         {
-            throw new NotImplementedException();
+            string SN = Depart("Close");
+            try
+            {
+                if (client == null)
+                {
+                    return Break(SN, false, "未打开");
+                }
+
+                ClientGroup?.ShutdownGracefullyAsync().Wait();
+                Channel.CloseAsync().Wait();
+                Channel.DisconnectAsync().Wait();
+                client = null;
+
+                return Break(SN, true);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, ex);
+            }
         }
 
         public T Create<T>() where T : class
         {
-            throw new NotImplementedException();
+            T cre = null;
+            //接口名称
+            string iname = typeof(T).Name;
+
+            //不存在则添加
+            if (creates.ContainsKey(iname))
+            {
+                cre = (T)creates[iname];
+            }
+            else
+            {
+                //代理
+                Proxy proxy = Proxy.Instance(new ProxyData.Basics
+                {
+                    Await = Await,
+                    channel = Channel,
+                    iName = iname,
+                    type = typeof(T)
+                });
+                cre = proxy.ActLike<T>();
+                //添加
+                creates.AddOrUpdate(iname, cre, (k, v) => cre);
+            }
+            return cre;
         }
 
         public OperateResult Register<I, O>()
         {
-            throw new NotImplementedException();
+            string SN = Depart("Register");
+            try
+            {
+                iRegister.Add(typeof(I).Name, typeof(O));
+                return Break(SN, true);
+            }
+            catch (Exception ex)
+            {
+                return Break(SN, false, ex.Message, ex);
+            }
         }
 
-        public void Response(byte[] data, IChannel channel = null)
+        public void Response(IByteBuffer data, IChannel channel)
         {
-            throw new NotImplementedException();
-        }
+            //实例化返回信息
+            Response message = new Response();
+
+            try
+            {
+                Types TAG = JsonTool.StringToJsonEntity<YSAI.Rpc.data.Type>(data.ToString(Encoding.UTF8)).TAG;
+                switch (TAG)
+                {
+                    case Types.request:
+                        //地址
+                        string address = Tool.IpHandle(channel.RemoteAddress);
+                        //请求的数据
+                        Request? request = JsonTool.StringToJsonEntity<Request>(data.ToString(Encoding.UTF8));
+                        //判断接口名是否在字典中
+                        if (!iRegister.ContainsKey(request.IName))
+                        {
+                            message.info = "接口不存在";
+                            //发送消息
+                            channel.WriteAndFlushAsync(message);
+                            //跳出
+                            return;
+                        }
+                        //取出这个程序集
+                        System.Type iType = iRegister[request.IName];
+                        //创建实例
+                        object? instance = Activator.CreateInstance(iType);
+                        //获取方法
+                        MethodInfo? method = iType.GetMethod(request.MName);
+                        //判断方法是否为空
+                        if (method == null)
+                        {
+                            message.info = "未找到该方法";
+                            channel.WriteAndFlushAsync(message);
+                            //跳出
+                            return;
+                        }
+                        //参数信息
+                        object[] paramters = request.Params.ToArray();
+                        //获取方法参数
+                        var methodParamters = method.GetParameters();
+                        //参数转换赋值
+                        for (int i = 0; i < methodParamters.Length; i++)
+                        {
+                            if (paramters[i].GetType() != methodParamters[i].ParameterType)
+                            {
+                                paramters[i] = JsonConvert.DeserializeObject(paramters[i].ToJson(), methodParamters[i].ParameterType);
+                            }
+                        }
+                        //执行此方法
+                        object? res = method.Invoke(instance, paramters);
 
+                        //返回响应信息
+                        message.info = "请求成功";
+                        message.status = true;
+                        message.data = res;
+                        //发送消息
+                        channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(message.ToJson())));
+                        break;
+                    case Types.response:
+                        Await.Set(channel.Id.AsShortText(), data.ToString(Encoding.UTF8));
+                        break;
+                    case Types.authentication:
+                        break;
+                    case Types.message:
+                        Await.Set(AuthenticationTag, data.ToString(Encoding.UTF8));
+                        break;
+                }
+            }
+            catch (Exception ex)
+            {
+                //返回响应信息
+                message.info = ex.Message;
+                //发送消息
+                channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(message.ToJson())));
+            }
+        }
         public void Exception(Exception ex)
         {
-            throw new NotImplementedException();
+            LogHelper.Error(ex.Message, $"{TAG}.log", ex);
+            Dispose();
         }
 
         public void Dispose()

+ 25 - 0
src/YSAI.Rpc/client/RpcClientHandler.cs

@@ -0,0 +1,25 @@
+using DotNetty.Buffers;
+using DotNetty.Transport.Channels;
+
+namespace YSAI.Rpc.client
+{
+    public class RpcClientHandler : ChannelHandlerAdapter
+    {
+        RpcClient rpcClient;
+        public RpcClientHandler(RpcClient rpcClient)
+        {
+            this.rpcClient = rpcClient;
+        }
+        public override void ChannelRead(IChannelHandlerContext context, object message)
+        {
+            rpcClient.Response(message as IByteBuffer, context.Channel);
+        }
+        public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();
+
+        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
+        {
+            rpcClient.Exception(exception);
+            context.CloseAsync();
+        }
+    }
+}

+ 36 - 0
src/YSAI.Rpc/data/Authentication.cs

@@ -0,0 +1,36 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace YSAI.Rpc.data
+{
+    /// <summary>
+    /// 身份认证
+    /// </summary>
+    public class Authentication
+    {
+        /// <summary>
+        /// 标识
+        /// </summary>
+        [JsonConverter(typeof(StringEnumConverter))]
+        public Types TAG = Types.authentication;
+
+
+        /// <summary>
+        /// 用户名
+        /// </summary>
+        public string UserName { get; set; }
+        /// <summary>
+        /// 密码
+        /// </summary>
+        public string Password { get; set; }
+
+        /// <summary>
+        /// 唯一标识
+        /// </summary>
+        public string ISn { get; set; }
+        /// <summary>
+        /// 接口名称集合
+        /// </summary>
+        public List<string> INs { get; set; }
+    }
+}

+ 17 - 0
src/YSAI.Rpc/data/AwaitData.cs

@@ -0,0 +1,17 @@
+namespace YSAI.Rpc.data
+{
+    /// <summary>
+    /// 等待传递数据
+    /// </summary>
+    public class AwaitData
+    {
+        /// <summary>
+        /// 等待处理
+        /// </summary>
+        public AutoResetEvent WaitHandler { get; set; } = new AutoResetEvent(false);
+        /// <summary>
+        /// 返回的数据
+        /// </summary>
+        public string RData { get; set; }
+    }
+}

+ 0 - 30
src/YSAI.Rpc/data/Basics.cs

@@ -1,30 +0,0 @@
-namespace YSAI.Rpc.data
-{
-    /// <summary>
-    /// 基础数据
-    /// </summary>
-    public class Basics
-    {
-        /// <summary>
-        /// IP
-        /// </summary>
-        public string Ip { get; set; }
-        /// <summary>
-        /// 端口
-        /// </summary>
-        public int Port { get; set; }
-        /// <summary>
-        /// 账号
-        /// </summary>
-        public string UserName { get; set; }
-        /// <summary>
-        /// 密码
-        /// </summary>
-        public string Password { get; set; }
-        /// <summary>
-        /// 超时时间
-        /// </summary>
-        public int TimeOut { get; set; }
-
-    }
-}

+ 42 - 0
src/YSAI.Rpc/data/Client.cs

@@ -0,0 +1,42 @@
+namespace YSAI.Rpc.data
+{
+    public class Client
+    {
+        /// <summary>
+        /// 基础数据
+        /// </summary>
+        public class Basics
+        {
+            /// <summary>
+            /// IP
+            /// </summary>
+            public string Ip { get; set; } = "127.0.0.1";
+            /// <summary>
+            /// 端口
+            /// </summary>
+            public int Port { get; set; } = 6688;
+            /// <summary>
+            /// 超时时间
+            /// </summary>
+            public int TimeOut { get; set; } = 1000;
+
+
+            /// <summary>
+            /// 用户名
+            /// </summary>
+            public string UserName { get; set; } = "ysai";
+            /// <summary>
+            /// 密码
+            /// </summary>
+            public string Password { get; set; } = "ysai";
+            /// <summary>
+            /// 唯一标识
+            /// </summary>
+            public string ISn { get; set; } = "888888";
+            /// <summary>
+            /// 接口名称集合
+            /// </summary>
+            public List<string> INs { get; set; }
+        }
+    }
+}

+ 12 - 23
src/YSAI.Rpc/data/Message.cs

@@ -1,38 +1,27 @@
-using YSAI.Unility;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using YSAI.Unility;
 
 namespace YSAI.Rpc.data
 {
-    /// <summary>
-    /// 消息
-    /// </summary>
     public class Message
     {
-        public Message() { }
-        public Message(string message, bool status = false, object? data = null)
-        {
-            this.message = message;
-            this.status = status;
-            this.data = data;
-        }
-
         /// <summary>
-        /// 消息
+        /// 标识
         /// </summary>
-        public string message { get; set; }
-
+        [JsonConverter(typeof(StringEnumConverter))]
+        public Types TAG = Types.message;
         /// <summary>
-        /// 成功还是失败
+        /// 信息
         /// </summary>
-        public bool status { get; set; }
-
+        public string Info { get; set; }
         /// <summary>
-        /// 数据
+        /// 状态
         /// </summary>
-        public object? data { get; set; }
-
+        public bool State { get; set; } = false;
         /// <summary>
-        /// 发送端时间
+        /// 时间
         /// </summary>
-        public string time { get; set; } = DateTime.Now.ToDateTimeString();
+        public string Time { get; set; } = DateTime.Now.ToDateTimeString();
     }
 }

+ 34 - 0
src/YSAI.Rpc/data/ProxyData.cs

@@ -0,0 +1,34 @@
+using DotNetty.Transport.Channels;
+using YSAI.Rpc.unility;
+
+namespace YSAI.Rpc.data
+{
+    /// <summary>
+    /// 代理数据
+    /// </summary>
+    public class ProxyData
+    {
+        /// <summary>
+        /// 基础数据
+        /// </summary>
+        public class Basics
+        {
+            /// <summary>
+            /// 通道
+            /// </summary>
+            public IChannel channel { get; set; }
+            /// <summary>
+            /// 接口名称
+            /// </summary>
+            public string iName { get; set; }
+            /// <summary>
+            /// 结构类型
+            /// </summary>
+            public System.Type type { get; set; }
+            /// <summary>
+            /// 等待任务
+            /// </summary>
+            public Await Await { get; set; }
+        }
+    }
+}

+ 9 - 1
src/YSAI.Rpc/data/Request.cs

@@ -1,10 +1,18 @@
-namespace YSAI.Rpc.data
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace YSAI.Rpc.data
 {
     /// <summary>
     /// 请求的数据类型
     /// </summary>
     public class Request
     {
+        /// <summary>
+        /// 标识
+        /// </summary>
+        [JsonConverter(typeof(StringEnumConverter))]
+        public Types TAG = Types.request;
         /// <summary>
         /// 接口名称
         /// </summary>

+ 45 - 0
src/YSAI.Rpc/data/Response.cs

@@ -0,0 +1,45 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using YSAI.Unility;
+
+namespace YSAI.Rpc.data
+{
+    /// <summary>
+    /// 响应
+    /// </summary>
+    public class Response
+    {
+        public Response() { }
+        public Response(string info, bool status = false, object? data = null)
+        {
+            this.info = info;
+            this.status = status;
+            this.data = data;
+        }
+        /// <summary>
+        /// 标识符
+        /// </summary>
+        [JsonConverter(typeof(StringEnumConverter))]
+        public Types TAG = Types.response;
+
+        /// <summary>
+        /// info
+        /// </summary>
+        public string info { get; set; }
+
+        /// <summary>
+        /// 成功还是失败
+        /// </summary>
+        public bool status { get; set; }
+
+        /// <summary>
+        /// 数据
+        /// </summary>
+        public object? data { get; set; }
+
+        /// <summary>
+        /// 发送端时间
+        /// </summary>
+        public string time { get; set; } = DateTime.Now.ToDateTimeString();
+    }
+}

+ 49 - 0
src/YSAI.Rpc/data/Service.cs

@@ -0,0 +1,49 @@
+namespace YSAI.Rpc.data
+{
+    public class Service
+    {
+        /// <summary>
+        /// 基础数据
+        /// </summary>
+        public class Basics
+        {
+            /// <summary>
+            /// 端口
+            /// </summary>
+            public int Port { get; set; } = 6688;
+
+            /// <summary>
+            /// 超时时间
+            /// </summary>
+            public int TimeOut { get; set; } = 1000;
+
+            /// <summary>
+            /// 用户名
+            /// </summary>
+            public string UserName { get; set; } = "ysai";
+            /// <summary>
+            /// 密码
+            /// </summary>
+            public string Password { get; set; } = "ysai";
+
+            /// <summary>
+            /// 信息
+            /// </summary>
+            public List<Info> Infos { get; set; }
+        }
+        /// <summary>
+        /// 用户信息
+        /// </summary>
+        public class Info
+        {
+            /// <summary>
+            /// 唯一标识
+            /// </summary>
+            public string ISn { get; set; } = "888888";
+            /// <summary>
+            /// 接口名称集合
+            /// </summary>
+            public List<string> INs { get; set; }
+        }
+    }
+}

+ 36 - 0
src/YSAI.Rpc/data/Type.cs

@@ -0,0 +1,36 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace YSAI.Rpc.data
+{
+    public class Type
+    {
+        /// <summary>
+        /// 标识
+        /// </summary>
+        [JsonConverter(typeof(StringEnumConverter))]
+        public Types TAG { get; set; }
+    }
+    /// <summary>
+    /// 类型枚举
+    /// </summary>
+    public enum Types
+    {
+        /// <summary>
+        /// 请求
+        /// </summary>
+        request,
+        /// <summary>
+        /// 响应
+        /// </summary>
+        response,
+        /// <summary>
+        /// 身份认证
+        /// </summary>
+        authentication,
+        /// <summary>
+        /// 消息
+        /// </summary>
+        message
+    }
+}

+ 6 - 5
src/YSAI.Rpc/interface/IRpc.cs

@@ -1,4 +1,5 @@
-using DotNetty.Transport.Channels;
+using DotNetty.Buffers;
+using DotNetty.Transport.Channels;
 using YSAI.Core.data;
 
 namespace YSAI.Rpc.@interface
@@ -39,13 +40,13 @@ namespace YSAI.Rpc.@interface
         /// 响应数据
         /// </summary>
         /// <param name="data">字节数据</param>
-        /// <param name="channel">为空则是客户端,不为空则是服务端</param>
-        void Response(byte[] data, IChannel channel = null);
+        /// <param name="channel">通道</param>
+        void Response(IByteBuffer data, IChannel channel);
 
         /// <summary>
-        /// 通道异常
+        /// 异常信息
         /// </summary>
-        /// <param name="ex">异常信息</param>
+        /// <param name="ex">异常</param>
         void Exception(Exception ex);
     }
 }

+ 179 - 17
src/YSAI.Rpc/service/RpcService.cs

@@ -1,7 +1,12 @@
-using DotNetty.Codecs;
+using DotNetty.Buffers;
+using DotNetty.Codecs;
 using DotNetty.Transport.Bootstrapping;
 using DotNetty.Transport.Channels;
 using DotNetty.Transport.Channels.Sockets;
+using ImpromptuInterface;
+using Newtonsoft.Json;
+using System.Collections.Concurrent;
+using System.Reflection;
 using System.Text;
 using YSAI.Core.data;
 using YSAI.Core.@interface;
@@ -34,7 +39,7 @@ namespace YSAI.Rpc.service
         /// 单例模式
         /// </summary>
         /// <returns></returns>
-        public static RpcService Instance(data.Basics basics)
+        public static RpcService Instance(Service.Basics basics)
         {
             if (ThisObjList.Count >= MaxInstanceCount)
             {
@@ -64,7 +69,7 @@ namespace YSAI.Rpc.service
         /// 构造函数
         /// </summary>
         /// <param name="param">参数</param>
-        public RpcService(data.Basics basics)
+        public RpcService(Service.Basics basics)
         {
             this.basics = basics;
         }
@@ -72,7 +77,7 @@ namespace YSAI.Rpc.service
         /// <summary>
         /// 基础数据
         /// </summary>
-        private data.Basics basics;
+        private Service.Basics basics;
 
         /// <summary>
         /// Netty 服务
@@ -96,7 +101,20 @@ namespace YSAI.Rpc.service
         /// <summary>
         /// 注册
         /// </summary>
-        private Dictionary<string, Type> iRegister { get; set; } = new Dictionary<string, Type>();
+        private Dictionary<string, System.Type> iRegister { get; set; } = new Dictionary<string, System.Type>();
+        /// <summary>
+        /// 创建
+        /// </summary>
+        private ConcurrentDictionary<string, object> creates { get; } = new ConcurrentDictionary<string, object>();
+        /// <summary>
+        /// 客户端信息
+        /// 接口名,通道
+        /// </summary>
+        public ConcurrentDictionary<List<string>, IChannel> clients = new ConcurrentDictionary<List<string>, IChannel>();
+        /// <summary>
+        /// 任务等待
+        /// </summary>
+        private Await Await = new Await();
 
         public OperateResult Open()
         {
@@ -118,7 +136,7 @@ namespace YSAI.Rpc.service
                         IChannelPipeline pipeline = channel.Pipeline;
                         pipeline.AddLast("framing-enc", new LengthFieldPrepender(8));
                         pipeline.AddLast("framing-dec", new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 8, 0, 8));
-                        pipeline.AddLast(new ServiceHandler(this));
+                        pipeline.AddLast(new RpcServiceHandler(this));
                     }));
 
                 Channel = server.BindAsync(basics.Port).Result;
@@ -158,7 +176,33 @@ namespace YSAI.Rpc.service
 
         public T Create<T>() where T : class
         {
-            throw new NotImplementedException();
+            T cre = null;
+            //接口名称
+            string iname = typeof(T).Name;
+
+            //不存在则添加
+            if (creates.ContainsKey(iname))
+            {
+                cre = (T)creates[iname];
+            }
+            else
+            {
+                //通过接口名检索到使用哪个通道
+                IChannel channel = clients.FirstOrDefault(c => c.Key.Contains(typeof(T).Name)).Value;
+
+                Proxy proxy = Proxy.Instance(new ProxyData.Basics
+                {
+                    Await = Await,
+                    channel = channel,
+                    iName = iname,
+                    type = typeof(T)
+                });
+                cre = proxy.ActLike<T>();
+                //添加
+                creates.AddOrUpdate(iname, cre, (k, v) => cre);
+            }
+
+            return cre;
         }
 
         public OperateResult Register<I, O>()
@@ -175,23 +219,141 @@ namespace YSAI.Rpc.service
             }
         }
 
-        public void Response(byte[] data, IChannel channel = null)
+        public void Response(IByteBuffer data, IChannel channel)
         {
-            //地址
-            string address = Tool.IpHandle(channel.RemoteAddress);
-            //请求的数据
-            Request? request = JsonTool.StringToJsonEntity<Request>(Encoding.UTF8.GetString(data));
-            //判断接口名是否在字典中
-            if (!iRegister.ContainsKey(request.IName))
+            //实例化返回信息
+            Response message = new Response();
+
+            try
             {
-                channel.WriteAndFlushAsync(new Message { message = "接口不存在" });
-            }
+                Types TAG = JsonTool.StringToJsonEntity<YSAI.Rpc.data.Type>(data.ToString(Encoding.UTF8)).TAG;
+                switch (TAG)
+                {
+                    case Types.request:
+                        //地址
+                        string address = Tool.IpHandle(channel.RemoteAddress);
+
+                        //请求的数据
+                        Request? request = JsonTool.StringToJsonEntity<Request>(data.ToString(Encoding.UTF8));
+                        //判断接口名是否在字典中
+                        if (!iRegister.ContainsKey(request.IName))
+                        {
+                            message.info = "接口不存在";
+                            //发送消息
+                            channel.WriteAndFlushAsync(message);
+                            //跳出
+                            return;
+                        }
+                        //取出这个程序集
+                        System.Type iType = iRegister[request.IName];
+                        //创建实例
+                        object? instance = Activator.CreateInstance(iType);
+                        //获取方法
+                        MethodInfo? method = iType.GetMethod(request.MName);
+                        //判断方法是否为空
+                        if (method == null)
+                        {
+                            message.info = "未找到该方法";
+                            channel.WriteAndFlushAsync(message);
+                            //跳出
+                            return;
+                        }
+                        //参数信息
+                        object[] paramters = request.Params.ToArray();
+                        //获取方法参数
+                        var methodParamters = method.GetParameters();
+                        //参数转换赋值
+                        for (int i = 0; i < methodParamters.Length; i++)
+                        {
+                            if (paramters[i].GetType() != methodParamters[i].ParameterType)
+                            {
+                                paramters[i] = JsonConvert.DeserializeObject(paramters[i].ToJson(), methodParamters[i].ParameterType);
+                            }
+                        }
+                        //执行此方法
+                        object? res = method.Invoke(instance, paramters);
+
+                        //返回响应信息
+                        message.info = "请求成功";
+                        message.status = true;
+                        message.data = res;
+                        //发送消息
+                        channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(message.ToJson())));
+                        break;
+                    case Types.response:
+                        Await.Set(channel.Id.AsShortText(), data.ToString(Encoding.UTF8));
+                        break;
+                    case Types.authentication:
+                        Authentication? authentication = JsonTool.StringToJsonEntity<Authentication>(data.ToString(Encoding.UTF8));
+                        if (basics.UserName == authentication.UserName && basics.Password == authentication.Password)
+                        {
+                            if (basics.Infos.Select(c => c.ISn == authentication.ISn).First())
+                            {
+                                if (basics.Infos.Select(c => c.INs.Comparer(authentication.INs).result).First())
+                                {
+                                    //认证成功
+                                    channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(new Message()
+                                    {
+                                        Info = "认证成功",
+                                        State = true,
+                                    }.ToJson())));
 
+                                    //添加客户端
+                                    clients.AddOrUpdate(basics.Infos.FirstOrDefault(c => c.ISn == authentication.ISn && c.INs.Comparer(authentication.INs).result).INs, channel, (k, v) => channel);
+                                }
+                                else
+                                {
+                                    //认证失败
+                                    channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(new Message()
+                                    {
+                                        Info = "认证失败,接口信息不一致",
+                                        State = false,
+                                    }.ToJson())));
+                                    //关闭客户端连接
+                                    channel.CloseAsync();
+                                }
+                            }
+                            else
+                            {
+                                //认证失败
+                                channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(new Message()
+                                {
+                                    Info = "认证失败,此唯一标识不存在",
+                                    State = false,
+                                }.ToJson())));
+                                //关闭客户端连接
+                                channel.CloseAsync();
+                            }
+                        }
+                        else
+                        {
+                            //认证失败
+                            channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(new Message()
+                            {
+                                Info = "认证失败,账号密码错误",
+                                State = false,
+                            }.ToJson())));
+                            //关闭客户端连接
+                            channel.CloseAsync();
+                        }
+                        break;
+                    case Types.message:
+
+                        break;
+                }
+            }
+            catch (Exception ex)
+            {
+                //返回响应信息
+                message.info = ex.Message;
+                //发送消息
+                channel.WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(message.ToJson())));
+            }
         }
 
         public void Exception(Exception ex)
         {
-            LogHelper.Error(ex.Message, $"{TAG}.log", ex);
+            LogHelper.Error(ex.Message, $"{TAG}.log", ex, false);
         }
 
         public void Dispose()

+ 4 - 5
src/YSAI.Rpc/service/ServiceHandler.cs

@@ -3,19 +3,18 @@ using DotNetty.Transport.Channels;
 
 namespace YSAI.Rpc.service
 {
-    public class ServiceHandler : ChannelHandlerAdapter
+    public class RpcServiceHandler : ChannelHandlerAdapter
     {
-        public ServiceHandler(RpcService rPCServer)
+        public RpcServiceHandler(RpcService rPCServer)
         {
             rpcService = rPCServer;
         }
 
         RpcService rpcService { get; }
+
         public override void ChannelRead(IChannelHandlerContext context, object message)
         {
-            var msg = message as IByteBuffer;
-            rpcService.Response(msg.Array, context.Channel);
-            context.CloseAsync();
+            rpcService.Response(message as IByteBuffer, context.Channel);
         }
         public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();
         public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)

+ 67 - 0
src/YSAI.Rpc/unility/Await.cs

@@ -0,0 +1,67 @@
+using System.Collections.Concurrent;
+using YSAI.Rpc.data;
+
+namespace YSAI.Rpc.unility
+{
+    /// <summary>
+    /// 等待
+    /// </summary>
+    public class Await
+    {
+        /// <summary>
+        /// 容器
+        /// </summary>
+        private ConcurrentDictionary<string, AwaitData> WaitIoc { get; set; } = new ConcurrentDictionary<string, AwaitData>();
+        /// <summary>
+        /// 开始
+        /// </summary>
+        /// <param name="tag">标识</param>
+        public void Start(string tag)
+        {
+            if (!WaitIoc.ContainsKey(tag))
+            {
+                AwaitData awaitData = new AwaitData();
+                WaitIoc.AddOrUpdate(tag, awaitData, (k, v) => awaitData);
+            }
+        }
+        /// <summary>
+        /// 设置
+        /// </summary>
+        /// <param name="tag">标签</param>
+        /// <param name="rData">返回的数据</param>
+        /// <exception cref="Exception"></exception>
+        public void Set(string tag, string rData)
+        {
+            if (WaitIoc.ContainsKey(tag))
+            {
+                AwaitData awaitData = WaitIoc[tag];
+                awaitData.RData = rData;
+                awaitData.WaitHandler.Set();
+            }
+            else
+            {
+                throw new Exception($"TAG({tag})不存在");
+            }
+        }
+        /// <summary>
+        /// 等待
+        /// </summary>
+        /// <param name="tag">标识</param>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public AwaitData Wait(string tag)
+        {
+            if (WaitIoc.ContainsKey(tag))
+            {
+                AwaitData awaitData = WaitIoc[tag];
+                awaitData.WaitHandler.WaitOne();
+                WaitIoc.TryRemove(tag, out _);
+                return awaitData;
+            }
+            else
+            {
+                throw new Exception($"TAG({tag})不存在");
+            }
+        }
+    }
+}

+ 122 - 0
src/YSAI.Rpc/unility/Proxy.cs

@@ -0,0 +1,122 @@
+using DotNetty.Buffers;
+using Newtonsoft.Json;
+using System.Dynamic;
+using System.Text;
+using YSAI.Rpc.data;
+using YSAI.Unility;
+
+namespace YSAI.Rpc.unility
+{
+
+    /// <summary>
+    /// 动作代理
+    /// </summary>
+    public class Proxy : DynamicObject
+    {
+        /// <summary>
+        /// 锁
+        /// </summary>
+        private static readonly object Lock = new object();
+
+        /// <summary>
+        /// 自身对象集合
+        /// </summary>
+        private static List<Proxy> ThisObjList = new List<Proxy>();
+
+        /// <summary>
+        /// 单例模式
+        /// </summary>
+        /// <returns></returns>
+        public static Proxy Instance(ProxyData.Basics basics)
+        {
+            Proxy? 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
+                    {
+                        Proxy exp2 = new Proxy(basics);
+                        ThisObjList.Add(exp2);
+                        return exp2;
+                    }
+                }
+            }
+            return exp;
+        }
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="basics">基础数据</param>
+        public Proxy(ProxyData.Basics basics)
+        {
+            this.basics = basics;
+        }
+
+        /// <summary>
+        /// 基础数据
+        /// </summary>
+        private ProxyData.Basics basics;
+
+        /// <summary>
+        /// 调用方法
+        /// </summary>
+        /// <returns>状态</returns>
+        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
+        {
+            try
+            {
+                //标识
+                string tag = basics.channel.Id.AsShortText();
+                //启动等待
+                basics.Await.Start(tag);
+
+                //设置参数
+                data.Request request = new data.Request
+                {
+                    IName = basics.iName,
+                    MName = binder.Name,
+                    Params = args.ToList()
+                };
+
+                //转换成字节
+                IByteBuffer sendBuffer = Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(request.ToJson()));
+                //发送
+                basics.channel.WriteAndFlushAsync(sendBuffer);
+                //响应的字符串
+                string res = basics.Await.Wait(tag).RData;
+                //转换
+                Response response = JsonConvert.DeserializeObject<Response>(res);
+                if (response == null)
+                {
+                    throw new Exception("超时未响应");
+                }
+                else if (response.status)
+                {
+                    System.Type? returnType = basics.type?.GetMethod(binder.Name)?.ReturnType;
+                    if (returnType == typeof(void))
+                    {
+                        result = null;
+                    }
+                    else
+                    {
+                        result = JsonConvert.DeserializeObject(response.data.ToJson(), returnType);
+                    }
+                    return true;
+                }
+                else
+                {
+                    throw new Exception($"异常错误消息:{response.info}");
+                }
+            }
+            catch (Exception ex)
+            {
+                throw ex;
+            }
+        }
+    }
+}

+ 123 - 106
src/YSAI.Test.All/Program.cs

@@ -1,35 +1,35 @@
-using YSAI.Core.communication.net.ws.client;
-using YSAI.Core.communication.net.ws.service;
-using YSAI.Unility;
+//using YSAI.Core.communication.net.ws.client;
+//using YSAI.Core.communication.net.ws.service;
+//using YSAI.Unility;
 
-WsServiceOperate tcpServiceOperate = WsServiceOperate.Instance(new WsServiceData.Basics() { LocalHostArray = new List<string> { "http://127.0.0.1:6688/" } });
-Console.WriteLine(tcpServiceOperate.On().ToJson().JsonFormatting());
+//WsServiceOperate tcpServiceOperate = WsServiceOperate.Instance(new WsServiceData.Basics() { LocalHostArray = new List<string> { "http://127.0.0.1:6688/" } });
+//Console.WriteLine(tcpServiceOperate.On().ToJson().JsonFormatting());
 
 
 
-WsClientOperate tcpClientOperate = WsClientOperate.Instance(new WsClientData.Basics());
-Console.WriteLine(tcpClientOperate.On().ToJson().JsonFormatting());
-tcpClientOperate.OnEvent += TcpClientOperate_OnEvent;
+//WsClientOperate tcpClientOperate = WsClientOperate.Instance(new WsClientData.Basics());
+//Console.WriteLine(tcpClientOperate.On().ToJson().JsonFormatting());
+//tcpClientOperate.OnEvent += TcpClientOperate_OnEvent;
 
 
-while (true)
-{
-    Console.ReadLine();
-    Console.WriteLine(tcpServiceOperate.Off().ToJson().JsonFormatting());
+//while (true)
+//{
+//    Console.ReadLine();
+//    Console.WriteLine(tcpServiceOperate.Off().ToJson().JsonFormatting());
 
-    Console.ReadLine();
-    Console.WriteLine(tcpClientOperate.Send(new byte[] { }).ToJson().JsonFormatting());
+//    Console.ReadLine();
+//    Console.WriteLine(tcpClientOperate.Send(new byte[] { }).ToJson().JsonFormatting());
 
-    Console.ReadLine();
-    Console.WriteLine(tcpServiceOperate.On().ToJson().JsonFormatting());
-}
+//    Console.ReadLine();
+//    Console.WriteLine(tcpServiceOperate.On().ToJson().JsonFormatting());
+//}
 
 
 
-void TcpClientOperate_OnEvent(object? sender, YSAI.Core.data.EventResult e)
-{
-    Console.WriteLine(e.ToJson().JsonFormatting());
-}
+//void TcpClientOperate_OnEvent(object? sender, YSAI.Core.data.EventResult e)
+//{
+//    Console.WriteLine(e.ToJson().JsonFormatting());
+//}
 
 
 
@@ -788,104 +788,121 @@ void TcpClientOperate_OnEvent(object? sender, YSAI.Core.data.EventResult e)
 //    public string Description { get; set; }
 //}
 
-//using System.Collections.Concurrent;
-//using YSAI.Core.data;
-//using YSAI.Log;
-//using YSAI.Opc.ua.client;
-//using YSAI.Unility;
+using System.Collections.Concurrent;
+using YSAI.Core.data;
+using YSAI.Log;
+using YSAI.Opc.ua.client;
+using YSAI.Unility;
 
-///// <summary>
-///// 读取Csv,返回行集合
-///// </summary>
-///// <param name="path"></param>
-///// <param name="hasTitle"></param>
-///// <returns></returns>
-//List<string> ReadCsv(string path, bool hasTitle)
-//{
-//    if (!File.Exists(path))
-//        return new List<string>();
+/// <summary>
+/// 读取Csv,返回行集合
+/// </summary>
+/// <param name="path"></param>
+/// <param name="hasTitle"></param>
+/// <returns></returns>
+List<string> ReadCsv(string path, bool hasTitle)
+{
+    if (!File.Exists(path))
+        return new List<string>();
+
+    var lines = File.ReadAllLines(path).ToList();
+    if (hasTitle)
+    {
+        lines.RemoveAt(0);
+    }
+    return lines;
+}
 
-//    var lines = File.ReadAllLines(path).ToList();
-//    if (hasTitle)
-//    {
-//        lines.RemoveAt(0);
-//    }
-//    return lines;
-//}
+//Address address = JsonTool.StringToJsonEntity<Address>(FileTool.FileToString("C:\\Users\\Shun\\Desktop\\[6032]Node_Address 202310120854271486.json"));
 
-////Address address = JsonTool.StringToJsonEntity<Address>(FileTool.FileToString("C:\\Users\\Shun\\Desktop\\[6032]Node_Address 202310120854271486.json"));
+Address address = new Address();
+address.SN = Guid.NewGuid().ToString();
+address.CreationTime = DateTime.Now;
+address.AddressArray = new List<AddressDetails>();
+List<string> strings = ReadCsv("C:\\Users\\Shun\\Desktop\\6022.csv", true);
+foreach (var item in strings)
+{
+    string[] str = item.Split(",");
+    string addressD = str[0].Replace("\"", "");
+    address.AddressArray.Add(new AddressDetails()
+    {
+        AddressName = $"ns=2;s=6022.6022.{addressD}",
+        SN = Guid.NewGuid().ToString()
+    });
+}
 
-//Address address = new Address();
-//address.SN = Guid.NewGuid().ToString();
-//address.CreationTime = DateTime.Now;
-//address.AddressArray = new List<AddressDetails>();
-//List<string> strings = ReadCsv("C:\\Users\\Shun\\Desktop\\6022.csv", true);
-//foreach (var item in strings)
+//address.AddressArray.Add(new AddressDetails()
 //{
-//    string[] str = item.Split(",");
-//    string addressD = str[0].Replace("\"", "");
-//    address.AddressArray.Add(new AddressDetails()
-//    {
-//        AddressName = $"ns=2;s=6022.6022.{addressD}",
-//        SN = Guid.NewGuid().ToString()
-//    });
-//}
-
-////address.AddressArray.Add(new AddressDetails()
-////{
-////    AddressName = $"ns=2;s=6022.6022.LAP5_DP2113_AUTO",
-////    SN = Guid.NewGuid().ToString()
-////});
-
-////address.AddressArray.Add(new AddressDetails()
-////{
-////    AddressName = $"ns=2;s=6022.6022.LAP5_DP2112_STATE_RUN",
-////    SN = Guid.NewGuid().ToString()
-////});
+//    AddressName = $"ns=2;s=6022.6022.LAP5_DP2113_AUTO",
+//    SN = Guid.NewGuid().ToString()
+//});
 
-//OpcUaClientOperate opcUaClientOperate = OpcUaClientOperate.Instance(new OpcUaClientData.Basics
+//address.AddressArray.Add(new AddressDetails()
 //{
-//    ServerUrl = "opc.tcp://192.168.2.220:49320",
-//    CustomName = "YSAI 性能测试",
-//    TaskNumber = 10,
-//    TaskHandleInterval = 1,
-//    SubscribeSingleGroupMaxCount = 1000,
+//    AddressName = $"ns=2;s=6022.6022.LAP5_DP2112_STATE_RUN",
+//    SN = Guid.NewGuid().ToString()
 //});
-//Console.WriteLine(opcUaClientOperate.On().ToJson().JsonFormatting());
-//opcUaClientOperate.OnEvent += OpcUaClientOperate_OnEvent;
 
-//while (true)
-//{
-//    Console.ReadLine();
-//    OperateResult operateResult = opcUaClientOperate.Subscribe(address);
-//    Console.WriteLine(operateResult.ToJson().JsonFormatting());
-//}
+OpcUaClientOperate opcUaClientOperate = OpcUaClientOperate.Instance(new OpcUaClientData.Basics
+{
+    ServerUrl = "opc.tcp://192.168.2.220:49320",
+    CustomName = "YSAI 性能测试",
+    TaskNumber = 10,
+    TaskHandleInterval = 1,
+    SubscribeSingleGroupMaxCount = 1000,
+});
+Console.WriteLine(opcUaClientOperate.On().ToJson().JsonFormatting());
+opcUaClientOperate.OnEvent += OpcUaClientOperate_OnEvent;
 
-//void OpcUaClientOperate_OnEvent(object? sender, EventResult e)
-//{
-//    switch (e.RType)
-//    {
-//        case YSAI.Core.@enum.ResultType.KeyValue:
+while (true)
+{
+    Console.ReadLine();
 
-//            ConcurrentDictionary<string, AddressValue> pairs = e.GetRData<ConcurrentDictionary<string, AddressValue>>();
-//            foreach (var item in pairs)
-//            {
-//                if (item.Value.AddressName.Equals("ns=2;s=6022.6022.LAP5_DP2112_STATE_RUN") || item.Value.AddressName.Equals("ns=2;s=6022.6022.LAP5_DP2113_AUTO"))
-//                {
-//                    String str = String.Format("{0,-100}{1,-100}", item.Key, item.Value.Value);
-//                    LogHelper.Verbose(str);
-//                }
+    OperateResult operateResult = opcUaClientOperate.Subscribe(address);
+    Console.WriteLine(operateResult.ToJson().JsonFormatting());
+    Console.ReadLine();
+    operateResult = opcUaClientOperate.UnSubscribe(address);
+    Console.WriteLine(operateResult.ToJson().JsonFormatting());
+    Console.ReadLine();
+    address = new Address();
+    address.SN = Guid.NewGuid().ToString();
+    address.CreationTime = DateTime.Now;
+    address.AddressArray = new List<AddressDetails>();
+    address.AddressArray.Add(new AddressDetails()
+    {
+        AddressName = $"6666",
+        SN = Guid.NewGuid().ToString()
+    });
+    operateResult = opcUaClientOperate.Subscribe(address);
+    Console.WriteLine(operateResult.ToJson().JsonFormatting());
 
-//                //String str = String.Format("{0,-100}{1,-100}", item.Key, item.Value.Value);
-//                //LogHelper.Verbose(str);
-//            }
-//            break;
+}
 
-//        default:
-//            //Console.WriteLine(e.Message);
-//            break;
-//    }
-//}
+void OpcUaClientOperate_OnEvent(object? sender, EventResult e)
+{
+    switch (e.RType)
+    {
+        case YSAI.Core.@enum.ResultType.KeyValue:
+
+            ConcurrentDictionary<string, AddressValue> pairs = e.GetRData<ConcurrentDictionary<string, AddressValue>>();
+            foreach (var item in pairs)
+            {
+                if (item.Value.AddressName.Equals("ns=2;s=6022.6022.LAP5_DP2112_STATE_RUN") || item.Value.AddressName.Equals("ns=2;s=6022.6022.LAP5_DP2113_AUTO"))
+                {
+                    String str = String.Format("{0,-100}{1,-100}", item.Key, item.Value.Value);
+                    LogHelper.Verbose(str);
+                }
+
+                //String str = String.Format("{0,-100}{1,-100}", item.Key, item.Value.Value);
+                //LogHelper.Verbose(str);
+            }
+            break;
+
+        default:
+            //Console.WriteLine(e.Message);
+            break;
+    }
+}
 
 //using Opc.Ua;
 //using YSAI.Core.data;

+ 1 - 1
src/YSAI.Test.All/YSAI.Test.All.csproj

@@ -13,7 +13,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\YSAI.Core\YSAI.Core.csproj" />
+    <ProjectReference Include="..\YSAI.Opc\YSAI.Opc.csproj" />
   </ItemGroup>
 
 

+ 59 - 0
src/YSAI.Test.RPC.Client/Program.cs

@@ -0,0 +1,59 @@
+using YSAI.Rpc.client;
+using YSAI.Unility;
+
+namespace YSAI.Test.RPC.Client
+{
+    public class Test
+    {
+        public string aaaa { get; set; }
+        public string bbbb { get; set; }
+    }
+    public interface IHello
+    {
+        void Kitty(Test test);
+        string Get();
+    }
+
+    public class hello : IHello
+    {
+        public string Get()
+        {
+            return "我是客户端的GET方法:" + DateTime.Now.ToDateTimeString();
+        }
+
+        public void Kitty(Test test)
+        {
+            Console.WriteLine(test.ToJson().JsonFormatting());
+        }
+    }
+    internal class Program
+    {
+        static void Main(string[] args)
+        {
+            RpcClient rpcClient = RpcClient.Instance(new Rpc.data.Client.Basics
+            {
+                Ip = "127.0.0.1",
+                Port = 6688,
+                TimeOut = 10000,
+                Password = "ysai",
+                UserName = "ysai",
+                ISn = "RPC1",
+                INs = new List<string> { "IHello" },
+            });
+
+            Console.WriteLine(rpcClient.Open().ToJson().JsonFormatting());
+            //注册
+            rpcClient.Register<IHello, hello>();
+
+            while (true)
+            {
+                Console.ReadLine();
+                //创建
+                IHello hello = rpcClient.Create<IHello>();
+                hello.Kitty(new Test { aaaa = "客户端 => 服务端", bbbb = DateTime.Now.ToDateTimeString() });
+                Console.WriteLine(hello.Get());
+            }
+
+        }
+    }
+}

+ 14 - 0
src/YSAI.Test.RPC.Client/YSAI.Test.RPC.Client.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\YSAI.Rpc\YSAI.Rpc.csproj" />
+  </ItemGroup>
+
+</Project>

+ 78 - 0
src/YSAI.Test.RPC.Server/Program.cs

@@ -0,0 +1,78 @@
+using YSAI.Rpc.service;
+using YSAI.Unility;
+
+namespace YSAI.Test.RPC.Server
+{
+    public class Test
+    {
+        public string aaaa { get; set; }
+        public string bbbb { get; set; }
+    }
+    public interface IHello
+    {
+        void Kitty(Test test);
+        string Get();
+    }
+
+    public class hello : IHello
+    {
+        public string Get()
+        {
+            return "我是服务端的GET方法:" + DateTime.Now.ToDateTimeString();
+        }
+
+        public void Kitty(Test test)
+        {
+            Console.WriteLine(test.ToJson().JsonFormatting());
+        }
+    }
+    public class hello2 : IHello
+    {
+        public string Get()
+        {
+            return "我是服务端的GET方法:" + DateTime.Now.ToDateTimeString();
+        }
+
+        public void Kitty(Test test)
+        {
+            Console.WriteLine(test.ToJson().JsonFormatting());
+        }
+    }
+    internal class Program
+    {
+        static void Main(string[] args)
+        {
+            RpcService rpvService = RpcService.Instance(new Rpc.data.Service.Basics
+            {
+                Port = 6688,
+                TimeOut = 10000,
+                UserName = "ysai",
+                Password = "ysai",
+                Infos = new List<Rpc.data.Service.Info>
+                {
+                    new Rpc.data.Service.Info
+                    {
+                        INs=new List<string>{ "IHello" },
+                        ISn="RPC1"
+                    }
+                }
+            });
+
+            Console.WriteLine(rpvService.Open().ToJson().JsonFormatting());
+
+            //注册
+            rpvService.Register<IHello, hello>();
+
+
+            while (true)
+            {
+                Console.ReadLine();
+                //创建
+                IHello hello = rpvService.Create<IHello>();
+                hello.Kitty(new Test { aaaa = "服务端 => 客户端", bbbb = DateTime.Now.ToDateTimeString() });
+                Console.WriteLine(hello.Get());
+            }
+
+        }
+    }
+}

+ 14 - 0
src/YSAI.Test.RPC.Server/YSAI.Test.RPC.Server.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\YSAI.Rpc\YSAI.Rpc.csproj" />
+  </ItemGroup>
+
+</Project>