From b3921b1037de270e5f169e2984e445641adf5a3b Mon Sep 17 00:00:00 2001 From: Kimdiego2098 <2248356998@qq.com> Date: Fri, 13 Oct 2023 19:16:12 +0800 Subject: [PATCH] update touchsocket --- framework/Demo/Directory.Build.props | 2 +- framework/Foundation/Directory.Build.props | 8 +- .../DLT645/PackHelper.cs | 1 - ...hingsGateway.Foundation.Adapter.DLT645.xml | 442 - .../ModbusSerialServer/ModbusSerialServer.cs | 11 +- .../Modbus/ModbusTcpDtu/ModbusTcpDtu.cs | 13 +- .../Modbus/ModbusTcpServer/ModbusTcpServer.cs | 11 +- ...ngsGateway.Foundation.Adapter.OPCDA.csproj | 5 + ...ThingsGateway.Foundation.Adapter.OPCDA.xml | 579 - ...ThingsGateway.Foundation.Adapter.OPCUA.xml | 468 - .../Siemens/DateTime.cs | 1 - .../Siemens/Helper/PackHelper.cs | 1 - .../Siemens/SiemensS7PLC.cs | 20 +- ...ingsGateway.Foundation.Adapter.Siemens.xml | 336 - .../ReadWriteDevicesSerialSessionBase.cs | 50 +- .../ReadWriteDevicesTcpClientBase.cs | 32 +- .../ReadWriteDevicesTcpServerBase.cs | 39 +- .../ReadWriteDevicesUdpSessionBase.cs | 10 +- .../ReadWriteDevicesTcpDataHandleAdapter.cs | 21 + .../ReadWriteDevicesUdpDataHandleAdapter.cs | 5 - .../ThingsGateway.Foundation.csproj | 15 + .../Core/Common/DisposableObject.cs | 2 +- .../Core/DataAdapter/DataHandlingAdapter.cs | 13 - .../DataAdapter/NormalDataHandlingAdapter.cs | 18 + .../Package/FixedHeaderPackageAdapter.cs | 125 + .../Package/FixedSizePackageAdapter.cs | 64 + .../Package/TerminatorPackageAdapter.cs | 59 + .../DataAdapter/SingleStreamAdapterOption.cs | 15 +- .../SingleStreamDataHandlingAdapter.cs | 147 +- .../Test/SingleStreamDataAdapterTester.cs | 12 +- .../TouchSocket/Core/Dependency/Container.cs | 61 +- .../Core/Dependency/ContainerExtension.cs | 63 +- .../TouchSocket/Core/Dependency/IContainer.cs | 3 +- .../Core/Dependency/ManualContainer.cs | 7 +- .../Core/Extensions/TaskExtension.cs | 20 + .../TouchSocket/Core/Plugins/IPlugin.cs | 8 - .../TouchSocket/Core/Plugins/PluginBase.cs | 3 - .../Core/{Common => Threading}/Locker.cs | 0 .../Dmtp/Components/Http/HttpDmtpClient.cs | 16 +- .../Dmtp/Components/Http/HttpDmtpService.cs | 4 +- .../Components/Http/HttpDmtpSocketClient.cs | 45 +- .../Dmtp/Components/TCP/TcpDmtpClient.cs | 51 +- .../Dmtp/Components/TCP/TcpDmtpService.cs | 4 +- .../Components/TCP/TcpDmtpSocketClient.cs | 37 +- .../Dmtp/Components/Udp/UdpDmtp.cs | 8 +- .../WebSocket/WebSocketDmtpClient.cs | 45 +- .../Actor/DmtpFileTransferActor.cs | 2 +- .../TouchSocket/Http/Components/HttpClient.cs | 15 +- .../Http/Components/HttpClientSlim.cs | 12 +- .../Http/Components/HttpSocketClient.cs | 19 +- .../Plugins/Interfaces/IHttpDeletePlugin.cs | 37 - .../Http/Plugins/Interfaces/IHttpGetPlugin.cs | 37 - .../Http/Plugins/Interfaces/IHttpPlugin.cs | 3 +- .../Plugins/Interfaces/IHttpPostPlugin.cs | 37 - .../WebSockets/Common/InternalWebSocket.cs | 16 +- .../Common/WebSocketReceiveResult.cs | 5 + .../WebSockets/Components/WebSocketClient.cs | 60 +- .../WebSocketDataHandlingAdapter.cs | 41 +- .../Http/WebSockets/DelegateCollection.cs | 2 +- ...ensions.cs => WebSocketClientExtension.cs} | 2 +- .../WebSocketConfigExtension.cs} | 2 +- ...ions.cs => WebSocketDataFrameExtension.cs} | 2 +- ...ensions.cs => WebSocketServerExtension.cs} | 12 +- .../Http/WebSockets/Interface/IWebSocket.cs | 9 +- .../WebSockets/Plugins/WebSocketFeature.cs | 26 +- .../TouchSocket/Rpc/Enum/CodeGeneratorFlag.cs | 6 - .../EventArgs/SerialConnectingEventArgs.cs | 2 +- .../SerialPort/Interface/ISerial.cs | 16 +- .../Interface/ISerialSessionBase.cs | 4 +- .../SerialPort/SerialPort/BaseSerial.cs | 14 +- .../SerialPort/SerialPort/SerialCore.cs | 396 + .../SerialPort/SerialPort/SerialSession.cs | 624 +- .../TouchSocket/Socket/BaseSocket.cs | 15 +- .../Socket/Common/Options/TcpListenOption.cs | 5 - .../Socket/Components/Core/TcpCore.cs | 1009 +- .../Socket/Components/NAT/NATService.cs | 22 +- .../Socket/Components/NAT/NATSocketClient.cs | 14 +- .../Socket/Components/Tcp/SocketClient.cs | 720 +- .../Socket/Components/Tcp/TcpClient.cs | 1987 ++- .../Socket/Components/Tcp/TcpService.cs | 119 +- .../Socket/Components/Tcp/TcpServiceBase.cs | 72 +- .../Socket/Components/Udp/UdpSession.cs | 174 +- .../Udp/NormalUdpDataHandlingAdapter.cs | 9 - .../DataAdapter/Udp/UdpPackageAdapter.cs | 9 - .../TouchSocket/Socket/DelegateCollection.cs | 104 +- .../TouchSocket/Socket/Enum/ReceiveType.cs | 48 - .../Socket/Extensions/ClientExtension.cs | 31 +- .../Socket/Extensions/ServiceExtension.cs | 28 +- .../Extensions/TouchSocketConfigExtension.cs | 22 - .../TouchSocket/Socket/Interface/IClient.cs | 14 +- .../Socket/Interface/ISender/ISender.cs | 3 - .../Socket/Interface/ITcpClient.cs | 9 +- .../Socket/Interface/ITcpClientBase.cs | 16 +- .../Interfaces/ITcpDisconnectingPlugin.cs | 3 - .../Receiver/IReceiver.cs} | 31 +- .../TouchSocket/Socket/Receiver/Receiver.cs | 91 + .../Socket/Receiver/ReceiverResult.cs | 56 + .../Socket/WaitingClient/IWaitingClient.cs | 2 +- .../Socket/WaitingClient/ResponsedData.cs | 10 +- .../Socket/WaitingClient/WaitingClient.cs | 556 +- .../WaitingClient/WaitingClientExtension.cs | 63 +- .../Socket/WaitingClient/WaitingOptions.cs | 31 +- .../SwaggerDescriptionAttribute.cs} | 30 +- .../WebApi.Swagger/Common/OpenApiComponent.cs | 22 + .../WebApi.Swagger/Common/OpenApiContent.cs | 22 + .../WebApi.Swagger/Common/OpenApiDataTypes.cs | 80 + .../WebApi.Swagger/Common/OpenApiInfo.cs | 25 + .../WebApi.Swagger/Common/OpenApiParameter.cs | 28 + .../WebApi.Swagger/Common/OpenApiPath.cs | 18 + .../WebApi.Swagger/Common/OpenApiPathValue.cs | 40 + .../WebApi.Swagger/Common/OpenApiProperty.cs | 34 + .../Common/OpenApiRequestBody.cs | 25 + .../WebApi.Swagger/Common/OpenApiResponse.cs | 25 + .../WebApi.Swagger/Common/OpenApiRoot.cs | 31 + .../WebApi.Swagger/Common/OpenApiSchema.cs | 37 + .../Extensions/OpenApiExtension.cs | 40 + .../SwaggerPluginsManagerExtension.cs} | 23 +- .../OpenApiStringEnumConverter.cs | 24 + .../WebApi.Swagger/Plugins/SwaggerPlugin.cs | 701 + .../WebApi.Swagger/api/favicon-16x16.png | Bin 0 -> 665 bytes .../WebApi.Swagger/api/favicon-32x32.png | Bin 0 -> 628 bytes .../TouchSocket/WebApi.Swagger/api/index.html | 59 + .../WebApi.Swagger/api/oauth2-redirect.html | 75 + .../WebApi.Swagger/api/openapi.json | 78 + .../WebApi.Swagger/api/swagger-ui-bundle.js | 3 + .../api/swagger-ui-es-bundle-core.js | 3 + .../api/swagger-ui-es-bundle.js | 3 + .../api/swagger-ui-standalone-preset.js | 3 + .../WebApi.Swagger/api/swagger-ui.css | 11073 ++++++++++++++++ .../WebApi.Swagger/api/swagger-ui.js | 3 + framework/Plugin/Directory.Build.props | 2 +- .../ModbusSerialServer/ModbusSerialServer.cs | 8 +- .../ModbusTcpServer/ModbusTcpServer.cs | 8 +- framework/UpgradeManger/Directory.Build.props | 2 +- .../UpgradeManger/UpgradeManger.cs | 5 +- framework/Web/Directory.Build.props | 2 +- .../ManageGateway/ManageGatewayConfig.cs | 1 - .../Workers/Upgrade/UpgradeWorker.cs | 3 +- 138 files changed, 16480 insertions(+), 5630 deletions(-) delete mode 100644 framework/Foundation/ThingsGateway.Foundation.Adapter.DLT645/ThingsGateway.Foundation.Adapter.DLT645.xml delete mode 100644 framework/Foundation/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.xml delete mode 100644 framework/Foundation/ThingsGateway.Foundation.Adapter.OPCUA/ThingsGateway.Foundation.Adapter.OPCUA.xml delete mode 100644 framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/ThingsGateway.Foundation.Adapter.Siemens.xml rename framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/{Common => Threading}/Locker.cs (100%) delete mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpDeletePlugin.cs delete mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpGetPlugin.cs delete mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPostPlugin.cs rename framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/{WebSocketClientExtensions.cs => WebSocketClientExtension.cs} (99%) rename framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/{Config/WebSocketConfigExtensions.cs => Extensions/WebSocketConfigExtension.cs} (98%) rename framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/{WebSocketDataFrameExtensions.cs => WebSocketDataFrameExtension.cs} (99%) rename framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/{WebSocketServerExtensions.cs => WebSocketServerExtension.cs} (94%) create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/SerialCore.cs delete mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Enum/ReceiveType.cs rename framework/Foundation/ThingsGateway.Foundation/TouchSocket/{Http/Plugins/Interfaces/IHttpPutPlugin.cs => Socket/Receiver/IReceiver.cs} (51%) create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/Receiver.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/ReceiverResult.cs rename framework/Foundation/ThingsGateway.Foundation/TouchSocket/{Core/DataAdapter/Decorators/AdapterData.cs => WebApi.Swagger/Attributes/SwaggerDescriptionAttribute.cs} (61%) create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiComponent.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiContent.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiDataTypes.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiInfo.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiParameter.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiPath.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiPathValue.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiProperty.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiRequestBody.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiResponse.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiRoot.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiSchema.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Extensions/OpenApiExtension.cs rename framework/Foundation/ThingsGateway.Foundation/TouchSocket/{Core/DataAdapter/Decorators/SingleStreamDecorator.cs => WebApi.Swagger/Extensions/SwaggerPluginsManagerExtension.cs} (58%) create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/JsonConverters/OpenApiStringEnumConverter.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Plugins/SwaggerPlugin.cs create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/favicon-16x16.png create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/favicon-32x32.png create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/index.html create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/oauth2-redirect.html create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/openapi.json create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/swagger-ui-bundle.js create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/swagger-ui-es-bundle-core.js create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/swagger-ui-es-bundle.js create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/swagger-ui-standalone-preset.js create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/swagger-ui.css create mode 100644 framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/swagger-ui.js diff --git a/framework/Demo/Directory.Build.props b/framework/Demo/Directory.Build.props index 20a309ed5..3ccfa4e10 100644 --- a/framework/Demo/Directory.Build.props +++ b/framework/Demo/Directory.Build.props @@ -1,6 +1,6 @@ - 3.0.0.7 + 3.0.0.8 latest net6.0;net7.0 Diego diff --git a/framework/Foundation/Directory.Build.props b/framework/Foundation/Directory.Build.props index d8b50c242..5d68b0ac6 100644 --- a/framework/Foundation/Directory.Build.props +++ b/framework/Foundation/Directory.Build.props @@ -1,6 +1,6 @@ - 3.0.0.7 + 3.0.0.8 True latest net45;netstandard2.0;net6.0;net7.0 @@ -42,5 +42,9 @@ - + + True + Embedded + True + \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.DLT645/DLT645/PackHelper.cs b/framework/Foundation/ThingsGateway.Foundation.Adapter.DLT645/DLT645/PackHelper.cs index a4a8a9c57..36cc22f97 100644 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.DLT645/DLT645/PackHelper.cs +++ b/framework/Foundation/ThingsGateway.Foundation.Adapter.DLT645/DLT645/PackHelper.cs @@ -10,7 +10,6 @@ //------------------------------------------------------------------------------ #endregion - namespace ThingsGateway.Foundation.Adapter.DLT645; internal static class PackHelper diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.DLT645/ThingsGateway.Foundation.Adapter.DLT645.xml b/framework/Foundation/ThingsGateway.Foundation.Adapter.DLT645/ThingsGateway.Foundation.Adapter.DLT645.xml deleted file mode 100644 index 20cd4d39e..000000000 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.DLT645/ThingsGateway.Foundation.Adapter.DLT645.xml +++ /dev/null @@ -1,442 +0,0 @@ - - - - ThingsGateway.Foundation.Adapter.DLT645 - - - - - 解析参数 - - - - - 解析长度 - - - - - 小数位 - - - - - 有符号解析 - - - - - 获取返回的解析信息 - - - - - - - 获取DLT645报文 - - - - - DLT645_2007 - - - - - DLT645_2007 - - - - - - 增加FE FE FE FE的报文头部 - - - - - 写入需操作员代码 - - - - - 写入密码 - - - - - 通讯地址BCD码,一般应该是12个字符 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 广播校时 - - - - - - - - 冻结 - - - - - - - - 读取通信地址 - - - - - - - 修改波特率 - - - - - - - - 更新通信地址 - - - - - - - - 修改密码 - - - - - - - - - - 控制码 - - - - - 读数据 - - - - - 读后续数据 - - - - - 读站号 - - - - - 写数据 - - - - - 写站号 - - - - - 广播校时 - - - - - 冻结 - - - - - 更新波特率 - - - - - 更新密码 - - - - - DLT645_2007Address - - - - - - - - - - - - - - - - 数据标识 - - - - - 反转解析 - - - - - 站号信息 - - - - - - - - 解析地址 - - - - - - - - - - - DLT645_2007 - - - - - DLT645_2007 - - - - - DLT645协议转换double - - 带数据项标识 - - - - - - - - - - - - DLT645_2007DataHandleAdapter - - - - - 增加FE FE FE FE的报文头部 - - - - - - - - - - - - - - DLT645_2007 - - - - - DLT645_2007 - - - - - - 增加FE FE FE FE的报文头部 - - - - - 写入需操作员代码 - - - - - 写入密码 - - - - - 通讯地址BCD码,一般应该是12个字符 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 广播校时 - - - - - - - - 冻结 - - - - - - - - 读取通信地址 - - - - - - - 修改波特率 - - - - - - - - 更新通信地址 - - - - - - - - 修改密码 - - - - - - - - - - - - - - - - - - - - - - - diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusSerialServer/ModbusSerialServer.cs b/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusSerialServer/ModbusSerialServer.cs index 72ef82039..8c35a4af6 100644 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusSerialServer/ModbusSerialServer.cs +++ b/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusSerialServer/ModbusSerialServer.cs @@ -24,7 +24,7 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase /// /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 /// - public Func WriteData; + public Func> WriteData; /// /// 继电器 @@ -267,12 +267,13 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase { return Task.FromResult(Write(address, value)); } + /// - protected override void Received(ByteBlock byteBlock, IRequestInfo requestInfo) + protected override async Task Received(SerialSession client, ReceivedDataEventArgs e) { try { - + var requestInfo = e.RequestInfo; //接收外部报文 if (requestInfo is ModbusSerialServerMessage modbusServerMessage) { @@ -312,7 +313,7 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase if (WriteData != null) { // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 - if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess) + if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess) { var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length)); if (result.IsSuccess) @@ -349,7 +350,7 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase if (WriteData != null) { - if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess) + if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess) { var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); if (result.IsSuccess) diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusTcpDtu/ModbusTcpDtu.cs b/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusTcpDtu/ModbusTcpDtu.cs index d584581ef..7e6740455 100644 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusTcpDtu/ModbusTcpDtu.cs +++ b/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusTcpDtu/ModbusTcpDtu.cs @@ -84,15 +84,6 @@ public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase return new OperResult(ex); } } - /// - /// - /// - /// - /// - protected override void Connecting(SocketClient client, ConnectingEventArgs e) - { - Logger?.Debug(client.IP + ":" + client.Port + "正在连接"); - } /// public override void SetDataAdapter(object socketClient = null) @@ -195,7 +186,7 @@ public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase var item = commandResult.Content; if (FrameTime != 0) Thread.Sleep(FrameTime); - var WaitingClientEx = client.GetWaitingClientEx(new() { BreakTrigger = true }); + var WaitingClientEx = client.GetWaitingClient(new() { ThrowBreakException = true }); var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); return (MessageBase)result.RequestInfo; } @@ -222,7 +213,7 @@ public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase var item = commandResult.Content; await Task.Delay(FrameTime, cancellationToken); - var WaitingClientEx = client.GetWaitingClientEx(new() { BreakTrigger = true }); + var WaitingClientEx = client.GetWaitingClient(new() { ThrowBreakException = true }); var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); return (MessageBase)result.RequestInfo; } diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusTcpServer/ModbusTcpServer.cs b/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusTcpServer/ModbusTcpServer.cs index 94ab2e8b1..8e1626837 100644 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusTcpServer/ModbusTcpServer.cs +++ b/framework/Foundation/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusTcpServer/ModbusTcpServer.cs @@ -25,7 +25,7 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase /// /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 /// - public Func WriteData; + public Func> WriteData; /// /// 继电器 @@ -284,12 +284,13 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase { return Task.FromResult(Write(address, value)); } + /// - protected override void Received(SocketClient client, IRequestInfo requestInfo) + protected override async Task Received(SocketClient client, ReceivedDataEventArgs e) { try { - + var requestInfo = e.RequestInfo; //接收外部报文 if (requestInfo is ModbusTcpServerMessage modbusServerMessage) { @@ -330,7 +331,7 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase if (WriteData != null) { // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 - if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) + if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) { var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length)); if (result.IsSuccess) @@ -367,7 +368,7 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase if (WriteData != null) { - if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) + if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) { var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); if (result.IsSuccess) diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.csproj b/framework/Foundation/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.csproj index c63216103..b43726008 100644 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.csproj +++ b/framework/Foundation/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.csproj @@ -1,3 +1,8 @@  + + + net45;netstandard2.0;net6.0;net7.0 + + diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.xml b/framework/Foundation/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.xml deleted file mode 100644 index 6e3ec486d..000000000 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.xml +++ /dev/null @@ -1,579 +0,0 @@ - - - - ThingsGateway.Foundation.Adapter.OPCDA - - - - - 移除符合条件的元素 - - - - - - - - The WIN32 system default locale. - - - - - The WIN32 user default locale. - - - - - 创建一个COM服务器的实例。 - - - - - 指定错误消息文本检索系统。 - - - - - 初始化COM安全。 - - - - - 从枚举器读取guid。 - - - - - 从枚举器读取guid。 - - - - - 释放 COM 对象 - - - - - - windows的filetime是从1601-1-1 00:00:00开始的,datetime是从1-1-1 00:00:00开始的 - datetime和filetime的滴答单位都是100ns(100纳秒,千万分之一秒),所以转换时只需要考虑开始时间即可 - - - - - 值变化 - - - - - - 读取 - - - - - - 写入 - - - - - - 返回结果 - - - - - ID - - - - - Quality - - - - - TimeStamp - - - - - Value - - - - - 建立连接 - - - - - - 组读取 - - - - - - OpcItem - - - - - OpcItem - - - - - - AccessPath - - - - - Blob - - - - - BlobSize - - - - - ClientHandle - - - - - active(1) or not(0) - - - - - 数据项在opc server的完全名称 - - - - - Quality - - - - - RunTimeDataType - - - - - ServerHandle - - - - - TimeStamp - - - - - Value - - - - - - - - 获取节点 - - - - - 服务器状态 - - - - - - ServerStatus - - - - - CurrentTime - - - - - LastUpdateTime - - - - - ServerState - - - - - StartTime - - - - - VendorInfo - - - - - Version - - - - - OpcDiscovery - - - - - GetOpcServer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 订阅变化项 - - - - - - OPCDAClient - - - - - 当前保存的需订阅列表 - - - - - - - - - - - 数据变化事件 - - - - - 是否连接成功 - - - - - 当前配置 - - - - - 添加节点,需要在连接成功后执行 - - 组名称/变量节点,注意每次添加的组名称不能相同 - - - - 设置节点并保存,每次重连会自动添加节点 - - - - - - - 连接服务器 - - - - - 断开连接 - - - - - 浏览节点 - - - - - - - 获取服务状态 - - - - - - 初始化设置 - - - - - - 按OPC组读取组内变量,结果会在订阅事件中返回 - - 组名称,值为null时读取全部组 - - - - - 移除节点 - - - - - - - - - 批量写入值 - - - - - - - - - OPCDA连接配置项 - - - - - 是否订阅 - - - - - 内部检测重连间隔/min - - - - - 死区 - - - - - 分组大小 - - - - - OPCIP - - - - - OPCNAME - - - - - 订阅间隔 - - - - - - - - - - diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.OPCUA/ThingsGateway.Foundation.Adapter.OPCUA.xml b/framework/Foundation/ThingsGateway.Foundation.Adapter.OPCUA/ThingsGateway.Foundation.Adapter.OPCUA.xml deleted file mode 100644 index 7ba6d412c..000000000 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.OPCUA/ThingsGateway.Foundation.Adapter.OPCUA.xml +++ /dev/null @@ -1,468 +0,0 @@ - - - - ThingsGateway.Foundation.Adapter.OPCUA - - - - - 移除符合条件的元素 - - - - - - - - 异步Select - - - - - - - - - - 辅助类 - - - - - Browses the address space and returns the references found. - - The session. - The set of browse operations to perform. - if set to true a exception will be thrown on an error. - - The references found. Null if an error occurred. - - - - - Create the continuation point collection from the browse result - collection for the BrowseNext service. - - The browse result collection to use. - The collection of continuation points for the BrowseNext service. - - - - 浏览地址空间 - - - - - - - - - - - 浏览地址空间 - - - - - - - - - - - 浏览地址空间并返回指定类型的所有节点 - - - - - - - - - - Collects the fields for the instance. - - - - - Collects the fields for the type. - - - - - Constructs an event object from a notification. - - The session. - The monitored item that produced the notification. - The notification. - The known event types. - Mapping between event types and known event types. - - The event object. Null if the notification is not a valid event type. - - - - - Discovers the servers on the local machine. - - The configuration. - A list of server urls. - - - - Finds the type of the event for the notification. - - The monitored item. - The notification. - The NodeId of the EventType. - - - - 指定的属性的显示文本。 - - - - - Finds the endpoint that best matches the current settings. - - The discovery URL. - if set to true select an endpoint that uses security. - The best available endpoint. - - - - 返回一组相对路径的节点id - - - - - Collects the fields for the instance node. - - The session. - The node id. - The parent path. - The event fields. - The node id for the declaration of the field. - The table of found nodes. - - - - 判断指定的select子句包含的浏览路径。 - - - - - 访问级别属性的显示文本。 - - - - - 事件通知属性的显示文本 - - - - - 扩展方法 - - - - - 解析获取DataValue - - - - - - 解析获取object - - - - - - DecodeRawData - - - - - - OPCUAValue解析为Jtoken - - - - - - - - - CreateEncoder - - - - - - 维度 - - - - - - - OPCUAClient配置项 - - - - - OPCUrl - - - - - 登录账号 - - - - - 登录密码 - - - - - 检查域 - - - - - 更新间隔 - - - - - 是否订阅 - - - - - 分组大小 - - - - - 死区 - - - - - KeepAliveInterval/ms - - - - - 安全策略 - - - - - - - - 订阅委托 - - - - - - OPCUAClient - - - - - 当前配置 - - - - - ProductUri - - - - - 当前保存的变量名称列表 - - - - - 当前的变量名称/OPC变量节点 - - - - - 当前的订阅组,组名称/组 - - - - - SessionReconnectHandler - - - - - 默认的构造函数,实例化一个新的OPC UA类 - - - - - 订阅 - - - - - 配置信息 - - - - - 连接状态 - - - - - OPCUAClient - - - - - 当前活动会话。 - - - - - 新增订阅,需要指定订阅组名称,订阅的tag名数组 - - - - - 移除所有的订阅消息 - - - - - 移除订阅消息 - - 组名称 - - - - 浏览一个节点的引用 - - 节点值 - 引用节点描述 - - - - 调用服务器的方法 - - 方法的父节点tag - 方法的节点tag - 传递的参数 - 输出的结果值 - - - - 读取历史数据 - - 节点的索引 - 开始时间 - 结束时间 - 读取的个数 - 是否包含边界 - cancellationToken - 读取的数据列表 - - - - 连接到服务器 - - - - - 断开连接。 - - - - - Creates a new session. - - The new session object. - - - - 从服务器读取值 - - - - - 异步写opc标签 - - - - - 从服务器读取值 - - - - - 从服务器或缓存读取节点 - - - - - 读取一个节点的所有属性 - - - - - 读取节点的所有属性 - - - - - 读取一个节点的所有属性 - - 节点信息 - 节点的特性值 - - - - - - - 连接处理器连接事件处理完成。 - - - - - 读取属性过程中用于描述的 - - - - - 属性的名称 - - - - - 操作结果状态描述 - - - - - 属性的类型描述 - - - - - 属性的值,如果读取错误,返回文本描述 - - - - diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/DateTime.cs b/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/DateTime.cs index eca649652..20f847b23 100644 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/DateTime.cs +++ b/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/DateTime.cs @@ -10,7 +10,6 @@ //------------------------------------------------------------------------------ #endregion - namespace ThingsGateway.Foundation.Adapter.Siemens; /// diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/Helper/PackHelper.cs b/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/Helper/PackHelper.cs index 3c2e0a19f..c8eb1acb5 100644 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/Helper/PackHelper.cs +++ b/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/Helper/PackHelper.cs @@ -10,7 +10,6 @@ //------------------------------------------------------------------------------ #endregion - namespace ThingsGateway.Foundation.Adapter.Siemens; internal static class PackHelper diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/SiemensS7PLC.cs b/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/SiemensS7PLC.cs index f7fe75d4f..090ed8b5e 100644 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/SiemensS7PLC.cs +++ b/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/Siemens/SiemensS7PLC.cs @@ -67,7 +67,6 @@ namespace ThingsGateway.Foundation.Adapter.Siemens break; } - tcpClient.Connected += Connected; } /// @@ -293,8 +292,8 @@ namespace ThingsGateway.Foundation.Adapter.Siemens /// public override void SetDataAdapter(object socketClient = null) { - SiemensS7PLCDataHandleAdapter DataHandleAdapter = new(); - TcpClient.SetDataHandlingAdapter(DataHandleAdapter); + SiemensS7PLCDataHandleAdapter dataHandleAdapter = new(); + TcpClient.SetDataHandlingAdapter(dataHandleAdapter); } @@ -476,17 +475,20 @@ namespace ThingsGateway.Foundation.Adapter.Siemens } #endregion - private void Connected(ITcpClient client, ConnectedEventArgs e) + /// + protected override async Task Connected(ITcpClient client, ConnectedEventArgs e) { try { - var result1 = SendThenResponse(ISO_CR); + NormalDataHandlingAdapter dataHandleAdapter = new(); + TcpClient.SetDataHandlingAdapter(dataHandleAdapter); + var result1 = await SendThenResponseAsync(ISO_CR); if (!result1.IsSuccess) { Logger?.Warning($"{client.IP} : {client.Port}:ISO_TP握手失败-{result1.Message}"); return; } - var result2 = SendThenResponse(S7_PN); + var result2 = await SendThenResponseAsync(S7_PN); if (!result2.IsSuccess) { Logger?.Warning($"{client.IP} : {client.Port}:PDU初始化失败-{result2.Message}"); @@ -499,7 +501,11 @@ namespace ThingsGateway.Foundation.Adapter.Siemens { Logger.Exception(ex); } - + finally + { + SetDataAdapter(); + } + await base.Connected(client, e); } } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/ThingsGateway.Foundation.Adapter.Siemens.xml b/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/ThingsGateway.Foundation.Adapter.Siemens.xml deleted file mode 100644 index 9c3c53e72..000000000 --- a/framework/Foundation/ThingsGateway.Foundation.Adapter.Siemens/ThingsGateway.Foundation.Adapter.Siemens.xml +++ /dev/null @@ -1,336 +0,0 @@ - - - - ThingsGateway.Foundation.Adapter.Siemens - - - - - 区域 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 西门子PLC地址数据信息 - - - - - bit位偏移 - - - - - 数据块代码 - - - - - DB块数据信息 - - - - - 获取起始地址 - - - - - - - - 获取bit - - - - - - - 解析地址 - - - - - - - 解析地址 - - - - - - - - - - - - - - https://github.com/S7NetPlus/s7netplus/blob/develop/S7.Net/Types/DateTime.cs - Contains the methods to convert between and S7 representation of datetime values. - - - - - The maximum value supported by the specification. - - - - - The minimum value supported by the specification. - - - - - Parses a value from bytes. - - Input bytes read from PLC. - A object representing the value read from PLC. - Thrown when the length of - is not 8 or any value in - is outside the valid range of values. - - - - Parses an array of values from bytes. - - Input bytes read from PLC. - An array of objects representing the values read from PLC. - Thrown when the length of - is not a multiple of 8 or any value in - is outside the valid range of values. - - - - Converts a value to a byte array. - - The DateTime value to convert. - A byte array containing the S7 date time representation of . - Thrown when the value of - is before - or after . - - - - Converts an array of values to a byte array. - - The DateTime values to convert. - A byte array containing the S7 date time representations of . - Thrown when any value of - is before - or after . - - - - S7连读写请求头(包含ISO头和COTP头) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 相关命令含义源自网络资料/Shrap7/s7netplus - - - - - 传入PLC类型,程序内会改变相应PLC类型的S7协议LocalTSAP, RemoteTSAP等 - - - - - - - 当前PLC类型 - - - - - - - - - - - - - - - 远程TSAP,需重新连接 - - - - - 本地TSAP,需重新连接 - - - - - PDULength - - - - - 机架号,需重新连接 - - - - - 槽号,需重新连接 - - - - - - - - 读取日期 - - - - - - 读取时间 - - - - - - 读取变长字符串 - - - - - - - - - - - - - - - - - - - 写入日期 - - - - - - 写入时间 - - - - - - SiemensS7PLCDataHandleAdapter - - - - - - - - - - - - - - - - diff --git a/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesSerialSessionBase.cs b/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesSerialSessionBase.cs index 2465bf681..851468567 100644 --- a/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesSerialSessionBase.cs +++ b/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesSerialSessionBase.cs @@ -24,7 +24,7 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase public ReadWriteDevicesSerialSessionBase(SerialSession serialSession) { SerialSession = serialSession; - WaitingClientEx = SerialSession.GetWaitingClientEx(new() { BreakTrigger = true }); + WaitingClientEx = SerialSession.GetWaitingClient(new() { ThrowBreakException = true }); SerialSession.Received -= Received; SerialSession.Connecting -= Connecting; SerialSession.Connected -= Connected; @@ -40,21 +40,15 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase /// /// 接收解析 /// - protected virtual void Received(ByteBlock byteBlock, IRequestInfo requestInfo) + /// + /// + /// + protected virtual Task Received(SerialSession client, ReceivedDataEventArgs e) { + return EasyTask.CompletedTask; + } + - } - private void Received(SerialSession client, ByteBlock byteBlock, IRequestInfo requestInfo) - { - try - { - Received(byteBlock, requestInfo); - } - catch (Exception ex) - { - Logger.Exception(this, ex); - } - } /// /// 串口管理对象 @@ -102,8 +96,8 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase { try { - waitingOptions ??= new WaitingOptions { BreakTrigger = true, ThrowBreakException = true, AdapterFilter = AdapterFilter.NoneAll }; - ResponsedData result = SerialSession.GetWaitingClientEx(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); + waitingOptions ??= new WaitingOptions { ThrowBreakException = true }; + ResponsedData result = SerialSession.GetWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); return OperResult.CreateSuccessResult(result.Data); } catch (Exception ex) @@ -117,8 +111,8 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase { try { - waitingOptions ??= new WaitingOptions { ThrowBreakException = true, AdapterFilter = AdapterFilter.NoneAll }; - ResponsedData result = await SerialSession.GetWaitingClientEx(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); + waitingOptions ??= new WaitingOptions { ThrowBreakException = true }; + ResponsedData result = await SerialSession.GetWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); return OperResult.CreateSuccessResult(result.Data); } catch (Exception ex) @@ -132,24 +126,34 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase { return SerialSession.SerialProperty.ToString(); } - private void Connected(ISerialSession client, ConnectedEventArgs e) + /// + /// Connected + /// + /// + /// + /// + protected virtual Task Connected(ISerialSession client, ConnectedEventArgs e) { Logger?.Debug(client.SerialProperty.ToString() + "连接成功"); + SetDataAdapter(); + return EasyTask.CompletedTask; } - private void Connecting(ISerialSession client, SerialConnectingEventArgs e) + private Task Connecting(ISerialSession client, SerialConnectingEventArgs e) { Logger?.Debug(client.SerialProperty.ToString() + "正在连接"); - SetDataAdapter(); + return EasyTask.CompletedTask; } - private void Disconnected(ISerialSessionBase client, DisconnectEventArgs e) + private Task Disconnected(ISerialSessionBase client, DisconnectEventArgs e) { Logger?.Debug(client.SerialProperty.ToString() + "断开连接-" + e.Message); + return EasyTask.CompletedTask; } - private void Disconnecting(ISerialSessionBase client, DisconnectEventArgs e) + private Task Disconnecting(ISerialSessionBase client, DisconnectEventArgs e) { Logger?.Debug(client.SerialProperty.ToString() + "正在主动断开连接-" + e.Message); + return EasyTask.CompletedTask; } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesTcpClientBase.cs b/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesTcpClientBase.cs index b318277ae..b91a8f44e 100644 --- a/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesTcpClientBase.cs +++ b/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesTcpClientBase.cs @@ -23,7 +23,7 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase public ReadWriteDevicesTcpClientBase(TcpClient tcpClient) { TcpClient = tcpClient; - WaitingClientEx = TcpClient.GetWaitingClientEx(new() { BreakTrigger = true }); + WaitingClientEx = TcpClient.GetWaitingClient(new() { ThrowBreakException = true }); TcpClient.Connecting -= Connecting; TcpClient.Connected -= Connected; TcpClient.Disconnecting -= Disconnecting; @@ -59,7 +59,7 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase /// public override Task ConnectAsync(CancellationToken cancellationToken) { - return TcpClient.ConnectAsync(ConnectTimeOut); + return TcpClient.ConnectAsync(ConnectTimeOut, cancellationToken); } /// @@ -86,8 +86,8 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase { try { - waitingOptions ??= new WaitingOptions { BreakTrigger = true, ThrowBreakException = true, AdapterFilter = AdapterFilter.NoneAll }; - ResponsedData result = TcpClient.GetWaitingClientEx(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); + waitingOptions ??= new WaitingOptions { ThrowBreakException = true, }; + ResponsedData result = TcpClient.GetWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); return OperResult.CreateSuccessResult(result.Data); } catch (Exception ex) @@ -101,8 +101,8 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase { try { - waitingOptions ??= new WaitingOptions { ThrowBreakException = true, AdapterFilter = AdapterFilter.NoneAll }; - ResponsedData result = await TcpClient.GetWaitingClientEx(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); + waitingOptions ??= new WaitingOptions { ThrowBreakException = true }; + ResponsedData result = await TcpClient.GetWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); return OperResult.CreateSuccessResult(result.Data); } catch (Exception ex) @@ -116,24 +116,34 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase { return TcpClient.RemoteIPHost.ToString(); } - private void Connected(ITcpClient client, ConnectedEventArgs e) + /// + /// Connected + /// + /// + /// + /// + protected virtual Task Connected(ITcpClient client, ConnectedEventArgs e) { Logger?.Debug(client.RemoteIPHost.ToString() + "连接成功"); + SetDataAdapter(); + return EasyTask.CompletedTask; } - private void Connecting(ITcpClient client, ConnectingEventArgs e) + private Task Connecting(ITcpClient client, ConnectingEventArgs e) { Logger?.Debug(client.RemoteIPHost.ToString() + "正在连接"); - SetDataAdapter(); + return EasyTask.CompletedTask; } - private void Disconnected(ITcpClientBase client, DisconnectEventArgs e) + private Task Disconnected(ITcpClientBase client, DisconnectEventArgs e) { Logger?.Debug(client.IP + ":" + client.Port + "断开连接-" + e.Message); + return EasyTask.CompletedTask; } - private void Disconnecting(ITcpClientBase client, DisconnectEventArgs e) + private Task Disconnecting(ITcpClientBase client, DisconnectEventArgs e) { Logger?.Debug(client.IP + ":" + client.Port + "正在主动断开连接-" + e.Message); + return EasyTask.CompletedTask; } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesTcpServerBase.cs b/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesTcpServerBase.cs index cbb282acf..0c556764f 100644 --- a/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesTcpServerBase.cs +++ b/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesTcpServerBase.cs @@ -77,57 +77,54 @@ public abstract class ReadWriteDevicesTcpServerBase : ReadWriteDevicesBase if (CascadeDisposal) TcpService.SafeDispose(); } + /// + /// 接收解析 + /// + /// + /// + /// + protected virtual Task Received(SocketClient client, ReceivedDataEventArgs e) + { + return EasyTask.CompletedTask; + } /// public override string ToString() { return TcpService.ServerName; } - /// - /// 接收解析 - /// - protected virtual void Received(SocketClient client, IRequestInfo requestInfo) - { - } /// /// /// /// /// - protected virtual void Connected(SocketClient client, ConnectedEventArgs e) + protected virtual Task Connected(SocketClient client, ConnectedEventArgs e) { Logger?.Debug(client.IP + ":" + client.Port + "连接成功"); + return EasyTask.CompletedTask; } /// - protected virtual void Connecting(SocketClient client, ConnectingEventArgs e) + protected virtual Task Connecting(SocketClient client, ConnectingEventArgs e) { Logger?.Debug(client.IP + ":" + client.Port + "正在连接"); SetDataAdapter(client); + return EasyTask.CompletedTask; } /// - protected virtual void Disconnected(ITcpClientBase client, DisconnectEventArgs e) + protected virtual Task Disconnected(ITcpClientBase client, DisconnectEventArgs e) { Logger?.Debug(client.IP + ":" + client.Port + "断开连接-" + e.Message); + return EasyTask.CompletedTask; } /// - protected virtual void Disconnecting(ITcpClientBase client, DisconnectEventArgs e) + protected virtual Task Disconnecting(ITcpClientBase client, DisconnectEventArgs e) { Logger?.Debug(client.IP + ":" + client.Port + "正在主动断开连接-" + e.Message); + return EasyTask.CompletedTask; } - private void Received(SocketClient client, ByteBlock byteBlock, IRequestInfo requestInfo) - { - try - { - Received(client, requestInfo); - } - catch (Exception ex) - { - Logger.Exception(this, ex); - } - } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesUdpSessionBase.cs b/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesUdpSessionBase.cs index b10cba786..b51d372a7 100644 --- a/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesUdpSessionBase.cs +++ b/framework/Foundation/ThingsGateway.Foundation/Foundation/BaseReadWrite/ReadWriteDevicesUdpSessionBase.cs @@ -22,7 +22,7 @@ public abstract class ReadWriteDevicesUdpSessionBase : ReadWriteDevicesBase { UdpSession = udpSession; SetDataAdapter(); - WaitingClientEx = UdpSession.GetWaitingClientEx(new() { BreakTrigger = true }); + WaitingClientEx = UdpSession.GetWaitingClient(new() { ThrowBreakException = true }); } /// @@ -64,8 +64,8 @@ public abstract class ReadWriteDevicesUdpSessionBase : ReadWriteDevicesBase { try { - waitingOptions ??= new WaitingOptions { ThrowBreakException = true, AdapterFilter = AdapterFilter.NoneAll }; - ResponsedData result = UdpSession.GetWaitingClientEx(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); + waitingOptions ??= new WaitingOptions { ThrowBreakException = true }; + ResponsedData result = UdpSession.GetWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken); return OperResult.CreateSuccessResult(result.Data); } catch (Exception ex) @@ -79,8 +79,8 @@ public abstract class ReadWriteDevicesUdpSessionBase : ReadWriteDevicesBase { try { - waitingOptions ??= new WaitingOptions { ThrowBreakException = true, AdapterFilter = AdapterFilter.NoneAll }; - ResponsedData result = await UdpSession.GetWaitingClientEx(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); + waitingOptions ??= new WaitingOptions { ThrowBreakException = true }; + ResponsedData result = await UdpSession.GetWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken); return OperResult.CreateSuccessResult(result.Data); } catch (Exception ex) diff --git a/framework/Foundation/ThingsGateway.Foundation/Foundation/DataHandleAdapter/ReadWriteDevicesTcpDataHandleAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/Foundation/DataHandleAdapter/ReadWriteDevicesTcpDataHandleAdapter.cs index 77fed8c95..0d170183f 100644 --- a/framework/Foundation/ThingsGateway.Foundation/Foundation/DataHandleAdapter/ReadWriteDevicesTcpDataHandleAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/Foundation/DataHandleAdapter/ReadWriteDevicesTcpDataHandleAdapter.cs @@ -143,6 +143,21 @@ public abstract class ReadWriteDevicesTcpDataHandleAdapter : CustomDat GoSend(bytes, 0, bytes.Length); Logger?.Trace($"{FoundationConst.LogMessageHeader}{ToString()}- 发送:{Request.SendBytes.ToHexString(' ')}"); } + /// + /// 发送方法,会重新建立 + /// + protected async Task GoSendAsync(byte[] item) + { + byte[] bytes; + if (IsSendPackCommand) + bytes = PackCommand(item); + else + bytes = item; + Request = GetInstance(); + Request.SendBytes = bytes; + await GoSendAsync(bytes, 0, bytes.Length); + Logger?.Trace($"{FoundationConst.LogMessageHeader}{ToString()}- 发送:{Request.SendBytes.ToHexString(' ')}"); + } /// protected override void PreviewSend(byte[] buffer, int offset, int length) @@ -150,6 +165,12 @@ public abstract class ReadWriteDevicesTcpDataHandleAdapter : CustomDat GoSend(buffer); } + /// + protected override Task PreviewSendAsync(byte[] buffer, int offset, int length) + { + return GoSendAsync(buffer); + } + /// /// 报文拆包 /// diff --git a/framework/Foundation/ThingsGateway.Foundation/Foundation/DataHandleAdapter/ReadWriteDevicesUdpDataHandleAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/Foundation/DataHandleAdapter/ReadWriteDevicesUdpDataHandleAdapter.cs index 78783d61b..0d9449275 100644 --- a/framework/Foundation/ThingsGateway.Foundation/Foundation/DataHandleAdapter/ReadWriteDevicesUdpDataHandleAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/Foundation/DataHandleAdapter/ReadWriteDevicesUdpDataHandleAdapter.cs @@ -128,11 +128,6 @@ public abstract class ReadWriteDevicesUdpDataHandleAdapter : UdpDataHa { throw new System.NotImplementedException();//因为设置了不支持拼接发送,所以该方法可以不实现。 } - /// - protected override void PreviewSend(IRequestInfo requestInfo) - { - throw new System.NotImplementedException();//因为设置了不支持,所以该方法可以不实现。 - } /// protected override void Reset() diff --git a/framework/Foundation/ThingsGateway.Foundation/ThingsGateway.Foundation.csproj b/framework/Foundation/ThingsGateway.Foundation/ThingsGateway.Foundation.csproj index f29b9f7bf..78cf7d45d 100644 --- a/framework/Foundation/ThingsGateway.Foundation/ThingsGateway.Foundation.csproj +++ b/framework/Foundation/ThingsGateway.Foundation/ThingsGateway.Foundation.csproj @@ -19,5 +19,20 @@ + + + + + + + + + + + + + + + diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Common/DisposableObject.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Common/DisposableObject.cs index 6cd075fe5..b51f652d0 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Common/DisposableObject.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Common/DisposableObject.cs @@ -30,7 +30,7 @@ namespace ThingsGateway.Foundation.Core /// 具有释放的对象。 /// 并未实现析构函数相关。 /// - public class DisposableObject : IDisposable + public partial class DisposableObject : IDisposable { /// /// 判断是否已释放。 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/DataHandlingAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/DataHandlingAdapter.cs index cf39ca15b..fad43c73a 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/DataHandlingAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/DataHandlingAdapter.cs @@ -69,14 +69,6 @@ namespace ThingsGateway.Foundation.Core this.Owner = owner; } - /// - /// 发送数据的切入点,该方法由框架自动调用。 - /// - /// - public void SendInput(IRequestInfo requestInfo) - { - this.PreviewSend(requestInfo); - } /// /// 在解析时发生错误。 @@ -96,11 +88,6 @@ namespace ThingsGateway.Foundation.Core } } - /// - /// 当发送数据前预先处理数据 - /// - /// - protected abstract void PreviewSend(IRequestInfo requestInfo); /// /// 重置解析器到初始状态,一般在被触发时,由返回值指示是否调用。 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/NormalDataHandlingAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/NormalDataHandlingAdapter.cs index e66b81f17..b6e092a35 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/NormalDataHandlingAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/NormalDataHandlingAdapter.cs @@ -78,6 +78,24 @@ namespace ThingsGateway.Foundation.Core throw new System.NotImplementedException(); } + /// + protected override Task PreviewSendAsync(IRequestInfo requestInfo) + { + throw new NotImplementedException(); + } + + /// + protected override Task PreviewSendAsync(byte[] buffer, int offset, int length) + { + return this.GoSendAsync(buffer, offset, length); + } + + /// + protected override Task PreviewSendAsync(IList> transferBytes) + { + throw new NotImplementedException(); + } + /// /// /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/FixedHeaderPackageAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/FixedHeaderPackageAdapter.cs index b1e39dbb5..b54bcb721 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/FixedHeaderPackageAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/FixedHeaderPackageAdapter.cs @@ -242,6 +242,131 @@ namespace ThingsGateway.Foundation.Core throw new NotImplementedException(); } + /// + protected override Task PreviewSendAsync(IRequestInfo requestInfo) + { + throw new NotImplementedException(); + } + + /// + protected override async Task PreviewSendAsync(byte[] buffer, int offset, int length) + { + if (length < this.MinPackageSize) + { + throw new Exception("发送数据小于设定值,相同解析器可能无法收到有效数据,已终止发送"); + } + + if (length > this.MaxPackageSize) + { + throw new Exception("发送数据大于设定值,相同解析器可能无法收到有效数据,已终止发送"); + } + + ByteBlock byteBlock = null; + byte[] lenBytes = null; + + switch (this.FixedHeaderType) + { + case FixedHeaderType.Byte: + { + var dataLen = (byte)(length - offset); + byteBlock = new ByteBlock(dataLen + 1); + lenBytes = new byte[] { dataLen }; + break; + } + case FixedHeaderType.Ushort: + { + var dataLen = (ushort)(length - offset); + byteBlock = new ByteBlock(dataLen + 2); + lenBytes = TouchSocketBitConverter.Default.GetBytes(dataLen); + break; + } + case FixedHeaderType.Int: + { + var dataLen = length - offset; + byteBlock = new ByteBlock(dataLen + 4); + lenBytes = TouchSocketBitConverter.Default.GetBytes(dataLen); + break; + } + } + + try + { + byteBlock.Write(lenBytes); + byteBlock.Write(buffer, offset, length); + await this.GoSendAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + finally + { + byteBlock.Dispose(); + } + } + + /// + protected override async Task PreviewSendAsync(IList> transferBytes) + { + if (transferBytes.Count == 0) + { + return; + } + + var length = 0; + foreach (var item in transferBytes) + { + length += item.Count; + } + + if (length < this.MinPackageSize) + { + throw new Exception("发送数据小于设定值,相同解析器可能无法收到有效数据,已终止发送"); + } + + if (length > this.MaxPackageSize) + { + throw new Exception("发送数据大于设定值,相同解析器可能无法收到有效数据,已终止发送"); + } + + ByteBlock byteBlock = null; + byte[] lenBytes = null; + + switch (this.FixedHeaderType) + { + case FixedHeaderType.Byte: + { + var dataLen = (byte)length; + byteBlock = new ByteBlock(dataLen + 1); + lenBytes = new byte[] { dataLen }; + break; + } + case FixedHeaderType.Ushort: + { + var dataLen = (ushort)length; + byteBlock = new ByteBlock(dataLen + 2); + lenBytes = TouchSocketBitConverter.Default.GetBytes(dataLen); + break; + } + case FixedHeaderType.Int: + { + byteBlock = new ByteBlock(length + 4); + lenBytes = TouchSocketBitConverter.Default.GetBytes(length); + break; + } + } + + try + { + byteBlock.Write(lenBytes); + foreach (var item in transferBytes) + { + byteBlock.Write(item.Array, item.Offset, item.Count); + } + await this.GoSendAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + finally + { + byteBlock.Dispose(); + } + } + /// /// /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/FixedSizePackageAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/FixedSizePackageAdapter.cs index 6dc97ddeb..290d4ade1 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/FixedSizePackageAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/FixedSizePackageAdapter.cs @@ -183,6 +183,70 @@ namespace ThingsGateway.Foundation.Core throw new NotImplementedException(); } + /// + protected override Task PreviewSendAsync(IRequestInfo requestInfo) + { + throw new NotImplementedException(); + } + + /// + protected override async Task PreviewSendAsync(byte[] buffer, int offset, int length) + { + var dataLen = length - offset; + if (dataLen > this.FixedSize) + { + throw new OverlengthException("发送的数据包长度大于FixedSize"); + } + var byteBlock = new ByteBlock(this.FixedSize); + + byteBlock.Write(buffer, offset, length); + for (var i = byteBlock.Pos; i < this.FixedSize; i++) + { + byteBlock.Buffer[i] = 0; + } + byteBlock.SetLength(this.FixedSize); + try + { + await this.GoSendAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + finally + { + byteBlock.Dispose(); + } + } + + /// + protected override async Task PreviewSendAsync(IList> transferBytes) + { + var length = 0; + foreach (var item in transferBytes) + { + length += item.Count; + } + + if (length > this.FixedSize) + { + throw new OverlengthException("发送的数据包长度大于FixedSize"); + } + var byteBlock = new ByteBlock(this.FixedSize); + + foreach (var item in transferBytes) + { + byteBlock.Write(item.Array, item.Offset, item.Count); + } + + Array.Clear(byteBlock.Buffer, byteBlock.Pos, this.FixedSize); + byteBlock.SetLength(this.FixedSize); + try + { + await this.GoSendAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + finally + { + byteBlock.Dispose(); + } + } + /// /// /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/TerminatorPackageAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/TerminatorPackageAdapter.cs index f44b188a2..76b95c5e5 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/TerminatorPackageAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Package/TerminatorPackageAdapter.cs @@ -240,5 +240,64 @@ namespace ThingsGateway.Foundation.Core byteBlock.Dispose(); } } + + /// + protected override Task PreviewSendAsync(IRequestInfo requestInfo) + { + throw new NotImplementedException(); + } + + /// + protected override async Task PreviewSendAsync(byte[] buffer, int offset, int length) + { + if (length > this.MaxPackageSize) + { + throw new Exception("发送的数据长度大于适配器设定的最大值,接收方可能会抛弃。"); + } + var dataLen = length - offset + this.m_terminatorCode.Length; + var byteBlock = new ByteBlock(dataLen); + byteBlock.Write(buffer, offset, length); + byteBlock.Write(this.m_terminatorCode); + + try + { + await this.GoSendAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + finally + { + byteBlock.Dispose(); + } + } + + /// + protected override async Task PreviewSendAsync(IList> transferBytes) + { + var length = 0; + foreach (var item in transferBytes) + { + length += item.Count; + } + if (length > this.MaxPackageSize) + { + throw new Exception("发送的数据长度大于适配器设定的最大值,接收方可能会抛弃。"); + } + var dataLen = length + this.m_terminatorCode.Length; + var byteBlock = new ByteBlock(dataLen); + foreach (var item in transferBytes) + { + byteBlock.Write(item.Array, item.Offset, item.Count); + } + + byteBlock.Write(this.m_terminatorCode); + + try + { + await this.GoSendAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + finally + { + byteBlock.Dispose(); + } + } } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/SingleStreamAdapterOption.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/SingleStreamAdapterOption.cs index ef58c32b1..0efd7ee87 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/SingleStreamAdapterOption.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/SingleStreamAdapterOption.cs @@ -10,6 +10,19 @@ //------------------------------------------------------------------------------ #endregion +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// API首页:http://rrqm_home.gitee.io/touchsocket/ +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + namespace ThingsGateway.Foundation.Core { /// @@ -37,4 +50,4 @@ namespace ThingsGateway.Foundation.Core /// public bool? UpdateCacheTimeWhenRev { get; set; } } -} +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/SingleStreamDataHandlingAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/SingleStreamDataHandlingAdapter.cs index 405ea2591..d296be02b 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/SingleStreamDataHandlingAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/SingleStreamDataHandlingAdapter.cs @@ -36,9 +36,9 @@ namespace ThingsGateway.Foundation.Core public TimeSpan CacheTimeout { get; set; } = TimeSpan.FromSeconds(1); /// - /// 是否启用缓存超时。默认true。 + /// 是否启用缓存超时。默认false。 /// - public bool CacheTimeoutEnable { get; set; } = true; + public bool CacheTimeoutEnable { get; set; } = false; /// /// 当接收数据处理完成后,回调该函数执行接收 @@ -46,10 +46,15 @@ namespace ThingsGateway.Foundation.Core public Action ReceivedCallBack { get; set; } /// - /// 当接收数据处理完成后,回调该函数执行发送 + /// 当发送数据处理完成后,回调该函数执行发送 /// public Action SendCallBack { get; set; } + /// + /// 当发送数据处理完成后,回调该函数执行异步发送 + /// + public Func SendAsyncCallBack { get; set; } + /// /// 是否在收到数据时,即刷新缓存时间。默认true。 /// @@ -64,6 +69,12 @@ namespace ThingsGateway.Foundation.Core /// protected DateTime LastCacheTime { get; set; } + /// + public override bool CanSendRequestInfo => false; + + /// + public override bool CanSplicingSend => false; + /// /// 收到数据的切入点,该方法由框架自动调用。 /// @@ -80,6 +91,17 @@ namespace ThingsGateway.Foundation.Core } } + #region SendInput + + /// + /// 发送数据的切入点,该方法由框架自动调用。 + /// + /// + public void SendInput(IRequestInfo requestInfo) + { + this.PreviewSend(requestInfo); + } + /// /// 发送数据的切入点,该方法由框架自动调用。 /// @@ -100,6 +122,98 @@ namespace ThingsGateway.Foundation.Core this.PreviewSend(transferBytes); } + /// + /// 发送数据的切入点,该方法由框架自动调用。 + /// + /// + /// + public Task SendInputAsync(IRequestInfo requestInfo) + { + return this.PreviewSendAsync(requestInfo); + } + + /// + /// 发送数据的切入点,该方法由框架自动调用。 + /// + /// + /// + /// + public Task SendInputAsync(byte[] buffer, int offset, int length) + { + return this.PreviewSendAsync(buffer, offset, length); + } + + /// + /// 发送数据的切入点,该方法由框架自动调用。 + /// + /// + public Task SendInputAsync(IList> transferBytes) + { + return this.PreviewSendAsync(transferBytes); + } + + /// + /// 当发送数据前预先处理数据 + /// + /// + protected virtual void PreviewSend(IRequestInfo requestInfo) + { + throw new NotImplementedException(); + } + + /// + /// 当发送数据前预先处理数据 + /// + /// 数据 + /// 偏移 + /// 长度 + protected virtual void PreviewSend(byte[] buffer, int offset, int length) + { + this.GoSend(buffer, offset, length); + } + + /// + /// 组合发送预处理数据, + /// 当属性SplicingSend实现为True时,系统才会调用该方法。 + /// + /// 代发送数据组合 + protected virtual void PreviewSend(IList> transferBytes) + { + throw new NotImplementedException(); + } + + /// + /// 当发送数据前预先处理数据 + /// + /// + protected virtual Task PreviewSendAsync(IRequestInfo requestInfo) + { + throw new NotImplementedException(); + } + + /// + /// 当发送数据前预先处理数据 + /// + /// 数据 + /// 偏移 + /// 长度 + protected virtual Task PreviewSendAsync(byte[] buffer, int offset, int length) + { + return this.GoSendAsync(buffer, offset, length); + } + + /// + /// 组合发送预处理数据, + /// 当属性SplicingSend实现为True时,系统才会调用该方法。 + /// + /// 代发送数据组合 + protected virtual Task PreviewSendAsync(IList> transferBytes) + { + throw new NotImplementedException(); + } + + #endregion SendInput + /// /// /// @@ -130,27 +244,24 @@ namespace ThingsGateway.Foundation.Core this.SendCallBack.Invoke(buffer, offset, length); } + /// + /// 异步发送已经经过预先处理后的数据 + /// + /// + /// + /// + /// + protected Task GoSendAsync(byte[] buffer, int offset, int length) + { + return this.SendAsyncCallBack.Invoke(buffer, offset, length); + } + /// /// 当接收到数据后预先处理数据,然后调用处理数据 /// /// protected abstract void PreviewReceived(ByteBlock byteBlock); - /// - /// 当发送数据前预先处理数据 - /// - /// 数据 - /// 偏移 - /// 长度 - protected abstract void PreviewSend(byte[] buffer, int offset, int length); - - /// - /// 组合发送预处理数据, - /// 当属性SplicingSend实现为True时,系统才会调用该方法。 - /// - /// 代发送数据组合 - protected abstract void PreviewSend(IList> transferBytes); - /// /// 重置解析器到初始状态,一般在被触发时,由返回值指示是否调用。 /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Test/SingleStreamDataAdapterTester.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Test/SingleStreamDataAdapterTester.cs index 4596419f0..aa9e183f8 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Test/SingleStreamDataAdapterTester.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Test/SingleStreamDataAdapterTester.cs @@ -63,9 +63,11 @@ namespace ThingsGateway.Foundation.Core /// public static SingleStreamDataAdapterTester CreateTester(SingleStreamDataHandlingAdapter adapter, int bufferLength = 1024, Action receivedCallBack = default) { - var tester = new SingleStreamDataAdapterTester(); - tester.m_adapter = adapter; - tester.m_bufferLength = bufferLength; + var tester = new SingleStreamDataAdapterTester + { + m_adapter = adapter, + m_bufferLength = bufferLength + }; adapter.SendCallBack = tester.SendCallback; adapter.ReceivedCallBack = tester.OnReceived; tester.m_receivedCallBack = receivedCallBack; @@ -129,7 +131,7 @@ namespace ThingsGateway.Foundation.Core { while (!this.m_dispose) { - if (this.tryGet(out var byteBlocks)) + if (this.TryGet(out var byteBlocks)) { foreach (var block in byteBlocks) { @@ -163,7 +165,7 @@ namespace ThingsGateway.Foundation.Core this.m_asyncBytes.Enqueue(asyncByte); } - private bool tryGet(out List byteBlocks) + private bool TryGet(out List byteBlocks) { byteBlocks = new List(); ByteBlock block = null; diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/Container.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/Container.cs index f864e8a0a..f6def309e 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/Container.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/Container.cs @@ -70,10 +70,9 @@ namespace ThingsGateway.Foundation.Core /// /// /// - /// /// /// - public object Resolve(Type fromType, object[] ps = null, string key = "") + public object Resolve(Type fromType, string key = "") { if (fromType == typeof(IContainer)) { @@ -108,11 +107,11 @@ namespace ThingsGateway.Foundation.Core { if (descriptor.ToType.IsGenericType) { - return (descriptor.ToInstance = this.Create(descriptor, descriptor.ToType.MakeGenericType(fromType.GetGenericArguments()), ps)); + return (descriptor.ToInstance = this.Create(descriptor, descriptor.ToType.MakeGenericType(fromType.GetGenericArguments()))); } else { - return (descriptor.ToInstance = this.Create(descriptor, descriptor.ToType, ps)); + return descriptor.ToInstance = this.Create(descriptor, descriptor.ToType); } } } @@ -120,11 +119,11 @@ namespace ThingsGateway.Foundation.Core if (descriptor.ToType.IsGenericType) { - return this.Create(descriptor, descriptor.ToType.MakeGenericType(fromType.GetGenericArguments()), ps); + return this.Create(descriptor, descriptor.ToType.MakeGenericType(fromType.GetGenericArguments())); } else { - return this.Create(descriptor, descriptor.ToType, ps); + return this.Create(descriptor, descriptor.ToType); } } } @@ -143,10 +142,10 @@ namespace ThingsGateway.Foundation.Core } lock (descriptor) { - return descriptor.ToInstance ??= this.Create(descriptor, descriptor.ToType, ps); + return descriptor.ToInstance ??= this.Create(descriptor, descriptor.ToType); } } - return this.Create(descriptor, descriptor.ToType, ps); + return this.Create(descriptor, descriptor.ToType); } else { @@ -172,7 +171,7 @@ namespace ThingsGateway.Foundation.Core this.m_registrations.TryRemove(k, out _); } - private object Create(DependencyDescriptor descriptor, Type toType, object[] ops) + private object Create(DependencyDescriptor descriptor, Type toType) { var ctor = toType.GetConstructors().FirstOrDefault(x => x.IsDefined(typeof(DependencyInjectAttribute), true)); if (ctor is null) @@ -198,28 +197,21 @@ namespace ThingsGateway.Foundation.Core { for (var i = 0; i < parameters.Length; i++) { - if (ops != null && ops.Length - 1 >= i) + if (parameters[i].ParameterType.IsPrimitive || parameters[i].ParameterType == typeof(string)) { - ps[i] = ops[i]; + ps[i] = parameters[i].HasDefaultValue ? parameters[i].DefaultValue : default; } else { - if (parameters[i].ParameterType.IsPrimitive || parameters[i].ParameterType == typeof(string)) + if (parameters[i].IsDefined(typeof(DependencyInjectAttribute), true)) { - ps[i] = parameters[i].HasDefaultValue ? parameters[i].DefaultValue : default; + var attribute = parameters[i].GetCustomAttribute(); + var type = attribute.Type ?? parameters[i].ParameterType; + ps[i] = this.Resolve(type, attribute.Key); } else { - if (parameters[i].IsDefined(typeof(DependencyInjectAttribute), true)) - { - var attribute = parameters[i].GetCustomAttribute(); - var type = attribute.Type ?? parameters[i].ParameterType; - ps[i] = this.Resolve(type, default, attribute.Key); - } - else - { - ps[i] = this.Resolve(parameters[i].ParameterType, null); - } + ps[i] = this.Resolve(parameters[i].ParameterType, null); } } } @@ -239,7 +231,7 @@ namespace ThingsGateway.Foundation.Core { var attribute = item.GetCustomAttribute(); var type = attribute.Type ?? item.PropertyType; - obj = this.Resolve(type, default, attribute.Key); + obj = this.Resolve(type, attribute.Key); } else { @@ -259,28 +251,21 @@ namespace ThingsGateway.Foundation.Core ps = new object[parameters.Length]; for (var i = 0; i < ps.Length; i++) { - if (ops != null && ops.Length - 1 >= i) + if (parameters[i].ParameterType.IsPrimitive || parameters[i].ParameterType == typeof(string)) { - ps[i] = ops[i]; + ps[i] = parameters[i].HasDefaultValue ? parameters[i].DefaultValue : default; } else { - if (parameters[i].ParameterType.IsPrimitive || parameters[i].ParameterType == typeof(string)) + if (parameters[i].IsDefined(typeof(DependencyInjectAttribute), true)) { - ps[i] = parameters[i].HasDefaultValue ? parameters[i].DefaultValue : default; + var attribute = parameters[i].GetCustomAttribute(); + var type = attribute.Type ?? parameters[i].ParameterType; + ps[i] = this.Resolve(type, attribute.Key); } else { - if (parameters[i].IsDefined(typeof(DependencyInjectAttribute), true)) - { - var attribute = parameters[i].GetCustomAttribute(); - var type = attribute.Type ?? parameters[i].ParameterType; - ps[i] = this.Resolve(type, default, attribute.Key); - } - else - { - ps[i] = this.Resolve(parameters[i].ParameterType, null); - } + ps[i] = this.Resolve(parameters[i].ParameterType, null); } } } diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/ContainerExtension.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/ContainerExtension.cs index 1b499954d..255261124 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/ContainerExtension.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/ContainerExtension.cs @@ -381,35 +381,11 @@ namespace ThingsGateway.Foundation.Core /// /// /// - /// /// /// - public static T Resolve(this IContainer container, object[] ps, string key = "") + public static T Resolve(this IContainer container, string key = "") { - return (T)container.Resolve(typeof(T), ps, key); - } - - /// - /// 创建类型对应的实例 - /// - /// - /// - /// - public static T Resolve(this IContainer container) - { - return Resolve(container, null); - } - - /// - /// 创建类型对应的实例 - /// - /// - /// - /// - /// - public static T Resolve(this IContainer container, string key) - { - return Resolve(container, null, key); + return (T)container.Resolve(typeof(T), key); } /// @@ -461,7 +437,7 @@ namespace ThingsGateway.Foundation.Core { var attribute = parameters[i].GetCustomAttribute(); var type = attribute.Type ?? parameters[i].ParameterType; - ps[i] = container.Resolve(type, default, attribute.Key); + ps[i] = container.Resolve(type, attribute.Key); } else { @@ -489,47 +465,22 @@ namespace ThingsGateway.Foundation.Core return (T)ResolveWithoutRoot(container, typeof(T)); } - /// - /// 尝试创建类型对应的实例,如果类型没有注册,则会返回null或者默认值类型。 - /// - /// - /// - /// - /// - /// - public static T TryResolve(this IContainer container, object[] ps, string key = "") - { - return (T)container.TryResolve(typeof(T), ps, key); - } - /// /// 尝试创建类型对应的实例,如果类型没有注册,则会返回null或者默认值类型。 /// /// /// - /// /// /// - public static object TryResolve(this IContainer container, Type fromType, object[] ps, string key = "") + public static object TryResolve(this IContainer container, Type fromType, string key = "") { if (container.IsRegistered(fromType)) { - return container.Resolve(fromType, ps, key); + return container.Resolve(fromType, key); } return default; } - /// - /// 尝试创建类型对应的实例,如果类型没有注册,则会返回null或者默认值类型。 - /// - /// - /// - /// - public static T TryResolve(this IContainer container) - { - return TryResolve(container, null); - } - /// /// 尝试创建类型对应的实例,如果类型没有注册,则会返回null或者默认值类型。 /// @@ -537,9 +488,9 @@ namespace ThingsGateway.Foundation.Core /// /// /// - public static T TryResolve(this IContainer container, string key) + public static T TryResolve(this IContainer container, string key = "") { - return TryResolve(container, null, key); + return (T)TryResolve(container, typeof(T), key); } #endregion Resolve diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/IContainer.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/IContainer.cs index b1b4c276b..9469ba4db 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/IContainer.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/IContainer.cs @@ -34,10 +34,9 @@ namespace ThingsGateway.Foundation.Core /// 创建目标类型的对应实例。 /// /// - /// /// /// - object Resolve(Type fromType, object[] ps = null, string key = ""); + object Resolve(Type fromType, string key = ""); /// /// 判断某类型是否已经注册 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/ManualContainer.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/ManualContainer.cs index 4bb373698..ac690cbb2 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/ManualContainer.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Dependency/ManualContainer.cs @@ -67,13 +67,13 @@ namespace ThingsGateway.Foundation.Core /// /// - public object Resolve(Type fromType, object[] ps = null, string key = "") + public object Resolve(Type fromType, string key = "") { if (fromType.FullName == "ThingsGateway.Foundation.Core.IContainer") { return this; } - if (this.TryResolve(fromType, out var instance, ps, key)) + if (this.TryResolve(fromType, out var instance, key)) { return instance; } @@ -100,10 +100,9 @@ namespace ThingsGateway.Foundation.Core /// /// /// - /// /// /// - protected virtual bool TryResolve(Type fromType, out object instance, object[] ps = null, string key = "") + protected virtual bool TryResolve(Type fromType, out object instance, string key = "") { if (key.IsNullOrEmpty()) { diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Extensions/TaskExtension.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Extensions/TaskExtension.cs index 347fdce9d..12e647418 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Extensions/TaskExtension.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Extensions/TaskExtension.cs @@ -22,6 +22,7 @@ // 感谢您的下载和使用 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ + namespace ThingsGateway.Foundation.Core { /// @@ -29,5 +30,24 @@ namespace ThingsGateway.Foundation.Core /// public static class TaskExtension { + /// + /// 同步获取配置ConfigureAwait为false时的结果。 + /// + /// + /// + /// + public static T GetFalseAwaitResult(this Task task) + { + return task.ConfigureAwait(false).GetAwaiter().GetResult(); + } + + /// + /// 同步配置ConfigureAwait为false时的执行。 + /// + /// + public static void GetFalseAwaitResult(this Task task) + { + task.ConfigureAwait(false).GetAwaiter().GetResult(); + } } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Plugins/IPlugin.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Plugins/IPlugin.cs index a9560a008..22b6b9c12 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Plugins/IPlugin.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Plugins/IPlugin.cs @@ -30,14 +30,6 @@ namespace ThingsGateway.Foundation.Core /// public interface IPlugin : IDisposable { - /// - /// 插件执行顺序 - /// 该属性值越大,越靠前执行。值相等时,按添加先后顺序 - /// 该属性效果,仅在之前设置有效。 - /// - [Obsolete("该属性已被弃用,插件顺序将直接由添加顺序决定。本设置将在正式版发布时直接删除", true)] - int Order { get; set; } - /// /// 在插件被成功添加在时执行。 /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Plugins/PluginBase.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Plugins/PluginBase.cs index 432213d56..f5bc0520e 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Plugins/PluginBase.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Plugins/PluginBase.cs @@ -17,9 +17,6 @@ namespace ThingsGateway.Foundation.Core /// public class PluginBase : DisposableObject, IPlugin { - /// - [Obsolete("该属性已被弃用,插件顺序将直接由添加顺序决定。本设置将在正式版发布时直接删除", true)] - public int Order { get; set; } /// protected virtual void Loaded(IPluginsManager pluginsManager) diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Common/Locker.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Threading/Locker.cs similarity index 100% rename from framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Common/Locker.cs rename to framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/Threading/Locker.cs diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpClient.cs index 60849010a..7518df21c 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpClient.cs @@ -184,7 +184,7 @@ namespace ThingsGateway.Foundation.Dmtp } if (!this.Online) { - await base.ConnectAsync(timeout); + await base.ConnectAsync(timeout, token); } var request = new HttpRequest() @@ -223,20 +223,20 @@ namespace ThingsGateway.Foundation.Dmtp } /// - protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + protected override async Task ReceivedData(ReceivedDataEventArgs e) { - if (this.Protocol == DmtpUtility.DmtpProtocol && requestInfo is DmtpMessage message) + if (this.Protocol == DmtpUtility.DmtpProtocol && e.RequestInfo is DmtpMessage message) { if (!this.m_smtpActor.InputReceivedData(message)) { if (this.PluginsManager.Enable) { - this.PluginsManager.Raise(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); + await this.PluginsManager.RaiseAsync(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); } } - return false; + return; } - return base.HandleReceivedData(byteBlock, requestInfo); + await base.ReceivedData(e); } /// @@ -251,9 +251,9 @@ namespace ThingsGateway.Foundation.Dmtp } /// - protected override void OnDisconnected(DisconnectEventArgs e) + protected override async Task OnDisconnected(DisconnectEventArgs e) { - base.OnDisconnected(e); + await base.OnDisconnected(e); this.DmtpActor.Close(false, e.Message); } diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpService.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpService.cs index 2d9af9731..fe2bdd436 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpService.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpService.cs @@ -62,10 +62,10 @@ namespace ThingsGateway.Foundation.Dmtp } /// - protected override void OnConnected(TClient socketClient, ConnectedEventArgs e) + protected override async Task OnConnected(TClient socketClient, ConnectedEventArgs e) { socketClient.m_internalOnRpcActorInit = this.PrivateOnRpcActorInit; - base.OnConnected(socketClient, e); + await base.OnConnected(socketClient, e); } private IDmtpActor OnServiceFindDmtpActor(string id) diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpSocketClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpSocketClient.cs index 824b0a719..59f4d7dd8 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpSocketClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Http/HttpDmtpSocketClient.cs @@ -96,42 +96,55 @@ namespace ThingsGateway.Foundation.Dmtp base.Dispose(disposing); } + ///// + //protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + //{ + // if (this.Protocol == DmtpUtility.DmtpProtocol && requestInfo is DmtpMessage message) + // { + // if (!this.m_smtpActor.InputReceivedData(message)) + // { + // if (this.PluginsManager.Enable) + // { + // this.PluginsManager.Raise(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); + // } + // } + + // return false; + // } + // else + // { + // return base.HandleReceivedData(byteBlock, requestInfo); + // } + //} + /// - protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + protected override async Task ReceivedData(ReceivedDataEventArgs e) { - if (this.Protocol == DmtpUtility.DmtpProtocol && requestInfo is DmtpMessage message) + if (this.Protocol == DmtpUtility.DmtpProtocol && e.RequestInfo is DmtpMessage message) { if (!this.m_smtpActor.InputReceivedData(message)) { - if (this.PluginsManager.Enable) - { - this.PluginsManager.Raise(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); - } + await this.PluginsManager.RaiseAsync(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); } - - return false; - } - else - { - return base.HandleReceivedData(byteBlock, requestInfo); } + await base.ReceivedData(e); } /// /// /// /// - protected override void OnDisconnected(DisconnectEventArgs e) + protected override async Task OnDisconnected(DisconnectEventArgs e) { this.DmtpActor?.Close(false, e.Message); - base.OnDisconnected(e); + await base.OnDisconnected(e); } /// /// /// /// - protected override void OnReceivedHttpRequest(HttpRequest request) + protected override async Task OnReceivedHttpRequest(HttpRequest request) { if (request.IsMethod(DmtpUtility.Dmtp) && request.IsUpgrade() && string.Equals(request.Headers.Get(HttpHeaders.Upgrade), DmtpUtility.Dmtp, StringComparison.OrdinalIgnoreCase)) @@ -142,7 +155,7 @@ namespace ThingsGateway.Foundation.Dmtp this.DefaultSend(new HttpResponse().SetStatus(101, "Switching Protocols").BuildAsBytes()); return; } - base.OnReceivedHttpRequest(request); + await base.OnReceivedHttpRequest(request); } private void SetRpcActor(DmtpActor actor) diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpClient.cs index 4f49188f2..1f220a7ae 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpClient.cs @@ -38,23 +38,24 @@ namespace ThingsGateway.Foundation.Dmtp this.Protocol = DmtpUtility.DmtpProtocol; } + /// + public IDmtpActor DmtpActor { get => this.m_smtpActor; } + /// public string Id => this.DmtpActor.Id; #region 字段 + private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); private bool m_allowRoute; private Func m_findDmtpActor; private SealedDmtpActor m_smtpActor; - private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); + #endregion 字段 /// public bool IsHandshaked => this.DmtpActor != null && this.DmtpActor.IsHandshaked; - /// - public IDmtpActor DmtpActor { get => this.m_smtpActor; } - /// /// 发送关闭消息。 /// @@ -153,7 +154,7 @@ namespace ThingsGateway.Foundation.Dmtp } if (!this.Online) { - await base.ConnectAsync(timeout); + await base.ConnectAsync(timeout, token); } await this.m_smtpActor.HandshakeAsync(this.Config.GetValue(DmtpConfigExtension.VerifyTokenProperty), @@ -235,20 +236,6 @@ namespace ThingsGateway.Foundation.Dmtp base.Dispose(disposing); } - /// - protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) - { - var message = (DmtpMessage)requestInfo; - if (!this.m_smtpActor.InputReceivedData(message)) - { - if (this.PluginsManager.Enable) - { - this.PluginsManager.Raise(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); - } - } - return false; - } - /// protected override void LoadConfig(TouchSocketConfig config) { @@ -274,14 +261,31 @@ namespace ThingsGateway.Foundation.Dmtp } /// - protected override void OnDisconnected(DisconnectEventArgs e) + protected override async Task OnDisconnected(DisconnectEventArgs e) { - base.OnDisconnected(e); + await base.OnDisconnected(e); this.DmtpActor.Close(false, e.Message); } + /// + protected override async Task ReceivedData(ReceivedDataEventArgs e) + { + var message = (DmtpMessage)e.RequestInfo; + if (!this.m_smtpActor.InputReceivedData(message)) + { + await this.PluginsManager.RaiseAsync(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); + } + + await base.ReceivedData(e); + } + #region 内部委托绑定 + private void DmtpActorSend(DmtpActor actor, ArraySegment[] transferBytes) + { + base.Send(transferBytes); + } + private void OnDmtpActorClose(DmtpActor actor, string msg) { base.Close(msg); @@ -330,11 +334,6 @@ namespace ThingsGateway.Foundation.Dmtp this.OnRouting(e); } - private void DmtpActorSend(DmtpActor actor, ArraySegment[] transferBytes) - { - base.Send(transferBytes); - } - #endregion 内部委托绑定 #region 事件触发 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpService.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpService.cs index 2d153ac2d..d40d998f0 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpService.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpService.cs @@ -68,14 +68,14 @@ namespace ThingsGateway.Foundation.Dmtp /// /// /// - protected override void OnConnecting(TClient socketClient, ConnectingEventArgs e) + protected override async Task OnConnecting(TClient socketClient, ConnectingEventArgs e) { socketClient.SetDmtpActor(new SealedDmtpActor(this.m_allowRoute) { Id = e.Id, OnFindDmtpActor = this.m_allowRoute ? (this.m_findDmtpActor ?? this.OnServiceFindDmtpActor) : null }); - base.OnConnecting(socketClient, e); + await base.OnConnecting(socketClient, e); } private IDmtpActor OnServiceFindDmtpActor(string id) diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpSocketClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpSocketClient.cs index ee0d3e66e..17708afb4 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpSocketClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/TCP/TcpDmtpSocketClient.cs @@ -40,12 +40,12 @@ namespace ThingsGateway.Foundation.Dmtp this.Protocol = DmtpUtility.DmtpProtocol; } - /// - public bool IsHandshaked => this.DmtpActor != null && this.DmtpActor.IsHandshaked; - /// public IDmtpActor DmtpActor { get => this.m_smtpActor; } + /// + public bool IsHandshaked => this.DmtpActor != null && this.DmtpActor.IsHandshaked; + /// /// 验证超时时间,默认为3000ms /// @@ -201,26 +201,23 @@ namespace ThingsGateway.Foundation.Dmtp } /// - protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + protected override async Task ReceivedData(ReceivedDataEventArgs e) { - var message = (DmtpMessage)requestInfo; + var message = (DmtpMessage)e.RequestInfo; if (!this.m_smtpActor.InputReceivedData(message)) { - if (this.PluginsManager.Enable) - { - this.PluginsManager.Raise(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); - } + await this.PluginsManager.RaiseAsync(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); } - return false; + await base.ReceivedData(e); } /// - protected override void OnConnected(ConnectedEventArgs e) + protected override async Task OnConnected(ConnectedEventArgs e) { this.m_smtpActor.Id = this.Id; - base.OnConnected(e); + await base.OnConnected(e); - Task.Run(async () => + _ = Task.Run(async () => { await Task.Delay(this.VerifyTimeout); if (!this.IsHandshaked) @@ -232,15 +229,10 @@ namespace ThingsGateway.Foundation.Dmtp } /// - protected override void OnDisconnected(DisconnectEventArgs e) + protected override async Task OnDisconnected(DisconnectEventArgs e) { this.DmtpActor.Close(false, e.Message); - base.OnDisconnected(e); - } - - private void ThisOnResetId(DmtpActor rpcActor, WaitSetId waitSetId) - { - this.DirectResetId(waitSetId.NewId); + await base.OnDisconnected(e); } private void ThisDmtpActorOutputSend(DmtpActor actor, ArraySegment[] transferBytes) @@ -248,6 +240,11 @@ namespace ThingsGateway.Foundation.Dmtp base.Send(transferBytes); } + private void ThisOnResetId(DmtpActor rpcActor, WaitSetId waitSetId) + { + this.DirectResetId(waitSetId.NewId); + } + #region 发送 /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Udp/UdpDmtp.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Udp/UdpDmtp.cs index 308d4007e..c5b5bd764 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Udp/UdpDmtp.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/Udp/UdpDmtp.cs @@ -75,20 +75,20 @@ namespace ThingsGateway.Foundation.Dmtp.Rpc } /// - protected override void HandleReceivedData(EndPoint remoteEndPoint, ByteBlock byteBlock, IRequestInfo requestInfo) + protected override async Task ReceivedData(UdpReceivedDataEventArgs e) { - var client = this.PrivateGetUdpDmtpClient(remoteEndPoint); + var client = this.PrivateGetUdpDmtpClient(e.EndPoint); if (client == null) { return; } - var message = DmtpMessage.CreateFrom(byteBlock); + var message = DmtpMessage.CreateFrom(e.ByteBlock); if (!client.InputReceivedData(message)) { if (this.PluginsManager.Enable) { - this.PluginsManager.Raise(nameof(IDmtpReceivedPlugin.OnDmtpReceived), client, new DmtpMessageEventArgs(message)); + await this.PluginsManager.RaiseAsync(nameof(IDmtpReceivedPlugin.OnDmtpReceived), client, new DmtpMessageEventArgs(message)); } } } diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/WebSocket/WebSocketDmtpClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/WebSocket/WebSocketDmtpClient.cs index 93e53db4f..4b7eea4bc 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/WebSocket/WebSocketDmtpClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Components/WebSocket/WebSocketDmtpClient.cs @@ -49,9 +49,7 @@ namespace ThingsGateway.Foundation.Dmtp } #region 字段 -#pragma warning disable CS0414 - private bool m_allowRoute; -#pragma warning restore CS0414 + private ClientWebSocket m_client; private Func m_findDmtpActor; private ValueCounter m_receiveCounter; @@ -236,18 +234,6 @@ namespace ThingsGateway.Foundation.Dmtp this.DmtpActor.ResetId(newId); } - /// - /// 配置 - /// - /// - /// - public IWebSocketDmtpClient Setup(string ipHost) - { - var config = new TouchSocketConfig(); - config.SetRemoteIPHost(new IPHost(ipHost)); - return this.Setup(config); - } - /// /// 配置 /// @@ -297,7 +283,6 @@ namespace ThingsGateway.Foundation.Dmtp if (this.Container.IsRegistered(typeof(IDmtpRouteService))) { - this.m_allowRoute = true; this.m_findDmtpActor = this.Container.Resolve().FindDmtpActor; } } @@ -421,10 +406,7 @@ namespace ThingsGateway.Foundation.Dmtp var message = (DmtpMessage)requestInfo; if (!this.m_dmtpActor.InputReceivedData(message)) { - if (this.PluginsManager.Enable) - { - this.PluginsManager.Raise(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); - } + this.PluginsManager.Raise(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message)); } } @@ -534,5 +516,28 @@ namespace ThingsGateway.Foundation.Dmtp } #endregion 事件触发 + + #region Receiver + + /// + /// 不支持该功能 + /// + /// + /// + public IReceiver CreateReceiver() + { + throw new NotSupportedException("不支持该功能"); + } + + /// + /// 不支持该功能 + /// + /// + public void ClearReceiver() + { + throw new NotSupportedException("不支持该功能"); + } + + #endregion } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Features/FileTransfer/Actor/DmtpFileTransferActor.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Features/FileTransfer/Actor/DmtpFileTransferActor.cs index 80fbc1162..17482521b 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Features/FileTransfer/Actor/DmtpFileTransferActor.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Dmtp/Features/FileTransfer/Actor/DmtpFileTransferActor.cs @@ -1507,7 +1507,7 @@ namespace ThingsGateway.Foundation.Dmtp.FileTransfer waitFileSection.Message = ex.Message; } - waitFileSection.Value.Dispose(); + waitFileSection.Value.SafeDispose(); waitFileSection.Value = default; using (var byteBlock = new ByteBlock()) { diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpClient.cs index 41629af51..2b087df15 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpClient.cs @@ -213,14 +213,11 @@ namespace ThingsGateway.Foundation.Http base.Dispose(disposing); } - /// + /// - /// - /// - /// - protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + protected override Task ReceivedData(ReceivedDataEventArgs e) { - if (requestInfo is HttpResponse response) + if (e.RequestInfo is HttpResponse response) { if (this.m_getContent) { @@ -229,18 +226,18 @@ namespace ThingsGateway.Foundation.Http this.m_waitData.Set(response); } - return false; + return base.ReceivedData(e); } /// /// /// /// - protected override void OnConnecting(ConnectingEventArgs e) + protected override async Task OnConnecting(ConnectingEventArgs e) { this.Protocol = Protocol.Http; this.SetDataHandlingAdapter(new HttpClientDataHandlingAdapter()); - base.OnConnecting(e); + await base.OnConnecting(e); } } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpClientSlim.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpClientSlim.cs index f749387de..81238a441 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpClientSlim.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpClientSlim.cs @@ -20,6 +20,8 @@ using System.Threading.Tasks; using ThingsGateway.Foundation.Core; using ThingsGateway.Foundation.Sockets; +using HttpClient = System.Net.Http.HttpClient; +using HttpMethod = System.Net.Http.HttpMethod; namespace ThingsGateway.Foundation.Http { @@ -100,16 +102,6 @@ namespace ThingsGateway.Foundation.Http return this; } - /// - /// 配置 - /// - /// - /// - public HttpClientSlim Setup(string remoteIPHost) - { - return this.Setup(new TouchSocketConfig().SetRemoteIPHost(remoteIPHost)); - } - private void BuildConfig(TouchSocketConfig config) { this.Config = config; diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpSocketClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpSocketClient.cs index 01514558d..49e1cc224 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpSocketClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Components/HttpSocketClient.cs @@ -39,34 +39,35 @@ namespace ThingsGateway.Foundation.Http } /// - protected override void OnConnecting(ConnectingEventArgs e) + protected override async Task OnConnecting(ConnectingEventArgs e) { this.SetDataHandlingAdapter(new HttpServerDataHandlingAdapter()); - base.OnConnecting(e); + await base.OnConnecting(e); } /// - protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + protected override async Task ReceivedData(ReceivedDataEventArgs e) { - if (requestInfo is HttpRequest request) + if (e.RequestInfo is HttpRequest request) { - this.OnReceivedHttpRequest(request); + await this.OnReceivedHttpRequest(request); } - - return false; + await base.ReceivedData(e); } /// /// 当收到到Http请求时。覆盖父类方法将不会触发插件。 /// - protected virtual void OnReceivedHttpRequest(HttpRequest request) + protected virtual Task OnReceivedHttpRequest(HttpRequest request) { if (this.PluginsManager.GetPluginCount(nameof(IHttpPlugin.OnHttpRequest)) > 0) { var e = new HttpContextEventArgs(new HttpContext(request)); - this.PluginsManager.Raise(nameof(IHttpPlugin.OnHttpRequest), this, e); + return this.PluginsManager.RaiseAsync(nameof(IHttpPlugin.OnHttpRequest), this, e); } + + return EasyTask.CompletedTask; } } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpDeletePlugin.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpDeletePlugin.cs deleted file mode 100644 index 5ac418583..000000000 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpDeletePlugin.cs +++ /dev/null @@ -1,37 +0,0 @@ -#region copyright -//------------------------------------------------------------------------------ -// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 -// 此代码版权(除特别声明外的代码)归作者本人Diego所有 -// 源代码使用协议遵循本仓库的开源协议及附加协议 -// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway -// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway -// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ -// QQ群:605534569 -//------------------------------------------------------------------------------ -#endregion - -namespace ThingsGateway.Foundation.Http -{ - /// - /// IHttpDeletePlugin - /// - [Obsolete("该插件已被弃用,请考虑使用“IHttpPlugin”插件代替使用。本插件将在正式版发布时直接移除。", true)] - public interface IHttpDeletePlugin : IPlugin where TClient : IHttpSocketClient - { - /// - /// 在收到Delete时 - /// - /// - /// - /// - Task OnHttpDelete(TClient client, HttpContextEventArgs e); - } - - /// - /// IHttpDeletePlugin - /// - [Obsolete("该插件已被弃用,请考虑使用“IHttpPlugin”插件代替使用。本插件将在正式版发布时直接移除。", true)] - public interface IHttpDeletePlugin : IHttpDeletePlugin - { - } -} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpGetPlugin.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpGetPlugin.cs deleted file mode 100644 index c186f0721..000000000 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpGetPlugin.cs +++ /dev/null @@ -1,37 +0,0 @@ -#region copyright -//------------------------------------------------------------------------------ -// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 -// 此代码版权(除特别声明外的代码)归作者本人Diego所有 -// 源代码使用协议遵循本仓库的开源协议及附加协议 -// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway -// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway -// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ -// QQ群:605534569 -//------------------------------------------------------------------------------ -#endregion - -namespace ThingsGateway.Foundation.Http -{ - /// - /// IHttpGetPlugin - /// - [Obsolete("该插件已被弃用,请考虑使用“IHttpPlugin”插件代替使用。本插件将在正式版发布时直接移除。", true)] - public interface IHttpGetPlugin : IPlugin where TClient : IHttpSocketClient - { - /// - /// 在收到Get时 - /// - /// - /// - /// - Task OnHttpGet(TClient client, HttpContextEventArgs e); - } - - /// - /// IHttpGetPlugin - /// - [Obsolete("该插件已被弃用,请考虑使用“IHttpPlugin”插件代替使用。本插件将在正式版发布时直接移除。", true)] - public interface IHttpGetPlugin : IHttpGetPlugin - { - } -} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPlugin.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPlugin.cs index 06e577a96..56182f61b 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPlugin.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPlugin.cs @@ -31,8 +31,7 @@ namespace ThingsGateway.Foundation.Http public interface IHttpPlugin : IPlugin where TClient : IHttpSocketClient { /// - /// 在收到Http请求时。注意:此插件的执行在,, - /// ,之前。 + /// 在收到Http请求时 /// /// /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPostPlugin.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPostPlugin.cs deleted file mode 100644 index f88361ea2..000000000 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPostPlugin.cs +++ /dev/null @@ -1,37 +0,0 @@ -#region copyright -//------------------------------------------------------------------------------ -// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 -// 此代码版权(除特别声明外的代码)归作者本人Diego所有 -// 源代码使用协议遵循本仓库的开源协议及附加协议 -// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway -// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway -// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ -// QQ群:605534569 -//------------------------------------------------------------------------------ -#endregion - -namespace ThingsGateway.Foundation.Http -{ - /// - /// IHttpPostPlugin - /// - [Obsolete("该插件已被弃用,请考虑使用“IHttpPlugin”插件代替使用。本插件将在正式版发布时直接移除。", true)] - public interface IHttpPostPlugin : IPlugin where TClient : IHttpSocketClient - { - /// - /// 在收到Post时 - /// - /// - /// - /// - Task OnHttpPost(TClient client, HttpContextEventArgs e); - } - - /// - /// IHttpPostPlugin - /// - [Obsolete("该插件已被弃用,请考虑使用“IHttpPlugin”插件代替使用。本插件将在正式版发布时直接移除。", true)] - public interface IHttpPostPlugin : IHttpPostPlugin - { - } -} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Common/InternalWebSocket.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Common/InternalWebSocket.cs index b81239a2c..909a18bbf 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Common/InternalWebSocket.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Common/InternalWebSocket.cs @@ -75,7 +75,17 @@ namespace ThingsGateway.Foundation.Http.WebSockets await this.m_resetEventForRead.WaitOneAsync(token); return new WebSocketReceiveResult(this.ComplateRead, this.m_dataFrame); } - +#if NET6_0_OR_GREATER + public async ValueTask ValueReadAsync(CancellationToken token) + { + if (!this.m_receive) + { + return new WebSocketReceiveResult(this.ComplateRead, null); + } + await this.m_resetEventForRead.WaitOneAsync(token); + return new WebSocketReceiveResult(this.ComplateRead, this.m_dataFrame); + } +#endif #region 发送 public void Send(WSDataFrame dataFrame, bool endOfMessage = true) { @@ -145,7 +155,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets protected override void Dispose(bool disposing) { - this.m_client.RemoveValue(WebSocketClientExtensions.WebSocketProperty); + this.m_client.RemoveValue(WebSocketClientExtension.WebSocketProperty); this.m_resetEventForComplateRead.SafeDispose(); this.m_resetEventForRead.SafeDispose(); this.m_dataFrame = null; @@ -154,8 +164,8 @@ namespace ThingsGateway.Foundation.Http.WebSockets private void ComplateRead() { - this.m_resetEventForComplateRead.Set(); this.m_dataFrame = default; + this.m_resetEventForComplateRead.Set(); } } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Common/WebSocketReceiveResult.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Common/WebSocketReceiveResult.cs index 7d681abd2..5ba7cb48c 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Common/WebSocketReceiveResult.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Common/WebSocketReceiveResult.cs @@ -41,5 +41,10 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// WebSocket数据帧 /// public WSDataFrame DataFrame { get; private set; } + + /// + /// 连接已关闭 + /// + public bool IsClosed => this.DataFrame == null; } } diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Components/WebSocketClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Components/WebSocketClient.cs index d8b43ec98..ca5b9a078 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Components/WebSocketClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Components/WebSocketClient.cs @@ -39,10 +39,14 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// /// /// - protected override void OnHandleWSDataFrame(WSDataFrame dataFrame) + protected override async Task OnReceivedWSDataFrame(WSDataFrame dataFrame) { - this.Received?.Invoke(this, dataFrame); - base.OnHandleWSDataFrame(dataFrame); + if (this.Received != null) + { + await this.Received.Invoke(this, dataFrame); + } + + await base.OnReceivedWSDataFrame(dataFrame); } } @@ -211,20 +215,44 @@ namespace ThingsGateway.Foundation.Http.WebSockets #endregion 事件 + ///// + //protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + //{ + // if (this.GetHandshaked()) + // { + // var dataFrame = (WSDataFrame)requestInfo; + // this.OnReceivedWSDataFrame(dataFrame); + // } + // else + // { + // if (requestInfo is HttpResponse response) + // { + // response.Flag = false; + // base.HandleReceivedData(byteBlock, requestInfo); + // SpinWait.SpinUntil(() => + // { + // return (bool)response.Flag; + // }, 3000); + // } + // } + + // return false; + //} + /// - protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + protected override async Task ReceivedData(ReceivedDataEventArgs e) { if (this.GetHandshaked()) { - var dataFrame = (WSDataFrame)requestInfo; - this.OnHandleWSDataFrame(dataFrame); + var dataFrame = (WSDataFrame)e.RequestInfo; + await this.OnReceivedWSDataFrame(dataFrame); } else { - if (requestInfo is HttpResponse response) + if (e.RequestInfo is HttpResponse response) { response.Flag = false; - base.HandleReceivedData(byteBlock, requestInfo); + await base.ReceivedData(e); SpinWait.SpinUntil(() => { return (bool)response.Flag; @@ -232,32 +260,32 @@ namespace ThingsGateway.Foundation.Http.WebSockets } } - return false; + await base.ReceivedData(e); } /// /// /// /// - protected override void OnDisconnected(DisconnectEventArgs e) + protected override async Task OnDisconnected(DisconnectEventArgs e) { this.SetValue(WebSocketFeature.HandshakedProperty, false); - if (this.TryGetValue(WebSocketClientExtensions.WebSocketProperty, out var internalWebSocket)) + if (this.TryGetValue(WebSocketClientExtension.WebSocketProperty, out var internalWebSocket)) { _ = internalWebSocket.TryInputReceiveAsync(null); } - base.OnDisconnected(e); + await base.OnDisconnected(e); } /// /// 当收到WS数据时。 /// /// - protected virtual void OnHandleWSDataFrame(WSDataFrame dataFrame) + protected virtual async Task OnReceivedWSDataFrame(WSDataFrame dataFrame) { - if (this.TryGetValue(WebSocketClientExtensions.WebSocketProperty, out var internalWebSocket)) + if (this.TryGetValue(WebSocketClientExtension.WebSocketProperty, out var internalWebSocket)) { - if (internalWebSocket.TryInputReceiveAsync(dataFrame).ConfigureAwait(false).GetAwaiter().GetResult()) + if (await internalWebSocket.TryInputReceiveAsync(dataFrame)) { return; } @@ -265,7 +293,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets if (this.PluginsManager.Enable) { - this.PluginsManager.Raise(nameof(IWebSocketReceivedPlugin.OnWebSocketReceived), this, new WSDataFrameEventArgs(dataFrame)); + await this.PluginsManager.RaiseAsync(nameof(IWebSocketReceivedPlugin.OnWebSocketReceived), this, new WSDataFrameEventArgs(dataFrame)); } } } diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/DataAdapter/WebSocketDataHandlingAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/DataAdapter/WebSocketDataHandlingAdapter.cs index 4cccd6ce5..bf749c184 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/DataAdapter/WebSocketDataHandlingAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/DataAdapter/WebSocketDataHandlingAdapter.cs @@ -28,7 +28,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// /// WebSocket适配器 /// - public class WebSocketDataHandlingAdapter : SingleStreamDataHandlingAdapter + public sealed class WebSocketDataHandlingAdapter : SingleStreamDataHandlingAdapter { private WSDataFrame m_dataFrameTemp; @@ -42,16 +42,6 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// private ByteBlock m_tempByteBlock; - /// - /// - /// - public override bool CanSendRequestInfo => false; - - /// - /// - /// - public override bool CanSplicingSend => false; - /// /// 解码 /// @@ -183,35 +173,6 @@ namespace ThingsGateway.Foundation.Http.WebSockets } } - /// - /// 当发送数据前处理数据 - /// - /// - /// - /// - protected override void PreviewSend(byte[] buffer, int offset, int length) - { - this.GoSend(buffer, offset, length); - } - - /// - /// - /// - /// - protected override void PreviewSend(IList> transferBytes) - { - throw new System.NotImplementedException();//因为设置了不支持拼接发送,所以该方法可以不实现。 - } - - /// - /// - /// - /// - protected override void PreviewSend(IRequestInfo requestInfo) - { - throw new NotImplementedException(); - } - /// /// /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/DelegateCollection.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/DelegateCollection.cs index acea55482..c32afe367 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/DelegateCollection.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/DelegateCollection.cs @@ -30,5 +30,5 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// /// /// - public delegate void WSDataFrameEventHandler(TClient client, WSDataFrame dataFrame); + public delegate Task WSDataFrameEventHandler(TClient client, WSDataFrame dataFrame); } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketClientExtensions.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketClientExtension.cs similarity index 99% rename from framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketClientExtensions.cs rename to framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketClientExtension.cs index efaea5180..9be29ad14 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketClientExtensions.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketClientExtension.cs @@ -28,7 +28,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// /// WSClientExtensions /// - public static class WebSocketClientExtensions + public static class WebSocketClientExtension { #region DependencyProperty diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Config/WebSocketConfigExtensions.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketConfigExtension.cs similarity index 98% rename from framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Config/WebSocketConfigExtensions.cs rename to framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketConfigExtension.cs index 8291817ce..72c52ff7d 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Config/WebSocketConfigExtensions.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketConfigExtension.cs @@ -29,7 +29,7 @@ namespace ThingsGateway.Foundation.Sockets /// /// WebSocketConfigExtensions /// - public static class WebSocketConfigExtensions + public static class WebSocketConfigExtension { /// /// 构建WebSocketClient类客户端,并连接 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketDataFrameExtensions.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketDataFrameExtension.cs similarity index 99% rename from framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketDataFrameExtensions.cs rename to framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketDataFrameExtension.cs index a35996bea..c49d88646 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketDataFrameExtensions.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketDataFrameExtension.cs @@ -29,7 +29,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// /// WSDataFrame辅助扩展类 /// - public static class WebSocketDataFrameExtensions + public static class WebSocketDataFrameExtension { /// /// 追加二进制流 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketServerExtensions.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketServerExtension.cs similarity index 94% rename from framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketServerExtensions.cs rename to framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketServerExtension.cs index 25b7f8f59..491b0c582 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketServerExtensions.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Extensions/WebSocketServerExtension.cs @@ -28,7 +28,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// /// WebSocketServerExtensions /// - public static class WebSocketServerExtensions + public static class WebSocketServerExtension { /// /// 转化Protocol协议标识为 @@ -49,7 +49,8 @@ namespace ThingsGateway.Foundation.Http.WebSockets { IsPermitOperation = true }; - await client.PluginsManager.RaiseAsync(nameof(IWebSocketHandshakingPlugin.OnWebSocketHandshaking), client, args); + await client.PluginsManager.RaiseAsync(nameof(IWebSocketHandshakingPlugin.OnWebSocketHandshaking), client, args).ConfigureAwait(false); + if (args.Context.Response.Responsed) { return false; @@ -63,9 +64,10 @@ namespace ThingsGateway.Foundation.Http.WebSockets using (var byteBlock = new ByteBlock()) { args.Context.Response.Build(byteBlock); - await client.DefaultSendAsync(byteBlock); + await client.DefaultSendAsync(byteBlock).ConfigureAwait(false); } - await client.PluginsManager.RaiseAsync(nameof(IWebSocketHandshakedPlugin.OnWebSocketHandshaked), client, new HttpContextEventArgs(httpContext)); + await client.PluginsManager.RaiseAsync(nameof(IWebSocketHandshakedPlugin.OnWebSocketHandshaked), client, new HttpContextEventArgs(httpContext)) + .ConfigureAwait(false); return true; } else @@ -74,7 +76,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets using (var byteBlock = new ByteBlock()) { args.Context.Response.Build(byteBlock); - await client.DefaultSendAsync(byteBlock); + await client.DefaultSendAsync(byteBlock).ConfigureAwait(false); } client.Close("主动拒绝WebSocket连接"); diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Interface/IWebSocket.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Interface/IWebSocket.cs index a3fd3ad34..2897cff84 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Interface/IWebSocket.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Interface/IWebSocket.cs @@ -68,7 +68,14 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// /// Task ReadAsync(CancellationToken token); - +#if NET6_0_OR_GREATER + /// + /// 值异步等待读取数据 + /// + /// + /// + public ValueTask ValueReadAsync(CancellationToken token); +#endif /// /// 采用WebSocket协议,发送WS数据。发送结束后,请及时释放 /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Plugins/WebSocketFeature.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Plugins/WebSocketFeature.cs index c4304ae2d..a0e8bc2d1 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Plugins/WebSocketFeature.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/WebSockets/Plugins/WebSocketFeature.cs @@ -71,7 +71,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// /// 验证连接 /// - public Func VerifyConnection { get; set; } + public Func> VerifyConnection { get; set; } /// /// 用于WebSocket连接的路径,默认为“/ws” @@ -98,7 +98,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets { if (client.Protocol == Protocol.Http) { - if (this.VerifyConnection.Invoke(client, e.Context)) + if (await this.VerifyConnection.Invoke(client, e.Context)) { e.Handled = true; _ = client.SwitchProtocolToWebSocket(e.Context); @@ -117,7 +117,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets public async Task OnTcpDisconnected(ITcpClientBase client, DisconnectEventArgs e) { client.SetValue(HandshakedProperty, false); - if (client.TryGetValue(WebSocketClientExtensions.WebSocketProperty, out var internalWebSocket)) + if (client.TryGetValue(WebSocketClientExtension.WebSocketProperty, out var internalWebSocket)) { _ = internalWebSocket.TryInputReceiveAsync(null); } @@ -145,6 +145,21 @@ namespace ThingsGateway.Foundation.Http.WebSockets /// /// public WebSocketFeature SetVerifyConnection(Func func) + { + this.VerifyConnection = async (client, context) => + { + await EasyTask.CompletedTask; + return func.Invoke(client, context); + }; + return this; + } + + /// + /// 验证连接 + /// + /// + /// + public WebSocketFeature SetVerifyConnection(Func> func) { this.VerifyConnection = func; return this; @@ -186,7 +201,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets ((HttpSocketClient)client).PongWS(); return; } - if (client.TryGetValue(WebSocketClientExtensions.WebSocketProperty, out var internalWebSocket)) + if (client.TryGetValue(WebSocketClientExtension.WebSocketProperty, out var internalWebSocket)) { if (await internalWebSocket.TryInputReceiveAsync(dataFrame)) { @@ -196,8 +211,9 @@ namespace ThingsGateway.Foundation.Http.WebSockets await this.m_pluginsManager.RaiseAsync(nameof(IWebSocketReceivedPlugin.OnWebSocketReceived), client, new WSDataFrameEventArgs(dataFrame)); } - private bool ThisVerifyConnection(IHttpSocketClient client, HttpContext context) + private async Task ThisVerifyConnection(IHttpSocketClient client, HttpContext context) { + await EasyTask.CompletedTask; if (context.Request.Method == HttpMethod.Get) { if (this.WSUrl == "/" || context.Request.UrlEquals(this.WSUrl)) diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Rpc/Enum/CodeGeneratorFlag.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Rpc/Enum/CodeGeneratorFlag.cs index 00b8f8e18..6938fd58d 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Rpc/Enum/CodeGeneratorFlag.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Rpc/Enum/CodeGeneratorFlag.cs @@ -41,12 +41,6 @@ namespace ThingsGateway.Foundation.Rpc /// ExtensionAsync = 2, - /// - /// 包含扩展(源代码生成无效) - /// - [Obsolete("该值已被弃用,请使用颗粒度更小的配置", true)] - IncludeExtension = 4, - /// /// 生成实例类同步代码(源代码生成无效) /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/EventArgs/SerialConnectingEventArgs.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/EventArgs/SerialConnectingEventArgs.cs index c5e21432c..8f4e52bb8 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/EventArgs/SerialConnectingEventArgs.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/EventArgs/SerialConnectingEventArgs.cs @@ -19,7 +19,7 @@ namespace ThingsGateway.Foundation.Serial; /// /// /// -public delegate void SerialConnectingEventHandler(TClient client, SerialConnectingEventArgs e); +public delegate Task SerialConnectingEventHandler(TClient client, SerialConnectingEventArgs e); /// /// 客户端连接事件。 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/Interface/ISerial.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/Interface/ISerial.cs index 49cc6bf5f..46c2eb575 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/Interface/ISerial.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/Interface/ISerial.cs @@ -15,21 +15,7 @@ namespace ThingsGateway.Foundation.Serial; /// /// 串口基接口 /// -public interface ISerial : IDisposable +public interface ISerial : ISocket { - /// - /// 发送缓存区大小。最小值=1024。 - /// - int SendBufferSize { get; set; } - - /// - /// 接收缓存区大小。最小值=1024。 - /// - int ReceiveBufferSize { get; set; } - - /// - /// 日志记录器 - /// - ILog Logger { get; set; } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/Interface/ISerialSessionBase.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/Interface/ISerialSessionBase.cs index 07f435be0..9750924ef 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/Interface/ISerialSessionBase.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/Interface/ISerialSessionBase.cs @@ -53,9 +53,9 @@ public interface ISerialSessionBase : IClient, ISender, IDefaultSender, IPluginO /// - /// 接收模式 + /// 判断是否在线 /// - public ReceiveType ReceiveType { get; } + bool Online { get; } /// /// 串口描述 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/BaseSerial.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/BaseSerial.cs index 31a563c72..ba85ce55c 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/BaseSerial.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/BaseSerial.cs @@ -21,21 +21,23 @@ public abstract class BaseSerial : DependencyObject, ISerial /// 同步根。 /// protected readonly object SyncRoot = new object(); - private int m_receiveBufferSize = 1024 * 64; - private int m_sendBufferSize = 1024 * 64; + private int m_receiveBufferSize = 1024 * 10; + private int m_sendBufferSize = 1024 * 10; /// public virtual int SendBufferSize { - get => m_sendBufferSize; - set => m_sendBufferSize = value < 1024 ? 1024 : value; + get => this.m_sendBufferSize; + set => this.m_sendBufferSize = value < 1024 ? 1024 : value; } + /// public virtual int ReceiveBufferSize { - get => m_receiveBufferSize; - set => m_receiveBufferSize = value < 1024 ? 1024 : value; + get => this.m_receiveBufferSize; + set => this.m_receiveBufferSize = value < 1024 ? 1024 : value; } + /// public ILog Logger { get; set; } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/SerialCore.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/SerialCore.cs new file mode 100644 index 000000000..c934e4833 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/SerialCore.cs @@ -0,0 +1,396 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using System.Diagnostics; +using System.IO.Ports; + +namespace ThingsGateway.Foundation.Serial; +internal sealed class InternalSerialCore : SerialCore +{ + +} +/// +/// Serial核心 +/// +public class SerialCore : IDisposable, ISender +{ + /// + /// 初始缓存大小 + /// + public const int BufferSize = 1024 * 10; + #region 字段 + + /// + /// 同步根 + /// + public readonly object SyncRoot = new object(); + + private long m_bufferRate; + private bool m_disposedValue; + private SpinLock m_lock; + private bool m_online => MainSerialPort?.IsOpen == true; + private int m_receiveBufferSize = BufferSize; + private ValueCounter m_receiveCounter; + private int m_sendBufferSize = BufferSize; + private ValueCounter m_sendCounter; + private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); + #endregion 字段 + + /// + /// Tcp核心 + /// + public SerialCore() + { + this.m_lock = new SpinLock(Debugger.IsAttached); + this.m_receiveCounter = new ValueCounter + { + Period = TimeSpan.FromSeconds(1), + OnPeriod = this.OnReceivePeriod + }; + + this.m_sendCounter = new ValueCounter + { + Period = TimeSpan.FromSeconds(1), + OnPeriod = this.OnSendPeriod + }; + } + + /// + /// 析构函数 + /// + ~SerialCore() + { + this.Dispose(disposing: false); + } + + /// + public bool CanSend => this.m_online; + + /// + /// 当中断Tcp的时候。当为时,意味着是调用。当为时,则是其他中断。 + /// + public Action OnBreakOut { get; set; } + + /// + /// 当发生异常的时候 + /// + public Action OnException { get; set; } + + /// + /// 在线状态 + /// + public bool Online { get => this.m_online; } + /// + /// UserToken + /// + public ByteBlock UserToken { get; set; } + + /// + /// 当收到数据的时候 + /// + public Action OnReceived { get; set; } + + /// + /// 接收缓存池(可以设定初始值,运行时的值会根据流速自动调整) + /// + public int ReceiveBufferSize + { + get => this.m_receiveBufferSize; + set + { + this.m_receiveBufferSize = value; + if (this.MainSerialPort != null && !MainSerialPort.IsOpen) + { + this.MainSerialPort.ReadBufferSize = value; + } + } + } + + /// + /// 接收计数器 + /// + public ValueCounter ReceiveCounter { get => this.m_receiveCounter; } + + /// + /// 发送缓存池(可以设定初始值,运行时的值会根据流速自动调整) + /// + public int SendBufferSize + { + get => this.m_sendBufferSize; + set + { + this.m_sendBufferSize = value; + if (this.MainSerialPort != null && !MainSerialPort.IsOpen) + { + this.MainSerialPort.WriteBufferSize = value; + } + } + } + + /// + /// 发送计数器 + /// + public ValueCounter SendCounter { get => this.m_sendCounter; } + + /// + /// SerialPort + /// + public SerialPort MainSerialPort { get; private set; } + + + /// + /// 开始以Iocp方式接收 + /// + public virtual void BeginIocpReceive() + { + var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize); + this.UserToken = byteBlock; + byteBlock.SetLength(0); + if (this.MainSerialPort.BytesToRead > 0) + { + this.ProcessReceived(); + } + MainSerialPort.DataReceived += MainSerialPort_DataReceived; + } + + private void MainSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) + { + try + { + this.m_bufferRate = 1; + this.ProcessReceived(); + } + catch (Exception ex) + { + this.PrivateBreakOut(false, ex.Message); + } + } + + /// + /// 请求关闭 + /// + /// + public virtual void Close(string msg) + { + this.PrivateBreakOut(true, msg); + } + + /// + /// 释放对象 + /// + public void Dispose() + { + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// 重置环境,并设置新的。 + /// + /// + public virtual void Reset(SerialPort socket) + { + if (socket is null) + { + throw new ArgumentNullException(nameof(socket)); + } + + if (!socket.IsOpen) + { + throw new Exception("新的SerialPort必须在连接状态。"); + } + this.Reset(); + this.MainSerialPort = socket; + } + + /// + /// 重置环境。 + /// + public virtual void Reset() + { + this.m_receiveCounter.Reset(); + this.m_sendCounter.Reset(); + this.MainSerialPort = null; + this.OnReceived = null; + this.OnBreakOut = null; + this.UserToken = null; + this.m_bufferRate = 1; + this.m_lock = new SpinLock(); + this.m_receiveBufferSize = BufferSize; + this.m_sendBufferSize = BufferSize; + } + + /// + /// 发送数据。 + /// + /// 内部会根据是否启用Ssl,进行直接发送,还是Ssl发送。 + /// + /// + /// + /// + /// + public virtual void Send(byte[] buffer, int offset, int length) + { + var lockTaken = false; + try + { + this.m_lock.Enter(ref lockTaken); + this.MainSerialPort.Write(buffer, offset, length); + } + finally + { + if (lockTaken) this.m_lock.Exit(false); + } + this.m_sendCounter.Increment(length); + } + + /// + /// 异步发送数据。 + /// + /// + /// + /// + /// + /// + public virtual async Task SendAsync(byte[] buffer, int offset, int length) + { + try + { + await this.m_semaphore.WaitAsync(); + + this.MainSerialPort.Write(buffer, offset, length); + } + finally + { + this.m_semaphore.Release(); + } + + this.m_sendCounter.Increment(length); + } + + /// + /// 当中断Tcp时。 + /// + /// 当为时,意味着是调用。当为时,则是其他中断。 + /// + protected virtual void BreakOut(bool manual, string msg) + { + this.OnBreakOut?.Invoke(this, manual, msg); + } + + /// + /// 释放对象 + /// + /// + protected virtual void Dispose(bool disposing) + { + if (!this.m_disposedValue) + { + if (disposing) + { + } + + this.m_disposedValue = true; + } + UserToken.SafeDispose(); + } + + /// + /// 当发生异常的时候 + /// + /// + protected virtual void Exception(Exception ex) + { + this.OnException?.Invoke(this, ex); + } + + /// + /// 当收到数据的时候 + /// + /// + protected virtual void Received(ByteBlock byteBlock) + { + this.OnReceived?.Invoke(this, byteBlock); + } + + private void HandleBuffer(ByteBlock byteBlock) + { + try + { + this.m_receiveCounter.Increment(byteBlock.Length); + this.Received(byteBlock); + } + catch (Exception ex) + { + this.Exception(ex); + } + finally + { + byteBlock.Dispose(); + } + } + + private void OnReceivePeriod(long value) + { + this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value); + } + + private void OnSendPeriod(long value) + { + this.SendBufferSize = TouchSocketUtility.HitBufferLength(value); + } + + private void PrivateBreakOut(bool manual, string msg) + { + lock (this.SyncRoot) + { + if (this.m_online) + { + this.BreakOut(manual, msg); + } + } + } + + private void ProcessReceived() + { + if (!this.m_online) + { + UserToken?.SafeDispose(); + return; + } + if (MainSerialPort.BytesToRead > 0) + { + var byteBlock = UserToken; + byte[] buffer = BytePool.Default.Rent(MainSerialPort.BytesToRead); + int num = MainSerialPort.Read(buffer, 0, MainSerialPort.BytesToRead); + byteBlock.Write(buffer, 0, num); + byteBlock.SetLength(num); + this.HandleBuffer(byteBlock); + try + { + var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength)); + newByteBlock.SetLength(0); + UserToken = newByteBlock; + + if (MainSerialPort.BytesToRead > 0) + { + this.m_bufferRate += 2; + this.ProcessReceived(); + } + } + catch (Exception ex) + { + this.PrivateBreakOut(false, ex.Message); + } + } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/SerialSession.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/SerialSession.cs index d480a658f..11fc28c9b 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/SerialSession.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/SerialPort/SerialPort/SerialSession.cs @@ -14,6 +14,7 @@ using System.IO.Ports; namespace ThingsGateway.Foundation.Serial; + /// public class SerialSession : SerialSessionBase { @@ -22,15 +23,18 @@ public class SerialSession : SerialSessionBase /// public ReceivedEventHandler Received { get; set; } - /// - /// 接收数据 - /// - /// - /// - protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + /// + protected override async Task ReceivedData(ReceivedDataEventArgs e) { - this.Received?.Invoke(this, byteBlock, requestInfo); - return false; + if (this.Received != null) + { + await this.Received.Invoke(this, e); + if (e.Handled) + { + return; + } + } + await base.ReceivedData(e); } } @@ -46,33 +50,22 @@ public class SerialSessionBase : BaseSerial, ISerialSession public SerialSessionBase() { this.Protocol = SerialPort; - this.m_receiveCounter = new ValueCounter - { - Period = TimeSpan.FromSeconds(1), - OnPeriod = this.OnReceivePeriod - }; - this.m_sendCounter = new ValueCounter - { - Period = TimeSpan.FromSeconds(1), - OnPeriod = this.OnSendPeriod - }; + this.m_serialCore = new InternalSerialCore(); } - #region 变量 private DelaySender m_delaySender; - private long m_bufferRate = 1; private bool m_online => MainSerialPort?.IsOpen == true; - private ValueCounter m_receiveCounter; - private ValueCounter m_sendCounter; - + private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); + private readonly InternalSerialCore m_serialCore; #endregion 变量 #region 事件 /// public ConnectedEventHandler Connected { get; set; } + /// public SerialConnectingEventHandler Connecting { get; set; } @@ -82,26 +75,28 @@ public class SerialSessionBase : BaseSerial, ISerialSession /// public DisconnectEventHandler Disconnecting { get; set; } - private void PrivateOnConnected(object o) + private Task PrivateOnConnected(object o) { - var e = (ConnectedEventArgs)o; - this.OnConnected(e); - if (e.Handled) - { - return; - } - this.PluginsManager.Raise(nameof(ITcpConnectedPlugin.OnTcpConnected), this, e); + return this.OnConnected((ConnectedEventArgs)o); } /// - /// 已经建立Tcp连接 + /// 已经建立连接 /// /// - protected virtual void OnConnected(ConnectedEventArgs e) + protected virtual async Task OnConnected(ConnectedEventArgs e) { try { - this.Connected?.Invoke(this, e); + if (this.Connected != null) + { + await this.Connected.Invoke(this, e); + if (e.Handled) + { + return; + } + } + await this.PluginsManager.RaiseAsync(nameof(ITcpConnectedPlugin.OnTcpConnected), this, e); } catch (Exception ex) { @@ -109,30 +104,33 @@ public class SerialSessionBase : BaseSerial, ISerialSession } } - private void PrivateOnConnecting(SerialConnectingEventArgs e) + private Task PrivateOnConnecting(SerialConnectingEventArgs e) { if (this.CanSetDataHandlingAdapter) { this.SetDataHandlingAdapter(this.Config.GetValue(TouchSocketConfigExtension.TcpDataHandlingAdapterProperty).Invoke()); } - this.OnConnecting(e); - if (e.Handled) - { - return; - } - this.PluginsManager.Raise(nameof(ITcpConnectingPlugin.OnTcpConnecting), this, e); + return this.OnConnecting(e); } /// - /// 准备连接的时候,此时已初始化Socket,但是并未建立Tcp连接 + /// 准备连接的时候,此时并未建立连接 /// /// - protected virtual void OnConnecting(SerialConnectingEventArgs e) + protected virtual async Task OnConnecting(SerialConnectingEventArgs e) { try { - this.Connecting?.Invoke(this, e); + if (this.Connecting != null) + { + await this.Connecting.Invoke(this, e); + if (e.Handled) + { + return; + } + } + await this.PluginsManager.RaiseAsync(nameof(ITcpConnectingPlugin.OnTcpConnecting), this, e); } catch (Exception ex) { @@ -140,25 +138,30 @@ public class SerialSessionBase : BaseSerial, ISerialSession } } - private void PrivateOnDisconnected(DisconnectEventArgs e) + private Task PrivateOnDisconnected(object obj) { - this.OnDisconnected(e); - if (e.Handled) - { - return; - } - this.PluginsManager.Raise(nameof(ITcpDisconnectedPlugin.OnTcpDisconnected), this, e); + this.m_receiver?.TryInputReceive(default, default); + return this.OnDisconnected((DisconnectEventArgs)obj); } /// /// 断开连接。在客户端未设置连接状态时,不会触发 /// /// - protected virtual void OnDisconnected(DisconnectEventArgs e) + protected virtual async Task OnDisconnected(DisconnectEventArgs e) { try { - this.Disconnected?.Invoke(this, e); + if (this.Disconnected != null) + { + await this.Disconnected.Invoke(this, e).ConfigureAwait(false); + if (e.Handled) + { + return; + } + } + + await this.PluginsManager.RaiseAsync(nameof(ITcpDisconnectedPlugin.OnTcpDisconnected), this, e).ConfigureAwait(false); } catch (Exception ex) { @@ -166,28 +169,29 @@ public class SerialSessionBase : BaseSerial, ISerialSession } } - private void PrivateOnDisconnecting(DisconnectEventArgs e) + private Task PrivateOnDisconnecting(object obj) { - this.OnDisconnecting(e); - if (e.Handled) - { - return; - } - this.PluginsManager.Raise(nameof(ITcpDisconnectingPlugin.OnTcpDisconnecting), this, e); + return this.OnDisconnecting((DisconnectEventArgs)obj); } /// /// 即将断开连接(仅主动断开时有效)。 - /// - /// 当主动调用Close断开时。 - /// /// /// - protected virtual void OnDisconnecting(DisconnectEventArgs e) + protected virtual async Task OnDisconnecting(DisconnectEventArgs e) { try { - this.Disconnecting?.Invoke(this, e); + if (this.Disconnecting != null) + { + await this.Disconnecting.Invoke(this, e).ConfigureAwait(false); + if (e.Handled) + { + return; + } + } + + await this.PluginsManager.RaiseAsync(nameof(ITcpDisconnectingPlugin.OnTcpDisconnecting), this, e).ConfigureAwait(false); } catch (Exception ex) { @@ -200,16 +204,10 @@ public class SerialSessionBase : BaseSerial, ISerialSession #region 属性 /// - public DateTime LastReceivedTime => this.m_receiveCounter.LastIncrement; + public DateTime LastReceivedTime => this.GetTcpCore().ReceiveCounter.LastIncrement; /// - public DateTime LastSendTime => this.m_sendCounter.LastIncrement; - - /// - public Func OnHandleRawBuffer { get; set; } - - /// - public Func OnHandleReceivedData { get; set; } + public DateTime LastSendTime => this.GetTcpCore().SendCounter.LastIncrement; /// public IContainer Container { get; private set; } @@ -223,10 +221,7 @@ public class SerialSessionBase : BaseSerial, ISerialSession /// public SingleStreamDataHandlingAdapter DataHandlingAdapter { get; private set; } - - /// /// - /// public SerialProperty SerialProperty { get; private set; } /// public SerialPort MainSerialPort { get; private set; } @@ -241,9 +236,6 @@ public class SerialSessionBase : BaseSerial, ISerialSession public IPluginsManager PluginsManager { get; private set; } - /// - public ReceiveType ReceiveType { get; private set; } - /// public Protocol Protocol { get; set; } @@ -261,32 +253,13 @@ public class SerialSessionBase : BaseSerial, ISerialSession { if (this.m_online) { - this.PrivateOnDisconnecting(new DisconnectEventArgs(true, msg)); - + Task.Factory.StartNew(this.PrivateOnDisconnecting, new DisconnectEventArgs(true, msg)); this.MainSerialPort.TryClose(); - - this.MainSerialPort.SafeDispose(); - this.m_delaySender.SafeDispose(); - this.DataHandlingAdapter.SafeDispose(); - this.PrivateOnDisconnected(new DisconnectEventArgs(true, msg)); + this.BreakOut(default, true, msg); } } } - private void BreakOut(string msg) - { - lock (this.SyncRoot) - { - if (this.m_online) - { - - this.MainSerialPort.SafeDispose(); - this.m_delaySender.SafeDispose(); - this.DataHandlingAdapter.SafeDispose(); - this.PrivateOnDisconnected(new DisconnectEventArgs(false, msg)); - } - } - } /// /// @@ -298,15 +271,8 @@ public class SerialSessionBase : BaseSerial, ISerialSession { if (this.m_online) { - - this.MainSerialPort.TryClose(); - this.PrivateOnDisconnecting(new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开")); - - this.MainSerialPort.SafeDispose(); - this.m_delaySender.SafeDispose(); - this.DataHandlingAdapter.SafeDispose(); - this.PluginsManager.SafeDispose(); - this.PrivateOnDisconnected(new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开")); + Task.Factory.StartNew(this.PrivateOnDisconnecting, new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开")); + this.BreakOut(default, true, $"{nameof(Dispose)}主动断开"); } } base.Dispose(disposing); @@ -315,6 +281,7 @@ public class SerialSessionBase : BaseSerial, ISerialSession #endregion 断开操作 #region Connect + /// /// 打开串口 /// @@ -332,25 +299,32 @@ public class SerialSessionBase : BaseSerial, ISerialSession } if (this.Config == null) { - throw new ArgumentNullException("配置文件不能为空。"); + throw new ArgumentNullException(nameof(this.Config), "配置文件不能为空。"); } var serialProperty = this.Config.GetValue(SerialConfigExtension.SerialProperty) ?? throw new ArgumentNullException("串口配置不能为空。"); - this.MainSerialPort.SafeDispose(); var serialPort = CreateSerial(serialProperty); - var args = new SerialConnectingEventArgs(this.MainSerialPort); - this.PrivateOnConnecting(args); + this.PrivateOnConnecting(new(serialPort)) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + serialPort.Open(); - this.SetSerialPort(serialPort); - - this.BeginReceive(); - this.PrivateOnConnected(new ConnectedEventArgs()); + + Task.Factory.StartNew(this.PrivateOnConnected, new ConnectedEventArgs()); } } + + private void BeginReceive() + { + this.GetTcpCore().BeginIocpReceive(); + } + + /// public virtual ISerialSession Connect() { @@ -366,43 +340,66 @@ public class SerialSessionBase : BaseSerial, ISerialSession return this.Connect(); }); } + + #endregion Connect + + #region Receiver + + private Receiver m_receiver; + + /// + public IReceiver CreateReceiver() + { + return this.m_receiver ??= new Receiver(this); + } + + /// + public void ClearReceiver() + { + this.m_receiver = null; + } + #endregion - private void OnReceivePeriod(long value) + private void BreakOut(SerialCore core, bool manual, string msg) { - this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value); + lock (this.SyncRoot) + { + if (this.m_online) + { + this.MainSerialPort.SafeDispose(); + this.m_delaySender.SafeDispose(); + this.DataHandlingAdapter.SafeDispose(); + Task.Factory.StartNew(this.PrivateOnDisconnected, new DisconnectEventArgs(manual, msg)); + } + } } - private void OnSendPeriod(long value) + private SerialCore GetTcpCore() { - this.SendBufferSize = TouchSocketUtility.HitBufferLength(value); + this.ThrowIfDisposed(); + return this.m_serialCore ?? throw new ObjectDisposedException(this.GetType().Name); } + + /// public override int ReceiveBufferSize { - get => base.ReceiveBufferSize; + get => this.GetTcpCore().ReceiveBufferSize; set { - base.ReceiveBufferSize = value; - if (this.MainSerialPort != null && !MainSerialPort.IsOpen) - { - this.MainSerialPort.ReadBufferSize = base.ReceiveBufferSize; - } + this.GetTcpCore().ReceiveBufferSize = value; } } /// public override int SendBufferSize { - get => base.SendBufferSize; + get => this.GetTcpCore().SendBufferSize; set { - base.SendBufferSize = value; - if (this.MainSerialPort != null && !MainSerialPort.IsOpen) - { - this.MainSerialPort.WriteBufferSize = base.SendBufferSize; - } + this.GetTcpCore().SendBufferSize = value; } } @@ -480,32 +477,24 @@ public class SerialSessionBase : BaseSerial, ISerialSession private void PrivateHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) { - if (this.OnHandleReceivedData?.Invoke(byteBlock, requestInfo) == false) + if (this.m_receiver != null) { - return; - } - - if (this.HandleReceivedData(byteBlock, requestInfo)) - { - return; - } - - if (this.PluginsManager.Enable) - { - var args = new ReceivedDataEventArgs(byteBlock, requestInfo); - this.PluginsManager.Raise(nameof(ITcpReceivedPlugin.OnTcpReceived), this, args); + if (this.m_receiver.TryInputReceive(byteBlock, requestInfo)) + { + return; + } } + this.ReceivedData(new ReceivedDataEventArgs(byteBlock, requestInfo)).GetFalseAwaitResult(); } /// - /// 处理已接收到的数据。 + /// 当收到适配器处理的数据时。 /// - /// 以二进制流形式传递 - /// 以解析的数据对象传递 + /// /// 如果返回则表示数据已被处理,且不会再向下传递。 - protected virtual bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + protected virtual Task ReceivedData(ReceivedDataEventArgs e) { - return false; + return this.PluginsManager.RaiseAsync(nameof(ITcpReceivedPlugin.OnTcpReceived), this, e); } /// @@ -515,12 +504,12 @@ public class SerialSessionBase : BaseSerial, ISerialSession /// 偏移 /// 长度 /// 返回值表示是否允许发送 - protected virtual bool HandleSendingData(byte[] buffer, int offset, int length) + protected virtual async Task SendingData(byte[] buffer, int offset, int length) { - if (this.PluginsManager.Enable) + if (this.PluginsManager.GetPluginCount(nameof(ITcpSendingPlugin.OnTcpSending)) > 0) { var args = new SendingEventArgs(buffer, offset, length); - this.PluginsManager.Raise(nameof(ITcpSendingPlugin.OnTcpSending), this, args); + await this.PluginsManager.RaiseAsync(nameof(ITcpSendingPlugin.OnTcpSending), this, args).ConfigureAwait(false); return args.IsPermitOperation; } return true; @@ -534,16 +523,6 @@ public class SerialSessionBase : BaseSerial, ISerialSession { this.SerialProperty = config.GetValue(SerialConfigExtension.SerialProperty); this.Logger ??= this.Container.Resolve(); - this.ReceiveType = config.GetValue(TouchSocketConfigExtension.ReceiveTypeProperty); - } - - /// - /// 在延迟发生错误 - /// - /// - protected virtual void OnDelaySenderError(Exception ex) - { - this.Logger.Log(LogLevel.Error, this, "发送错误", ex); } /// @@ -567,33 +546,10 @@ public class SerialSessionBase : BaseSerial, ISerialSession adapter.OnLoaded(this); adapter.ReceivedCallBack = this.PrivateHandleReceivedData; adapter.SendCallBack = this.DefaultSend; + adapter.SendAsyncCallBack = this.DefaultSendAsync; this.DataHandlingAdapter = adapter; } - private void BeginReceive() - { - - if (this.ReceiveType == ReceiveType.Iocp) - { - SerialReceivedEventArgs eventArgs = new(); - var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize); - byteBlock.SetLength(0); - eventArgs.UserToken = byteBlock; - if (this.MainSerialPort.BytesToRead > 0) - { - this.ProcessReceived(eventArgs); - } - MainSerialPort.DataReceived += this.EventArgs_Completed; - } - else if (this.ReceiveType == ReceiveType.Bio) - { - new Thread(this.BeginBio) - { - IsBackground = true - } - .Start(); - } - } private static SerialPort CreateSerial(SerialProperty serialProperty) { SerialPort serialPort = new(serialProperty.PortName, serialProperty.BaudRate, serialProperty.Parity, serialProperty.DataBits, serialProperty.StopBits) @@ -604,88 +560,6 @@ public class SerialSessionBase : BaseSerial, ISerialSession return serialPort; } - private void EventArgs_Completed(object sender, SerialDataReceivedEventArgs e) - { - try - { - this.m_bufferRate = 1; - SerialReceivedEventArgs eventArgs = new(); - var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength)); - newByteBlock.SetLength(0); - eventArgs.UserToken = newByteBlock; - if (MainSerialPort.BytesToRead > 0) - { - this.ProcessReceived(eventArgs); - } - } - catch (Exception ex) - { - this.BreakOut(ex.Message); - } - } - private void BeginBio() - { - while (true) - { - var byteBlock = new ByteBlock(this.ReceiveBufferSize); - try - { - int r = MainSerialPort.Read(byteBlock.Buffer, 0, MainSerialPort.BytesToRead); - if (r == 0) - { - this.BreakOut("远程终端主动关闭"); - return; - } - - byteBlock.SetLength(r); - this.HandleBuffer(byteBlock); - } - catch (Exception ex) - { - this.BreakOut(ex.Message); - return; - } - } - } - - - /// - /// 处理数据 - /// - private void HandleBuffer(ByteBlock byteBlock) - { - try - { - this.m_receiveCounter.Increment(byteBlock.Length); - if (this.OnHandleRawBuffer?.Invoke(byteBlock) == false) - { - return; - } - if (this.DisposedValue) - { - return; - } - if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(ITcpReceivingPlugin.OnTcpReceiving), this, new ByteBlockEventArgs(byteBlock))) - { - return; - } - if (this.DataHandlingAdapter == null) - { - this.Logger.Error(this, TouchSocketResource.NullDataAdapter.GetDescription()); - return; - } - this.DataHandlingAdapter.ReceivedInput(byteBlock); - } - catch (Exception ex) - { - this.Logger.Log(LogLevel.Error, this, "在处理数据时发生错误", ex); - } - finally - { - byteBlock.Dispose(); - } - } - #region 发送 #region 同步发送 @@ -757,12 +631,14 @@ public class SerialSessionBase : BaseSerial, ISerialSession { length += item.Count; } - using var byteBlock = new ByteBlock(length); - foreach (var item in transferBytes) + using (var byteBlock = new ByteBlock(length)) { - byteBlock.Write(item.Array, item.Offset, item.Count); + foreach (var item in transferBytes) + { + byteBlock.Write(item.Array, item.Offset, item.Count); + } + this.DataHandlingAdapter.SendInput(byteBlock.Buffer, 0, byteBlock.Len); } - this.DataHandlingAdapter.SendInput(byteBlock.Buffer, 0, byteBlock.Len); } } @@ -773,18 +649,20 @@ public class SerialSessionBase : BaseSerial, ISerialSession /// /// /// - /// - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// + /// public virtual Task SendAsync(byte[] buffer, int offset, int length) { - return Task.Run(() => + this.ThrowIfDisposed(); + if (this.DataHandlingAdapter == null) { - this.Send(buffer, offset, length); - }); + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); + } + return this.DataHandlingAdapter.SendInputAsync(buffer, offset, length); } /// @@ -796,118 +674,78 @@ public class SerialSessionBase : BaseSerial, ISerialSession /// public virtual Task SendAsync(IRequestInfo requestInfo) { - return Task.Run(() => + this.ThrowIfDisposed(); + if (this.DataHandlingAdapter == null) { - this.Send(requestInfo); - }); + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); + } + if (!this.DataHandlingAdapter.CanSendRequestInfo) + { + throw new NotSupportedException($"当前适配器不支持对象发送。"); + } + return this.DataHandlingAdapter.SendInputAsync(requestInfo); } /// /// /// - /// - /// - /// - /// + /// + /// + /// public virtual Task SendAsync(IList> transferBytes) { - return Task.Run(() => + this.ThrowIfDisposed(); + if (this.DataHandlingAdapter == null) { - this.Send(transferBytes); - }); + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); + } + if (this.DataHandlingAdapter.CanSplicingSend) + { + return this.DataHandlingAdapter.SendInputAsync(transferBytes); + } + else + { + var length = 0; + foreach (var item in transferBytes) + { + length += item.Count; + } + using (var byteBlock = new ByteBlock(length)) + { + foreach (var item in transferBytes) + { + byteBlock.Write(item.Array, item.Offset, item.Count); + } + return this.DataHandlingAdapter.SendInputAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + } } #endregion 异步发送 - /// /// - /// - /// - /// - /// - /// - /// - /// public void DefaultSend(byte[] buffer, int offset, int length) { - if (!this.m_online) + if (this.SendingData(buffer, offset, length).GetFalseAwaitResult()) { - throw new NotConnectedException(TouchSocketResource.NotConnected.GetDescription()); - } - if (this.HandleSendingData(buffer, offset, length)) - { - if (this.m_delaySender != null && length < this.m_delaySender.DelayLength) - { - this.m_delaySender.Send(QueueDataBytes.CreateNew(buffer, offset, length)); - } - else - { - this.MainSerialPort.AbsoluteSend(buffer, offset, length); - } - - this.m_sendCounter.Increment(length); + this.GetTcpCore().Send(buffer, offset, length); } } - /// /// - /// - /// - /// - /// - /// - /// - /// - public Task DefaultSendAsync(byte[] buffer, int offset, int length) + public async Task DefaultSendAsync(byte[] buffer, int offset, int length) { - return Task.Run(() => + if (await this.SendingData(buffer, offset, length)) { - this.DefaultSend(buffer, offset, length); - }); + await this.GetTcpCore().SendAsync(buffer, offset, length); + } } #endregion 发送 - private void ProcessReceived(SerialReceivedEventArgs e) - { - if (!this.m_online) - { - e.UserToken.SafeDispose(); - return; - } - if (MainSerialPort.BytesToRead > 0) - { - byte[] buffer = new byte[2048]; - var byteBlock = (ByteBlock)e.UserToken; - int num = MainSerialPort.Read(buffer, 0, MainSerialPort.BytesToRead); - byteBlock.Write(buffer, 0, num); - byteBlock.SetLength(num); - this.HandleBuffer(byteBlock); - try - { - var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength)); - newByteBlock.SetLength(0); - e.UserToken = newByteBlock; + #region 自定义 - if (MainSerialPort.BytesToRead > 0) - { - this.m_bufferRate += 2; - this.ProcessReceived(e); - } - } - catch (Exception ex) - { - e.UserToken.SafeDispose(); - this.BreakOut(ex.Message); - } - } - else - { - e.UserToken.SafeDispose(); - this.BreakOut("远程终端主动关闭"); - } - } private void SetSerialPort(SerialPort serialPort) { @@ -917,15 +755,63 @@ public class SerialSessionBase : BaseSerial, ISerialSession } this.MainSerialPort = serialPort; + this.SerialProperty ??= new(); + this.SerialProperty.Parity = serialPort.Parity; + this.SerialProperty.PortName = serialPort.PortName; + this.SerialProperty.StopBits = serialPort.StopBits; + this.SerialProperty.DataBits = serialPort.DataBits; + this.SerialProperty.BaudRate = serialPort.BaudRate; + var delaySenderOption = this.Config.GetValue(TouchSocketConfigExtension.DelaySenderProperty); if (delaySenderOption != null) { this.m_delaySender = new DelaySender(delaySenderOption, this.MainSerialPort.AbsoluteSend); } + this.m_serialCore.Reset(serialPort); + this.m_serialCore.OnReceived = this.HandleReceived; + this.m_serialCore.OnBreakOut = this.BreakOut; + } - /// - public override string ToString() + + private void HandleReceived(SerialCore core, ByteBlock byteBlock) { - return SerialProperty?.ToString(); + try + { + if (this.DisposedValue) + { + return; + } + if (this.ReceivingData(byteBlock).GetFalseAwaitResult()) + { + return; + } + + if (this.DataHandlingAdapter == null) + { + this.Logger.Error(this, TouchSocketResource.NullDataAdapter.GetDescription()); + return; + } + this.DataHandlingAdapter.ReceivedInput(byteBlock); + } + catch (Exception ex) + { + this.Logger.Log(LogLevel.Error, this, "在处理数据时发生错误", ex); + } } + + /// + /// 当收到原始数据 + /// + /// + /// 如果返回则表示数据已被处理,且不会再向下传递。 + protected virtual Task ReceivingData(ByteBlock byteBlock) + { + if (this.PluginsManager.GetPluginCount(nameof(ITcpReceivingPlugin.OnTcpReceiving)) > 0) + { + return this.PluginsManager.RaiseAsync(nameof(ITcpReceivingPlugin.OnTcpReceiving), this, new ByteBlockEventArgs(byteBlock)); + } + return Task.FromResult(false); + } + + #endregion } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/BaseSocket.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/BaseSocket.cs index 5494ab5f3..0aa687c0e 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/BaseSocket.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/BaseSocket.cs @@ -34,21 +34,24 @@ namespace ThingsGateway.Foundation.Sockets /// 同步根。 /// protected readonly object SyncRoot = new object(); - private int m_receiveBufferSize = 1024 * 64; - private int m_sendBufferSize = 1024 * 64; + + private int m_receiveBufferSize = 1024 * 10; + private int m_sendBufferSize = 1024 * 10; /// public virtual int SendBufferSize { - get => m_sendBufferSize; - set => m_sendBufferSize = value < 1024 ? 1024 : value; + get => this.m_sendBufferSize; + set => this.m_sendBufferSize = value < 1024 ? 1024 : value; } + /// public virtual int ReceiveBufferSize { - get => m_receiveBufferSize; - set => m_receiveBufferSize = value < 1024 ? 1024 : value; + get => this.m_receiveBufferSize; + set => this.m_receiveBufferSize = value < 1024 ? 1024 : value; } + /// public ILog Logger { get; set; } } diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Common/Options/TcpListenOption.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Common/Options/TcpListenOption.cs index f4bb9286c..7d8f9627a 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Common/Options/TcpListenOption.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Common/Options/TcpListenOption.cs @@ -32,11 +32,6 @@ namespace ThingsGateway.Foundation.Sockets /// public int SendTimeout { get; set; } - /// - /// 接收类型 - /// - public ReceiveType ReceiveType { get; set; } = ReceiveType.Iocp; - /// /// 是否使用地址复用 /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Core/TcpCore.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Core/TcpCore.cs index 54e779927..3dabb6e1b 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Core/TcpCore.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Core/TcpCore.cs @@ -14,547 +14,558 @@ using System.Diagnostics; using System.Net.Security; using System.Net.Sockets; -namespace ThingsGateway.Foundation.Sockets +namespace ThingsGateway.Foundation.Sockets; + +/// +/// Tcp核心 +/// +public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender { + private const string m_msg1 = "远程终端主动关闭"; + + /// + /// 初始缓存大小 + /// + public const int BufferSize = 1024 * 10; + #region 字段 + + /// + /// 同步根 + /// + public readonly object SyncRoot = new object(); + + private long m_bufferRate; + private bool m_disposedValue; + private SpinLock m_lock; + private volatile bool m_online; + private int m_receiveBufferSize = BufferSize; + private ValueCounter m_receiveCounter; + private int m_sendBufferSize = BufferSize; + private ValueCounter m_sendCounter; + private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); + #endregion 字段 + /// /// Tcp核心 /// - public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender + public TcpCore() { - private const string m_msg1 = "远程终端主动关闭"; - - #region 字段 - - /// - /// 同步根 - /// - public readonly object SyncRoot = new object(); - - private long m_bufferRate; - private bool m_disposedValue; - private SpinLock m_lock; - private volatile bool m_online; - private int m_receiveBufferSize = 64 * 1024; - private ValueCounter m_receiveCounter; - private int m_sendBufferSize = 1024 * 64; - private ValueCounter m_sendCounter; - private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); - #endregion 字段 - - /// - /// Tcp核心 - /// - public TcpCore() + this.m_lock = new SpinLock(Debugger.IsAttached); + this.m_receiveCounter = new ValueCounter { - this.m_lock = new SpinLock(Debugger.IsAttached); - this.m_receiveCounter = new ValueCounter - { - Period = TimeSpan.FromSeconds(1), - OnPeriod = this.OnReceivePeriod - }; + Period = TimeSpan.FromSeconds(1), + OnPeriod = this.OnReceivePeriod + }; - this.m_sendCounter = new ValueCounter - { - Period = TimeSpan.FromSeconds(1), - OnPeriod = this.OnSendPeriod - }; + this.m_sendCounter = new ValueCounter + { + Period = TimeSpan.FromSeconds(1), + OnPeriod = this.OnSendPeriod + }; + } + + /// + /// 析构函数 + /// + ~TcpCore() + { + this.Dispose(disposing: false); + } + + /// + public bool CanSend => this.m_online; + + /// + /// 当中断Tcp的时候。当为时,意味着是调用。当为时,则是其他中断。 + /// + public Action OnBreakOut { get; set; } + + /// + /// 当发生异常的时候 + /// + public Action OnException { get; set; } + + /// + /// 在线状态 + /// + public bool Online { get => this.m_online; } + + /// + /// 当收到数据的时候 + /// + public Action OnReceived { get; set; } + + /// + /// 接收缓存池(可以设定初始值,运行时的值会根据流速自动调整) + /// + public int ReceiveBufferSize + { + get => this.m_receiveBufferSize; + set + { + this.m_receiveBufferSize = value; + this.Socket.ReceiveBufferSize = value; } + } - /// - /// 析构函数 - /// - ~TcpCore() + /// + /// 接收计数器 + /// + public ValueCounter ReceiveCounter { get => this.m_receiveCounter; } + + /// + /// 发送缓存池(可以设定初始值,运行时的值会根据流速自动调整) + /// + public int SendBufferSize + { + get => this.m_sendBufferSize; + set { - this.Dispose(disposing: false); + this.m_sendBufferSize = value; + this.Socket.SendBufferSize = value; } + } - /// - public bool CanSend => this.m_online; + /// + /// 发送计数器 + /// + public ValueCounter SendCounter { get => this.m_sendCounter; } - /// - /// 当中断Tcp的时候。当为时,意味着是调用。当为时,则是其他中断。 - /// - public Action OnBreakOut { get; set; } + /// + /// Socket + /// + public Socket Socket { get; private set; } - /// - /// 当发生异常的时候 - /// - public Action OnException { get; set; } + /// + /// 提供一个用于客户端-服务器通信的流,该流使用安全套接字层 (SSL) 安全协议对服务器和(可选)客户端进行身份验证。 + /// + public SslStream SslStream { get; private set; } - /// - /// 在线状态 - /// - public bool Online { get => this.m_online; } + /// + /// 是否启用了Ssl + /// + public bool UseSsl { get; private set; } - /// - /// 当收到数据的时候 - /// - public Action OnReceived { get; set; } + /// + /// 以Ssl服务器模式授权 + /// + /// + public virtual void Authenticate(ServiceSslOption sslOption) + { + var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); + sslStream.AuthenticateAsServer(sslOption.Certificate); - /// - /// 接收缓存池(可以设定初始值,运行时的值会根据流速自动调整) - /// - public int ReceiveBufferSize + this.SslStream = sslStream; + this.UseSsl = true; + } + + /// + /// 以Ssl客户端模式授权 + /// + /// + public virtual void Authenticate(ClientSslOption sslOption) + { + var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); + if (sslOption.ClientCertificates == null) { - get => this.m_receiveBufferSize; - set - { - this.m_receiveBufferSize = value; - this.Socket.ReceiveBufferSize = value; - } + sslStream.AuthenticateAsClient(sslOption.TargetHost); } - - /// - /// 接收计数器 - /// - public ValueCounter ReceiveCounter { get => this.m_receiveCounter; } - - /// - /// 发送缓存池(可以设定初始值,运行时的值会根据流速自动调整) - /// - public int SendBufferSize + else { - get => this.m_sendBufferSize; - set - { - this.m_sendBufferSize = value; - this.Socket.SendBufferSize = value; - } + sslStream.AuthenticateAsClient(sslOption.TargetHost, sslOption.ClientCertificates, sslOption.SslProtocols, sslOption.CheckCertificateRevocation); } + this.SslStream = sslStream; + this.UseSsl = true; + } - /// - /// 发送计数器 - /// - public ValueCounter SendCounter { get => this.m_sendCounter; } + /// + /// 以Ssl服务器模式授权 + /// + /// + /// + public virtual async Task AuthenticateAsync(ServiceSslOption sslOption) + { + var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); + await sslStream.AuthenticateAsServerAsync(sslOption.Certificate); - /// - /// Socket - /// - public Socket Socket { get; private set; } + this.SslStream = sslStream; + this.UseSsl = true; + } - /// - /// 提供一个用于客户端-服务器通信的流,该流使用安全套接字层 (SSL) 安全协议对服务器和(可选)客户端进行身份验证。 - /// - public SslStream SslStream { get; private set; } - - /// - /// 是否启用了Ssl - /// - public bool UseSsl { get; private set; } - - /// - /// 以Ssl服务器模式授权 - /// - /// - public virtual void Authenticate(ServiceSslOption sslOption) + /// + /// 以Ssl客户端模式授权 + /// + /// + /// + public virtual async Task AuthenticateAsync(ClientSslOption sslOption) + { + var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); + if (sslOption.ClientCertificates == null) { - var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); - sslStream.AuthenticateAsServer(sslOption.Certificate); - - this.SslStream = sslStream; - this.UseSsl = true; + await sslStream.AuthenticateAsClientAsync(sslOption.TargetHost); } - - /// - /// 以Ssl客户端模式授权 - /// - /// - public virtual void Authenticate(ClientSslOption sslOption) + else { - var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); - if (sslOption.ClientCertificates == null) - { - sslStream.AuthenticateAsClient(sslOption.TargetHost); - } - else - { - sslStream.AuthenticateAsClient(sslOption.TargetHost, sslOption.ClientCertificates, sslOption.SslProtocols, sslOption.CheckCertificateRevocation); - } - this.SslStream = sslStream; - this.UseSsl = true; + await sslStream.AuthenticateAsClientAsync(sslOption.TargetHost, sslOption.ClientCertificates, sslOption.SslProtocols, sslOption.CheckCertificateRevocation); } + this.SslStream = sslStream; + this.UseSsl = true; + } - /// - /// 以Ssl服务器模式授权 - /// - /// - /// - public virtual async Task AuthenticateAsync(ServiceSslOption sslOption) + /// + /// 开始以Iocp方式接收 + /// + public virtual void BeginIocpReceive() + { + var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize); + this.UserToken = byteBlock; + this.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity); + if (!this.Socket.ReceiveAsync(this)) { - var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); - await sslStream.AuthenticateAsServerAsync(sslOption.Certificate); - - this.SslStream = sslStream; - this.UseSsl = true; + this.ProcessReceived(this); } + } - /// - /// 以Ssl客户端模式授权 - /// - /// - /// - public virtual async Task AuthenticateAsync(ClientSslOption sslOption) + /// + /// 开始以Ssl接收。 + /// + /// 注意,使用该方法时,应先完成授权。 + /// + /// + /// + public virtual async Task BeginSslReceive() + { + if (!this.UseSsl) { - var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false); - if (sslOption.ClientCertificates == null) - { - await sslStream.AuthenticateAsClientAsync(sslOption.TargetHost); - } - else - { - await sslStream.AuthenticateAsClientAsync(sslOption.TargetHost, sslOption.ClientCertificates, sslOption.SslProtocols, sslOption.CheckCertificateRevocation); - } - this.SslStream = sslStream; - this.UseSsl = true; + throw new Exception("请先完成Ssl验证授权"); } - - /// - /// 开始以Iocp方式接收 - /// - public virtual void BeginIocpReceive() - { - var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize); - this.UserToken = byteBlock; - this.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity); - if (!this.Socket.ReceiveAsync(this)) - { - this.ProcessReceived(this); - } - } - - /// - /// 开始以Ssl接收。 - /// - /// 注意,使用该方法时,应先完成授权。 - /// - /// - /// - public virtual async Task BeginSslReceive() - { - if (!this.UseSsl) - { - throw new Exception("请先完成Ssl验证授权"); - } - while (true) - { - var byteBlock = new ByteBlock(this.ReceiveBufferSize); - try - { - var r = await Task.Factory.FromAsync(this.SslStream.BeginRead, this.SslStream.EndRead, byteBlock.Buffer, 0, byteBlock.Capacity, default); - if (r == 0) - { - this.PrivateBreakOut(false, m_msg1); - return; - } - - byteBlock.SetLength(r); - this.HandleBuffer(byteBlock); - } - catch (Exception ex) - { - byteBlock.Dispose(); - this.PrivateBreakOut(false, ex.Message); - } - } - } - - /// - /// 请求关闭 - /// - /// - public virtual void Close(string msg) - { - this.PrivateBreakOut(true, msg); - } - - /// - /// 释放对象 - /// - public new void Dispose() - { - // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - /// - /// 重置环境,并设置新的。 - /// - /// - public virtual void Reset(Socket socket) - { - if (socket is null) - { - throw new ArgumentNullException(nameof(socket)); - } - - if (!socket.Connected) - { - throw new Exception("新的Socket必须在连接状态。"); - } - this.Reset(); - this.m_online = true; - this.Socket = socket; - } - - /// - /// 重置环境。 - /// - public virtual void Reset() - { - this.m_receiveCounter.Reset(); - this.m_sendCounter.Reset(); - this.SslStream?.Dispose(); - this.SslStream = null; - this.Socket = null; - } - - /// - /// 发送数据。 - /// - /// 内部会根据是否启用Ssl,进行直接发送,还是Ssl发送。 - /// - /// - /// - /// - /// - public virtual void Send(byte[] buffer, int offset, int length) - { - if (this.UseSsl) - { - this.SslStream.Write(buffer, offset, length); - } - else - { - var lockTaken = false; - try - { - this.m_lock.Enter(ref lockTaken); - while (length > 0) - { - var r = this.Socket.Send(buffer, offset, length, SocketFlags.None); - if (r == 0 && length > 0) - { - throw new Exception("发送数据不完全"); - } - offset += r; - length -= r; - } - } - finally - { - if (lockTaken) this.m_lock.Exit(false); - } - } - this.m_sendCounter.Increment(length); - } - - /// - /// 异步发送数据。 - /// - /// 内部会根据是否启用Ssl,进行直接发送,还是Ssl发送。 - /// - /// - /// - /// - /// - /// - /// - public virtual async Task SendAsync(byte[] buffer, int offset, int length) - { -#if NET6_0_OR_GREATER - if (this.UseSsl) - { - await this.SslStream.WriteAsync(new ReadOnlyMemory(buffer, offset, length), CancellationToken.None); - } - else - { - try - { - await this.m_semaphore.WaitAsync(); - - while (length > 0) - { - var r = await this.Socket.SendAsync(new ArraySegment(buffer, offset, length), SocketFlags.None, CancellationToken.None); - if (r == 0 && length > 0) - { - throw new Exception("发送数据不完全"); - } - offset += r; - length -= r; - } - } - finally - { - this.m_semaphore.Release(); - } - } -#else - if (this.UseSsl) - { - await this.SslStream.WriteAsync(buffer, offset, length, CancellationToken.None); - } - else - { - try - { - await this.m_semaphore.WaitAsync(); - - while (length > 0) - { - var r = this.Socket.Send(buffer, offset, length, SocketFlags.None); - if (r == 0 && length > 0) - { - throw new Exception("发送数据不完全"); - } - offset += r; - length -= r; - } - } - finally - { - this.m_semaphore.Release(); - } - } -#endif - - this.m_sendCounter.Increment(length); - } - - /// - /// 当中断Tcp时。 - /// - /// 当为时,意味着是调用。当为时,则是其他中断。 - /// - protected virtual void BreakOut(bool manual, string msg) - { - this.OnBreakOut?.Invoke(this, manual, msg); - } - - /// - /// 释放对象 - /// - /// - protected virtual void Dispose(bool disposing) - { - if (!this.m_disposedValue) - { - if (disposing) - { - } - - this.m_disposedValue = true; - } - base.Dispose(); - } - - /// - /// 当发生异常的时候 - /// - /// - protected virtual void Exception(Exception ex) - { - this.OnException?.Invoke(this, ex); - } - - /// - protected override sealed void OnCompleted(SocketAsyncEventArgs e) - { - if (e.LastOperation == SocketAsyncOperation.Receive) - { - try - { - this.m_bufferRate = 1; - this.ProcessReceived(e); - } - catch (Exception ex) - { - this.PrivateBreakOut(false, ex.Message); - } - } - } - - /// - /// 当收到数据的时候 - /// - /// - protected virtual void Received(ByteBlock byteBlock) - { - this.OnReceived?.Invoke(this, byteBlock); - } - - private void HandleBuffer(ByteBlock byteBlock) + while (true) { + var byteBlock = new ByteBlock(this.ReceiveBufferSize); try { - this.m_receiveCounter.Increment(byteBlock.Length); - this.Received(byteBlock); + var r = await Task.Factory.FromAsync(this.SslStream.BeginRead, this.SslStream.EndRead, byteBlock.Buffer, 0, byteBlock.Capacity, default); + if (r == 0) + { + this.PrivateBreakOut(false, m_msg1); + return; + } + + byteBlock.SetLength(r); + this.HandleBuffer(byteBlock); } catch (Exception ex) - { - this.Exception(ex); - } - finally { byteBlock.Dispose(); - } - } - - private void OnReceivePeriod(long value) - { - this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value); - } - - private void OnSendPeriod(long value) - { - this.SendBufferSize = TouchSocketUtility.HitBufferLength(value); - } - - private void PrivateBreakOut(bool manual, string msg) - { - lock (this.SyncRoot) - { - if (this.m_online) - { - this.m_online = false; - this.BreakOut(manual, msg); - } - } - } - - private void ProcessReceived(SocketAsyncEventArgs e) - { - if (e.SocketError != SocketError.Success) - { - this.PrivateBreakOut(false, e.SocketError.ToString()); - return; - } - else if (e.BytesTransferred > 0) - { - var byteBlock = (ByteBlock)e.UserToken; - byteBlock.SetLength(e.BytesTransferred); - this.HandleBuffer(byteBlock); - try - { - var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength)); - e.UserToken = newByteBlock; - e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Capacity); - - if (!this.Socket.ReceiveAsync(e)) - { - this.m_bufferRate += 2; - this.ProcessReceived(e); - } - } - catch (Exception ex) - { - this.PrivateBreakOut(false, ex.Message); - } - } - else - { - this.PrivateBreakOut(false, m_msg1); + this.PrivateBreakOut(false, ex.Message); } } } + + /// + /// 请求关闭 + /// + /// + public virtual void Close(string msg) + { + this.PrivateBreakOut(true, msg); + } + + /// + /// 释放对象 + /// + public new void Dispose() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// 重置环境,并设置新的。 + /// + /// + public virtual void Reset(Socket socket) + { + if (socket is null) + { + throw new ArgumentNullException(nameof(socket)); + } + + if (!socket.Connected) + { + throw new Exception("新的Socket必须在连接状态。"); + } + this.Reset(); + this.m_online = true; + this.Socket = socket; + } + + /// + /// 重置环境。 + /// + public virtual void Reset() + { + this.m_receiveCounter.Reset(); + this.m_sendCounter.Reset(); + this.SslStream?.Dispose(); + this.SslStream = null; + this.Socket = null; + this.OnReceived = null; + this.OnBreakOut = null; + this.UserToken = null; + this.m_bufferRate = 1; + this.m_lock = new SpinLock(); + this.m_receiveBufferSize = BufferSize; + this.m_sendBufferSize = BufferSize; + this.m_online = false; + } + + /// + /// 发送数据。 + /// + /// 内部会根据是否启用Ssl,进行直接发送,还是Ssl发送。 + /// + /// + /// + /// + /// + public virtual void Send(byte[] buffer, int offset, int length) + { + if (this.UseSsl) + { + this.SslStream.Write(buffer, offset, length); + } + else + { + var lockTaken = false; + try + { + this.m_lock.Enter(ref lockTaken); + while (length > 0) + { + var r = this.Socket.Send(buffer, offset, length, SocketFlags.None); + if (r == 0 && length > 0) + { + throw new Exception("发送数据不完全"); + } + offset += r; + length -= r; + } + } + finally + { + if (lockTaken) this.m_lock.Exit(false); + } + } + this.m_sendCounter.Increment(length); + } + + /// + /// 异步发送数据。 + /// + /// 内部会根据是否启用Ssl,进行直接发送,还是Ssl发送。 + /// + /// + /// + /// + /// + /// + /// + public virtual async Task SendAsync(byte[] buffer, int offset, int length) + { +#if NET6_0_OR_GREATER + if (this.UseSsl) + { + await this.SslStream.WriteAsync(new ReadOnlyMemory(buffer, offset, length), CancellationToken.None); + } + else + { + try + { + await this.m_semaphore.WaitAsync(); + + while (length > 0) + { + var r = await this.Socket.SendAsync(new ArraySegment(buffer, offset, length), SocketFlags.None, CancellationToken.None); + if (r == 0 && length > 0) + { + throw new Exception("发送数据不完全"); + } + offset += r; + length -= r; + } + } + finally + { + this.m_semaphore.Release(); + } + } +#else + if (this.UseSsl) + { + await this.SslStream.WriteAsync(buffer, offset, length, CancellationToken.None); + } + else + { + try + { + await this.m_semaphore.WaitAsync(); + + while (length > 0) + { + var r = this.Socket.Send(buffer, offset, length, SocketFlags.None); + if (r == 0 && length > 0) + { + throw new Exception("发送数据不完全"); + } + offset += r; + length -= r; + } + } + finally + { + this.m_semaphore.Release(); + } + } +#endif + + this.m_sendCounter.Increment(length); + } + + /// + /// 当中断Tcp时。 + /// + /// 当为时,意味着是调用。当为时,则是其他中断。 + /// + protected virtual void BreakOut(bool manual, string msg) + { + this.OnBreakOut?.Invoke(this, manual, msg); + } + + /// + /// 释放对象 + /// + /// + protected virtual void Dispose(bool disposing) + { + if (!this.m_disposedValue) + { + if (disposing) + { + } + + this.m_disposedValue = true; + } + base.Dispose(); + } + + /// + /// 当发生异常的时候 + /// + /// + protected virtual void Exception(Exception ex) + { + this.OnException?.Invoke(this, ex); + } + + /// + protected override sealed void OnCompleted(SocketAsyncEventArgs e) + { + if (e.LastOperation == SocketAsyncOperation.Receive) + { + try + { + this.m_bufferRate = 1; + this.ProcessReceived(e); + } + catch (Exception ex) + { + this.PrivateBreakOut(false, ex.Message); + } + } + } + + /// + /// 当收到数据的时候 + /// + /// + protected virtual void Received(ByteBlock byteBlock) + { + this.OnReceived?.Invoke(this, byteBlock); + } + + private void HandleBuffer(ByteBlock byteBlock) + { + try + { + this.m_receiveCounter.Increment(byteBlock.Length); + this.Received(byteBlock); + } + catch (Exception ex) + { + this.Exception(ex); + } + finally + { + byteBlock.Dispose(); + } + } + + private void OnReceivePeriod(long value) + { + this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value); + } + + private void OnSendPeriod(long value) + { + this.SendBufferSize = TouchSocketUtility.HitBufferLength(value); + } + + private void PrivateBreakOut(bool manual, string msg) + { + lock (this.SyncRoot) + { + if (this.m_online) + { + this.m_online = false; + this.BreakOut(manual, msg); + } + } + } + + private void ProcessReceived(SocketAsyncEventArgs e) + { + if (e.SocketError != SocketError.Success) + { + this.PrivateBreakOut(false, e.SocketError.ToString()); + return; + } + else if (e.BytesTransferred > 0) + { + var byteBlock = (ByteBlock)e.UserToken; + byteBlock.SetLength(e.BytesTransferred); + this.HandleBuffer(byteBlock); + try + { + var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength)); + e.UserToken = newByteBlock; + e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Capacity); + + if (!this.Socket.ReceiveAsync(e)) + { + this.m_bufferRate += 2; + this.ProcessReceived(e); + } + } + catch (Exception ex) + { + this.PrivateBreakOut(false, ex.Message); + } + } + else + { + this.PrivateBreakOut(false, m_msg1); + } + } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/NAT/NATService.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/NAT/NATService.cs index 4fb70b0ab..dd1f4e191 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/NAT/NATService.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/NAT/NATService.cs @@ -44,23 +44,22 @@ namespace ThingsGateway.Foundation.Sockets /// 在NAT服务器收到数据时。 /// /// - /// - /// + /// /// 需要转发的数据。 - protected virtual byte[] OnNATReceived(NATSocketClient socketClient, ByteBlock byteBlock, IRequestInfo requestInfo) + protected virtual byte[] OnNATReceived(NATSocketClient socketClient, ReceivedDataEventArgs e) { - return byteBlock?.ToArray(); + return e.ByteBlock?.ToArray(); } /// /// /// /// - /// - /// - protected override sealed void OnReceived(NATSocketClient socketClient, ByteBlock byteBlock, IRequestInfo requestInfo) + /// + protected override sealed async Task OnReceived(NATSocketClient socketClient, ReceivedDataEventArgs e) { - var data = this.OnNATReceived(socketClient, byteBlock, requestInfo); + await EasyTask.CompletedTask; + var data = this.OnNATReceived(socketClient, e); if (data != null) { socketClient.SendToTargetClient(data, 0, data.Length); @@ -82,12 +81,11 @@ namespace ThingsGateway.Foundation.Sockets /// /// /// - /// - /// + /// /// - protected virtual byte[] OnTargetClientReceived(NATSocketClient socketClient, ITcpClient tcpClient, ByteBlock byteBlock, IRequestInfo requestInfo) + protected virtual byte[] OnTargetClientReceived(NATSocketClient socketClient, ITcpClient tcpClient, ReceivedDataEventArgs e) { - return byteBlock?.ToArray(); + return e.ByteBlock?.ToArray(); } } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/NAT/NATSocketClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/NAT/NATSocketClient.cs index be559590f..68093c623 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/NAT/NATSocketClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/NAT/NATSocketClient.cs @@ -31,7 +31,7 @@ namespace ThingsGateway.Foundation.Sockets public class NATSocketClient : SocketClient { internal Action m_internalDis; - internal Func m_internalTargetClientRev; + internal Func m_internalTargetClientRev; private readonly ConcurrentList m_targetClients = new ConcurrentList(); /// @@ -100,18 +100,19 @@ namespace ThingsGateway.Foundation.Sockets /// /// /// - protected override void OnDisconnected(DisconnectEventArgs e) + protected override async Task OnDisconnected(DisconnectEventArgs e) { foreach (var client in this.m_targetClients) { client.TryShutdown(); client.SafeDispose(); } - base.OnDisconnected(e); + await base.OnDisconnected(e); } - private void TcpClient_Disconnected(ITcpClientBase client, DisconnectEventArgs e) + private async Task TcpClient_Disconnected(ITcpClientBase client, DisconnectEventArgs e) { + await EasyTask.CompletedTask; foreach (var item in client.PluginsManager.Plugins) { if (typeof(ReconnectionPlugin<>) == item.GetType().GetGenericTypeDefinition()) @@ -125,8 +126,9 @@ namespace ThingsGateway.Foundation.Sockets this.m_internalDis?.Invoke(this, (ITcpClient)client, e); } - private void TcpClient_Received(TcpClient client, ByteBlock byteBlock, IRequestInfo requestInfo) + private async Task TcpClient_Received(TcpClient client, ReceivedDataEventArgs e) { + await EasyTask.CompletedTask; if (this.DisposedValue) { return; @@ -134,7 +136,7 @@ namespace ThingsGateway.Foundation.Sockets try { - var data = this.m_internalTargetClientRev?.Invoke(this, client, byteBlock, requestInfo); + var data = this.m_internalTargetClientRev?.Invoke(this, client, e); if (data != null) { if (this.CanSend) diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/SocketClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/SocketClient.cs index 250a2bfa4..aa37a1e25 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/SocketClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/SocketClient.cs @@ -23,7 +23,6 @@ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ using System.Diagnostics; -using System.Net.Security; using System.Net.Sockets; namespace ThingsGateway.Foundation.Sockets @@ -40,24 +39,26 @@ namespace ThingsGateway.Foundation.Sockets public SocketClient() { this.Protocol = Protocol.Tcp; - this.m_receiveCounter = new ValueCounter - { - Period = TimeSpan.FromSeconds(1), - OnPeriod = this.OnReceivePeriod - }; - m_sendCounter = new ValueCounter - { - Period = TimeSpan.FromSeconds(1), - OnPeriod = this.OnSendPeriod - }; + //this.m_receiveCounter = new ValueCounter + //{ + // Period = TimeSpan.FromSeconds(1), + // OnPeriod = this.OnReceivePeriod + //}; + //m_sendCounter = new ValueCounter + //{ + // Period = TimeSpan.FromSeconds(1), + // OnPeriod = this.OnSendPeriod + //}; } #region 变量 - private ValueCounter m_receiveCounter; - private ValueCounter m_sendCounter; - private long m_bufferRate = 1; + + //private ValueCounter m_receiveCounter; + //private ValueCounter m_sendCounter; + //private long m_bufferRate = 1; private DelaySender m_delaySender; - private Stream m_workStream; + + private TcpCore m_tcpCore; #endregion 变量 @@ -87,6 +88,15 @@ namespace ThingsGateway.Foundation.Sockets /// public bool IsClient => false; + /// + public DateTime LastReceivedTime => this.GetTcpCore().ReceiveCounter.LastIncrement; + + /// + public DateTime LastSendTime => this.GetTcpCore().SendCounter.LastIncrement; + + /// + public TcpListenOption ListenOption { get; private set; } + /// public Socket MainSocket { get; private set; } @@ -102,36 +112,18 @@ namespace ThingsGateway.Foundation.Sockets /// public Protocol Protocol { get; set; } - /// - public TcpListenOption ListenOption { get; private set; } - - /// - public ReceiveType ReceiveType { get; private set; } - /// public TcpServiceBase Service { get; private set; } - /// - public bool UseSsl { get; private set; } - - /// - public DateTime LastReceivedTime => this.m_receiveCounter.LastIncrement; - - /// - public DateTime LastSendTime => this.m_sendCounter.LastIncrement; - - /// - public Func OnHandleRawBuffer { get; set; } - - /// - public Func OnHandleReceivedData { get; set; } - /// public string ServiceIP { get; private set; } /// public int ServicePort { get; private set; } + /// + public bool UseSsl { get; private set; } + #endregion 属性 #region Internal @@ -140,71 +132,50 @@ namespace ThingsGateway.Foundation.Sockets { try { - if (this.ReceiveType == ReceiveType.Iocp) - { - var eventArgs = new SocketAsyncEventArgs(); - eventArgs.Completed += this.EventArgs_Completed; - var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize); - eventArgs.UserToken = byteBlock; - eventArgs.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity); - if (!this.MainSocket.ReceiveAsync(eventArgs)) - { - this.ProcessReceived(eventArgs); - } - } + this.m_tcpCore.BeginIocpReceive(); + //if (this.ReceiveType == ReceiveType.Iocp) + //{ + // //var eventArgs = new SocketAsyncEventArgs(); + // //eventArgs.Completed += this.EventArgs_Completed; + // //var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize); + // //eventArgs.UserToken = byteBlock; + // //eventArgs.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity); + // //if (!this.MainSocket.ReceiveAsync(eventArgs)) + // //{ + // // this.ProcessReceived(eventArgs); + // //} + + //} } catch (Exception ex) { - this.BreakOut(ex.Message, false); + this.BreakOut(default, false, ex.Message); } } - internal void BeginReceiveSsl(ServiceSslOption sslOption) + internal Task AuthenticateAsync(ServiceSslOption sslOption) { - var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.MainSocket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.MainSocket, false), false); - sslStream.AuthenticateAsServer(sslOption.Certificate); - this.m_workStream = sslStream; - this.UseSsl = true; - if (this.ReceiveType == ReceiveType.Iocp) - { - this.BeginSsl(); - } + return this.m_tcpCore.AuthenticateAsync(sslOption); + } + internal Task BeginReceiveSsl() + { + return this.m_tcpCore.BeginSslReceive(); } - internal void InternalConnected(ConnectedEventArgs e) + internal Task InternalConnected(ConnectedEventArgs e) { this.Online = true; - - this.OnConnected(e); - - if (e.Handled) - { - return; - } - - - if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(ITcpConnectedPlugin.OnTcpConnected), this, e)) - { - return; - } + return this.OnConnected(e); } - internal void InternalConnecting(ConnectingEventArgs e) + internal Task InternalConnecting(ConnectingEventArgs e) { - this.OnConnecting(e); - if (e.Handled) - { - return; - } - if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(ITcpConnectingPlugin.OnTcpConnecting), this, e)) - { - return; - } + return this.OnConnecting(e); } - internal void InternalInitialized() + internal Task InternalInitialized() { - this.OnInitialized(); + return this.OnInitialized(); } internal void InternalSetConfig(TouchSocketConfig config) @@ -223,15 +194,15 @@ namespace ThingsGateway.Foundation.Sockets this.Id = id; } - internal void InternalSetPluginsManager(IPluginsManager pluginsManager) - { - this.PluginsManager = pluginsManager; - } - internal void InternalSetListenOption(TcpListenOption option) { this.ListenOption = option; - this.ReceiveType = option.ReceiveType; + //this.ReceiveType = option.ReceiveType; + } + + internal void InternalSetPluginsManager(IPluginsManager pluginsManager) + { + this.PluginsManager = pluginsManager; } internal void InternalSetService(TcpServiceBase serviceBase) @@ -239,18 +210,68 @@ namespace ThingsGateway.Foundation.Sockets this.Service = serviceBase; } - internal void InternalSetSocket(Socket mainSocket) + internal void InternalSetSocket(Socket socket) { - this.MainSocket = mainSocket ?? throw new ArgumentNullException(nameof(mainSocket)); - this.IP = mainSocket.RemoteEndPoint.GetIP(); - this.Port = mainSocket.RemoteEndPoint.GetPort(); - this.ServiceIP = mainSocket.LocalEndPoint.GetIP(); - this.ServicePort = mainSocket.LocalEndPoint.GetPort(); + this.MainSocket = socket ?? throw new ArgumentNullException(nameof(socket)); + this.IP = socket.RemoteEndPoint.GetIP(); + this.Port = socket.RemoteEndPoint.GetPort(); + this.ServiceIP = socket.LocalEndPoint.GetIP(); + this.ServicePort = socket.LocalEndPoint.GetPort(); if (this.Config.GetValue(TouchSocketConfigExtension.DelaySenderProperty) is DelaySenderOption senderOption) { this.m_delaySender = new DelaySender(senderOption, this.MainSocket.AbsoluteSend); } + + var tcpCore = this.Service.RentTcpCore(); + tcpCore.Reset(socket); + tcpCore.OnReceived = this.HandleReceived; + tcpCore.OnBreakOut = this.BreakOut; + this.m_tcpCore = tcpCore; + } + + private void BreakOut(TcpCore core, bool manual, string msg) + { + if (this.GetSocketCliectCollection().TryRemove(this.Id, out _)) + { + if (this.Online) + { + this.Online = false; + this.MainSocket.SafeDispose(); + this.m_delaySender.SafeDispose(); + this.DataHandlingAdapter.SafeDispose(); + Task.Factory.StartNew(this.PrivateOnDisconnected, new DisconnectEventArgs(manual, msg)); + } + } + + base.Dispose(true); + } + + private void HandleReceived(TcpCore core, ByteBlock byteBlock) + { + try + { + if (this.DisposedValue) + { + return; + } + + if (this.ReceivingData(byteBlock).ConfigureAwait(false).GetAwaiter().GetResult()) + { + return; + } + + if (this.DataHandlingAdapter == null) + { + this.Logger.Error(this, TouchSocketResource.NullDataAdapter.GetDescription()); + return; + } + this.DataHandlingAdapter.ReceivedInput(byteBlock); + } + catch (Exception ex) + { + this.Logger.Log(LogLevel.Error, this, "在处理数据时发生错误", ex); + } } #endregion Internal @@ -264,43 +285,74 @@ namespace ThingsGateway.Foundation.Sockets public DisconnectEventHandler Disconnecting { get; set; } /// - /// 当客户端完整建立TCP连接。 + /// 当客户端完整建立Tcp连接。 /// /// - protected virtual void OnConnected(ConnectedEventArgs e) + protected virtual async Task OnConnected(ConnectedEventArgs e) { - this.Service.OnInternalConnected(this, e); + if (await this.PluginsManager.RaiseAsync(nameof(ITcpConnectedPlugin.OnTcpConnected), this, e)) + { + return; + } + await this.Service.OnInternalConnected(this, e); } /// /// 客户端正在连接。 /// - protected virtual void OnConnecting(ConnectingEventArgs e) + protected virtual async Task OnConnecting(ConnectingEventArgs e) { - this.Service.OnInternalConnecting(this, e); + if (await this.PluginsManager.RaiseAsync(nameof(ITcpConnectingPlugin.OnTcpConnecting), this, e)) + { + return; + } + await this.Service.OnInternalConnecting(this, e); } /// /// 客户端已断开连接。 /// /// - protected virtual void OnDisconnected(DisconnectEventArgs e) + protected virtual async Task OnDisconnected(DisconnectEventArgs e) { - this.Disconnected?.Invoke(this, e); + if (this.Disconnected != null) + { + await this.Disconnected.Invoke(this, e); + if (e.Handled) + { + return; + } + } + + if (await this.PluginsManager.RaiseAsync(nameof(ITcpDisconnectedPlugin.OnTcpDisconnected), this, e)) + { + return; + } + await this.Service.OnInternalDisconnected(this, e); } /// /// 即将断开连接(仅主动断开时有效)。 - /// - /// 当主动调用Close断开时。 - /// /// /// - protected virtual void OnDisconnecting(DisconnectEventArgs e) + protected virtual async Task OnDisconnecting(DisconnectEventArgs e) { try { - this.Disconnecting?.Invoke(this, e); + if (this.Disconnecting != null) + { + await this.Disconnecting.Invoke(this, e); + if (e.Handled) + { + return; + } + } + + if (await this.PluginsManager.RaiseAsync(nameof(ITcpDisconnectingPlugin.OnTcpDisconnecting), this, e)) + { + return; + } + await this.Service.OnInternalDisconnecting(this, e); } catch (Exception ex) { @@ -311,42 +363,33 @@ namespace ThingsGateway.Foundation.Sockets /// /// 当初始化完成时,执行在之前。 /// - protected virtual void OnInitialized() + protected virtual Task OnInitialized() { + return EasyTask.CompletedTask; } - private void PrivateOnDisconnected(DisconnectEventArgs e) + private Task PrivateOnDisconnecting(object obj) { - this.OnDisconnected(e); - if (e.Handled) - { - return; - } - - if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(ITcpDisconnectedPlugin.OnTcpDisconnected), this, e)) - { - return; - } - - if (!e.Handled) - { - this.Service.OnInternalDisconnected(this, e); - } + return this.OnDisconnecting((DisconnectEventArgs)obj); } - private void PrivateOnDisconnecting(DisconnectEventArgs e) + private async Task PrivateOnDisconnected(object obj) { - this.OnDisconnecting(e); - if (e.Handled) + this.m_receiver?.TryInputReceive(default, default); + var e = (DisconnectEventArgs)obj; + try { - return; + await this.OnDisconnected(e); } - - if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(ITcpDisconnectingPlugin.OnTcpDisconnecting), this, e)) + catch (Exception) { - return; } - this.Service.OnInternalDisconnecting(this, e); + finally + { + var tcp = this.m_tcpCore; + this.m_tcpCore = null; + this.Service?.ReturnTcpCore(tcp); + } } #endregion 事件&委托 @@ -354,62 +397,37 @@ namespace ThingsGateway.Foundation.Sockets /// public override int ReceiveBufferSize { - get => base.ReceiveBufferSize; + get => this.GetTcpCore().ReceiveBufferSize; set { - base.ReceiveBufferSize = value; - if (this.MainSocket != null) - { - this.MainSocket.ReceiveBufferSize = base.ReceiveBufferSize; - } + this.GetTcpCore().ReceiveBufferSize = value; } } /// public override int SendBufferSize { - get => base.SendBufferSize; + get => this.GetTcpCore().SendBufferSize; set { - base.SendBufferSize = value; - if (this.MainSocket != null) - { - this.MainSocket.SendBufferSize = base.SendBufferSize; - } + this.GetTcpCore().SendBufferSize = value; } } - private void OnSendPeriod(long value) - { - this.SendBufferSize = TouchSocketUtility.HitBufferLength(value); - } - private void OnReceivePeriod(long value) - { - this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value); - } - /// public virtual void Close(string msg = TouchSocketCoreUtility.Empty) { - lock (this.SyncRoot) { if (this.Online) { + Task.Factory.StartNew(this.PrivateOnDisconnecting, new DisconnectEventArgs(true, msg)); this.MainSocket.TryClose(); - this.PrivateOnDisconnecting(new DisconnectEventArgs(true, msg)); + this.BreakOut(default, true, msg); } - this.BreakOut(msg, true); } } - /// - public Stream GetStream() - { - this.m_workStream ??= new NetworkStream(this.MainSocket, true); - return this.m_workStream; - } - /// /// /// @@ -440,6 +458,7 @@ namespace ThingsGateway.Foundation.Sockets /// protected void DirectResetId(string newId) { + this.ThrowIfDisposed(); if (string.IsNullOrEmpty(newId)) { throw new ArgumentException($"“{nameof(newId)}”不能为 null 或空。", nameof(newId)); @@ -455,11 +474,7 @@ namespace ThingsGateway.Foundation.Sockets socketClient.Id = newId; if (this.GetSocketCliectCollection().TryAdd(socketClient)) { - if (this.PluginsManager.Enable) - { - var e = new IdChangedEventArgs(oldId, newId); - this.PluginsManager.Raise(nameof(IIdChangedPlugin.OnIdChanged), socketClient, e); - } + this.IdChanged(oldId, newId).ConfigureAwait(false).GetAwaiter().GetResult(); return; } else @@ -488,43 +503,73 @@ namespace ThingsGateway.Foundation.Sockets { if (this.Online) { - this.PrivateOnDisconnecting(new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开")); + Task.Factory.StartNew(this.PrivateOnDisconnecting, new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开")); + this.BreakOut(default, true, $"{nameof(Dispose)}主动断开"); } - this.BreakOut($"{nameof(Dispose)}主动断开", true); + base.Dispose(disposing); } } /// - /// 处理已接收到的数据。 - /// 根据不同的数据处理适配器,会传递不同的数据 - /// - /// 以二进制流形式传递 - /// 以解析的数据对象传递 - /// 如果返回则表示数据已被处理,且不会再向下传递。 - protected virtual bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) - { - return false; - } - - /// - /// 当即将发送时。 + /// 当即将发送时,如果覆盖父类方法,则不会触发插件。 /// /// 数据缓存区 /// 偏移 /// 长度 /// 返回值表示是否允许发送 - protected virtual bool HandleSendingData(byte[] buffer, int offset, int length) + protected virtual async Task SendingData(byte[] buffer, int offset, int length) { - if (this.PluginsManager.Enable) + if (this.PluginsManager.GetPluginCount(nameof(ITcpSendingPlugin.OnTcpSending)) > 0) { var args = new SendingEventArgs(buffer, offset, length); - this.PluginsManager.Raise(nameof(ITcpSendingPlugin.OnTcpSending), this, args); + await this.PluginsManager.RaiseAsync(nameof(ITcpSendingPlugin.OnTcpSending), this, args).ConfigureAwait(false); return args.IsPermitOperation; } return true; } + /// + /// 当Id更新的时候触发 + /// + /// + /// + /// + protected Task IdChanged(string oldId, string newId) + { + return this.PluginsManager.RaiseAsync(nameof(IIdChangedPlugin.OnIdChanged), this, new IdChangedEventArgs(oldId, newId)); + } + + /// + /// 当收到适配器处理的数据时。 + /// + /// 如果返回则表示数据已被处理,且不会再向下传递。 + protected virtual async Task ReceivedData(ReceivedDataEventArgs e) + { + await this.PluginsManager.RaiseAsync(nameof(ITcpReceivedPlugin.OnTcpReceived), this, e); + + if (e.Handled) + { + return; + } + + await this.Service.OnInternalReceivedData(this, e); + } + + /// + /// 当收到原始数据 + /// + /// + /// 如果返回则表示数据已被处理,且不会再向下传递。 + protected virtual Task ReceivingData(ByteBlock byteBlock) + { + if (this.PluginsManager.GetPluginCount(nameof(ITcpReceivingPlugin.OnTcpReceiving)) > 0) + { + return this.PluginsManager.RaiseAsync(nameof(ITcpReceivingPlugin.OnTcpReceiving), this, new ByteBlockEventArgs(byteBlock)); + } + return Task.FromResult(false); + } + /// /// 设置适配器,该方法不会检验的值。 /// @@ -545,237 +590,51 @@ namespace ThingsGateway.Foundation.Sockets adapter.OnLoaded(this); adapter.ReceivedCallBack = this.PrivateHandleReceivedData; adapter.SendCallBack = this.DefaultSend; + adapter.SendAsyncCallBack = this.DefaultSendAsync; this.DataHandlingAdapter = adapter; } - private void BeginSsl() - { - if (!this.DisposedValue) - { - var byteBlock = new ByteBlock(this.ReceiveBufferSize); - try - { - this.m_workStream.BeginRead(byteBlock.Buffer, 0, byteBlock.Capacity, this.EndSsl, byteBlock); - } - catch (Exception ex) - { - byteBlock.SafeDispose(); - this.BreakOut(ex.Message, false); - } - } - } - - private void BreakOut(string msg, bool manual) - { - lock (this.SyncRoot) - { - if (this.GetSocketCliectCollection().TryRemove(this.Id, out _)) - { - if (this.Online) - { - this.Online = false; - this.MainSocket.SafeDispose(); - this.m_delaySender.SafeDispose(); - this.DataHandlingAdapter.SafeDispose(); - this.PrivateOnDisconnected(new DisconnectEventArgs(manual, msg)); - } - } - - base.Dispose(true); - } - } - - private void EndSsl(IAsyncResult result) - { - var byteBlock = (ByteBlock)result.AsyncState; - try - { - var r = this.m_workStream.EndRead(result); - if (r == 0) - { - this.BreakOut("远程终端主动关闭", false); - return; - } - byteBlock.SetLength(r); - - this.HandleBuffer(byteBlock); - this.BeginSsl(); - } - catch (Exception ex) - { - byteBlock.SafeDispose(); - this.BreakOut(ex.Message, false); - } - } - - private void EventArgs_Completed(object sender, SocketAsyncEventArgs e) - { - try - { - this.m_bufferRate = 1; - this.ProcessReceived(e); - } - catch (Exception ex) - { - e.SafeDispose(); - this.BreakOut(ex.Message, false); - } - } - private SocketClientCollection GetSocketCliectCollection() { - return this.Service?.SocketClients as SocketClientCollection; + return this.Service.SocketClients as SocketClientCollection; } - private void HandleBuffer(ByteBlock byteBlock) + private TcpCore GetTcpCore() { - try - { - if (this.DisposedValue) - { - return; - } - this.m_receiveCounter.Increment(byteBlock.Length); - if (this.OnHandleRawBuffer?.Invoke(byteBlock) == false) - { - return; - } - if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(ITcpReceivingPlugin.OnTcpReceiving), this, new ByteBlockEventArgs(byteBlock))) - { - return; - } - if (this.DataHandlingAdapter == null) - { - this.Logger.Error(this, TouchSocketResource.NullDataAdapter.GetDescription()); - return; - } - this.DataHandlingAdapter.ReceivedInput(byteBlock); - } - catch (Exception ex) - { - this.Logger.Log(LogLevel.Error, this, "在处理数据时发生错误", ex); - } - finally - { - byteBlock.Dispose(); - } + this.ThrowIfDisposed(); + return this.m_tcpCore ?? throw new ObjectDisposedException(this.GetType().Name); } private void PrivateHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) { - if (this.OnHandleReceivedData?.Invoke(byteBlock, requestInfo) == false) + if (this.m_receiver != null) { - return; - } - - if (this.HandleReceivedData(byteBlock, requestInfo)) - { - return; - } - - if (this.PluginsManager.GetPluginCount(nameof(ITcpReceivedPlugin.OnTcpReceived)) > 0 && this.PluginsManager.Raise(nameof(ITcpReceivedPlugin.OnTcpReceived), this, new ReceivedDataEventArgs(byteBlock, requestInfo))) - { - return; - } - - this.Service.OnInternalReceivedData(this, byteBlock, requestInfo); - } - - private void ProcessReceived(SocketAsyncEventArgs e) - { - if (this.DisposedValue) - { - e.SafeDispose(); - return; - } - - if (e.SocketError != SocketError.Success) - { - e.SafeDispose(); - this.BreakOut(e.SocketError.ToString(), false); - } - else if (e.BytesTransferred > 0) - { - var byteBlock = (ByteBlock)e.UserToken; - byteBlock.SetLength(e.BytesTransferred); - this.HandleBuffer(byteBlock); - try + if (this.m_receiver.TryInputReceive(byteBlock, requestInfo)) { - var newByteBlock = new ByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength)); - e.UserToken = newByteBlock; - e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Capacity); - - if (!this.MainSocket.ReceiveAsync(e)) - { - this.m_bufferRate += 2; - this.ProcessReceived(e); - } - } - catch (Exception ex) - { - this.BreakOut(ex.Message, false); + return; } } - else - { - e.SafeDispose(); - this.BreakOut("远程主机主动断开连接", false); - } + this.ReceivedData(new ReceivedDataEventArgs(byteBlock, requestInfo)).GetFalseAwaitResult(); } #region 发送 - /// /// - /// - /// - /// - /// - /// - /// - /// public void DefaultSend(byte[] buffer, int offset, int length) { - if (!this.CanSend) + if (this.SendingData(buffer, offset, length).GetFalseAwaitResult()) { - throw new NotConnectedException(TouchSocketResource.NotConnected.GetDescription()); - } - if (this.HandleSendingData(buffer, offset, length)) - { - if (this.UseSsl) - { - this.m_workStream.Write(buffer, offset, length); - } - else - { - if (this.m_delaySender != null && length < this.m_delaySender.DelayLength) - { - this.m_delaySender.Send(QueueDataBytes.CreateNew(buffer, offset, length)); - } - else - { - this.MainSocket.AbsoluteSend(buffer, offset, length); - } - } - this.m_sendCounter.Increment(length); + this.GetTcpCore().Send(buffer, offset, length); } } - /// /// - /// - /// - /// - /// - /// - /// - /// - public Task DefaultSendAsync(byte[] buffer, int offset, int length) + public async Task DefaultSendAsync(byte[] buffer, int offset, int length) { - return Task.Run(() => + if (await this.SendingData(buffer, offset, length)) { - this.DefaultSend(buffer, offset, length); - }); + await this.GetTcpCore().SendAsync(buffer, offset, length); + } } #region 同步发送 @@ -789,10 +648,7 @@ namespace ThingsGateway.Foundation.Sockets /// public virtual void Send(IRequestInfo requestInfo) { - if (this.DisposedValue) - { - return; - } + this.ThrowIfDisposed(); if (this.DataHandlingAdapter == null) { throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); @@ -815,10 +671,7 @@ namespace ThingsGateway.Foundation.Sockets /// public virtual void Send(byte[] buffer, int offset, int length) { - if (this.DisposedValue) - { - return; - } + this.ThrowIfDisposed(); if (this.DataHandlingAdapter == null) { throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); @@ -832,10 +685,7 @@ namespace ThingsGateway.Foundation.Sockets /// public virtual void Send(IList> transferBytes) { - if (this.DisposedValue) - { - return; - } + this.ThrowIfDisposed(); if (this.DataHandlingAdapter == null) { throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); @@ -867,7 +717,7 @@ namespace ThingsGateway.Foundation.Sockets #region 异步发送 /// - /// IOCP发送 + /// /// /// /// @@ -877,10 +727,12 @@ namespace ThingsGateway.Foundation.Sockets /// public virtual Task SendAsync(byte[] buffer, int offset, int length) { - return Task.Run(() => + this.ThrowIfDisposed(); + if (this.DataHandlingAdapter == null) { - this.Send(buffer, offset, length); - }); + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); + } + return this.DataHandlingAdapter.SendInputAsync(buffer, offset, length); } /// @@ -892,22 +744,51 @@ namespace ThingsGateway.Foundation.Sockets /// public virtual Task SendAsync(IRequestInfo requestInfo) { - return Task.Run(() => + this.ThrowIfDisposed(); + if (this.DataHandlingAdapter == null) { - this.Send(requestInfo); - }); + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); + } + if (!this.DataHandlingAdapter.CanSendRequestInfo) + { + throw new NotSupportedException($"当前适配器不支持对象发送。"); + } + return this.DataHandlingAdapter.SendInputAsync(requestInfo); } /// /// /// /// + /// + /// public virtual Task SendAsync(IList> transferBytes) { - return Task.Run(() => + this.ThrowIfDisposed(); + if (this.DataHandlingAdapter == null) { - this.Send(transferBytes); - }); + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); + } + if (this.DataHandlingAdapter.CanSplicingSend) + { + return this.DataHandlingAdapter.SendInputAsync(transferBytes); + } + else + { + var length = 0; + foreach (var item in transferBytes) + { + length += item.Count; + } + using (var byteBlock = new ByteBlock(length)) + { + foreach (var item in transferBytes) + { + byteBlock.Write(item.Array, item.Offset, item.Count); + } + return this.DataHandlingAdapter.SendInputAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + } } #endregion 异步发送 @@ -970,13 +851,22 @@ namespace ThingsGateway.Foundation.Sockets #endregion 发送 - /// + #region Receiver + + private Receiver m_receiver; + /// - /// - /// - public override string ToString() + public IReceiver CreateReceiver() { - return this.GetIPPort(); + return this.m_receiver ??= new Receiver(this); } + + /// + public void ClearReceiver() + { + this.m_receiver = null; + } + + #endregion } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpClient.cs index c46a3bbc9..3477347f1 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpClient.cs @@ -22,1186 +22,969 @@ // 感谢您的下载和使用 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -using System.Net.Security; using System.Net.Sockets; using System.Runtime.InteropServices; -namespace ThingsGateway.Foundation.Sockets; - -/// -/// 简单TCP客户端 -/// -public class TcpClient : TcpClientBase -{ - - /// - /// 接收到数据 - /// - public ReceivedEventHandler Received { get; set; } - - /// - /// 接收数据 - /// - /// - /// - protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) - { - this.Received?.Invoke(this, byteBlock, requestInfo); - return false; - } -} - -/// -/// TCP客户端 -/// -[System.Diagnostics.DebuggerDisplay("{IP}:{Port}")] -public class TcpClientBase : BaseSocket, ITcpClient +namespace ThingsGateway.Foundation.Sockets { /// - /// 构造函数 + /// 简单Tcp客户端 /// - public TcpClientBase() + public class TcpClient : TcpClientBase { - this.Protocol = Protocol.Tcp; - this.m_receiveCounter = new ValueCounter + /// + /// 接收到数据 + /// + public ReceivedEventHandler Received { get; set; } + + /// + protected override async Task ReceivedData(ReceivedDataEventArgs e) { - Period = TimeSpan.FromSeconds(1), - OnPeriod = this.OnReceivePeriod - }; - this.m_sendCounter = new ValueCounter - { - Period = TimeSpan.FromSeconds(1), - OnPeriod = this.OnSendPeriod - }; - this.m_tcpCore = new InternalTcpCore() - { - OnReceived = this.HandleBuffer - }; - } - - #region 变量 - - private DelaySender m_delaySender; - private Stream m_workStream; - private long m_bufferRate = 1; - private volatile bool m_online; - private ValueCounter m_receiveCounter; - private ValueCounter m_sendCounter; - private InternalTcpCore m_tcpCore; - - #endregion 变量 - - #region 事件 - - /// - public ConnectedEventHandler Connected { get; set; } - - /// - public ConnectingEventHandler Connecting { get; set; } - - /// - public DisconnectEventHandler Disconnected { get; set; } - - /// - public DisconnectEventHandler Disconnecting { get; set; } - - private void PrivateOnConnected(object o) - { - var e = (ConnectedEventArgs)o; - this.OnConnected(e); - if (e.Handled) - { - return; - } - this.PluginsManager.Raise(nameof(ITcpConnectedPlugin.OnTcpConnected), this, e); - } - - /// - /// 已经建立Tcp连接 - /// - /// - protected virtual void OnConnected(ConnectedEventArgs e) - { - try - { - this.Connected?.Invoke(this, e); - } - catch (Exception ex) - { - this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Connected)}中发生错误。", ex); - } - } - - private void PrivateOnConnecting(ConnectingEventArgs e) - { - if (this.CanSetDataHandlingAdapter) - { - this.SetDataHandlingAdapter(this.Config.GetValue(TouchSocketConfigExtension.TcpDataHandlingAdapterProperty).Invoke()); - } - - this.OnConnecting(e); - if (e.Handled) - { - return; - } - this.PluginsManager.Raise(nameof(ITcpConnectingPlugin.OnTcpConnecting), this, e); - } - - /// - /// 准备连接的时候,此时已初始化Socket,但是并未建立Tcp连接 - /// - /// - protected virtual void OnConnecting(ConnectingEventArgs e) - { - try - { - this.Connecting?.Invoke(this, e); - } - catch (Exception ex) - { - this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.OnConnecting)}中发生错误。", ex); - } - } - - private void PrivateOnDisconnected(DisconnectEventArgs e) - { - this.OnDisconnected(e); - if (e.Handled) - { - return; - } - this.PluginsManager.Raise(nameof(ITcpDisconnectedPlugin.OnTcpDisconnected), this, e); - } - - /// - /// 断开连接。在客户端未设置连接状态时,不会触发 - /// - /// - protected virtual void OnDisconnected(DisconnectEventArgs e) - { - try - { - this.Disconnected?.Invoke(this, e); - } - catch (Exception ex) - { - this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Disconnected)}中发生错误。", ex); - } - } - - private void PrivateOnDisconnecting(DisconnectEventArgs e) - { - this.OnDisconnecting(e); - if (e.Handled) - { - return; - } - this.PluginsManager.Raise(nameof(ITcpDisconnectingPlugin.OnTcpDisconnecting), this, e); - } - - /// - /// 即将断开连接(仅主动断开时有效)。 - /// - /// 当主动调用Close断开时。 - /// - /// - /// - protected virtual void OnDisconnecting(DisconnectEventArgs e) - { - try - { - this.Disconnecting?.Invoke(this, e); - } - catch (Exception ex) - { - this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Disconnecting)}中发生错误。", ex); - } - } - - #endregion 事件 - - #region 属性 - - /// - public DateTime LastReceivedTime => this.m_receiveCounter.LastIncrement; - - /// - public DateTime LastSendTime => this.m_sendCounter.LastIncrement; - - /// - public Func OnHandleRawBuffer { get; set; } - - /// - public Func OnHandleReceivedData { get; set; } - - /// - public IContainer Container { get; private set; } - - /// - public virtual bool CanSetDataHandlingAdapter => true; - - /// - public TouchSocketConfig Config { get; private set; } - - /// - public SingleStreamDataHandlingAdapter DataHandlingAdapter { get; private set; } - - /// - public string IP { get; private set; } - - /// - public Socket MainSocket { get; private set; } - - /// - public bool Online { get => this.m_online; } - - /// - public bool CanSend => this.m_online; - - /// - public IPluginsManager PluginsManager { get; private set; } - - /// - public int Port { get; private set; } - - /// - public ReceiveType ReceiveType { get; private set; } - - /// - public bool UseSsl { get; private set; } - - /// - public Protocol Protocol { get; set; } - - /// - public IPHost RemoteIPHost { get; private set; } - - /// - public bool IsClient => true; - - #endregion 属性 - private EasyLock EasyLock { get; set; } = new(); - - #region 断开操作 - - /// - public virtual void Close(string msg = TouchSocketCoreUtility.Empty) - { - lock (this.SyncRoot) - { - if (this.m_online) + if (this.Received != null) { - this.PrivateOnDisconnecting(new DisconnectEventArgs(true, msg)); - - this.m_online = false; - this.MainSocket.TryClose(); - - this.MainSocket.SafeDispose(); - this.m_delaySender.SafeDispose(); - this.m_workStream.SafeDispose(); - this.DataHandlingAdapter.SafeDispose(); - this.PrivateOnDisconnected(new DisconnectEventArgs(true, msg)); - } - } - } - - private void BreakOut(string msg) - { - lock (this.SyncRoot) - { - if (this.m_online) - { - this.m_online = false; - this.MainSocket.SafeDispose(); - this.m_delaySender.SafeDispose(); - this.m_workStream.SafeDispose(); - this.DataHandlingAdapter.SafeDispose(); - this.PrivateOnDisconnected(new DisconnectEventArgs(false, msg)); - } - } - } - - /// - /// - /// - /// - protected override void Dispose(bool disposing) - { - lock (this.SyncRoot) - { - if (this.m_online) - { - this.m_online = false; - this.MainSocket.TryClose(); - this.PrivateOnDisconnecting(new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开")); - - this.MainSocket.SafeDispose(); - this.m_delaySender.SafeDispose(); - this.m_workStream.SafeDispose(); - this.DataHandlingAdapter.SafeDispose(); - this.PluginsManager.SafeDispose(); - this.PrivateOnDisconnected(new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开")); - } - } - base.Dispose(disposing); - } - - #endregion 断开操作 - - #region Connect - - /// - /// 建立Tcp的连接。 - /// - /// - /// - /// - /// - /// - protected void TcpConnect(int timeout) - { - lock (this.SyncRoot) - { - if (this.m_online) - { - return; - } - if (this.DisposedValue) - { - throw new ObjectDisposedException(this.GetType().FullName); - } - if (this.Config == null) - { - throw new ArgumentNullException(nameof(this.Config), "配置文件不能为空。"); - } - var iPHost = this.Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty) ?? throw new ArgumentNullException(nameof(IPHost), "iPHost不能为空。"); - this.MainSocket.SafeDispose(); - var socket = this.CreateSocket(iPHost); - this.PrivateOnConnecting(new ConnectingEventArgs(socket)); - if (timeout == 5000) - { - socket.Connect(iPHost.Host, iPHost.Port); - } - else - { - var task = Task.Run(() => + await this.Received.Invoke(this, e); + if (e.Handled) { - socket.Connect(iPHost.Host, iPHost.Port); - }); - task.ConfigureAwait(false); - if (!task.Wait(timeout)) - { - socket.SafeDispose(); - throw new TimeoutException(); - } - } - this.m_online = true; - this.SetSocket(socket); - this.BeginReceive(); - this.PrivateOnConnected(new ConnectedEventArgs()); - } - } - - /// - /// 异步连接服务器 - /// - /// - /// - /// - /// - /// - /// - protected virtual async Task TcpConnectAsync(int timeout, CancellationToken cancellationToken = default) - { - try - { - await EasyLock.WaitAsync(); - if (m_online) - { - return; - } - if (DisposedValue) - { - throw new ObjectDisposedException(GetType().FullName); - } - if (Config == null) - { - throw new ArgumentNullException("配置文件不能为空。"); - } - var iPHost = Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty) ?? throw new ArgumentNullException("iPHost不能为空。"); - MainSocket.SafeDispose(); - var socket = CreateSocket(iPHost); - var args = new ConnectingEventArgs(socket); - PrivateOnConnecting(args); - -#if (NET6_0_OR_GREATER) - using CancellationTokenSource cancellationTokenSource = new(timeout); - using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken); - try - { - await socket.ConnectAsync(iPHost.EndPoint, stoppingToken.Token); - } - catch (OperationCanceledException) - { - throw new TimeoutException("连接超时"); - } - success(socket); - -#else - using CancellationTokenSource cancellationTokenSource = new(); - using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken); - var task = Task.Factory.FromAsync(socket.BeginConnect(iPHost.EndPoint, null, null), socket.EndConnect); - var result = await Task.WhenAny(task, Task.Delay(timeout, stoppingToken.Token)); - if (result == task) - { - cancellationTokenSource.Cancel(); - if (task.Exception != null) - { - socket?.SafeDispose(); - throw task.Exception; - } - else - { - success(socket); - } - } - else - { - socket?.SafeDispose(); - throw new TimeoutException("连接超时"); - } -#endif - - } - finally - { - EasyLock.Release(); - } - - void success(Socket socket) - { - m_online = true; - SetSocket(socket); - BeginReceive(); - PrivateOnConnected(new ConnectedEventArgs()); - } - } - - /// - public virtual ITcpClient Connect(int timeout = 5000) - { - TcpConnect(timeout); - return this; - } - - /// - public virtual async Task ConnectAsync(int timeout = 5000) - { - await TcpConnectAsync(timeout); - return this; - } - /// - public virtual async Task ConnectAsync(int timeout, CancellationToken cancellationToken) - { - await TcpConnectAsync(timeout, cancellationToken); - return this; - } - #endregion - - /// - public Stream GetStream() - { - this.ThrowIfDisposed(); - this.m_workStream ??= new NetworkStream(this.MainSocket, true); - return this.m_workStream; - } - - private void OnReceivePeriod(long value) - { - this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value); - } - - private void OnSendPeriod(long value) - { - this.SendBufferSize = TouchSocketUtility.HitBufferLength(value); - } - - /// - public override int ReceiveBufferSize - { - get => base.ReceiveBufferSize; - set - { - base.ReceiveBufferSize = value; - if (this.MainSocket != null) - { - this.MainSocket.ReceiveBufferSize = base.ReceiveBufferSize; - } - } - } - - /// - public override int SendBufferSize - { - get => base.SendBufferSize; - set - { - base.SendBufferSize = value; - if (this.MainSocket != null) - { - this.MainSocket.SendBufferSize = base.SendBufferSize; - } - } - } - - /// - public virtual void SetDataHandlingAdapter(SingleStreamDataHandlingAdapter adapter) - { - if (!this.CanSetDataHandlingAdapter) - { - throw new Exception($"不允许自由调用{nameof(SetDataHandlingAdapter)}进行赋值。"); - } - - this.SetAdapter(adapter); - } - - /// - public ITcpClient Setup(string ipHost) - { - var config = new TouchSocketConfig(); - config.SetRemoteIPHost(new IPHost(ipHost)); - return this.Setup(config); - } - - /// - public ITcpClient Setup(TouchSocketConfig config) - { - if (config == null) - { - throw new ArgumentNullException(nameof(config)); - } - - this.ThrowIfDisposed(); - - this.BuildConfig(config); - - this.PluginsManager.Raise(nameof(ILoadingConfigPlugin.OnLoadingConfig), this, new ConfigEventArgs(config)); - this.LoadConfig(this.Config); - this.PluginsManager.Raise(nameof(ILoadedConfigPlugin.OnLoadedConfig), this, new ConfigEventArgs(config)); - - return this; - } - - private void BuildConfig(TouchSocketConfig config) - { - this.Config = config; - - if (!(config.GetValue(TouchSocketCoreConfigExtension.ContainerProperty) is IContainer container)) - { - container = new Container(); - } - - if (!container.IsRegistered(typeof(ILog))) - { - container.RegisterSingleton(); - } - - if (!(config.GetValue(TouchSocketCoreConfigExtension.PluginsManagerProperty) is IPluginsManager pluginsManager)) - { - pluginsManager = new PluginsManager(container); - } - - if (container.IsRegistered(typeof(IPluginsManager))) - { - pluginsManager = container.Resolve(); - } - else - { - container.RegisterSingleton(pluginsManager); - } - - if (config.GetValue(TouchSocketCoreConfigExtension.ConfigureContainerProperty) is Action actionContainer) - { - actionContainer.Invoke(container); - } - - if (config.GetValue(TouchSocketCoreConfigExtension.ConfigurePluginsProperty) is Action actionPluginsManager) - { - pluginsManager.Enable = true; - actionPluginsManager.Invoke(pluginsManager); - } - this.Container = container; - this.PluginsManager = pluginsManager; - } - - private void PrivateHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) - { - if (this.OnHandleReceivedData?.Invoke(byteBlock, requestInfo) == false) - { - return; - } - - if (this.HandleReceivedData(byteBlock, requestInfo)) - { - return; - } - - if (this.PluginsManager.Enable) - { - var args = new ReceivedDataEventArgs(byteBlock, requestInfo); - this.PluginsManager.Raise(nameof(ITcpReceivedPlugin.OnTcpReceived), this, args); - } - } - - /// - /// 处理已接收到的数据。 - /// - /// 以二进制流形式传递 - /// 以解析的数据对象传递 - /// 如果返回则表示数据已被处理,且不会再向下传递。 - protected virtual bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) - { - return false; - } - - /// - /// 当即将发送时,如果覆盖父类方法,则不会触发插件。 - /// - /// 数据缓存区 - /// 偏移 - /// 长度 - /// 返回值表示是否允许发送 - protected virtual bool HandleSendingData(byte[] buffer, int offset, int length) - { - if (this.PluginsManager.Enable) - { - var args = new SendingEventArgs(buffer, offset, length); - this.PluginsManager.Raise(nameof(ITcpSendingPlugin.OnTcpSending), this, args); - return args.IsPermitOperation; - } - return true; - } - - /// - /// 加载配置 - /// - /// - protected virtual void LoadConfig(TouchSocketConfig config) - { - this.RemoteIPHost = config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty); - this.Logger ??= this.Container.Resolve(); - this.ReceiveType = config.GetValue(TouchSocketConfigExtension.ReceiveTypeProperty); - } - - /// - /// 设置适配器,该方法不会检验的值。 - /// - /// - protected void SetAdapter(SingleStreamDataHandlingAdapter adapter) - { - this.ThrowIfDisposed(); - if (adapter is null) - { - throw new ArgumentNullException(nameof(adapter)); - } - - if (this.Config != null) - { - adapter.Config(this.Config); - } - - adapter.Logger = this.Logger; - adapter.OnLoaded(this); - adapter.ReceivedCallBack = this.PrivateHandleReceivedData; - adapter.SendCallBack = this.DefaultSend; - this.DataHandlingAdapter = adapter; - } - - private void BeginReceive() - { - if (this.Config.GetValue(TouchSocketConfigExtension.SslOptionProperty) != null) - { - this.UseSsl = true; - } - if (this.UseSsl) - { - var sslOption = (ClientSslOption)this.Config.GetValue(TouchSocketConfigExtension.SslOptionProperty); - var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.MainSocket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.MainSocket, false), false); - if (sslOption.ClientCertificates == null) - { - sslStream.AuthenticateAsClient(sslOption.TargetHost); - } - else - { - sslStream.AuthenticateAsClient(sslOption.TargetHost, sslOption.ClientCertificates, sslOption.SslProtocols, sslOption.CheckCertificateRevocation); - } - this.m_workStream = sslStream; - if (this.ReceiveType != ReceiveType.None) - { - this.BeginSsl(); - } - } - else - { - if (this.ReceiveType == ReceiveType.Iocp) - { - var eventArgs = new SocketAsyncEventArgs(); - eventArgs.Completed += this.EventArgs_Completed; - - var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize); - eventArgs.UserToken = byteBlock; - eventArgs.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity); - if (!this.MainSocket.ReceiveAsync(eventArgs)) - { - this.ProcessReceived(eventArgs); - } - } - else if (this.ReceiveType == ReceiveType.Bio) - { - new Thread(this.BeginBio) - { - IsBackground = true - } - .Start(); - } - } - } - - private void BeginBio() - { - while (true) - { - var byteBlock = new ByteBlock(this.ReceiveBufferSize); - try - { - var r = this.MainSocket.Receive(byteBlock.Buffer); - if (r == 0) - { - this.BreakOut("远程终端主动关闭"); return; } + } + await base.ReceivedData(e); + } + } - byteBlock.SetLength(r); - this.HandleBuffer(byteBlock); + /// + /// Tcp客户端 + /// + [System.Diagnostics.DebuggerDisplay("{IP}:{Port}")] + public class TcpClientBase : BaseSocket, ITcpClient + { + /// + /// 构造函数 + /// + public TcpClientBase() + { + this.Protocol = Protocol.Tcp; + this.m_tcpCore = new InternalTcpCore(); + } + + #region 变量 + + private DelaySender m_delaySender; + private volatile bool m_online; + private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); + private readonly InternalTcpCore m_tcpCore; + #endregion 变量 + + #region 事件 + + /// + public ConnectedEventHandler Connected { get; set; } + + /// + public ConnectingEventHandler Connecting { get; set; } + + /// + public DisconnectEventHandler Disconnected { get; set; } + + /// + public DisconnectEventHandler Disconnecting { get; set; } + + private Task PrivateOnConnected(object o) + { + return this.OnConnected((ConnectedEventArgs)o); + } + + /// + /// 已经建立Tcp连接 + /// + /// + protected virtual async Task OnConnected(ConnectedEventArgs e) + { + try + { + if (this.Connected != null) + { + await this.Connected.Invoke(this, e); + if (e.Handled) + { + return; + } + } + await this.PluginsManager.RaiseAsync(nameof(ITcpConnectedPlugin.OnTcpConnected), this, e); } catch (Exception ex) { - this.BreakOut(ex.Message); - return; + this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Connected)}中发生错误。", ex); } } - } - private void BeginSsl() - { - var byteBlock = new ByteBlock(this.ReceiveBufferSize); - try + private Task PrivateOnConnecting(ConnectingEventArgs e) { - this.m_workStream.BeginRead(byteBlock.Buffer, 0, byteBlock.Capacity, this.EndSsl, byteBlock); - } - catch (Exception ex) - { - byteBlock.Dispose(); - this.BreakOut(ex.Message); - } - } - - private void EndSsl(IAsyncResult result) - { - var byteBlock = (ByteBlock)result.AsyncState; - try - { - var r = this.m_workStream.EndRead(result); - if (r == 0) + if (this.CanSetDataHandlingAdapter) { - this.BreakOut("远程终端主动关闭"); + this.SetDataHandlingAdapter(this.Config.GetValue(TouchSocketConfigExtension.TcpDataHandlingAdapterProperty).Invoke()); } - byteBlock.SetLength(r); - this.HandleBuffer(byteBlock); - this.BeginSsl(); - } - catch (Exception ex) - { - byteBlock.Dispose(); - this.BreakOut(ex.Message); - } - } - private Socket CreateSocket(IPHost iPHost) - { - Socket socket; - if (iPHost.HostNameType == UriHostNameType.Dns) - { - socket = new Socket(SocketType.Stream, ProtocolType.Tcp) - { - SendTimeout = this.Config.GetValue(TouchSocketConfigExtension.SendTimeoutProperty) - }; + return this.OnConnecting(e); } - else + + /// + /// 准备连接的时候,此时已初始化Socket,但是并未建立Tcp连接 + /// + /// + protected virtual async Task OnConnecting(ConnectingEventArgs e) { - socket = new Socket(iPHost.EndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) + try { - SendTimeout = this.Config.GetValue(TouchSocketConfigExtension.SendTimeoutProperty) - }; + if (this.Connecting != null) + { + await this.Connecting.Invoke(this, e); + if (e.Handled) + { + return; + } + } + await this.PluginsManager.RaiseAsync(nameof(ITcpConnectingPlugin.OnTcpConnecting), this, e); + } + catch (Exception ex) + { + this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.OnConnecting)}中发生错误。", ex); + } } - if (this.Config.GetValue(TouchSocketConfigExtension.KeepAliveValueProperty) is KeepAliveValue keepAliveValue) + + private Task PrivateOnDisconnected(object obj) { + this.m_receiver?.TryInputReceive(default, default); + return this.OnDisconnected((DisconnectEventArgs)obj); + } + + /// + /// 断开连接。在客户端未设置连接状态时,不会触发 + /// + /// + protected virtual async Task OnDisconnected(DisconnectEventArgs e) + { + try + { + if (this.Disconnected != null) + { + await this.Disconnected.Invoke(this, e).ConfigureAwait(false); + if (e.Handled) + { + return; + } + } + + await this.PluginsManager.RaiseAsync(nameof(ITcpDisconnectedPlugin.OnTcpDisconnected), this, e).ConfigureAwait(false); + } + catch (Exception ex) + { + this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Disconnected)}中发生错误。", ex); + } + } + + private Task PrivateOnDisconnecting(object obj) + { + return this.OnDisconnecting((DisconnectEventArgs)obj); + } + + /// + /// 即将断开连接(仅主动断开时有效)。 + /// + /// + protected virtual async Task OnDisconnecting(DisconnectEventArgs e) + { + try + { + if (this.Disconnecting != null) + { + await this.Disconnecting.Invoke(this, e).ConfigureAwait(false); + if (e.Handled) + { + return; + } + } + + await this.PluginsManager.RaiseAsync(nameof(ITcpDisconnectingPlugin.OnTcpDisconnecting), this, e).ConfigureAwait(false); + } + catch (Exception ex) + { + this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Disconnecting)}中发生错误。", ex); + } + } + + #endregion 事件 + + #region 属性 + + /// + public DateTime LastReceivedTime => this.GetTcpCore().ReceiveCounter.LastIncrement; + + /// + public DateTime LastSendTime => this.GetTcpCore().SendCounter.LastIncrement; + + /// + public IContainer Container { get; private set; } + + /// + public virtual bool CanSetDataHandlingAdapter => true; + + /// + public TouchSocketConfig Config { get; private set; } + + /// + public SingleStreamDataHandlingAdapter DataHandlingAdapter { get; private set; } + + /// + public string IP { get; private set; } + + /// + public Socket MainSocket { get; private set; } + + /// + public bool Online { get => this.m_online; } + + /// + public bool CanSend => this.m_online; + + /// + public IPluginsManager PluginsManager { get; private set; } + + /// + public int Port { get; private set; } + + + + /// + public bool UseSsl => this.GetTcpCore().UseSsl; + + /// + public Protocol Protocol { get; set; } + + /// + public IPHost RemoteIPHost { get; private set; } + + /// + public bool IsClient => true; + + #endregion 属性 + + #region 断开操作 + + /// + public virtual void Close(string msg = TouchSocketCoreUtility.Empty) + { + lock (this.SyncRoot) + { + if (this.m_online) + { + Task.Factory.StartNew(this.PrivateOnDisconnecting, new DisconnectEventArgs(true, msg)); + this.MainSocket.TryClose(); + this.BreakOut(default, true, msg); + } + } + } + + + /// + /// + /// + /// + protected override void Dispose(bool disposing) + { + lock (this.SyncRoot) + { + if (this.m_online) + { + Task.Factory.StartNew(this.PrivateOnDisconnecting, new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开")); + this.BreakOut(default, true, $"{nameof(Dispose)}主动断开"); + } + } + base.Dispose(disposing); + } + + #endregion 断开操作 + + #region Connect + + /// + /// 建立Tcp的连接。 + /// + /// + /// + /// + /// + /// + protected void TcpConnect(int timeout) + { + lock (this.SyncRoot) + { + if (this.m_online) + { + return; + } + if (this.DisposedValue) + { + throw new ObjectDisposedException(this.GetType().FullName); + } + if (this.Config == null) + { + throw new ArgumentNullException(nameof(this.Config), "配置文件不能为空。"); + } + var iPHost = this.Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty) ?? throw new ArgumentNullException(nameof(IPHost), "iPHost不能为空。"); + this.MainSocket.SafeDispose(); + var socket = this.CreateSocket(iPHost); + this.PrivateOnConnecting(new ConnectingEventArgs(socket)) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + if (timeout == 5000) + { + socket.Connect(iPHost.Host, iPHost.Port); + } + else + { + var task = Task.Run(() => + { + socket.Connect(iPHost.Host, iPHost.Port); + }); + task.ConfigureAwait(false); + if (!task.Wait(timeout)) + { + socket.SafeDispose(); + throw new TimeoutException(); + } + } + this.m_online = true; + this.SetSocket(socket); + this.BeginReceive(); + this.PrivateOnConnected(new ConnectedEventArgs()) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + } + } + + private void BeginReceive() + { + if (this.Config.GetValue(TouchSocketConfigExtension.SslOptionProperty) is ClientSslOption sslOption) + { + this.GetTcpCore().Authenticate(sslOption); + _ = this.GetTcpCore().BeginSslReceive(); + } + else + { + this.GetTcpCore().BeginIocpReceive(); + } + } + + + /// + /// 异步连接服务器 + /// + protected async Task TcpConnectAsync(int timeout, CancellationToken cancellationToken = default) + { + try + { + await this.m_semaphore.WaitAsync(); + if (this.m_online) + { + return; + } + if (this.DisposedValue) + { + throw new ObjectDisposedException(this.GetType().FullName); + } + if (this.Config == null) + { + throw new ArgumentNullException(nameof(this.Config), "配置文件不能为空。"); + } + var iPHost = this.Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty) ?? throw new ArgumentNullException(nameof(IPHost), "iPHost不能为空。"); + this.MainSocket.SafeDispose(); + var socket = this.CreateSocket(iPHost); + await this.PrivateOnConnecting(new ConnectingEventArgs(socket)); + +#if NET6_0_OR_GREATER + using CancellationTokenSource cancellationTokenSource = new(timeout); + using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken); + try + { + await socket.ConnectAsync(iPHost.EndPoint, stoppingToken.Token); + } + catch (OperationCanceledException) + { + throw new TimeoutException("连接超时"); + } + await SuccessAsync(socket); +#else + + using CancellationTokenSource cancellationTokenSource = new(); + using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken); + var task = Task.Factory.FromAsync(socket.BeginConnect(iPHost.EndPoint, null, null), socket.EndConnect); + var result = await Task.WhenAny(task, Task.Delay(timeout, stoppingToken.Token)); + if (result == task) + { + cancellationTokenSource.Cancel(); + if (task.Exception != null) + { + socket?.SafeDispose(); + throw task.Exception; + } + else + { + await SuccessAsync(socket); + } + } + else + { + socket?.SafeDispose(); + throw new TimeoutException("连接超时"); + } + + + + +#endif + async Task SuccessAsync(Socket socket) + { + this.m_online = true; + this.SetSocket(socket); + this.BeginReceive(); + await (this.PrivateOnConnected(new ConnectedEventArgs())); + } + } + finally + { + this.m_semaphore.Release(); + } + } + + + /// + public virtual ITcpClient Connect(int timeout = 5000) + { + this.TcpConnect(timeout); + return this; + } + + /// + public virtual async Task ConnectAsync(int timeout = 5000) + { + await this.TcpConnectAsync(timeout); + return this; + } + /// + public virtual async Task ConnectAsync(int timeout, CancellationToken cancellationToken) + { + await TcpConnectAsync(timeout, cancellationToken); + return this; + } + + #endregion Connect + + #region Receiver + + private Receiver m_receiver; + + /// + public IReceiver CreateReceiver() + { + return this.m_receiver ??= new Receiver(this); + } + + /// + public void ClearReceiver() + { + this.m_receiver = null; + } + + #endregion + + private void BreakOut(TcpCore core, bool manual, string msg) + { + lock (this.SyncRoot) + { + if (this.m_online) + { + this.m_online = false; + this.MainSocket.SafeDispose(); + this.m_delaySender.SafeDispose(); + this.DataHandlingAdapter.SafeDispose(); + Task.Factory.StartNew(this.PrivateOnDisconnected, new DisconnectEventArgs(manual, msg)); + } + } + } + + private TcpCore GetTcpCore() + { + this.ThrowIfDisposed(); + return this.m_tcpCore ?? throw new ObjectDisposedException(this.GetType().Name); + } + + + /// + public override int ReceiveBufferSize + { + get => this.GetTcpCore().ReceiveBufferSize; + set + { + this.GetTcpCore().ReceiveBufferSize = value; + } + } + + /// + public override int SendBufferSize + { + get => this.GetTcpCore().SendBufferSize; + set + { + this.GetTcpCore().SendBufferSize = value; + } + } + + /// + public virtual void SetDataHandlingAdapter(SingleStreamDataHandlingAdapter adapter) + { + if (!this.CanSetDataHandlingAdapter) + { + throw new Exception($"不允许自由调用{nameof(SetDataHandlingAdapter)}进行赋值。"); + } + + this.SetAdapter(adapter); + } + + /// + public ITcpClient Setup(TouchSocketConfig config) + { + if (config == null) + { + throw new ArgumentNullException(nameof(config)); + } + + this.ThrowIfDisposed(); + + this.BuildConfig(config); + + this.PluginsManager.Raise(nameof(ILoadingConfigPlugin.OnLoadingConfig), this, new ConfigEventArgs(config)); + this.LoadConfig(this.Config); + this.PluginsManager.Raise(nameof(ILoadedConfigPlugin.OnLoadedConfig), this, new ConfigEventArgs(config)); + + return this; + } + + private void BuildConfig(TouchSocketConfig config) + { + this.Config = config; + + if (!(config.GetValue(TouchSocketCoreConfigExtension.ContainerProperty) is IContainer container)) + { + container = new Container(); + } + + if (!container.IsRegistered(typeof(ILog))) + { + container.RegisterSingleton(); + } + + if (!(config.GetValue(TouchSocketCoreConfigExtension.PluginsManagerProperty) is IPluginsManager pluginsManager)) + { + pluginsManager = new PluginsManager(container); + } + + if (container.IsRegistered(typeof(IPluginsManager))) + { + pluginsManager = container.Resolve(); + } + else + { + container.RegisterSingleton(pluginsManager); + } + + if (config.GetValue(TouchSocketCoreConfigExtension.ConfigureContainerProperty) is Action actionContainer) + { + actionContainer.Invoke(container); + } + + if (config.GetValue(TouchSocketCoreConfigExtension.ConfigurePluginsProperty) is Action actionPluginsManager) + { + pluginsManager.Enable = true; + actionPluginsManager.Invoke(pluginsManager); + } + this.Container = container; + this.PluginsManager = pluginsManager; + } + + private void PrivateHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) + { + if (this.m_receiver != null) + { + if (this.m_receiver.TryInputReceive(byteBlock, requestInfo)) + { + return; + } + } + this.ReceivedData(new ReceivedDataEventArgs(byteBlock, requestInfo)).GetFalseAwaitResult(); + } + + /// + /// 当收到适配器处理的数据时。 + /// + /// + /// 如果返回则表示数据已被处理,且不会再向下传递。 + protected virtual Task ReceivedData(ReceivedDataEventArgs e) + { + return this.PluginsManager.RaiseAsync(nameof(ITcpReceivedPlugin.OnTcpReceived), this, e); + } + + + /// + /// 当即将发送时,如果覆盖父类方法,则不会触发插件。 + /// + /// 数据缓存区 + /// 偏移 + /// 长度 + /// 返回值表示是否允许发送 + protected virtual async Task SendingData(byte[] buffer, int offset, int length) + { + if (this.PluginsManager.GetPluginCount(nameof(ITcpSendingPlugin.OnTcpSending)) > 0) + { + var args = new SendingEventArgs(buffer, offset, length); + await this.PluginsManager.RaiseAsync(nameof(ITcpSendingPlugin.OnTcpSending), this, args).ConfigureAwait(false); + return args.IsPermitOperation; + } + return true; + } + + /// + /// 加载配置 + /// + /// + protected virtual void LoadConfig(TouchSocketConfig config) + { + this.RemoteIPHost = config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty); + this.Logger ??= this.Container.Resolve(); + } + + /// + /// 设置适配器,该方法不会检验的值。 + /// + /// + protected void SetAdapter(SingleStreamDataHandlingAdapter adapter) + { + this.ThrowIfDisposed(); + if (adapter is null) + { + throw new ArgumentNullException(nameof(adapter)); + } + + if (this.Config != null) + { + adapter.Config(this.Config); + } + + adapter.Logger = this.Logger; + adapter.OnLoaded(this); + adapter.ReceivedCallBack = this.PrivateHandleReceivedData; + adapter.SendCallBack = this.DefaultSend; + adapter.SendAsyncCallBack = this.DefaultSendAsync; + this.DataHandlingAdapter = adapter; + } + + private Socket CreateSocket(IPHost iPHost) + { + Socket socket; + if (iPHost.HostNameType == UriHostNameType.Dns) + { + socket = new Socket(SocketType.Stream, ProtocolType.Tcp) + { + SendTimeout = this.Config.GetValue(TouchSocketConfigExtension.SendTimeoutProperty) + }; + } + else + { + socket = new Socket(iPHost.EndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) + { + SendTimeout = this.Config.GetValue(TouchSocketConfigExtension.SendTimeoutProperty) + }; + } + if (this.Config.GetValue(TouchSocketConfigExtension.KeepAliveValueProperty) is KeepAliveValue keepAliveValue) + { #if NET45_OR_GREATER - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); - socket.IOControl(IOControlCode.KeepAliveValues, keepAliveValue.KeepAliveTime, null); -#else - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); socket.IOControl(IOControlCode.KeepAliveValues, keepAliveValue.KeepAliveTime, null); - } +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); + socket.IOControl(IOControlCode.KeepAliveValues, keepAliveValue.KeepAliveTime, null); + } #endif - } - - var noDelay = this.Config.GetValue(TouchSocketConfigExtension.NoDelayProperty); - if (noDelay != null) - { - socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, noDelay); - } - - if (this.Config.GetValue(TouchSocketConfigExtension.BindIPHostProperty) != null) - { - if (this.Config.GetValue(TouchSocketConfigExtension.ReuseAddressProperty)) - { - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); } - socket.Bind(this.Config.GetValue(TouchSocketConfigExtension.BindIPHostProperty).EndPoint); - } - return socket; - } - private void EventArgs_Completed(object sender, SocketAsyncEventArgs e) - { - try - { - this.m_bufferRate = 1; - this.ProcessReceived(e); - } - catch (Exception ex) - { - e.SafeDispose(); - this.BreakOut(ex.Message); - } - } - - /// - /// 处理数据 - /// - private void HandleBuffer(ByteBlock byteBlock) - { - try - { - this.m_receiveCounter.Increment(byteBlock.Length); - if (this.OnHandleRawBuffer?.Invoke(byteBlock) == false) + var noDelay = this.Config.GetValue(TouchSocketConfigExtension.NoDelayProperty); + if (noDelay != null) { - return; + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, noDelay); } + + if (this.Config.GetValue(TouchSocketConfigExtension.BindIPHostProperty) != null) + { + if (this.Config.GetValue(TouchSocketConfigExtension.ReuseAddressProperty)) + { + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + } + socket.Bind(this.Config.GetValue(TouchSocketConfigExtension.BindIPHostProperty).EndPoint); + } + return socket; + } + + #region 发送 + + #region 同步发送 + + /// + /// + /// + /// + /// + /// + /// + public void Send(IRequestInfo requestInfo) + { if (this.DisposedValue) { return; } - if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(ITcpReceivingPlugin.OnTcpReceiving), this, new ByteBlockEventArgs(byteBlock))) - { - return; - } if (this.DataHandlingAdapter == null) { - this.Logger.Error(this, TouchSocketResource.NullDataAdapter.GetDescription()); - return; + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); } - this.DataHandlingAdapter.ReceivedInput(byteBlock); - } - catch (Exception ex) - { - this.Logger.Log(LogLevel.Error, this, "在处理数据时发生错误", ex); - } - finally - { - byteBlock.Dispose(); - } - } - private void HandleBuffer(TcpCore core, ByteBlock byteBlock) - { - this.ThrowIfDisposed(); - if (this.OnHandleRawBuffer?.Invoke(byteBlock) == false) - { - return; - } - - if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(ITcpReceivingPlugin.OnTcpReceiving), this, new ByteBlockEventArgs(byteBlock))) - { - return; - } - if (this.DataHandlingAdapter == null) - { - this.Logger.Error(this, TouchSocketResource.NullDataAdapter.GetDescription()); - return; - } - this.DataHandlingAdapter.ReceivedInput(byteBlock); - } - #region 发送 - - #region 同步发送 - - /// - /// - /// - /// - /// - /// - /// - public void Send(IRequestInfo requestInfo) - { - if (this.DisposedValue) - { - return; - } - if (this.DataHandlingAdapter == null) - { - throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); - } - if (!this.DataHandlingAdapter.CanSendRequestInfo) - { - throw new NotSupportedException($"当前适配器不支持对象发送。"); - } - this.DataHandlingAdapter.SendInput(requestInfo); - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public virtual void Send(byte[] buffer, int offset, int length) - { - if (this.DataHandlingAdapter == null) - { - throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); - } - this.DataHandlingAdapter.SendInput(buffer, offset, length); - } - - /// - /// - /// - /// - /// - /// - /// - public virtual void Send(IList> transferBytes) - { - if (this.DataHandlingAdapter == null) - { - throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); - } - - if (this.DataHandlingAdapter.CanSplicingSend) - { - this.DataHandlingAdapter.SendInput(transferBytes); - } - else - { - var length = 0; - foreach (var item in transferBytes) + if (!this.DataHandlingAdapter.CanSendRequestInfo) { - length += item.Count; + throw new NotSupportedException($"当前适配器不支持对象发送。"); } - using (var byteBlock = new ByteBlock(length)) + this.DataHandlingAdapter.SendInput(requestInfo); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public virtual void Send(byte[] buffer, int offset, int length) + { + if (this.DataHandlingAdapter == null) { - foreach (var item in transferBytes) - { - byteBlock.Write(item.Array, item.Offset, item.Count); - } - this.DataHandlingAdapter.SendInput(byteBlock.Buffer, 0, byteBlock.Len); + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); } + this.DataHandlingAdapter.SendInput(buffer, offset, length); } - } - #endregion 同步发送 - - #region 异步发送 - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public virtual Task SendAsync(byte[] buffer, int offset, int length) - { - return Task.Run(() => + /// + /// + /// + /// + /// + /// + /// + public virtual void Send(IList> transferBytes) { - this.Send(buffer, offset, length); - }); - } - - /// - /// - /// - /// - /// - /// - /// - public virtual Task SendAsync(IRequestInfo requestInfo) - { - return Task.Run(() => - { - this.Send(requestInfo); - }); - } - - /// - /// - /// - /// - /// - /// - /// - public virtual Task SendAsync(IList> transferBytes) - { - return Task.Run(() => - { - this.Send(transferBytes); - }); - } - - #endregion 异步发送 - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public void DefaultSend(byte[] buffer, int offset, int length) - { - if (!this.m_online) - { - throw new NotConnectedException(TouchSocketResource.NotConnected.GetDescription()); - } - if (this.HandleSendingData(buffer, offset, length)) - { - if (this.UseSsl) + if (this.DataHandlingAdapter == null) { - this.m_workStream.Write(buffer, offset, length); + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); + } + + if (this.DataHandlingAdapter.CanSplicingSend) + { + this.DataHandlingAdapter.SendInput(transferBytes); } else { - if (this.m_delaySender != null && length < this.m_delaySender.DelayLength) + var length = 0; + foreach (var item in transferBytes) { - this.m_delaySender.Send(QueueDataBytes.CreateNew(buffer, offset, length)); + length += item.Count; } - else + using (var byteBlock = new ByteBlock(length)) { - this.MainSocket.AbsoluteSend(buffer, offset, length); + foreach (var item in transferBytes) + { + byteBlock.Write(item.Array, item.Offset, item.Count); + } + this.DataHandlingAdapter.SendInput(byteBlock.Buffer, 0, byteBlock.Len); } } - this.m_sendCounter.Increment(length); - } - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task DefaultSendAsync(byte[] buffer, int offset, int length) - { - return Task.Run(() => - { - this.DefaultSend(buffer, offset, length); - }); - } - - #endregion 发送 - - private void SetSocket(Socket socket) - { - if (socket == null) - { - this.IP = null; - this.Port = -1; - return; } - this.IP = socket.RemoteEndPoint.GetIP(); - this.Port = socket.RemoteEndPoint.GetPort(); - this.MainSocket = socket; - var delaySenderOption = this.Config.GetValue(TouchSocketConfigExtension.DelaySenderProperty); - if (delaySenderOption != null) - { - this.m_delaySender = new DelaySender(delaySenderOption, this.MainSocket.AbsoluteSend); - } - } + #endregion 同步发送 - private void ProcessReceived(SocketAsyncEventArgs e) - { - if (!this.m_online) + #region 异步发送 + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public virtual Task SendAsync(byte[] buffer, int offset, int length) { - e.SafeDispose(); - return; + this.ThrowIfDisposed(); + if (this.DataHandlingAdapter == null) + { + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); + } + return this.DataHandlingAdapter.SendInputAsync(buffer, offset, length); } - if (e.SocketError != SocketError.Success) + + /// + /// + /// + /// + /// + /// + /// + public virtual Task SendAsync(IRequestInfo requestInfo) { - e.SafeDispose(); - this.BreakOut(e.SocketError.ToString()); - return; + this.ThrowIfDisposed(); + if (this.DataHandlingAdapter == null) + { + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); + } + if (!this.DataHandlingAdapter.CanSendRequestInfo) + { + throw new NotSupportedException($"当前适配器不支持对象发送。"); + } + return this.DataHandlingAdapter.SendInputAsync(requestInfo); } - else if (e.BytesTransferred > 0) + + /// + /// + /// + /// + /// + /// + public virtual Task SendAsync(IList> transferBytes) + { + this.ThrowIfDisposed(); + if (this.DataHandlingAdapter == null) + { + throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription()); + } + if (this.DataHandlingAdapter.CanSplicingSend) + { + return this.DataHandlingAdapter.SendInputAsync(transferBytes); + } + else + { + var length = 0; + foreach (var item in transferBytes) + { + length += item.Count; + } + using (var byteBlock = new ByteBlock(length)) + { + foreach (var item in transferBytes) + { + byteBlock.Write(item.Array, item.Offset, item.Count); + } + return this.DataHandlingAdapter.SendInputAsync(byteBlock.Buffer, 0, byteBlock.Len); + } + } + } + + #endregion 异步发送 + + /// + public void DefaultSend(byte[] buffer, int offset, int length) + { + if (this.SendingData(buffer, offset, length).GetFalseAwaitResult()) + { + this.GetTcpCore().Send(buffer, offset, length); + } + } + + /// + public async Task DefaultSendAsync(byte[] buffer, int offset, int length) + { + if (await this.SendingData(buffer, offset, length)) + { + await this.GetTcpCore().SendAsync(buffer, offset, length); + } + } + + #endregion 发送 + + private void SetSocket(Socket socket) + { + if (socket == null) + { + this.IP = null; + this.Port = -1; + return; + } + + this.IP = socket.RemoteEndPoint.GetIP(); + this.Port = socket.RemoteEndPoint.GetPort(); + this.MainSocket = socket; + var delaySenderOption = this.Config.GetValue(TouchSocketConfigExtension.DelaySenderProperty); + if (delaySenderOption != null) + { + this.m_delaySender = new DelaySender(delaySenderOption, this.MainSocket.AbsoluteSend); + } + this.m_tcpCore.Reset(socket); + this.m_tcpCore.OnReceived = this.HandleReceived; + this.m_tcpCore.OnBreakOut = this.BreakOut; + + } + + private void HandleReceived(TcpCore core, ByteBlock byteBlock) { - var byteBlock = (ByteBlock)e.UserToken; - byteBlock.SetLength(e.BytesTransferred); - this.HandleBuffer(byteBlock); try { - var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength)); - e.UserToken = newByteBlock; - e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Capacity); - - if (!this.MainSocket.ReceiveAsync(e)) + if (this.DisposedValue) { - this.m_bufferRate += 2; - this.ProcessReceived(e); + return; } + if (this.ReceivingData(byteBlock).GetFalseAwaitResult()) + { + return; + } + + if (this.DataHandlingAdapter == null) + { + this.Logger.Error(this, TouchSocketResource.NullDataAdapter.GetDescription()); + return; + } + this.DataHandlingAdapter.ReceivedInput(byteBlock); } catch (Exception ex) { - e.SafeDispose(); - this.BreakOut(ex.Message); + this.Logger.Log(LogLevel.Error, this, "在处理数据时发生错误", ex); } } - else + + /// + /// 当收到原始数据 + /// + /// + /// 如果返回则表示数据已被处理,且不会再向下传递。 + protected virtual Task ReceivingData(ByteBlock byteBlock) { - e.SafeDispose(); - this.BreakOut("远程终端主动关闭"); + if (this.PluginsManager.GetPluginCount(nameof(ITcpReceivingPlugin.OnTcpReceiving)) > 0) + { + return this.PluginsManager.RaiseAsync(nameof(ITcpReceivingPlugin.OnTcpReceiving), this, new ByteBlockEventArgs(byteBlock)); + } + return Task.FromResult(false); } - } - /// - /// - /// - /// - public override string ToString() - { - return this.GetIPPort(); + + } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpService.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpService.cs index bf121a4b2..f6b8c4280 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpService.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpService.cs @@ -27,11 +27,10 @@ using System.Net.Sockets; namespace ThingsGateway.Foundation.Sockets { /// - /// TCP泛型服务器,由使用者自己指定类型。 + /// Tcp泛型服务器,由使用者自己指定类型。 /// public class TcpService : TcpServiceBase, ITcpService where TClient : SocketClient, new() { - /// /// 构造函数 /// @@ -102,25 +101,17 @@ namespace ThingsGateway.Foundation.Sockets /// /// 即将断开连接(仅主动断开时有效)。 - /// - /// 当主动调用Close断开时,可通过终止断开行为。 - /// /// public DisconnectEventHandler Disconnecting { get; set; } - /// - /// 当客户端Id被修改时触发。 - /// - public IdChangedEventHandler IdChanged { get; set; } - /// /// /// /// /// - protected override sealed void OnClientConnected(ISocketClient socketClient, ConnectedEventArgs e) + protected override sealed Task OnClientConnected(ISocketClient socketClient, ConnectedEventArgs e) { - this.OnConnected((TClient)socketClient, e); + return this.OnConnected((TClient)socketClient, e); } /// @@ -128,9 +119,9 @@ namespace ThingsGateway.Foundation.Sockets /// /// /// - protected override sealed void OnClientConnecting(ISocketClient socketClient, ConnectingEventArgs e) + protected override sealed Task OnClientConnecting(ISocketClient socketClient, ConnectingEventArgs e) { - this.OnConnecting((TClient)socketClient, e); + return this.OnConnecting((TClient)socketClient, e); } /// @@ -138,9 +129,9 @@ namespace ThingsGateway.Foundation.Sockets /// /// /// - protected override sealed void OnClientDisconnected(ISocketClient socketClient, DisconnectEventArgs e) + protected override sealed Task OnClientDisconnected(ISocketClient socketClient, DisconnectEventArgs e) { - this.OnDisconnected((TClient)socketClient, e); + return this.OnDisconnected((TClient)socketClient, e); } /// @@ -148,20 +139,19 @@ namespace ThingsGateway.Foundation.Sockets /// /// /// - protected override sealed void OnClientDisconnecting(ISocketClient socketClient, DisconnectEventArgs e) + protected override sealed Task OnClientDisconnecting(ISocketClient socketClient, DisconnectEventArgs e) { - this.OnDisconnecting((TClient)socketClient, e); + return this.OnDisconnecting((TClient)socketClient, e); } /// /// /// /// - /// - /// - protected override sealed void OnClientReceivedData(ISocketClient socketClient, ByteBlock byteBlock, IRequestInfo requestInfo) + /// + protected override sealed Task OnClientReceivedData(ISocketClient socketClient, ReceivedDataEventArgs e) { - this.OnReceived((TClient)socketClient, byteBlock, requestInfo); + return this.OnReceived((TClient)socketClient, e); } /// @@ -169,9 +159,13 @@ namespace ThingsGateway.Foundation.Sockets /// /// /// - protected virtual void OnConnected(TClient socketClient, ConnectedEventArgs e) + protected virtual Task OnConnected(TClient socketClient, ConnectedEventArgs e) { - this.Connected?.Invoke(socketClient, e); + if (this.Connected != null) + { + return this.Connected.Invoke(socketClient, e); + } + return EasyTask.CompletedTask; } /// @@ -179,9 +173,13 @@ namespace ThingsGateway.Foundation.Sockets /// /// /// - protected virtual void OnConnecting(TClient socketClient, ConnectingEventArgs e) + protected virtual Task OnConnecting(TClient socketClient, ConnectingEventArgs e) { - this.Connecting?.Invoke(socketClient, e); + if (this.Connecting != null) + { + return this.Connecting.Invoke(socketClient, e); + } + return EasyTask.CompletedTask; } /// @@ -189,32 +187,37 @@ namespace ThingsGateway.Foundation.Sockets /// /// /// - protected virtual void OnDisconnected(TClient socketClient, DisconnectEventArgs e) + protected virtual Task OnDisconnected(TClient socketClient, DisconnectEventArgs e) { - this.Disconnected?.Invoke(socketClient, e); + if (this.Disconnected != null) + { + return this.Disconnected.Invoke(socketClient, e); + } + return EasyTask.CompletedTask; } /// /// 即将断开连接(仅主动断开时有效)。 - /// - /// 当主动调用Close断开时,可通过终止断开行为。 - /// /// /// /// - protected virtual void OnDisconnecting(TClient socketClient, DisconnectEventArgs e) + protected virtual Task OnDisconnecting(TClient socketClient, DisconnectEventArgs e) { - this.Disconnecting?.Invoke(socketClient, e); + if (this.Disconnected != null) + { + return this.Disconnecting.Invoke(socketClient, e); + } + return EasyTask.CompletedTask; } /// /// 当收到适配器数据。 /// /// - /// - /// - protected virtual void OnReceived(TClient socketClient, ByteBlock byteBlock, IRequestInfo requestInfo) + /// + protected virtual Task OnReceived(TClient socketClient, ReceivedDataEventArgs e) { + return EasyTask.CompletedTask; } #endregion 事件 @@ -224,7 +227,6 @@ namespace ThingsGateway.Foundation.Sockets return Interlocked.Increment(ref this.m_nextId).ToString(); } - /// /// 获取下一个新Id /// @@ -363,7 +365,6 @@ namespace ThingsGateway.Foundation.Sockets /// public override IService Setup(TouchSocketConfig config) { - if (config == null) { throw new ArgumentNullException(nameof(config)); @@ -434,7 +435,6 @@ namespace ThingsGateway.Foundation.Sockets ReuseAddress = this.Config.GetValue(TouchSocketConfigExtension.ReuseAddressProperty), NoDelay = this.Config.GetValue(TouchSocketConfigExtension.NoDelayProperty), Adapter = this.Config.GetValue(TouchSocketConfigExtension.TcpDataHandlingAdapterProperty), - ReceiveType = this.Config.GetValue(TouchSocketConfigExtension.ReceiveTypeProperty) }; option.Backlog = this.Config.GetValue(TouchSocketConfigExtension.BacklogProperty) ?? option.Backlog; option.SendTimeout = this.Config.GetValue(TouchSocketConfigExtension.SendTimeoutProperty); @@ -540,11 +540,11 @@ namespace ThingsGateway.Foundation.Sockets this.Clear(); this.m_serverState = ServerState.Disposed; - if (this.PluginsManager?.Enable == true) + if (this.PluginsManager.Enable) { this.m_pluginsManager.Raise(nameof(IServerStopedPlugin.OnServerStoped), this, new ServiceStateEventArgs(this.m_serverState, default)); } - this.PluginsManager?.SafeDispose(); + this.PluginsManager.SafeDispose(); } base.Dispose(disposing); } @@ -691,7 +691,7 @@ namespace ThingsGateway.Foundation.Sockets return new NormalDataHandlingAdapter(); } - private void OnClientSocketInit(object obj) + private async Task OnClientSocketInit(object obj) { var tuple = (Tuple)obj; var socket = tuple.Item1; @@ -717,30 +717,35 @@ namespace ThingsGateway.Foundation.Sockets { client.SetDataHandlingAdapter(this.GetAdapter(monitor)); } - client.InternalInitialized(); + + await client.InternalInitialized(); var args = new ConnectingEventArgs(socket) { Id = this.GetNextNewId() }; - client.InternalConnecting(args);//Connecting + await client.InternalConnecting(args);//Connecting if (args.IsPermitOperation) { client.InternalSetId(args.Id); - if (!client.MainSocket.Connected) + if (!socket.Connected) { - client.MainSocket.SafeDispose(); + socket.SafeDispose(); return; } if (this.m_socketClients.TryAdd(client)) { - client.InternalConnected(new ConnectedEventArgs()); - + _ = client.InternalConnected(new ConnectedEventArgs()); + if (!socket.Connected) + { + return; + } if (monitor.Option.UseSsl) { try { - client.BeginReceiveSsl(monitor.Option.ServiceSslOption); + await client.AuthenticateAsync(monitor.Option.ServiceSslOption); + _ = client.BeginReceiveSsl(); } catch (Exception ex) { @@ -772,7 +777,7 @@ namespace ThingsGateway.Foundation.Sockets } /// - /// TCP服务器 + /// Tcp服务器 /// public class TcpService : TcpService { @@ -781,16 +786,14 @@ namespace ThingsGateway.Foundation.Sockets /// public ReceivedEventHandler Received { get; set; } - /// /// - /// - /// - /// - /// - protected override void OnReceived(SocketClient socketClient, ByteBlock byteBlock, IRequestInfo requestInfo) + protected override Task OnReceived(SocketClient socketClient, ReceivedDataEventArgs e) { - this.Received?.Invoke(socketClient, byteBlock, requestInfo); - base.OnReceived(socketClient, byteBlock, requestInfo); + if (this.Received != null) + { + return this.Received.Invoke(socketClient, e); + } + return EasyTask.CompletedTask; } } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpServiceBase.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpServiceBase.cs index 17781af74..6b7c88d65 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpServiceBase.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Tcp/TcpServiceBase.cs @@ -22,6 +22,7 @@ // 感谢您的下载和使用 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ +using System.Collections.Concurrent; namespace ThingsGateway.Foundation.Sockets { @@ -122,29 +123,64 @@ namespace ThingsGateway.Foundation.Sockets /// public abstract IService Stop(); - internal void OnInternalConnected(ISocketClient socketClient, ConnectedEventArgs e) + private ConcurrentStack m_tcpCores = new ConcurrentStack(); + + /// + /// 租用TcpCore + /// + /// + public TcpCore RentTcpCore() { - this.OnClientConnected(socketClient, e); + if (this.m_tcpCores.TryPop(out var tcpCore)) + { + return tcpCore; + } + + return new InternalTcpCore(); } - internal void OnInternalConnecting(ISocketClient socketClient, ConnectingEventArgs e) + /// + protected override void Dispose(bool disposing) { - this.OnClientConnecting(socketClient, e); + while (this.m_tcpCores.TryPop(out var tcpCore)) + { + tcpCore.SafeDispose(); + } + base.Dispose(disposing); } - internal void OnInternalDisconnected(ISocketClient socketClient, DisconnectEventArgs e) + /// + /// 归还TcpCore + /// + /// + public void ReturnTcpCore(TcpCore tcpCore) { - this.OnClientDisconnected(socketClient, e); + this.m_tcpCores.Push(tcpCore); } - internal void OnInternalDisconnecting(ISocketClient socketClient, DisconnectEventArgs e) + internal Task OnInternalConnected(ISocketClient socketClient, ConnectedEventArgs e) { - this.OnClientDisconnecting(socketClient, e); + return this.OnClientConnected(socketClient, e); } - internal void OnInternalReceivedData(ISocketClient socketClient, ByteBlock byteBlock, IRequestInfo requestInfo) + internal Task OnInternalConnecting(ISocketClient socketClient, ConnectingEventArgs e) { - this.OnClientReceivedData(socketClient, byteBlock, requestInfo); + return this.OnClientConnecting(socketClient, e); + } + + internal Task OnInternalDisconnected(ISocketClient socketClient, DisconnectEventArgs e) + { + return this.OnClientDisconnected(socketClient, e); + } + + internal Task OnInternalDisconnecting(ISocketClient socketClient, DisconnectEventArgs e) + { + return this.OnClientDisconnecting(socketClient, e); + } + + internal Task OnInternalReceivedData(ISocketClient socketClient, ReceivedDataEventArgs e) + { + return this.OnClientReceivedData(socketClient, e); } /// @@ -152,39 +188,35 @@ namespace ThingsGateway.Foundation.Sockets /// /// /// - protected abstract void OnClientConnected(ISocketClient socketClient, ConnectedEventArgs e); + protected abstract Task OnClientConnected(ISocketClient socketClient, ConnectedEventArgs e); /// /// 客户端请求连接 /// /// /// - protected abstract void OnClientConnecting(ISocketClient socketClient, ConnectingEventArgs e); + protected abstract Task OnClientConnecting(ISocketClient socketClient, ConnectingEventArgs e); /// /// 客户端断开连接 /// /// /// - protected abstract void OnClientDisconnected(ISocketClient socketClient, DisconnectEventArgs e); + protected abstract Task OnClientDisconnected(ISocketClient socketClient, DisconnectEventArgs e); /// /// 即将断开连接(仅主动断开时有效)。 - /// - /// 当主动调用Close断开时,可通过终止断开行为。 - /// /// /// /// - protected abstract void OnClientDisconnecting(ISocketClient socketClient, DisconnectEventArgs e); + protected abstract Task OnClientDisconnecting(ISocketClient socketClient, DisconnectEventArgs e); /// /// 收到数据时 /// /// - /// - /// - protected abstract void OnClientReceivedData(ISocketClient socketClient, ByteBlock byteBlock, IRequestInfo requestInfo); + /// + protected abstract Task OnClientReceivedData(ISocketClient socketClient, ReceivedDataEventArgs e); #region Id发送 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Udp/UdpSession.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Udp/UdpSession.cs index ce7342b40..ad9f08bc8 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Udp/UdpSession.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Components/Udp/UdpSession.cs @@ -36,17 +36,20 @@ namespace ThingsGateway.Foundation.Sockets /// /// 当收到数据时 /// - public UdpReceivedEventHandler Received { get; set; } + public UdpReceivedEventHandler Received { get; set; } - /// /// - /// - /// - /// - /// - protected override void HandleReceivedData(EndPoint remoteEndPoint, ByteBlock byteBlock, IRequestInfo requestInfo) + protected override async Task ReceivedData(UdpReceivedDataEventArgs e) { - this.Received?.Invoke(remoteEndPoint, byteBlock, requestInfo); + if (this.Received != null) + { + await this.Received.Invoke(this, e); + if (e.Handled) + { + return; + } + } + await base.ReceivedData(e); } } @@ -70,16 +73,6 @@ namespace ThingsGateway.Foundation.Sockets this.SendBufferSize = 1024 * 64; } - /// - /// 处理未经过适配器的数据。返回值表示是否继续向下传递。 - /// - public Func OnHandleRawBuffer { get; set; } - - /// - /// 处理经过适配器后的数据。返回值表示是否继续向下传递。 - /// - public Func OnHandleReceivedData { get; set; } - /// /// /// @@ -342,7 +335,6 @@ namespace ThingsGateway.Foundation.Sockets this.PluginsManager.Raise(nameof(IServerStartedPlugin.OnServerStarted), this, new ServiceStateEventArgs(this.ServerState, ex) { Message = ex.Message }); throw; } - } /// @@ -391,11 +383,9 @@ namespace ThingsGateway.Foundation.Sockets /// /// 处理已接收到的数据。 /// - /// - /// 以二进制流形式传递 - /// 以解析的数据对象传递 - protected virtual void HandleReceivedData(EndPoint remoteEndPoint, ByteBlock byteBlock, IRequestInfo requestInfo) + protected virtual Task ReceivedData(UdpReceivedDataEventArgs e) { + return this.PluginsManager.RaiseAsync(nameof(IUdpReceivedPlugin.OnUdpReceived), this, e); } /// @@ -492,7 +482,7 @@ namespace ThingsGateway.Foundation.Sockets } #endif - #endregion + #endregion Windows下UDP连接被重置错误10054 this.PreviewBind(socket); @@ -500,55 +490,48 @@ namespace ThingsGateway.Foundation.Sockets this.Monitor = new UdpNetworkMonitor(iPHost, socket); - switch (this.Config.GetValue(TouchSocketConfigExtension.ReceiveTypeProperty)) + +#if NET45_OR_GREATER || NET6_0_OR_GREATER + for (var i = 0; i < threadCount; i++) { - case ReceiveType.Iocp: - { -#if NET45_OR_GREATER||NET6_0_OR_GREATER - for (var i = 0; i < threadCount; i++) - { - var eventArg = new SocketAsyncEventArgs(); - this.m_socketAsyncs.Add(eventArg); - eventArg.Completed += this.IO_Completed; - var byteBlock = new ByteBlock(this.ReceiveBufferSize); - eventArg.UserToken = byteBlock; - eventArg.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity); - eventArg.RemoteEndPoint = iPHost.EndPoint; - if (!socket.ReceiveFromAsync(eventArg)) - { - this.ProcessReceive(socket, eventArg); - } - } -#else - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - for (var i = 0; i < threadCount; i++) - { - var eventArg = new SocketAsyncEventArgs(); - this.m_socketAsyncs.Add(eventArg); - eventArg.Completed += this.IO_Completed; - var byteBlock = new ByteBlock(this.ReceiveBufferSize); - eventArg.UserToken = byteBlock; - eventArg.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity); - eventArg.RemoteEndPoint = iPHost.EndPoint; - if (!socket.ReceiveFromAsync(eventArg)) - { - this.ProcessReceive(socket, eventArg); - } - } - } - else - { - var thread = new Thread(this.Received); - thread.IsBackground = true; - thread.Start(); - } -#endif - break; - } - default: - throw new Exception("UDP中只支持Auto模式"); + var eventArg = new SocketAsyncEventArgs(); + this.m_socketAsyncs.Add(eventArg); + eventArg.Completed += this.IO_Completed; + var byteBlock = new ByteBlock(this.ReceiveBufferSize); + eventArg.UserToken = byteBlock; + eventArg.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity); + eventArg.RemoteEndPoint = iPHost.EndPoint; + if (!socket.ReceiveFromAsync(eventArg)) + { + this.ProcessReceive(socket, eventArg); + } } +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + for (var i = 0; i < threadCount; i++) + { + var eventArg = new SocketAsyncEventArgs(); + this.m_socketAsyncs.Add(eventArg); + eventArg.Completed += this.IO_Completed; + var byteBlock = new ByteBlock(this.ReceiveBufferSize); + eventArg.UserToken = byteBlock; + eventArg.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity); + eventArg.RemoteEndPoint = iPHost.EndPoint; + if (!socket.ReceiveFromAsync(eventArg)) + { + this.ProcessReceive(socket, eventArg); + } + } + } + else + { + var thread = new Thread(this.Received); + thread.IsBackground = true; + thread.Start(); + } +#endif + } private void Received() @@ -577,10 +560,6 @@ namespace ThingsGateway.Foundation.Sockets try { this.LastReceivedTime = DateTime.Now; - if (this.OnHandleRawBuffer?.Invoke(byteBlock) == false) - { - return; - } if (this.DisposedValue) { return; @@ -609,21 +588,14 @@ namespace ThingsGateway.Foundation.Sockets private void PrivateHandleReceivedData(EndPoint remoteEndPoint, ByteBlock byteBlock, IRequestInfo requestInfo) { - if (this.OnHandleReceivedData?.Invoke(byteBlock, requestInfo) == false) + if (this.m_receiver != null) { - return; - } - - if (this.PluginsManager.Enable) - { - var args = new UdpReceivedDataEventArgs(remoteEndPoint, byteBlock, requestInfo); - this.PluginsManager.Raise(nameof(IUdpReceivedPlugin.OnUdpReceived), this, args); - if (args.Handled) + if (this.m_receiver.TryInputReceive(byteBlock, requestInfo)) { return; } } - this.HandleReceivedData(remoteEndPoint, byteBlock, requestInfo); + this.ReceivedData(new UdpReceivedDataEventArgs(remoteEndPoint, byteBlock, requestInfo)).GetFalseAwaitResult(); } #region 向默认远程同步发送 @@ -663,7 +635,7 @@ namespace ThingsGateway.Foundation.Sockets { throw new NotSupportedException($"当前适配器不支持对象发送。"); } - this.DataHandlingAdapter.SendInput(requestInfo); + //this.DataHandlingAdapter.SendInput(requestInfo); } #endregion 向默认远程同步发送 @@ -682,9 +654,9 @@ namespace ThingsGateway.Foundation.Sockets public virtual Task SendAsync(byte[] buffer, int offset, int length) { return Task.Run(() => - { - this.Send(buffer, offset, length); - }); + { + this.Send(buffer, offset, length); + }); } /// @@ -737,9 +709,9 @@ namespace ThingsGateway.Foundation.Sockets public virtual Task SendAsync(EndPoint remoteEP, byte[] buffer, int offset, int length) { return Task.Run(() => - { - this.Send(remoteEP, buffer, offset, length); - }); + { + this.Send(remoteEP, buffer, offset, length); + }); } #endregion 向设置的远程异步发送 @@ -906,5 +878,23 @@ namespace ThingsGateway.Foundation.Sockets } #endregion 组合发送 + + #region Receiver + + private Receiver m_receiver; + + /// + public IReceiver CreateReceiver() + { + return this.m_receiver ??= new Receiver(this); + } + + /// + public void ClearReceiver() + { + this.m_receiver = null; + } + + #endregion } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DataAdapter/Udp/NormalUdpDataHandlingAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DataAdapter/Udp/NormalUdpDataHandlingAdapter.cs index 4e57a1470..c2aac3201 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DataAdapter/Udp/NormalUdpDataHandlingAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DataAdapter/Udp/NormalUdpDataHandlingAdapter.cs @@ -91,15 +91,6 @@ namespace ThingsGateway.Foundation.Sockets } } - /// - /// - /// - /// - protected override void PreviewSend(IRequestInfo requestInfo) - { - throw new System.NotImplementedException(); - } - /// /// /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DataAdapter/Udp/UdpPackageAdapter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DataAdapter/Udp/UdpPackageAdapter.cs index 5908d9d50..d2d5bff9a 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DataAdapter/Udp/UdpPackageAdapter.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DataAdapter/Udp/UdpPackageAdapter.cs @@ -365,15 +365,6 @@ namespace ThingsGateway.Foundation.Sockets } } - /// - /// - /// - /// - protected override void PreviewSend(IRequestInfo requestInfo) - { - throw new NotImplementedException(); - } - /// /// /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DelegateCollection.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DelegateCollection.cs index f95e06f7f..7741c9396 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DelegateCollection.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/DelegateCollection.cs @@ -22,67 +22,57 @@ // 感谢您的下载和使用 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -using System.Net; -/// -/// 显示信息 -/// -/// -/// -public delegate void MessageEventHandler(TClient client, MsgPermitEventArgs e); +namespace ThingsGateway.Foundation.Sockets +{ + /// + /// Connected + /// + /// + /// + /// + public delegate Task ConnectedEventHandler(TClient client, ConnectedEventArgs e); -/// -/// 普通通知 -/// -/// -/// -/// -public delegate void TouchSocketEventHandler(TClient client, PluginEventArgs e); + /// + /// Connecting + /// + /// + /// + /// + public delegate Task ConnectingEventHandler(TClient client, ConnectingEventArgs e); -/// -/// Id修改通知 -/// -/// -/// -/// -public delegate void IdChangedEventHandler(TClient client, IdChangedEventArgs e); + /// + /// 客户端断开连接 + /// + /// + /// + /// + public delegate Task DisconnectEventHandler(TClient client, DisconnectEventArgs e); -/// -/// Connecting -/// -/// -/// -/// -public delegate void ConnectingEventHandler(TClient client, ConnectingEventArgs e); + /// + /// 显示信息 + /// + /// + /// + public delegate void MessageEventHandler(TClient client, MsgPermitEventArgs e); -/// -/// Connected -/// -/// -/// -/// -public delegate void ConnectedEventHandler(TClient client, ConnectedEventArgs e); + /// + /// 接收数据 + /// + /// + /// + public delegate Task ReceivedEventHandler(TClient client, ReceivedDataEventArgs e); -/// -/// 客户端断开连接 -/// -/// -/// -/// -public delegate void DisconnectEventHandler(TClient client, DisconnectEventArgs e); + /// + /// 普通通知 + /// + /// + /// + /// + public delegate void TouchSocketEventHandler(TClient client, PluginEventArgs e); -/// -/// 接收数据 -/// -/// -/// -/// -public delegate void ReceivedEventHandler(TClient client, ByteBlock byteBlock, IRequestInfo requestInfo); - -/// -/// UDP接收 -/// -/// -/// -/// -public delegate void UdpReceivedEventHandler(EndPoint endpoint, ByteBlock byteBlock, IRequestInfo requestInfo); \ No newline at end of file + /// + /// Udp接收 + /// + public delegate Task UdpReceivedEventHandler(TClient client, UdpReceivedDataEventArgs e); +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Enum/ReceiveType.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Enum/ReceiveType.cs deleted file mode 100644 index 50d6b0118..000000000 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Enum/ReceiveType.cs +++ /dev/null @@ -1,48 +0,0 @@ -#region copyright -//------------------------------------------------------------------------------ -// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 -// 此代码版权(除特别声明外的代码)归作者本人Diego所有 -// 源代码使用协议遵循本仓库的开源协议及附加协议 -// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway -// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway -// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ -// QQ群:605534569 -//------------------------------------------------------------------------------ -#endregion - -//------------------------------------------------------------------------------ -// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 -// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 -// CSDN博客:https://blog.csdn.net/qq_40374647 -// 哔哩哔哩视频:https://space.bilibili.com/94253567 -// Gitee源代码仓库:https://gitee.com/RRQM_Home -// Github源代码仓库:https://github.com/RRQM -// API首页:http://rrqm_home.gitee.io/touchsocket/ -// 交流QQ群:234762506 -// 感谢您的下载和使用 -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -namespace ThingsGateway.Foundation.Sockets -{ - /// - /// 接收类型 - /// - public enum ReceiveType : byte - { - /// - /// 在该模式下,不会投递接收申请,用户可通过,获取到流以后,自己处理接收。 - /// 注意:连接端不会感知主动断开 - /// - None, - - /// - /// 该模式下会使用Iocp自动接收数据,然后主动触发。 - /// - Iocp, - - /// - /// 该模式下,会使用同步阻塞模式接收数据。注意:使用该模式时,每个回话连接会独占一个线程。 - /// - Bio - } -} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/ClientExtension.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/ClientExtension.cs index d2ea23ccb..3e2214e12 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/ClientExtension.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/ClientExtension.cs @@ -122,18 +122,39 @@ namespace ThingsGateway.Foundation.Sockets /// public static TClient Connect(this TClient client, string ipHost, int timeout = 5000) where TClient : ITcpClient { - client.Setup(ipHost); + TouchSocketConfig config; + if (client.Config == null) + { + config = new TouchSocketConfig(); + config.SetRemoteIPHost(ipHost); + client.Setup(config); + } + else + { + config = client.Config; + config.SetRemoteIPHost(ipHost); + } client.Connect(timeout); return client; } /// - public static Task ConnectAsync(this TClient client, string ipHost, int timeout = 5000) where TClient : ITcpClient + public static async Task ConnectAsync(this TClient client, string ipHost, int timeout = 5000) where TClient : ITcpClient { - return Task.Run(() => + TouchSocketConfig config; + if (client.Config == null) { - return Connect(client, ipHost, timeout); - }); + config = new TouchSocketConfig(); + config.SetRemoteIPHost(ipHost); + client.Setup(config); + } + else + { + config = client.Config; + config.SetRemoteIPHost(ipHost); + } + await client.ConnectAsync(timeout); + return client; } /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/ServiceExtension.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/ServiceExtension.cs index fb2a3a09e..967825079 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/ServiceExtension.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/ServiceExtension.cs @@ -22,8 +22,18 @@ namespace ThingsGateway.Foundation.Sockets /// public static TService Start(this TService service, params IPHost[] iPHosts) where TService : ITcpService { - service.Setup(new TouchSocketConfig() - .SetListenIPHosts(iPHosts)); + TouchSocketConfig config; + if (service.Config == null) + { + config = new TouchSocketConfig(); + config.SetListenIPHosts(iPHosts); + service.Setup(config); + } + else + { + config = service.Config; + config.SetListenIPHosts(iPHosts); + } service.Start(); return service; } @@ -35,8 +45,18 @@ namespace ThingsGateway.Foundation.Sockets /// public static TService Start(this TService service, IPHost iPHost) where TService : IUdpSession { - service.Setup(new TouchSocketConfig() - .SetBindIPHost(iPHost)); + TouchSocketConfig config; + if (service.Config == null) + { + config = new TouchSocketConfig(); + config.SetBindIPHost(iPHost); + service.Setup(config); + } + else + { + config = service.Config; + config.SetBindIPHost(iPHost); + } service.Start(); return service; } diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/TouchSocketConfigExtension.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/TouchSocketConfigExtension.cs index fcc5b6a4f..de13c6369 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/TouchSocketConfigExtension.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Extensions/TouchSocketConfigExtension.cs @@ -44,14 +44,6 @@ namespace ThingsGateway.Foundation.Sockets /// public static readonly DependencyProperty> TcpDataHandlingAdapterProperty = DependencyProperty>.Register("TcpDataHandlingAdapter", () => { return new NormalDataHandlingAdapter(); }); - /// - /// 接收类型,默认为 - /// 为自动接收数据,然后主动触发。 - /// 为不投递IO接收申请,用户可通过,获取到流以后,自己处理接收。注意:连接端不会感知主动断开 - /// 所需类型 - /// - public static readonly DependencyProperty ReceiveTypeProperty = DependencyProperty.Register("ReceiveType", ReceiveType.Iocp); - /// /// 数据处理适配器,默认为获取 /// 所需类型 @@ -83,20 +75,6 @@ namespace ThingsGateway.Foundation.Sockets return config; } - /// - /// 接收类型,默认为 - /// 为自动接收数据,然后主动触发。 - /// 为不投递IO接收申请,用户可通过,获取到流以后,自己处理接收。注意:连接端不会感知主动断开 - /// - /// - /// - /// - public static TouchSocketConfig SetReceiveType(this TouchSocketConfig config, ReceiveType value) - { - config.SetValue(ReceiveTypeProperty, value); - return config; - } - /// /// 设置(Udp系)数据处理适配器。 /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/IClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/IClient.cs index 67851956c..6166898e7 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/IClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/IClient.cs @@ -30,16 +30,16 @@ namespace ThingsGateway.Foundation.Sockets /// public interface IClient : IDependencyObject, IDisposable, ISocket { + /// + /// 获取一个同步数据接收器 + /// + /// + IReceiver CreateReceiver(); /// - /// 处理未经过适配器的数据。返回值表示是否继续向下传递。 + /// 移除同步数据接收器 /// - Func OnHandleRawBuffer { get; set; } - - /// - /// 处理经过适配器后的数据。返回值表示是否继续向下传递。 - /// - Func OnHandleReceivedData { get; set; } + void ClearReceiver(); /// /// 终端协议 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ISender/ISender.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ISender/ISender.cs index 3942d4966..55533caa6 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ISender/ISender.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ISender/ISender.cs @@ -22,7 +22,6 @@ // 感谢您的下载和使用 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -using System.Net.Sockets; namespace ThingsGateway.Foundation.Sockets { @@ -46,8 +45,6 @@ namespace ThingsGateway.Foundation.Sockets /// /// 异步发送数据。 - /// 时,如果使用独立线程发送,则不会触发异常。 - /// 时,相当于 /// 该发送会经过适配器封装,具体封装内容由适配器决定。 /// /// 数据缓存区 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ITcpClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ITcpClient.cs index c3a92c405..e91233ebc 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ITcpClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ITcpClient.cs @@ -26,7 +26,7 @@ namespace ThingsGateway.Foundation.Sockets { /// - /// TCP客户端终端接口 + /// Tcp客户端终端接口 /// public interface ITcpClient : ITcpClientBase, IClientSender, IPluginObject { @@ -65,12 +65,5 @@ namespace ThingsGateway.Foundation.Sockets /// /// ITcpClient Setup(TouchSocketConfig config); - - /// - /// 配置服务器 - /// - /// - /// - ITcpClient Setup(string ipHost); } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ITcpClientBase.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ITcpClientBase.cs index 48bca1866..5b924c053 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ITcpClientBase.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Interface/ITcpClientBase.cs @@ -23,13 +23,12 @@ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -using System.Net.Security; using System.Net.Sockets; namespace ThingsGateway.Foundation.Sockets { /// - /// TCP终端基础接口。 + /// Tcp终端基础接口。 /// /// 注意:该接口并不仅表示客户端。也实现了该接口。 /// @@ -80,7 +79,7 @@ namespace ThingsGateway.Foundation.Sockets /// /// 判断是否在线 - /// 该属性仅表示TCP状态是否在线 + /// 该属性仅表示Tcp状态是否在线 /// bool Online { get; } @@ -89,10 +88,7 @@ namespace ThingsGateway.Foundation.Sockets /// int Port { get; } - /// - /// 接收模式 - /// - public ReceiveType ReceiveType { get; } + /// /// 使用Ssl加密 @@ -106,11 +102,7 @@ namespace ThingsGateway.Foundation.Sockets /// void Close(string msg = TouchSocketCoreUtility.Empty); - /// - /// 获取流,在正常模式下为,在Ssl模式下为。 - /// - /// - Stream GetStream(); + /// /// 设置数据处理适配器 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Plugins/Interfaces/ITcpDisconnectingPlugin.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Plugins/Interfaces/ITcpDisconnectingPlugin.cs index a4c8fa047..5fed661df 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Plugins/Interfaces/ITcpDisconnectingPlugin.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Plugins/Interfaces/ITcpDisconnectingPlugin.cs @@ -19,9 +19,6 @@ namespace ThingsGateway.Foundation.Sockets { /// /// 即将断开连接(仅主动断开时有效)。 - /// - /// 当主动调用Close断开时。 - /// /// /// /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPutPlugin.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/IReceiver.cs similarity index 51% rename from framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPutPlugin.cs rename to framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/IReceiver.cs index 96bfcf765..98902a5bd 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Http/Plugins/Interfaces/IHttpPutPlugin.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/IReceiver.cs @@ -10,28 +10,27 @@ //------------------------------------------------------------------------------ #endregion -namespace ThingsGateway.Foundation.Http +namespace ThingsGateway.Foundation.Sockets { /// - /// IHttpPutPlugin + /// IReceiver /// - [Obsolete("该插件已被弃用,请考虑使用“IHttpPlugin”插件代替使用。本插件将在正式版发布时直接移除。", true)] - public interface IHttpPutPlugin : IPlugin where TClient : IHttpSocketClient + public interface IReceiver : IDisposable { /// - /// 在收到Put时 + /// 异步等待并读取 /// - /// - /// + /// /// - Task OnHttpPut(TClient client, HttpContextEventArgs e); - } + Task ReadAsync(CancellationToken token); - /// - /// IHttpPutPlugin - /// - [Obsolete("该插件已被弃用,请考虑使用“IHttpPlugin”插件代替使用。本插件将在正式版发布时直接移除。", true)] - public interface IHttpPutPlugin : IHttpPutPlugin - { +#if NET6_0_OR_GREATER + /// + /// 值异步等待并读取 + /// + /// + /// + public ValueTask ValueReadAsync(CancellationToken token); +#endif } -} \ No newline at end of file +} diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/Receiver.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/Receiver.cs new file mode 100644 index 000000000..6c8a746e7 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/Receiver.cs @@ -0,0 +1,91 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +namespace ThingsGateway.Foundation.Sockets +{ + /// + /// Receiver + /// + public class Receiver : DisposableObject, IReceiver + { + private readonly IClient m_client; + private readonly AutoResetEvent m_resetEventForComplateRead = new AutoResetEvent(false); + private readonly AsyncAutoResetEvent m_resetEventForRead = new AsyncAutoResetEvent(false); + private ByteBlock m_byteBlock; + private IRequestInfo m_requestInfo; + + /// + /// Receiver + /// + /// + public Receiver(IClient client) + { + this.m_client = client; + } + + /// + public async Task ReadAsync(CancellationToken token) + { + this.ThrowIfDisposed(); + await this.m_resetEventForRead.WaitOneAsync(token); + return new ReceiverResult(this.ComplateRead, this.m_byteBlock, this.m_requestInfo); + } + +#if NET6_0_OR_GREATER + /// + public async ValueTask ValueReadAsync(CancellationToken token) + { + this.ThrowIfDisposed(); + await this.m_resetEventForRead.WaitOneAsync(token); + return new ReceiverResult(this.ComplateRead, this.m_byteBlock, this.m_requestInfo); + } +#endif + + /// + public bool TryInputReceive(ByteBlock byteBlock, IRequestInfo requestInfo) + { + if (this.DisposedValue) + { + return false; + } + this.m_byteBlock = byteBlock; + this.m_requestInfo = requestInfo; + this.m_resetEventForRead.Set(); + if (byteBlock == null && requestInfo == null) + { + return true; + } + if (this.m_resetEventForComplateRead.WaitOne(TimeSpan.FromSeconds(10))) + { + return true; + } + return false; + } + + /// + protected override void Dispose(bool disposing) + { + this.m_client.ClearReceiver(); + this.m_resetEventForComplateRead.SafeDispose(); + this.m_resetEventForRead.SafeDispose(); + this.m_byteBlock = null; + base.Dispose(disposing); + } + + private void ComplateRead() + { + this.m_byteBlock = default; + this.m_requestInfo = default; + this.m_resetEventForComplateRead.Set(); + } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/ReceiverResult.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/ReceiverResult.cs new file mode 100644 index 000000000..bcd7bbb28 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/Receiver/ReceiverResult.cs @@ -0,0 +1,56 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +namespace ThingsGateway.Foundation.Sockets +{ + /// + /// ReceiverResult + /// + public readonly struct ReceiverResult : IDisposable + { + private readonly Action m_disAction; + + /// + /// SocketReceiveResult + /// + /// + /// + /// + public ReceiverResult(Action disAction, ByteBlock byteBlock, IRequestInfo requestInfo) + { + this.m_disAction = disAction; + this.ByteBlock = byteBlock; + this.RequestInfo = requestInfo; + } + + /// + /// 字节块 + /// + public ByteBlock ByteBlock { get; } + + /// + /// 数据对象 + /// + public IRequestInfo RequestInfo { get; } + + /// + /// 连接已关闭 + /// + public bool IsClosed => this.ByteBlock == null && this.RequestInfo == null; + + /// + public void Dispose() + { + m_disAction?.Invoke(); + } + } +} diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/IWaitingClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/IWaitingClient.cs index 942747584..839d1b982 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/IWaitingClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/IWaitingClient.cs @@ -28,7 +28,7 @@ namespace ThingsGateway.Foundation.Sockets /// /// 等待型客户端。 /// - public interface IWaitingClient : IWaitSender, IDisposable where TClient : IClient, IDefaultSender, ISender + public interface IWaitingClient : IWaitSender, IDisposable where TClient : IClient, ISender { /// /// 等待设置。 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/ResponsedData.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/ResponsedData.cs index 931eed314..c4384319f 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/ResponsedData.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/ResponsedData.cs @@ -30,18 +30,15 @@ namespace ThingsGateway.Foundation.Sockets /// public struct ResponsedData { - /// /// 构造函数 /// /// /// - /// - public ResponsedData(byte[] data, IRequestInfo requestInfo, bool isRawBuffer) + public ResponsedData(byte[] data, IRequestInfo requestInfo) { this.Data = data; this.RequestInfo = requestInfo; - this.IsRawBuffer = isRawBuffer; } /// @@ -53,10 +50,5 @@ namespace ThingsGateway.Foundation.Sockets /// RequestInfo /// public IRequestInfo RequestInfo { get; private set; } - - /// - /// 是否为原生缓存区。即没有经过适配器处理。 - /// - public bool IsRawBuffer { get; private set; } } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingClient.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingClient.cs index 34fa076a7..435d61010 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingClient.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingClient.cs @@ -10,353 +10,279 @@ //------------------------------------------------------------------------------ #endregion -namespace ThingsGateway.Foundation.Sockets; +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// API首页:http://rrqm_home.gitee.io/touchsocket/ +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ -internal class WaitingClient : DisposableObject, IWaitingClient where TClient : IClient, IDefaultSender, ISender +namespace ThingsGateway.Foundation.Sockets { - private readonly Func m_func; - private readonly EasyLock easyLock = new(); - private readonly WaitData m_waitData = new(); - private readonly WaitDataAsync m_waitDataAsync = new(); - - private volatile bool m_breaked; - - public WaitingClient(TClient client, WaitingOptions waitingOptions, Func func) + internal class WaitingClient : DisposableObject, IWaitingClient where TClient : IClient, ISender { - this.Client = client ?? throw new ArgumentNullException(nameof(client)); - this.WaitingOptions = waitingOptions; - this.m_func = func; - } + private readonly Func m_func; + private readonly SemaphoreSlim m_semaphoreSlim = new SemaphoreSlim(1, 1); + private volatile bool m_breaked; + private CancellationTokenSource m_cancellation; - public WaitingClient(TClient client, WaitingOptions waitingOptions) - { - this.Client = client ?? throw new ArgumentNullException(nameof(client)); - this.WaitingOptions = waitingOptions; - } - - public bool CanSend - { - get + public WaitingClient(TClient client, WaitingOptions waitingOptions, Func func) { - return this.Client is ITcpClientBase tcpClient ? tcpClient.CanSend : this.Client is IUdpSession; - } - } - - public TClient Client { get; private set; } - - public WaitingOptions WaitingOptions { get; set; } - - protected override void Dispose(bool disposing) - { - this.Client = default; - this.m_waitData.SafeDispose(); - this.m_waitDataAsync.SafeDispose(); - base.Dispose(disposing); - } - - private void Cancel() - { - this.m_waitData.Cancel(); - this.m_waitDataAsync.Cancel(); - } - - private void OnDisconnected(ITcpClientBase client, DisconnectEventArgs e) - { - this.m_breaked = true; - this.Cancel(); - } - private void OnSerialSessionDisconnected(ISerialSessionBase client, DisconnectEventArgs e) - { - this.m_breaked = true; - this.Cancel(); - } - private bool OnHandleRawBuffer(ByteBlock byteBlock) - { - var responsedData = new ResponsedData(byteBlock.ToArray(), null, true); - if (this.m_func == null || this.m_func.Invoke(responsedData)) - { - return !this.Set(responsedData); - } - else - { - return true; - } - } - - private bool OnHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) - { - ResponsedData responsedData; - if (byteBlock != null) - { - responsedData = new ResponsedData(byteBlock.ToArray(), requestInfo, false); - } - else - { - responsedData = new ResponsedData(null, requestInfo, false); + this.Client = client ?? throw new ArgumentNullException(nameof(client)); + this.WaitingOptions = waitingOptions; + this.m_func = func; } - if (this.m_func == null || this.m_func.Invoke(responsedData)) + public WaitingClient(TClient client, WaitingOptions waitingOptions) { - return !this.Set(responsedData); + this.Client = client ?? throw new ArgumentNullException(nameof(client)); + this.WaitingOptions = waitingOptions; } - else + + public bool CanSend { - return true; - } - } - - private void Reset() - { - this.m_waitData.Reset(); - this.m_waitDataAsync.Reset(); - } - - #region 同步Response - - public ResponsedData SendThenResponse(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - try - { - easyLock.Wait(); - this.m_breaked = false; - this.Reset(); - if (this.WaitingOptions.BreakTrigger && this.Client is ITcpClientBase tcpClient) + get { - tcpClient.Disconnected += this.OnDisconnected; - } - if (this.WaitingOptions.BreakTrigger && this.Client is ISerialSessionBase serialSession) - { - serialSession.Disconnected += this.OnSerialSessionDisconnected; - } - if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter) - { - this.Client.OnHandleReceivedData += this.OnHandleReceivedData; - } - else - { - this.Client.OnHandleRawBuffer += this.OnHandleRawBuffer; + return this.Client is ITcpClientBase tcpClient ? tcpClient.CanSend : this.Client is ISerialSessionBase serialSession ? serialSession.CanSend : this.Client is IUdpSession; } + } - if (this.WaitingOptions.RemoteIPHost != null && this.Client is IUdpSession session) + public TClient Client { get; private set; } + + public WaitingOptions WaitingOptions { get; set; } + + protected override void Dispose(bool disposing) + { + this.Client = default; + this.Cancel(); + base.Dispose(disposing); + } + + private void Cancel() + { + this.m_cancellation.Cancel(); + } + + #region 同步Response + + public ResponsedData SendThenResponse(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default) + { + try { - if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.SendAdapter) + this.m_semaphoreSlim.Wait(); + this.m_breaked = false; + if (token.CanBeCanceled) { - session.Send(this.WaitingOptions.RemoteIPHost.EndPoint, buffer, offset, length); + using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(timeout); + m_cancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, token); } else { - session.DefaultSend(this.WaitingOptions.RemoteIPHost.EndPoint, buffer, offset, length); + m_cancellation = new CancellationTokenSource(timeout); } - } - else - { - if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.SendAdapter) + using (m_cancellation) { - this.Client.Send(buffer, offset, length); - } - else - { - this.Client.DefaultSend(buffer, offset, length); - } - } - - this.m_waitData.SetCancellationToken(cancellationToken); - switch (this.m_waitData.Wait(timeout)) - { - case WaitDataStatus.SetRunning: - return this.m_waitData.WaitResult; - - case WaitDataStatus.Overtime: - throw new TimeoutException(); - case WaitDataStatus.Canceled: + if (this.WaitingOptions.RemoteIPHost != null && this.Client is IUdpSession session) { - return this.WaitingOptions.ThrowBreakException && this.m_breaked ? throw new Exception("等待已终止。可能是客户端已掉线,或者被注销。") : (ResponsedData)default; + using (var receiver = session.CreateReceiver()) + { + session.Send(this.WaitingOptions.RemoteIPHost.EndPoint, buffer, offset, length); + + while (true) + { + using (var receiverResult = receiver.ReadAsync(m_cancellation.Token).GetFalseAwaitResult()) + { + var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo); + } + } + } } - case WaitDataStatus.Default: - case WaitDataStatus.Disposed: - default: - throw new Exception("未知错误"); - } - } - finally - { - easyLock.Release(); - if (this.WaitingOptions.BreakTrigger && this.Client is ITcpClientBase tcpClient) - { - tcpClient.Disconnected -= this.OnDisconnected; - } - if (this.WaitingOptions.BreakTrigger && this.Client is ISerialSessionBase serialSession) - { - serialSession.Disconnected -= this.OnSerialSessionDisconnected; - } - if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter) - { - this.Client.OnHandleReceivedData -= this.OnHandleReceivedData; - } - else - { - this.Client.OnHandleRawBuffer -= this.OnHandleRawBuffer; - } - } - } - - - - public ResponsedData SendThenResponse(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - return this.SendThenResponse(buffer, 0, buffer.Length, timeout, cancellationToken); - } - - public ResponsedData SendThenResponse(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - return this.SendThenResponse(byteBlock.Buffer, 0, byteBlock.Len, timeout, cancellationToken); - } - - #endregion 同步Response - - #region Response异步 - - public async Task SendThenResponseAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - try - { - await easyLock.WaitAsync(); - this.m_breaked = false; - this.Reset(); - if (this.WaitingOptions.BreakTrigger && this.Client is ITcpClientBase tcpClient) - { - tcpClient.Disconnected += this.OnDisconnected; - } - if (this.WaitingOptions.BreakTrigger && this.Client is ISerialSessionBase serialSession) - { - serialSession.Disconnected += this.OnSerialSessionDisconnected; - } - if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter) - { - this.Client.OnHandleReceivedData += this.OnHandleReceivedData; - } - else - { - this.Client.OnHandleRawBuffer += this.OnHandleRawBuffer; - } - - if (this.WaitingOptions.RemoteIPHost != null && this.Client is IUdpSession session) - { - if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.SendAdapter) - { - session.Send(this.WaitingOptions.RemoteIPHost.EndPoint, buffer, offset, length); - } - else - { - session.DefaultSend(this.WaitingOptions.RemoteIPHost.EndPoint, buffer, offset, length); - } - } - else - { - if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.SendAdapter) - { - this.Client.Send(buffer, offset, length); - } - else - { - this.Client.DefaultSend(buffer, offset, length); - } - } - - this.m_waitDataAsync.SetCancellationToken(cancellationToken); - switch (await this.m_waitDataAsync.WaitAsync(timeout)) - { - case WaitDataStatus.SetRunning: - return this.m_waitData.WaitResult; - - case WaitDataStatus.Overtime: - throw new TimeoutException(); - case WaitDataStatus.Canceled: + else { - return this.WaitingOptions.ThrowBreakException && this.m_breaked ? throw new Exception("等待已终止。可能是客户端已掉线,或者被注销。") : (ResponsedData)default; + using (var receiver = this.Client.CreateReceiver()) + { + this.Client.Send(buffer, offset, length); + while (true) + { + using (var receiverResult = receiver.ReadAsync(m_cancellation.Token).GetFalseAwaitResult()) + { + if (receiverResult.IsClosed) + { + this.m_breaked = true; + this.Cancel(); + } + var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo); + + if (this.m_func == null) + { + return response; + } + else + { + if (this.m_func.Invoke(response)) + { + return response; + } + } + } + } + } } - case WaitDataStatus.Default: - case WaitDataStatus.Disposed: - default: - throw new Exception("未知错误"); + } + } + catch (OperationCanceledException) + { + return this.WaitingOptions.ThrowBreakException && this.m_breaked ? throw new Exception("等待已终止。可能是客户端已掉线,或者被注销。") : throw new TimeoutException(); + } + finally + { + this.m_semaphoreSlim.Release(); } } - finally + + public ResponsedData SendThenResponse(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default) { - easyLock.Release(); - if (this.WaitingOptions.BreakTrigger && this.Client is ITcpClientBase tcpClient) + return this.SendThenResponse(buffer, 0, buffer.Length, timeout, token); + } + + public ResponsedData SendThenResponse(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default) + { + return this.SendThenResponse(byteBlock.Buffer, 0, byteBlock.Len, timeout, token); + } + + #endregion 同步Response + + #region Response异步 + + public async Task SendThenResponseAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default) + { + try { - tcpClient.Disconnected -= this.OnDisconnected; + await this.m_semaphoreSlim.WaitAsync(); + this.m_breaked = false; + if (token.CanBeCanceled) + { + using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(timeout); + m_cancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, token); + } + else + { + m_cancellation = new CancellationTokenSource(timeout); + } + using (m_cancellation) + { + if (this.WaitingOptions.RemoteIPHost != null && this.Client is IUdpSession session) + { + using (var receiver = session.CreateReceiver()) + { + await session.SendAsync(this.WaitingOptions.RemoteIPHost.EndPoint, buffer, offset, length); + + while (true) + { + using (var receiverResult = await receiver.ReadAsync(m_cancellation.Token)) + { + var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo); + } + } + } + } + else + { + using (var receiver = this.Client.CreateReceiver()) + { + await this.Client.SendAsync(buffer, offset, length); + while (true) + { + using (var receiverResult = await receiver.ReadAsync(m_cancellation.Token)) + { + if (receiverResult.IsClosed) + { + this.m_breaked = true; + this.Cancel(); + } + var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo); + + if (this.m_func == null) + { + return response; + } + else + { + if (this.m_func.Invoke(response)) + { + return response; + } + } + } + } + } + } + } } - if (this.WaitingOptions.BreakTrigger && this.Client is ISerialSessionBase serialSession) + catch (OperationCanceledException) { - serialSession.Disconnected -= this.OnSerialSessionDisconnected; + return this.WaitingOptions.ThrowBreakException && this.m_breaked ? throw new Exception("等待已终止。可能是客户端已掉线,或者被注销。") : throw new TimeoutException(); } - if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter) + finally { - this.Client.OnHandleReceivedData -= this.OnHandleReceivedData; - } - else - { - this.Client.OnHandleRawBuffer -= this.OnHandleRawBuffer; + this.m_semaphoreSlim.Release(); } } + + public Task SendThenResponseAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default) + { + return this.SendThenResponseAsync(buffer, 0, buffer.Length, timeout, token); + } + + public Task SendThenResponseAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default) + { + return this.SendThenResponseAsync(byteBlock.Buffer, 0, byteBlock.Len, timeout, token); + } + + #endregion Response异步 + + #region 字节同步 + + public byte[] SendThenReturn(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default) + { + return this.SendThenResponse(buffer, offset, length, timeout, token).Data; + } + + public byte[] SendThenReturn(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default) + { + return this.SendThenReturn(buffer, 0, buffer.Length, timeout, token); + } + + public byte[] SendThenReturn(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default) + { + return this.SendThenReturn(byteBlock.Buffer, 0, byteBlock.Len, timeout, token); + } + + #endregion 字节同步 + + #region 字节异步 + + public async Task SendThenReturnAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default) + { + return (await this.SendThenResponseAsync(buffer, offset, length, timeout, token)).Data; + } + + public async Task SendThenReturnAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default) + { + return (await this.SendThenResponseAsync(buffer, 0, buffer.Length, timeout, token)).Data; + } + + public async Task SendThenReturnAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default) + { + return (await this.SendThenResponseAsync(byteBlock.Buffer, 0, byteBlock.Len, timeout, token)).Data; + } + + #endregion 字节异步 } - - public Task SendThenResponseAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - return this.SendThenResponseAsync(buffer, 0, buffer.Length, timeout, cancellationToken); - } - - public Task SendThenResponseAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - return this.SendThenResponseAsync(byteBlock.Buffer, 0, byteBlock.Len, timeout, cancellationToken); - } - - #endregion Response异步 - - #region 字节同步 - - public byte[] SendThenReturn(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - return this.SendThenResponse(buffer, offset, length, timeout, cancellationToken).Data; - } - - public byte[] SendThenReturn(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - return this.SendThenReturn(buffer, 0, buffer.Length, timeout, cancellationToken); - } - - public byte[] SendThenReturn(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - return this.SendThenReturn(byteBlock.Buffer, 0, byteBlock.Len, timeout, cancellationToken); - } - - #endregion 字节同步 - - #region 字节异步 - - public async Task SendThenReturnAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - return (await this.SendThenResponseAsync(buffer, offset, length, timeout, cancellationToken)).Data; - } - - public async Task SendThenReturnAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - return (await this.SendThenResponseAsync(buffer, 0, buffer.Length, timeout, cancellationToken)).Data; - } - - public async Task SendThenReturnAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default) - { - return (await this.SendThenResponseAsync(byteBlock.Buffer, 0, byteBlock.Len, timeout, cancellationToken)).Data; - } - - #endregion 字节异步 - - private bool Set(ResponsedData responsedData) - { - this.m_waitData.Set(responsedData); - this.m_waitDataAsync.Set(responsedData); - return true; - } -} +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingClientExtension.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingClientExtension.cs index 422e296af..af821c66f 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingClientExtension.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingClientExtension.cs @@ -10,36 +10,49 @@ //------------------------------------------------------------------------------ #endregion -namespace ThingsGateway.Foundation.Sockets; +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// API首页:http://rrqm_home.gitee.io/touchsocket/ +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ -/// -/// WaitingClientExtensions -/// -public static class WaitingClientExtension +namespace ThingsGateway.Foundation.Sockets { /// - /// 获取筛选条件的可等待的客户端。 + /// WaitingClientExtensions /// - /// - /// - /// - /// 当条件成立时返回 - /// - public static IWaitingClient GetWaitingClientEx(this TClient client, WaitingOptions waitingOptions, Func func) where TClient : IClient, IDefaultSender, ISender + public static class WaitingClientExtension { - return new WaitingClient(client, waitingOptions, func); - } + /// + /// 获取筛选条件的可等待的客户端。 + /// + /// + /// + /// + /// 当条件成立时返回 + /// + public static IWaitingClient GetWaitingClient(this TClient client, WaitingOptions waitingOptions, Func func) where TClient : IClient, ISender + { + return new WaitingClient(client, waitingOptions, func); + } - /// - /// 获取可等待的客户端。 - /// - /// - /// - /// - /// - public static IWaitingClient GetWaitingClientEx(this TClient client, WaitingOptions waitingOptions) where TClient : IClient, IDefaultSender, ISender - { - var waitingClient = new WaitingClient(client, waitingOptions); - return waitingClient; + /// + /// 获取可等待的客户端。 + /// + /// + /// + /// + /// + public static IWaitingClient GetWaitingClient(this TClient client, WaitingOptions waitingOptions) where TClient : IClient, ISender + { + return new WaitingClient(client, waitingOptions); + } } } \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingOptions.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingOptions.cs index b483496a0..52147b988 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingOptions.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Socket/WaitingClient/WaitingOptions.cs @@ -25,41 +25,12 @@ namespace ThingsGateway.Foundation.Sockets { - /// - /// 适配器筛选 - /// - public enum AdapterFilter - { - /// - /// 发送和接收都经过适配器 - /// - AllAdapter, - - /// - /// 发送经过适配器,接收不经过 - /// - SendAdapter, - - /// - /// 发送不经过适配器,接收经过 - /// - WaitAdapter, - - /// - /// 全都不经过适配器。 - /// - NoneAll - } - /// /// 等待设置 /// public class WaitingOptions { - /// - /// 适配器筛选 - /// - public AdapterFilter AdapterFilter { get; set; } = AdapterFilter.AllAdapter; + /// /// 当Client为Tcp系时。是否在断开连接时立即触发结果。默认会返回null。当时,会触发异常。 /// diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Decorators/AdapterData.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Attributes/SwaggerDescriptionAttribute.cs similarity index 61% rename from framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Decorators/AdapterData.cs rename to framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Attributes/SwaggerDescriptionAttribute.cs index 3f98b0f7e..674680bf4 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Decorators/AdapterData.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Attributes/SwaggerDescriptionAttribute.cs @@ -10,33 +10,33 @@ //------------------------------------------------------------------------------ #endregion -namespace ThingsGateway.Foundation.Core +namespace ThingsGateway.Foundation.WebApi.Swagger { /// - /// 适配器数据 + /// Swagger描述特性 /// - public ref struct AdapterData + [AttributeUsage(AttributeTargets.Class)] + public sealed class SwaggerDescriptionAttribute : Attribute { /// - /// 适配器数据 + /// Swagger描述特性 /// - /// - /// - public AdapterData(ByteBlock byteBlock, IRequestInfo requestInfo) + /// + public SwaggerDescriptionAttribute(params string[] groups) { - this.ByteBlock = byteBlock; - this.RequestInfo = requestInfo; + this.Groups = groups; } /// - /// ByteBlock + /// Swagger描述特性 /// - public ByteBlock ByteBlock { get; private set; } + public SwaggerDescriptionAttribute() + { + } /// - /// RequestInfo + /// 分组 /// - public IRequestInfo RequestInfo { get; private set; } - + public string[] Groups { get; set; } } -} +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiComponent.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiComponent.cs new file mode 100644 index 000000000..2fc37027e --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiComponent.cs @@ -0,0 +1,22 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiComponent + { + [JsonProperty("schemas")] + public Dictionary Schemas { get; set; } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiContent.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiContent.cs new file mode 100644 index 000000000..43bfe2541 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiContent.cs @@ -0,0 +1,22 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiContent + { + [JsonProperty("schema")] + public OpenApiSchema Schema { get; set; } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiDataTypes.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiDataTypes.cs new file mode 100644 index 000000000..1b0f6330f --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiDataTypes.cs @@ -0,0 +1,80 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + [JsonConverter(typeof(OpenApiStringEnumConverter))] + internal enum OpenApiDataTypes + { + /// + /// 字符串 + /// + String, + + /// + /// 数值 + /// + Number, + + /// + /// 整数 + /// + Integer, + + /// + /// 布尔值 + /// + Boolean, + + /// + /// 二进制(如文件) + /// + Binary, + + /// + /// 二进制集合 + /// + BinaryCollection, + + /// + /// 记录值(字典) + /// + Record, + + /// + /// 元组值 + /// + Tuple, + + /// + /// 数组 + /// + Array, + + /// + /// 对象 + /// + Object, + + /// + /// 结构 + /// + Struct, + + /// + /// Any + /// + Any + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiInfo.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiInfo.cs new file mode 100644 index 000000000..3e94c7909 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiInfo.cs @@ -0,0 +1,25 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiInfo + { + [JsonProperty("title")] + public string Title { get; set; } = "API V1"; + + [JsonProperty("version")] + public string Version { get; set; } = "1.0"; + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiParameter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiParameter.cs new file mode 100644 index 000000000..05fbaae04 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiParameter.cs @@ -0,0 +1,28 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiParameter + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("in")] + public string In { get; set; } + + [JsonProperty("schema")] + public OpenApiSchema Schema { get; set; } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiPath.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiPath.cs new file mode 100644 index 000000000..3dde0aa78 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiPath.cs @@ -0,0 +1,18 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiPath : Dictionary + { + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiPathValue.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiPathValue.cs new file mode 100644 index 000000000..fb090f94c --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiPathValue.cs @@ -0,0 +1,40 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiPathValue + { + [JsonProperty("tags")] + public IEnumerable Tags { get; set; } + + [JsonProperty("summary")] + public string Summary { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("operationId")] + public string OperationId { get; set; } + + [JsonProperty("requestBody")] + public OpenApiRequestBody RequestBody { get; set; } + + [JsonProperty("parameters")] + public IEnumerable Parameters { get; set; } + + [JsonProperty("responses")] + public Dictionary Responses { get; set; } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiProperty.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiProperty.cs new file mode 100644 index 000000000..6f16266ed --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiProperty.cs @@ -0,0 +1,34 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiProperty + { + [JsonProperty("type")] + public OpenApiDataTypes? Type { get; set; } + + [JsonProperty("readOnly")] + public bool? ReadOnly { get; set; } + + [JsonProperty("$ref")] + public string Ref { get; set; } + + [JsonProperty("format")] + public string Format { get; set; } + + [JsonProperty("items")] + public OpenApiProperty Items { get; set; } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiRequestBody.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiRequestBody.cs new file mode 100644 index 000000000..83ae3c5d9 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiRequestBody.cs @@ -0,0 +1,25 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiRequestBody + { + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("content")] + public Dictionary Content { get; set; } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiResponse.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiResponse.cs new file mode 100644 index 000000000..a164977dc --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiResponse.cs @@ -0,0 +1,25 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiResponse + { + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("content")] + public Dictionary Content { get; set; } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiRoot.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiRoot.cs new file mode 100644 index 000000000..413b79a77 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiRoot.cs @@ -0,0 +1,31 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiRoot + { + [JsonProperty("openapi")] + public string OpenApi { get; set; } = "3.0.1"; + + [JsonProperty("info")] + public OpenApiInfo Info { get; set; } + + [JsonProperty("paths")] + public Dictionary Paths { get; set; } + + [JsonProperty("components")] + public OpenApiComponent Components { get; set; } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiSchema.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiSchema.cs new file mode 100644 index 000000000..1a54a51d7 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Common/OpenApiSchema.cs @@ -0,0 +1,37 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiSchema + { + [JsonProperty("$ref")] + public string Ref { get; set; } + + [JsonProperty("type")] + public OpenApiDataTypes? Type { get; set; } + + [JsonProperty("format")] + public string Format { get; set; } + + [JsonProperty("properties")] + public Dictionary Properties { get; set; } + + [JsonProperty("items")] + public OpenApiSchema Items { get; set; } + + [JsonProperty("enum")] + public int[] Enum { get; set; } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Extensions/OpenApiExtension.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Extensions/OpenApiExtension.cs new file mode 100644 index 000000000..c08232c42 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Extensions/OpenApiExtension.cs @@ -0,0 +1,40 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal static class OpenApiExtension + { + public static bool IsPrimitive(this OpenApiDataTypes dataTypes) + { + switch (dataTypes) + { + case OpenApiDataTypes.String: + case OpenApiDataTypes.Number: + case OpenApiDataTypes.Integer: + case OpenApiDataTypes.Boolean: + return true; + + case OpenApiDataTypes.Binary: + case OpenApiDataTypes.BinaryCollection: + case OpenApiDataTypes.Record: + case OpenApiDataTypes.Tuple: + case OpenApiDataTypes.Array: + case OpenApiDataTypes.Object: + case OpenApiDataTypes.Struct: + case OpenApiDataTypes.Any: + default: + return false; + } + } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Decorators/SingleStreamDecorator.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Extensions/SwaggerPluginsManagerExtension.cs similarity index 58% rename from framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Decorators/SingleStreamDecorator.cs rename to framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Extensions/SwaggerPluginsManagerExtension.cs index 25b045de2..0143d6f29 100644 --- a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/Core/DataAdapter/Decorators/SingleStreamDecorator.cs +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Extensions/SwaggerPluginsManagerExtension.cs @@ -10,26 +10,21 @@ //------------------------------------------------------------------------------ #endregion -namespace ThingsGateway.Foundation.Core +namespace ThingsGateway.Foundation.WebApi.Swagger { /// - /// 单线程状况的流式数据处理适配器的装饰器。 + /// SwaggerPluginsManagerExtension /// - public class SingleStreamDecorator + public static class SwaggerPluginsManagerExtension { - private readonly SingleStreamDataHandlingAdapter m_adapter; /// - /// 单线程状况的流式数据处理适配器的装饰器。 + /// 使用插件。 /// - public SingleStreamDecorator(SingleStreamDataHandlingAdapter adapter) + /// + /// + public static SwaggerPlugin UseSwagger(this IPluginsManager pluginsManager) { - adapter.ReceivedCallBack = this.ReceivedCallBack; - this.m_adapter = adapter; - } - - private void ReceivedCallBack(ByteBlock block, IRequestInfo info) - { - + return pluginsManager.Add(); } } -} +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/JsonConverters/OpenApiStringEnumConverter.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/JsonConverters/OpenApiStringEnumConverter.cs new file mode 100644 index 000000000..4c384c11d --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/JsonConverters/OpenApiStringEnumConverter.cs @@ -0,0 +1,24 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + internal class OpenApiStringEnumConverter : StringEnumConverter + { + public OpenApiStringEnumConverter() : base(new CamelCaseNamingStrategy()) + { + } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Plugins/SwaggerPlugin.cs b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Plugins/SwaggerPlugin.cs new file mode 100644 index 000000000..9fefcbced --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/Plugins/SwaggerPlugin.cs @@ -0,0 +1,701 @@ +#region copyright +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ +// QQ群:605534569 +//------------------------------------------------------------------------------ +#endregion + +using Newtonsoft.Json; + +using System.Collections; +using System.Diagnostics; +using System.Reflection; +using System.Text; + +namespace ThingsGateway.Foundation.WebApi.Swagger +{ + /// + /// SwaggerPlugin + /// + public sealed class SwaggerPlugin : PluginBase, IServerStartedPlugin + { + private readonly IContainer m_container; + private readonly ILog m_logger; + private readonly Dictionary m_swagger = new Dictionary(); + + /// + /// SwaggerPlugin + /// + public SwaggerPlugin(IContainer container, ILog logger) + { + this.m_container = container; + this.m_logger = logger; + } + + /// + /// 是否在浏览器打开Swagger页面 + /// + public bool LaunchBrowser { get; set; } + + /// + /// 访问Swagger的前缀,默认“swagger” + /// + public string Prefix { get; set; } = "swagger"; + + /// + public async Task OnServerStarted(IService sender, ServiceStateEventArgs e) + { + if (e.ServerState != ServerState.Running) + { + return; + } + if (!this.m_container.IsRegistered(typeof(WebApiParserPlugin))) + { + this.m_logger.Warning($"该服务器中似乎没有添加{nameof(WebApiParserPlugin)}插件(WebApi插件)。"); + return; + } + + var webApiParserPlugin = this.m_container.Resolve(); + if (webApiParserPlugin == null) + { + return; + } + + var assembly = Assembly.GetExecutingAssembly(); + var names = assembly.GetManifestResourceNames(); + foreach (var item in names) + { + using (var stream = assembly.GetManifestResourceStream(item)) + { + var bytes = new byte[stream.Length]; + stream.Read(bytes, 0, bytes.Length); + var prefix = this.Prefix.IsNullOrEmpty() ? "/" : (this.Prefix.StartsWith("/") ? this.Prefix : $"/{this.Prefix}"); + var name = item.Replace("ThingsGateway.Foundation.WebApi.Swagger.api.", string.Empty); + if (name == "openapi.json") + { + try + { + bytes = this.BuildOpenApi(webApiParserPlugin); + } + catch (Exception ex) + { + this.m_logger.Exception(ex); + } + } + var key = prefix == "/" ? $"/{name}" : $"{prefix}/{name}"; + this.m_swagger.Add(key, bytes); + } + } + await e.InvokeNext(); + + if (this.LaunchBrowser) + { + var iphost = (sender as ITcpService).Monitors.First().Option.IpHost; + string host; + if (iphost.IsLoopback || iphost.DnsSafeHost == "127.0.0.1" || iphost.DnsSafeHost == "0.0.0.0") + { + host = "127.0.0.1"; + } + else + { + host = iphost.DnsSafeHost; + } + + var scheme = iphost.Scheme == "https" ? iphost.Scheme : "http"; + + var prefix = this.Prefix.IsNullOrEmpty() ? "/" : (this.Prefix.StartsWith("/") ? this.Prefix : $"/{this.Prefix}"); + var index = prefix == "/" ? $"/index.html" : $"{prefix}/index.html"; + this.OpenWebLink($"{scheme}://{host}:{iphost.Port}{index}"); + } + } + + /// + /// 设置访问Swagger的前缀,默认“swagger” + /// + /// + /// + public SwaggerPlugin SetPrefix(string value) + { + this.Prefix = value; + return this; + } + + /// + /// 在浏览器打开Swagger页面 + /// + /// + public SwaggerPlugin UseLaunchBrowser() + { + this.LaunchBrowser = true; + return this; + } + + /// + protected override void Loaded(IPluginsManager pluginsManager) + { + base.Loaded(pluginsManager); + pluginsManager.Add(nameof(IHttpPlugin.OnHttpRequest), this.OnHttpRequest); + } + + /// + /// 检查类型是否是 IFormFileCollection 类型 + /// + /// + /// + private static bool IsFormFileCollection(Type type) + { + // 如果是 MultifileCollection 类型则直接返回 + if (typeof(MultifileCollection).IsAssignableFrom(type)) + { + return true; + } + + // 处理 IFormFile 集合类型 + if (typeof(IEnumerable).IsAssignableFrom(type)) + { + // 检查是否是 IFormFile 数组类型 + if (type.IsArray) + { + // 获取数组元素类型 + var elementType = type.GetElementType(); + + // 检查元素类型是否是 IFormFile 类型 + if (elementType != null + && typeof(IFormFile).IsAssignableFrom(elementType)) + { + return true; + } + } + // 检查是否是 IFormFile 集合类型 + else + { + // 检查集合项类型是否是 IFormFile 类型 + if (type.IsGenericType && type.GenericTypeArguments.Length == 1 && typeof(IFormFile).IsAssignableFrom(type.GenericTypeArguments[0])) + { + return true; + } + } + } + + return false; + } + + private void OpenWebLink(string url) + { + try + { + var psi = new ProcessStartInfo + { + FileName = url, + UseShellExecute = true + }; + Process.Start(psi); + } + catch (Exception ex) + { + this.m_logger.Exception(ex); + } + } + + #region Build + + private void AddSchemaType(Type type, in List types) + { + if (type.IsArray) + { + type = type.GetElementType(); + } + else if (typeof(IEnumerable).IsAssignableFrom(type) && type.IsGenericType && type.GenericTypeArguments.Length == 1) + { + type = type.GetGenericArguments()[0]; + } + + if (type.IsPrimitive || type == typeof(string)) + { + return; + } + + if (types.Contains(type)) + { + return; + } + if (this.ParseDataTypes(type) != OpenApiDataTypes.Object) + { + return; + } + types.Add(type); + + foreach (var item in type.GetProperties()) + { + this.AddSchemaType(item.PropertyType, types); + } + } + + private void BuildGet(WebApiParserPlugin webApiParserPlugin, in List schemaTypeList, in Dictionary paths) + { + foreach (var item in webApiParserPlugin.GetRouteMap) + { + //解析get + var methodInstance = item.Value; + var openApiPath = new OpenApiPath(); + + var openApiPathValue = new OpenApiPathValue + { + Tags = this.GetTags(methodInstance), + Description = methodInstance.GetDescription(), + Summary = methodInstance.GetDescription() + }; + var i = 0; + if (methodInstance.MethodFlags.HasFlag(MethodFlags.IncludeCallContext)) + { + i = 1; + } + + var parameters = new List(); + for (; i < methodInstance.Parameters.Length; i++) + { + var parameter = methodInstance.Parameters[i]; + var openApiParameter = this.GetParameter(parameter); + openApiParameter.In = "query"; + + this.AddSchemaType(parameter.ParameterType, schemaTypeList); + parameters.Add(openApiParameter); + } + + openApiPathValue.Parameters = parameters.Count > 0 ? parameters : null; + + this.BuildResponse(methodInstance, openApiPathValue, schemaTypeList); + + openApiPath.Add("get", openApiPathValue); + paths.Add(item.Key, openApiPath); + } + } + + private byte[] BuildOpenApi(WebApiParserPlugin webApiParserPlugin) + { + var openApiRoot = new OpenApiRoot(); + openApiRoot.Info = new OpenApiInfo(); + + var paths = new Dictionary(); + + var schemaTypeList = new List(); + + this.BuildGet(webApiParserPlugin, schemaTypeList, paths); + this.BuildPost(webApiParserPlugin, schemaTypeList, paths); + + openApiRoot.Paths = paths; + + openApiRoot.Components = this.GetComponents(schemaTypeList); + + var jsonSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; + return JsonConvert.SerializeObject(openApiRoot, Formatting.Indented, jsonSetting).ToUTF8Bytes(); + } + + private void BuildPost(WebApiParserPlugin webApiParserPlugin, in List schemaTypeList, in Dictionary paths) + { + foreach (var item in webApiParserPlugin.PostRouteMap) + { + //解析post + var methodInstance = item.Value; + var openApiPath = new OpenApiPath(); + + var openApiPathValue = new OpenApiPathValue + { + Tags = this.GetTags(methodInstance), + Description = methodInstance.GetDescription(), + Summary = methodInstance.GetDescription() + }; + + var i = 0; + if (methodInstance.MethodFlags.HasFlag(MethodFlags.IncludeCallContext)) + { + i = 1; + } + + var parameters = new List(); + for (; i < methodInstance.Parameters.Length; i++) + { + var parameter = methodInstance.Parameters[i]; + if (this.ParseDataTypes(parameter.ParameterType).IsPrimitive()) + { + var openApiParameter = this.GetParameter(parameter); + openApiParameter.In = "query"; + this.AddSchemaType(parameter.ParameterType, schemaTypeList); + parameters.Add(openApiParameter); + } + } + + openApiPathValue.Parameters = parameters.Count > 0 ? parameters : default; + + ParameterInfo parameterInfo = null; + if (methodInstance.MethodFlags.HasFlag(MethodFlags.IncludeCallContext)) + { + if (methodInstance.Parameters.Length > 1) + { + parameterInfo = methodInstance.Parameters.Last(); + } + } + else + { + if (methodInstance.Parameters.Length > 0) + { + parameterInfo = methodInstance.Parameters.Last(); + } + } + if (parameterInfo != null && !this.ParseDataTypes(parameterInfo.ParameterType).IsPrimitive()) + { + this.AddSchemaType(parameterInfo.ParameterType, schemaTypeList); + + var body = new OpenApiRequestBody(); + body.Content = new Dictionary(); + var content = new OpenApiContent(); + content.Schema = this.CreateSchema(parameterInfo.ParameterType); + body.Content.Add("application/json", content); + openApiPathValue.RequestBody = body; + } + + this.BuildResponse(methodInstance, openApiPathValue, schemaTypeList); + + openApiPath.Add("post", openApiPathValue); + paths.Add(item.Key, openApiPath); + } + } + + private void BuildResponse(MethodInstance methodInstance, in OpenApiPathValue openApiPathValue, in List schemaTypeList) + { + var openApiResponse = new OpenApiResponse(); + openApiResponse.Description = "Success"; + + if (methodInstance.HasReturn) + { + openApiResponse.Content = new Dictionary(); + var openApiContent = new OpenApiContent(); + openApiResponse.Content.Add("application/json", openApiContent); + openApiContent.Schema = this.CreateSchema(methodInstance.ReturnType); + this.AddSchemaType(methodInstance.ReturnType, schemaTypeList); + } + + openApiPathValue.Responses = new Dictionary(); + openApiPathValue.Responses.Add("200", openApiResponse); + } + + private OpenApiProperty CreateProperty(Type type) + { + var openApiProperty = new OpenApiProperty(); + var dataTypes = this.ParseDataTypes(type); + + switch (dataTypes) + { + case OpenApiDataTypes.String: + case OpenApiDataTypes.Number: + case OpenApiDataTypes.Boolean: + case OpenApiDataTypes.Integer: + { + openApiProperty.Type = dataTypes; + break; + } + case OpenApiDataTypes.Binary: + break; + + case OpenApiDataTypes.BinaryCollection: + break; + + case OpenApiDataTypes.Record: + break; + + case OpenApiDataTypes.Tuple: + break; + + case OpenApiDataTypes.Array: + { + openApiProperty.Type = dataTypes; + openApiProperty.Items = this.CreateProperty(type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0]); + } + break; + + case OpenApiDataTypes.Object: + { + openApiProperty.Ref = $"#/components/schemas/{type.Name}"; + break; + } + case OpenApiDataTypes.Struct: + break; + + case OpenApiDataTypes.Any: + break; + + default: + break; + } + + openApiProperty.Format = this.GetSchemaFormat(type); + + return openApiProperty; + } + + private OpenApiSchema CreateSchema(Type type) + { + var schema = new OpenApiSchema(); + var dataTypes = this.ParseDataTypes(type); + + switch (dataTypes) + { + case OpenApiDataTypes.String: + case OpenApiDataTypes.Number: + case OpenApiDataTypes.Boolean: + case OpenApiDataTypes.Integer: + { + schema.Type = dataTypes; + break; + } + case OpenApiDataTypes.Binary: + break; + + case OpenApiDataTypes.BinaryCollection: + break; + + case OpenApiDataTypes.Record: + break; + + case OpenApiDataTypes.Tuple: + break; + + case OpenApiDataTypes.Array: + { + schema.Type = dataTypes; + schema.Items = this.CreateSchema(type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0]); + } + break; + + case OpenApiDataTypes.Object: + { + if (type.IsEnum) + { + schema.Enum = (int[])Enum.GetValues(type); + schema.Type = OpenApiDataTypes.Integer; + } + else + { + schema.Ref = $"#/components/schemas/{this.GetSchemaName(type)}"; + } + + break; + } + case OpenApiDataTypes.Struct: + break; + + case OpenApiDataTypes.Any: + break; + + default: + break; + } + + schema.Format = this.GetSchemaFormat(type); + return schema; + } + + private string GetSchemaName(Type type) + { + if (!type.IsGenericType) + { + return type.Name; + } + + StringBuilder stringBuilder = new StringBuilder(); + foreach (var item in type.GetGenericArguments()) + { + stringBuilder.Append(GetSchemaName(item)); + } + stringBuilder.Append(type.Name.Substring(0, type.Name.IndexOf('`'))); + return stringBuilder.ToString(); + } + + private OpenApiComponent GetComponents(List types) + { + if (types.Count == 0) + { + return default; + } + var components = new OpenApiComponent(); + components.Schemas = new Dictionary(); + foreach (var type in types) + { + var schema = this.CreateSchema(type); + var properties = new Dictionary(); + foreach (var propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) + { + properties.Add(propertyInfo.Name, this.CreateProperty(propertyInfo.PropertyType)); + } + schema.Properties = properties.Count == 0 ? default : properties; + components.Schemas.TryAdd(this.GetSchemaName(type), schema); + } + return components; + } + + private string GetIn(Type type) + { + if (type.IsPrimitive || type == typeof(string)) + { + return "query"; + } + + return null; + } + + private OpenApiParameter GetParameter(ParameterInfo parameterInfo) + { + var openApiParameter = new OpenApiParameter(); + openApiParameter.Name = parameterInfo.Name; + + var openApiSchema = this.CreateSchema(parameterInfo.ParameterType); + + openApiParameter.Schema = openApiSchema; + + return openApiParameter; + } + + private string GetSchemaFormat(Type type) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + return "int32"; + + case TypeCode.Int64: + case TypeCode.UInt64: + return "int64"; + + case TypeCode.Single: + return "float"; + + case TypeCode.Double: + case TypeCode.Decimal: + return "double"; + + case TypeCode.DateTime: + return "date-time"; + + case TypeCode.Boolean: + case TypeCode.Char: + case TypeCode.String: + return default; + + default: + return "object"; + } + } + + private IEnumerable GetTags(MethodInstance methodInstance) + { + var tags = new List(); + foreach (var item in methodInstance.ServerFromType.GetCustomAttributes(true)) + { + if (item is SwaggerDescriptionAttribute descriptionAttribute) + { + if (descriptionAttribute.Groups != null) + { + tags.AddRange(descriptionAttribute.Groups); + } + } + } + + foreach (var item in methodInstance.Info.GetCustomAttributes(true)) + { + if (item is SwaggerDescriptionAttribute descriptionAttribute) + { + if (descriptionAttribute.Groups != null) + { + tags.AddRange(descriptionAttribute.Groups); + } + } + } + + if (tags.Count == 0) + { + tags.Add(methodInstance.ServerFromType.Name); + } + + return tags; + } + + private Task OnHttpRequest(IHttpSocketClient client, HttpContextEventArgs e) + { + var context = e.Context; + var request = context.Request; + //var response = context.Response; + + if (this.m_swagger.TryGetValue(request.RelativeURL, out var bytes)) + { + e.Handled = true; + context.Response + .SetStatus() + .SetContentTypeByExtension(Path.GetExtension(request.RelativeURL)) + .SetContent(bytes); + context.Response.Answer(); + return EasyTask.CompletedTask; + } + return e.InvokeNext(); + } + + private OpenApiDataTypes ParseDataTypes(Type type) + { + // 空检查 + if (type is null) + { + return OpenApiDataTypes.Any; + } + + return type switch + { + // 字符串 + _ when typeof(string).IsAssignableFrom(type) + || typeof(char).IsAssignableFrom(type) => OpenApiDataTypes.String, + // 整数 + _ when type.IsInteger() => OpenApiDataTypes.Integer, + + // 数值 + _ when type.IsDecimal() => OpenApiDataTypes.Number, + // 布尔值 + _ when typeof(bool).IsAssignableFrom(type) => OpenApiDataTypes.Boolean, + // 日期 + _ when typeof(DateTime).IsAssignableFrom(type) + || typeof(DateTimeOffset).IsAssignableFrom(type) => OpenApiDataTypes.String, + // 二进制 + _ when typeof(IFormFile).IsAssignableFrom(type) => OpenApiDataTypes.Binary, + // 二进制集合 + _ when IsFormFileCollection(type) => OpenApiDataTypes.BinaryCollection, + // 记录值 + _ when type.IsDictionary() => OpenApiDataTypes.Record, + // 元组值 + _ when type.IsValueTuple() => OpenApiDataTypes.Tuple, + // 数组 + _ when type.IsArray || (typeof(IEnumerable).IsAssignableFrom(type) + && type.IsGenericType + && type.GenericTypeArguments.Length == 1) => OpenApiDataTypes.Array, + // 对象 + _ when type.IsEnum || (type != typeof(object) + && type.IsClass + && Type.GetTypeCode(type) == TypeCode.Object) => OpenApiDataTypes.Object, + // 结构 + _ when type.IsValueType + && !type.IsPrimitive + && !type.IsEnum => OpenApiDataTypes.Struct, + // 缺省值 + _ => OpenApiDataTypes.Any + }; + } + + #endregion Build + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/favicon-16x16.png b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..8b194e617af1c135e6b37939591d24ac3a5efa18 GIT binary patch literal 665 zcmV;K0%rY*P)}JKSduyL>)s!A4EhTMMEM%Q;aL6%l#xiZiF>S;#Y{N2Zz%pvTGHJduXuC6Lx-)0EGfRy*N{Tv4i8@4oJ41gw zKzThrcRe|7J~(YYIBq{SYCkn-KQm=N8$CrEK1CcqMI1dv9z#VRL_{D)L|`QmF8}}l zJ9JV`Q}p!p_4f7m_U`WQ@apR4;o;!mnU<7}iG_qr zF(e)x9~BG-3IzcG2M4an0002kNkl41`ZiN1i62V%{PM@Ry|IS_+Yc7{bb`MM~xm(7p4|kMHP&!VGuDW4kFixat zXw43VmgwEvB$hXt_u=vZ>+v4i7E}n~eG6;n4Z=zF1n?T*yg<;W6kOfxpC6nao>VR% z?fpr=asSJ&`L*wu^rLJ5Peq*PB0;alL#XazZCBxJLd&giTfw@!hW167F^`7kobi;( ze<<>qNlP|xy7S1zl@lZNIBR7#o9ybJsptO#%}P0hz~sBp00000NkvXXu0mjfUsDF? literal 0 HcmV?d00001 diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/favicon-32x32.png b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..249737fe44558e679f0b67134e274461d988fa98 GIT binary patch literal 628 zcmV-)0*n2LP)Ma*GM0}OV<074bNCP7P7GVd{iMr*I6y~TMLss@FjvgL~HxU z%Vvj33AwpD(Z4*$Mfx=HaU16axM zt2xG_rloN<$iy9j9I5 + + + + + Swagger UI + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/oauth2-redirect.html b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/oauth2-redirect.html new file mode 100644 index 000000000..a427621e7 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/oauth2-redirect.html @@ -0,0 +1,75 @@ + + + + Swagger UI: OAuth2 Redirect + + + + + \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/openapi.json b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/openapi.json new file mode 100644 index 000000000..07cc2e557 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/openapi.json @@ -0,0 +1,78 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "API V1", + "version": "v1" + }, + "paths": { + "/api/Todo": { + "get": { + "tags": [ + "Todo" + ], + "operationId": "ApiTodoGet", + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ToDoItem" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ToDoItem" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ToDoItem" + } + } + } + } + } + } + } + }, + "/api/Todo/{id}": { + "get": { + "tags": [ + "A" + ] + }, + "put": {}, + "delete": {} + } + }, + "components": { + "schemas": { + "ToDoItem": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "name": { + "type": "string", + "nullable": true + }, + "isCompleted": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/swagger-ui-bundle.js b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/swagger-ui-bundle.js new file mode 100644 index 000000000..3896e2b98 --- /dev/null +++ b/framework/Foundation/ThingsGateway.Foundation/TouchSocket/WebApi.Swagger/api/swagger-ui-bundle.js @@ -0,0 +1,3 @@ +/*! For license information please see swagger-ui-bundle.js.LICENSE.txt */ +!function (e, t) { "object" == typeof exports && "object" == typeof module ? module.exports = t(function () { try { return require("esprima") } catch (e) { } }()) : "function" == typeof define && define.amd ? define(["esprima"], t) : "object" == typeof exports ? exports.SwaggerUIBundle = t(function () { try { return require("esprima") } catch (e) { } }()) : e.SwaggerUIBundle = t(e.esprima) }(this, (function (e) { return function (e) { var t = {}; function n(r) { if (t[r]) return t[r].exports; var o = t[r] = { i: r, l: !1, exports: {} }; return e[r].call(o.exports, o, o.exports, n), o.l = !0, o.exports } return n.m = e, n.c = t, n.d = function (e, t, r) { n.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: r }) }, n.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, n.t = function (e, t) { if (1 & t && (e = n(e)), 8 & t) return e; if (4 & t && "object" == typeof e && e && e.__esModule) return e; var r = Object.create(null); if (n.r(r), Object.defineProperty(r, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e) for (var o in e) n.d(r, o, function (t) { return e[t] }.bind(null, o)); return r }, n.n = function (e) { var t = e && e.__esModule ? function () { return e.default } : function () { return e }; return n.d(t, "a", t), t }, n.o = function (e, t) { return Object.prototype.hasOwnProperty.call(e, t) }, n.p = "/dist", n(n.s = 549) }([function (e, t, n) { "use strict"; e.exports = n(129) }, function (e, t, n) { e.exports = function () { "use strict"; var e = Array.prototype.slice; function t(e, t) { t && (e.prototype = Object.create(t.prototype)), e.prototype.constructor = e } function n(e) { return i(e) ? e : $(e) } function r(e) { return s(e) ? e : K(e) } function o(e) { return u(e) ? e : Y(e) } function a(e) { return i(e) && !c(e) ? e : G(e) } function i(e) { return !(!e || !e[p]) } function s(e) { return !(!e || !e[f]) } function u(e) { return !(!e || !e[h]) } function c(e) { return s(e) || u(e) } function l(e) { return !(!e || !e[d]) } t(r, n), t(o, n), t(a, n), n.isIterable = i, n.isKeyed = s, n.isIndexed = u, n.isAssociative = c, n.isOrdered = l, n.Keyed = r, n.Indexed = o, n.Set = a; var p = "@@__IMMUTABLE_ITERABLE__@@", f = "@@__IMMUTABLE_KEYED__@@", h = "@@__IMMUTABLE_INDEXED__@@", d = "@@__IMMUTABLE_ORDERED__@@", m = "delete", v = 5, g = 1 << v, y = g - 1, b = {}, _ = { value: !1 }, w = { value: !1 }; function x(e) { return e.value = !1, e } function E(e) { e && (e.value = !0) } function S() { } function C(e, t) { t = t || 0; for (var n = Math.max(0, e.length - t), r = new Array(n), o = 0; o < n; o++)r[o] = e[o + t]; return r } function A(e) { return void 0 === e.size && (e.size = e.__iterate(O)), e.size } function k(e, t) { if ("number" != typeof t) { var n = t >>> 0; if ("" + n !== t || 4294967295 === n) return NaN; t = n } return t < 0 ? A(e) + t : t } function O() { return !0 } function j(e, t, n) { return (0 === e || void 0 !== n && e <= -n) && (void 0 === t || void 0 !== n && t >= n) } function T(e, t) { return P(e, t, 0) } function I(e, t) { return P(e, t, t) } function P(e, t, n) { return void 0 === e ? n : e < 0 ? Math.max(0, t + e) : void 0 === t ? e : Math.min(t, e) } var N = 0, M = 1, R = 2, D = "function" == typeof Symbol && Symbol.iterator, L = "@@iterator", B = D || L; function F(e) { this.next = e } function U(e, t, n, r) { var o = 0 === e ? t : 1 === e ? n : [t, n]; return r ? r.value = o : r = { value: o, done: !1 }, r } function q() { return { value: void 0, done: !0 } } function z(e) { return !!H(e) } function V(e) { return e && "function" == typeof e.next } function W(e) { var t = H(e); return t && t.call(e) } function H(e) { var t = e && (D && e[D] || e[L]); if ("function" == typeof t) return t } function J(e) { return e && "number" == typeof e.length } function $(e) { return null == e ? ie() : i(e) ? e.toSeq() : ce(e) } function K(e) { return null == e ? ie().toKeyedSeq() : i(e) ? s(e) ? e.toSeq() : e.fromEntrySeq() : se(e) } function Y(e) { return null == e ? ie() : i(e) ? s(e) ? e.entrySeq() : e.toIndexedSeq() : ue(e) } function G(e) { return (null == e ? ie() : i(e) ? s(e) ? e.entrySeq() : e : ue(e)).toSetSeq() } F.prototype.toString = function () { return "[Iterator]" }, F.KEYS = N, F.VALUES = M, F.ENTRIES = R, F.prototype.inspect = F.prototype.toSource = function () { return this.toString() }, F.prototype[B] = function () { return this }, t($, n), $.of = function () { return $(arguments) }, $.prototype.toSeq = function () { return this }, $.prototype.toString = function () { return this.__toString("Seq {", "}") }, $.prototype.cacheResult = function () { return !this._cache && this.__iterateUncached && (this._cache = this.entrySeq().toArray(), this.size = this._cache.length), this }, $.prototype.__iterate = function (e, t) { return pe(this, e, t, !0) }, $.prototype.__iterator = function (e, t) { return fe(this, e, t, !0) }, t(K, $), K.prototype.toKeyedSeq = function () { return this }, t(Y, $), Y.of = function () { return Y(arguments) }, Y.prototype.toIndexedSeq = function () { return this }, Y.prototype.toString = function () { return this.__toString("Seq [", "]") }, Y.prototype.__iterate = function (e, t) { return pe(this, e, t, !1) }, Y.prototype.__iterator = function (e, t) { return fe(this, e, t, !1) }, t(G, $), G.of = function () { return G(arguments) }, G.prototype.toSetSeq = function () { return this }, $.isSeq = ae, $.Keyed = K, $.Set = G, $.Indexed = Y; var Z, X, Q, ee = "@@__IMMUTABLE_SEQ__@@"; function te(e) { this._array = e, this.size = e.length } function ne(e) { var t = Object.keys(e); this._object = e, this._keys = t, this.size = t.length } function re(e) { this._iterable = e, this.size = e.length || e.size } function oe(e) { this._iterator = e, this._iteratorCache = [] } function ae(e) { return !(!e || !e[ee]) } function ie() { return Z || (Z = new te([])) } function se(e) { var t = Array.isArray(e) ? new te(e).fromEntrySeq() : V(e) ? new oe(e).fromEntrySeq() : z(e) ? new re(e).fromEntrySeq() : "object" == typeof e ? new ne(e) : void 0; if (!t) throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: " + e); return t } function ue(e) { var t = le(e); if (!t) throw new TypeError("Expected Array or iterable object of values: " + e); return t } function ce(e) { var t = le(e) || "object" == typeof e && new ne(e); if (!t) throw new TypeError("Expected Array or iterable object of values, or keyed object: " + e); return t } function le(e) { return J(e) ? new te(e) : V(e) ? new oe(e) : z(e) ? new re(e) : void 0 } function pe(e, t, n, r) { var o = e._cache; if (o) { for (var a = o.length - 1, i = 0; i <= a; i++) { var s = o[n ? a - i : i]; if (!1 === t(s[1], r ? s[0] : i, e)) return i + 1 } return i } return e.__iterateUncached(t, n) } function fe(e, t, n, r) { var o = e._cache; if (o) { var a = o.length - 1, i = 0; return new F((function () { var e = o[n ? a - i : i]; return i++ > a ? q() : U(t, r ? e[0] : i - 1, e[1]) })) } return e.__iteratorUncached(t, n) } function he(e, t) { return t ? de(t, e, "", { "": e }) : me(e) } function de(e, t, n, r) { return Array.isArray(t) ? e.call(r, n, Y(t).map((function (n, r) { return de(e, n, r, t) }))) : ve(t) ? e.call(r, n, K(t).map((function (n, r) { return de(e, n, r, t) }))) : t } function me(e) { return Array.isArray(e) ? Y(e).map(me).toList() : ve(e) ? K(e).map(me).toMap() : e } function ve(e) { return e && (e.constructor === Object || void 0 === e.constructor) } function ge(e, t) { if (e === t || e != e && t != t) return !0; if (!e || !t) return !1; if ("function" == typeof e.valueOf && "function" == typeof t.valueOf) { if ((e = e.valueOf()) === (t = t.valueOf()) || e != e && t != t) return !0; if (!e || !t) return !1 } return !("function" != typeof e.equals || "function" != typeof t.equals || !e.equals(t)) } function ye(e, t) { if (e === t) return !0; if (!i(t) || void 0 !== e.size && void 0 !== t.size && e.size !== t.size || void 0 !== e.__hash && void 0 !== t.__hash && e.__hash !== t.__hash || s(e) !== s(t) || u(e) !== u(t) || l(e) !== l(t)) return !1; if (0 === e.size && 0 === t.size) return !0; var n = !c(e); if (l(e)) { var r = e.entries(); return t.every((function (e, t) { var o = r.next().value; return o && ge(o[1], e) && (n || ge(o[0], t)) })) && r.next().done } var o = !1; if (void 0 === e.size) if (void 0 === t.size) "function" == typeof e.cacheResult && e.cacheResult(); else { o = !0; var a = e; e = t, t = a } var p = !0, f = t.__iterate((function (t, r) { if (n ? !e.has(t) : o ? !ge(t, e.get(r, b)) : !ge(e.get(r, b), t)) return p = !1, !1 })); return p && e.size === f } function be(e, t) { if (!(this instanceof be)) return new be(e, t); if (this._value = e, this.size = void 0 === t ? 1 / 0 : Math.max(0, t), 0 === this.size) { if (X) return X; X = this } } function _e(e, t) { if (!e) throw new Error(t) } function we(e, t, n) { if (!(this instanceof we)) return new we(e, t, n); if (_e(0 !== n, "Cannot step a Range by 0"), e = e || 0, void 0 === t && (t = 1 / 0), n = void 0 === n ? 1 : Math.abs(n), t < e && (n = -n), this._start = e, this._end = t, this._step = n, this.size = Math.max(0, Math.ceil((t - e) / n - 1) + 1), 0 === this.size) { if (Q) return Q; Q = this } } function xe() { throw TypeError("Abstract") } function Ee() { } function Se() { } function Ce() { } $.prototype[ee] = !0, t(te, Y), te.prototype.get = function (e, t) { return this.has(e) ? this._array[k(this, e)] : t }, te.prototype.__iterate = function (e, t) { for (var n = this._array, r = n.length - 1, o = 0; o <= r; o++)if (!1 === e(n[t ? r - o : o], o, this)) return o + 1; return o }, te.prototype.__iterator = function (e, t) { var n = this._array, r = n.length - 1, o = 0; return new F((function () { return o > r ? q() : U(e, o, n[t ? r - o++ : o++]) })) }, t(ne, K), ne.prototype.get = function (e, t) { return void 0 === t || this.has(e) ? this._object[e] : t }, ne.prototype.has = function (e) { return this._object.hasOwnProperty(e) }, ne.prototype.__iterate = function (e, t) { for (var n = this._object, r = this._keys, o = r.length - 1, a = 0; a <= o; a++) { var i = r[t ? o - a : a]; if (!1 === e(n[i], i, this)) return a + 1 } return a }, ne.prototype.__iterator = function (e, t) { var n = this._object, r = this._keys, o = r.length - 1, a = 0; return new F((function () { var i = r[t ? o - a : a]; return a++ > o ? q() : U(e, i, n[i]) })) }, ne.prototype[d] = !0, t(re, Y), re.prototype.__iterateUncached = function (e, t) { if (t) return this.cacheResult().__iterate(e, t); var n = W(this._iterable), r = 0; if (V(n)) for (var o; !(o = n.next()).done && !1 !== e(o.value, r++, this);); return r }, re.prototype.__iteratorUncached = function (e, t) { if (t) return this.cacheResult().__iterator(e, t); var n = W(this._iterable); if (!V(n)) return new F(q); var r = 0; return new F((function () { var t = n.next(); return t.done ? t : U(e, r++, t.value) })) }, t(oe, Y), oe.prototype.__iterateUncached = function (e, t) { if (t) return this.cacheResult().__iterate(e, t); for (var n, r = this._iterator, o = this._iteratorCache, a = 0; a < o.length;)if (!1 === e(o[a], a++, this)) return a; for (; !(n = r.next()).done;) { var i = n.value; if (o[a] = i, !1 === e(i, a++, this)) break } return a }, oe.prototype.__iteratorUncached = function (e, t) { if (t) return this.cacheResult().__iterator(e, t); var n = this._iterator, r = this._iteratorCache, o = 0; return new F((function () { if (o >= r.length) { var t = n.next(); if (t.done) return t; r[o] = t.value } return U(e, o, r[o++]) })) }, t(be, Y), be.prototype.toString = function () { return 0 === this.size ? "Repeat []" : "Repeat [ " + this._value + " " + this.size + " times ]" }, be.prototype.get = function (e, t) { return this.has(e) ? this._value : t }, be.prototype.includes = function (e) { return ge(this._value, e) }, be.prototype.slice = function (e, t) { var n = this.size; return j(e, t, n) ? this : new be(this._value, I(t, n) - T(e, n)) }, be.prototype.reverse = function () { return this }, be.prototype.indexOf = function (e) { return ge(this._value, e) ? 0 : -1 }, be.prototype.lastIndexOf = function (e) { return ge(this._value, e) ? this.size : -1 }, be.prototype.__iterate = function (e, t) { for (var n = 0; n < this.size; n++)if (!1 === e(this._value, n, this)) return n + 1; return n }, be.prototype.__iterator = function (e, t) { var n = this, r = 0; return new F((function () { return r < n.size ? U(e, r++, n._value) : q() })) }, be.prototype.equals = function (e) { return e instanceof be ? ge(this._value, e._value) : ye(e) }, t(we, Y), we.prototype.toString = function () { return 0 === this.size ? "Range []" : "Range [ " + this._start + "..." + this._end + (1 !== this._step ? " by " + this._step : "") + " ]" }, we.prototype.get = function (e, t) { return this.has(e) ? this._start + k(this, e) * this._step : t }, we.prototype.includes = function (e) { var t = (e - this._start) / this._step; return t >= 0 && t < this.size && t === Math.floor(t) }, we.prototype.slice = function (e, t) { return j(e, t, this.size) ? this : (e = T(e, this.size), (t = I(t, this.size)) <= e ? new we(0, 0) : new we(this.get(e, this._end), this.get(t, this._end), this._step)) }, we.prototype.indexOf = function (e) { var t = e - this._start; if (t % this._step == 0) { var n = t / this._step; if (n >= 0 && n < this.size) return n } return -1 }, we.prototype.lastIndexOf = function (e) { return this.indexOf(e) }, we.prototype.__iterate = function (e, t) { for (var n = this.size - 1, r = this._step, o = t ? this._start + n * r : this._start, a = 0; a <= n; a++) { if (!1 === e(o, a, this)) return a + 1; o += t ? -r : r } return a }, we.prototype.__iterator = function (e, t) { var n = this.size - 1, r = this._step, o = t ? this._start + n * r : this._start, a = 0; return new F((function () { var i = o; return o += t ? -r : r, a > n ? q() : U(e, a++, i) })) }, we.prototype.equals = function (e) { return e instanceof we ? this._start === e._start && this._end === e._end && this._step === e._step : ye(this, e) }, t(xe, n), t(Ee, xe), t(Se, xe), t(Ce, xe), xe.Keyed = Ee, xe.Indexed = Se, xe.Set = Ce; var Ae = "function" == typeof Math.imul && -2 === Math.imul(4294967295, 2) ? Math.imul : function (e, t) { var n = 65535 & (e |= 0), r = 65535 & (t |= 0); return n * r + ((e >>> 16) * r + n * (t >>> 16) << 16 >>> 0) | 0 }; function ke(e) { return e >>> 1 & 1073741824 | 3221225471 & e } function Oe(e) { if (!1 === e || null == e) return 0; if ("function" == typeof e.valueOf && (!1 === (e = e.valueOf()) || null == e)) return 0; if (!0 === e) return 1; var t = typeof e; if ("number" === t) { if (e != e || e === 1 / 0) return 0; var n = 0 | e; for (n !== e && (n ^= 4294967295 * e); e > 4294967295;)n ^= e /= 4294967295; return ke(n) } if ("string" === t) return e.length > Fe ? je(e) : Te(e); if ("function" == typeof e.hashCode) return e.hashCode(); if ("object" === t) return Ie(e); if ("function" == typeof e.toString) return Te(e.toString()); throw new Error("Value type " + t + " cannot be hashed.") } function je(e) { var t = ze[e]; return void 0 === t && (t = Te(e), qe === Ue && (qe = 0, ze = {}), qe++, ze[e] = t), t } function Te(e) { for (var t = 0, n = 0; n < e.length; n++)t = 31 * t + e.charCodeAt(n) | 0; return ke(t) } function Ie(e) { var t; if (De && void 0 !== (t = Re.get(e))) return t; if (void 0 !== (t = e[Be])) return t; if (!Ne) { if (void 0 !== (t = e.propertyIsEnumerable && e.propertyIsEnumerable[Be])) return t; if (void 0 !== (t = Me(e))) return t } if (t = ++Le, 1073741824 & Le && (Le = 0), De) Re.set(e, t); else { if (void 0 !== Pe && !1 === Pe(e)) throw new Error("Non-extensible objects are not allowed as keys."); if (Ne) Object.defineProperty(e, Be, { enumerable: !1, configurable: !1, writable: !1, value: t }); else if (void 0 !== e.propertyIsEnumerable && e.propertyIsEnumerable === e.constructor.prototype.propertyIsEnumerable) e.propertyIsEnumerable = function () { return this.constructor.prototype.propertyIsEnumerable.apply(this, arguments) }, e.propertyIsEnumerable[Be] = t; else { if (void 0 === e.nodeType) throw new Error("Unable to set a non-enumerable property on object."); e[Be] = t } } return t } var Pe = Object.isExtensible, Ne = function () { try { return Object.defineProperty({}, "@", {}), !0 } catch (e) { return !1 } }(); function Me(e) { if (e && e.nodeType > 0) switch (e.nodeType) { case 1: return e.uniqueID; case 9: return e.documentElement && e.documentElement.uniqueID } } var Re, De = "function" == typeof WeakMap; De && (Re = new WeakMap); var Le = 0, Be = "__immutablehash__"; "function" == typeof Symbol && (Be = Symbol(Be)); var Fe = 16, Ue = 255, qe = 0, ze = {}; function Ve(e) { _e(e !== 1 / 0, "Cannot perform this action with an infinite size.") } function We(e) { return null == e ? ot() : He(e) && !l(e) ? e : ot().withMutations((function (t) { var n = r(e); Ve(n.size), n.forEach((function (e, n) { return t.set(n, e) })) })) } function He(e) { return !(!e || !e[$e]) } t(We, Ee), We.of = function () { var t = e.call(arguments, 0); return ot().withMutations((function (e) { for (var n = 0; n < t.length; n += 2) { if (n + 1 >= t.length) throw new Error("Missing value for key: " + t[n]); e.set(t[n], t[n + 1]) } })) }, We.prototype.toString = function () { return this.__toString("Map {", "}") }, We.prototype.get = function (e, t) { return this._root ? this._root.get(0, void 0, e, t) : t }, We.prototype.set = function (e, t) { return at(this, e, t) }, We.prototype.setIn = function (e, t) { return this.updateIn(e, b, (function () { return t })) }, We.prototype.remove = function (e) { return at(this, e, b) }, We.prototype.deleteIn = function (e) { return this.updateIn(e, (function () { return b })) }, We.prototype.update = function (e, t, n) { return 1 === arguments.length ? e(this) : this.updateIn([e], t, n) }, We.prototype.updateIn = function (e, t, n) { n || (n = t, t = void 0); var r = vt(this, xn(e), t, n); return r === b ? void 0 : r }, We.prototype.clear = function () { return 0 === this.size ? this : this.__ownerID ? (this.size = 0, this._root = null, this.__hash = void 0, this.__altered = !0, this) : ot() }, We.prototype.merge = function () { return ft(this, void 0, arguments) }, We.prototype.mergeWith = function (t) { return ft(this, t, e.call(arguments, 1)) }, We.prototype.mergeIn = function (t) { var n = e.call(arguments, 1); return this.updateIn(t, ot(), (function (e) { return "function" == typeof e.merge ? e.merge.apply(e, n) : n[n.length - 1] })) }, We.prototype.mergeDeep = function () { return ft(this, ht, arguments) }, We.prototype.mergeDeepWith = function (t) { var n = e.call(arguments, 1); return ft(this, dt(t), n) }, We.prototype.mergeDeepIn = function (t) { var n = e.call(arguments, 1); return this.updateIn(t, ot(), (function (e) { return "function" == typeof e.mergeDeep ? e.mergeDeep.apply(e, n) : n[n.length - 1] })) }, We.prototype.sort = function (e) { return zt(pn(this, e)) }, We.prototype.sortBy = function (e, t) { return zt(pn(this, t, e)) }, We.prototype.withMutations = function (e) { var t = this.asMutable(); return e(t), t.wasAltered() ? t.__ensureOwner(this.__ownerID) : this }, We.prototype.asMutable = function () { return this.__ownerID ? this : this.__ensureOwner(new S) }, We.prototype.asImmutable = function () { return this.__ensureOwner() }, We.prototype.wasAltered = function () { return this.__altered }, We.prototype.__iterator = function (e, t) { return new et(this, e, t) }, We.prototype.__iterate = function (e, t) { var n = this, r = 0; return this._root && this._root.iterate((function (t) { return r++, e(t[1], t[0], n) }), t), r }, We.prototype.__ensureOwner = function (e) { return e === this.__ownerID ? this : e ? rt(this.size, this._root, e, this.__hash) : (this.__ownerID = e, this.__altered = !1, this) }, We.isMap = He; var Je, $e = "@@__IMMUTABLE_MAP__@@", Ke = We.prototype; function Ye(e, t) { this.ownerID = e, this.entries = t } function Ge(e, t, n) { this.ownerID = e, this.bitmap = t, this.nodes = n } function Ze(e, t, n) { this.ownerID = e, this.count = t, this.nodes = n } function Xe(e, t, n) { this.ownerID = e, this.keyHash = t, this.entries = n } function Qe(e, t, n) { this.ownerID = e, this.keyHash = t, this.entry = n } function et(e, t, n) { this._type = t, this._reverse = n, this._stack = e._root && nt(e._root) } function tt(e, t) { return U(e, t[0], t[1]) } function nt(e, t) { return { node: e, index: 0, __prev: t } } function rt(e, t, n, r) { var o = Object.create(Ke); return o.size = e, o._root = t, o.__ownerID = n, o.__hash = r, o.__altered = !1, o } function ot() { return Je || (Je = rt(0)) } function at(e, t, n) { var r, o; if (e._root) { var a = x(_), i = x(w); if (r = it(e._root, e.__ownerID, 0, void 0, t, n, a, i), !i.value) return e; o = e.size + (a.value ? n === b ? -1 : 1 : 0) } else { if (n === b) return e; o = 1, r = new Ye(e.__ownerID, [[t, n]]) } return e.__ownerID ? (e.size = o, e._root = r, e.__hash = void 0, e.__altered = !0, e) : r ? rt(o, r) : ot() } function it(e, t, n, r, o, a, i, s) { return e ? e.update(t, n, r, o, a, i, s) : a === b ? e : (E(s), E(i), new Qe(t, r, [o, a])) } function st(e) { return e.constructor === Qe || e.constructor === Xe } function ut(e, t, n, r, o) { if (e.keyHash === r) return new Xe(t, r, [e.entry, o]); var a, i = (0 === n ? e.keyHash : e.keyHash >>> n) & y, s = (0 === n ? r : r >>> n) & y; return new Ge(t, 1 << i | 1 << s, i === s ? [ut(e, t, n + v, r, o)] : (a = new Qe(t, r, o), i < s ? [e, a] : [a, e])) } function ct(e, t, n, r) { e || (e = new S); for (var o = new Qe(e, Oe(n), [n, r]), a = 0; a < t.length; a++) { var i = t[a]; o = o.update(e, 0, void 0, i[0], i[1]) } return o } function lt(e, t, n, r) { for (var o = 0, a = 0, i = new Array(n), s = 0, u = 1, c = t.length; s < c; s++, u <<= 1) { var l = t[s]; void 0 !== l && s !== r && (o |= u, i[a++] = l) } return new Ge(e, o, i) } function pt(e, t, n, r, o) { for (var a = 0, i = new Array(g), s = 0; 0 !== n; s++, n >>>= 1)i[s] = 1 & n ? t[a++] : void 0; return i[r] = o, new Ze(e, a + 1, i) } function ft(e, t, n) { for (var o = [], a = 0; a < n.length; a++) { var s = n[a], u = r(s); i(s) || (u = u.map((function (e) { return he(e) }))), o.push(u) } return mt(e, t, o) } function ht(e, t, n) { return e && e.mergeDeep && i(t) ? e.mergeDeep(t) : ge(e, t) ? e : t } function dt(e) { return function (t, n, r) { if (t && t.mergeDeepWith && i(n)) return t.mergeDeepWith(e, n); var o = e(t, n, r); return ge(t, o) ? t : o } } function mt(e, t, n) { return 0 === (n = n.filter((function (e) { return 0 !== e.size }))).length ? e : 0 !== e.size || e.__ownerID || 1 !== n.length ? e.withMutations((function (e) { for (var r = t ? function (n, r) { e.update(r, b, (function (e) { return e === b ? n : t(e, n, r) })) } : function (t, n) { e.set(n, t) }, o = 0; o < n.length; o++)n[o].forEach(r) })) : e.constructor(n[0]) } function vt(e, t, n, r) { var o = e === b, a = t.next(); if (a.done) { var i = o ? n : e, s = r(i); return s === i ? e : s } _e(o || e && e.set, "invalid keyPath"); var u = a.value, c = o ? b : e.get(u, b), l = vt(c, t, n, r); return l === c ? e : l === b ? e.remove(u) : (o ? ot() : e).set(u, l) } function gt(e) { return e = (e = (858993459 & (e -= e >> 1 & 1431655765)) + (e >> 2 & 858993459)) + (e >> 4) & 252645135, e += e >> 8, 127 & (e += e >> 16) } function yt(e, t, n, r) { var o = r ? e : C(e); return o[t] = n, o } function bt(e, t, n, r) { var o = e.length + 1; if (r && t + 1 === o) return e[t] = n, e; for (var a = new Array(o), i = 0, s = 0; s < o; s++)s === t ? (a[s] = n, i = -1) : a[s] = e[s + i]; return a } function _t(e, t, n) { var r = e.length - 1; if (n && t === r) return e.pop(), e; for (var o = new Array(r), a = 0, i = 0; i < r; i++)i === t && (a = 1), o[i] = e[i + a]; return o } Ke[$e] = !0, Ke[m] = Ke.remove, Ke.removeIn = Ke.deleteIn, Ye.prototype.get = function (e, t, n, r) { for (var o = this.entries, a = 0, i = o.length; a < i; a++)if (ge(n, o[a][0])) return o[a][1]; return r }, Ye.prototype.update = function (e, t, n, r, o, a, i) { for (var s = o === b, u = this.entries, c = 0, l = u.length; c < l && !ge(r, u[c][0]); c++); var p = c < l; if (p ? u[c][1] === o : s) return this; if (E(i), (s || !p) && E(a), !s || 1 !== u.length) { if (!p && !s && u.length >= wt) return ct(e, u, r, o); var f = e && e === this.ownerID, h = f ? u : C(u); return p ? s ? c === l - 1 ? h.pop() : h[c] = h.pop() : h[c] = [r, o] : h.push([r, o]), f ? (this.entries = h, this) : new Ye(e, h) } }, Ge.prototype.get = function (e, t, n, r) { void 0 === t && (t = Oe(n)); var o = 1 << ((0 === e ? t : t >>> e) & y), a = this.bitmap; return 0 == (a & o) ? r : this.nodes[gt(a & o - 1)].get(e + v, t, n, r) }, Ge.prototype.update = function (e, t, n, r, o, a, i) { void 0 === n && (n = Oe(r)); var s = (0 === t ? n : n >>> t) & y, u = 1 << s, c = this.bitmap, l = 0 != (c & u); if (!l && o === b) return this; var p = gt(c & u - 1), f = this.nodes, h = l ? f[p] : void 0, d = it(h, e, t + v, n, r, o, a, i); if (d === h) return this; if (!l && d && f.length >= xt) return pt(e, f, c, s, d); if (l && !d && 2 === f.length && st(f[1 ^ p])) return f[1 ^ p]; if (l && d && 1 === f.length && st(d)) return d; var m = e && e === this.ownerID, g = l ? d ? c : c ^ u : c | u, _ = l ? d ? yt(f, p, d, m) : _t(f, p, m) : bt(f, p, d, m); return m ? (this.bitmap = g, this.nodes = _, this) : new Ge(e, g, _) }, Ze.prototype.get = function (e, t, n, r) { void 0 === t && (t = Oe(n)); var o = (0 === e ? t : t >>> e) & y, a = this.nodes[o]; return a ? a.get(e + v, t, n, r) : r }, Ze.prototype.update = function (e, t, n, r, o, a, i) { void 0 === n && (n = Oe(r)); var s = (0 === t ? n : n >>> t) & y, u = o === b, c = this.nodes, l = c[s]; if (u && !l) return this; var p = it(l, e, t + v, n, r, o, a, i); if (p === l) return this; var f = this.count; if (l) { if (!p && --f < Et) return lt(e, c, f, s) } else f++; var h = e && e === this.ownerID, d = yt(c, s, p, h); return h ? (this.count = f, this.nodes = d, this) : new Ze(e, f, d) }, Xe.prototype.get = function (e, t, n, r) { for (var o = this.entries, a = 0, i = o.length; a < i; a++)if (ge(n, o[a][0])) return o[a][1]; return r }, Xe.prototype.update = function (e, t, n, r, o, a, i) { void 0 === n && (n = Oe(r)); var s = o === b; if (n !== this.keyHash) return s ? this : (E(i), E(a), ut(this, e, t, n, [r, o])); for (var u = this.entries, c = 0, l = u.length; c < l && !ge(r, u[c][0]); c++); var p = c < l; if (p ? u[c][1] === o : s) return this; if (E(i), (s || !p) && E(a), s && 2 === l) return new Qe(e, this.keyHash, u[1 ^ c]); var f = e && e === this.ownerID, h = f ? u : C(u); return p ? s ? c === l - 1 ? h.pop() : h[c] = h.pop() : h[c] = [r, o] : h.push([r, o]), f ? (this.entries = h, this) : new Xe(e, this.keyHash, h) }, Qe.prototype.get = function (e, t, n, r) { return ge(n, this.entry[0]) ? this.entry[1] : r }, Qe.prototype.update = function (e, t, n, r, o, a, i) { var s = o === b, u = ge(r, this.entry[0]); return (u ? o === this.entry[1] : s) ? this : (E(i), s ? void E(a) : u ? e && e === this.ownerID ? (this.entry[1] = o, this) : new Qe(e, this.keyHash, [r, o]) : (E(a), ut(this, e, t, Oe(r), [r, o]))) }, Ye.prototype.iterate = Xe.prototype.iterate = function (e, t) { for (var n = this.entries, r = 0, o = n.length - 1; r <= o; r++)if (!1 === e(n[t ? o - r : r])) return !1 }, Ge.prototype.iterate = Ze.prototype.iterate = function (e, t) { for (var n = this.nodes, r = 0, o = n.length - 1; r <= o; r++) { var a = n[t ? o - r : r]; if (a && !1 === a.iterate(e, t)) return !1 } }, Qe.prototype.iterate = function (e, t) { return e(this.entry) }, t(et, F), et.prototype.next = function () { for (var e = this._type, t = this._stack; t;) { var n, r = t.node, o = t.index++; if (r.entry) { if (0 === o) return tt(e, r.entry) } else if (r.entries) { if (o <= (n = r.entries.length - 1)) return tt(e, r.entries[this._reverse ? n - o : o]) } else if (o <= (n = r.nodes.length - 1)) { var a = r.nodes[this._reverse ? n - o : o]; if (a) { if (a.entry) return tt(e, a.entry); t = this._stack = nt(a, t) } continue } t = this._stack = this._stack.__prev } return q() }; var wt = g / 4, xt = g / 2, Et = g / 4; function St(e) { var t = Mt(); if (null == e) return t; if (Ct(e)) return e; var n = o(e), r = n.size; return 0 === r ? t : (Ve(r), r > 0 && r < g ? Nt(0, r, v, null, new Ot(n.toArray())) : t.withMutations((function (e) { e.setSize(r), n.forEach((function (t, n) { return e.set(n, t) })) }))) } function Ct(e) { return !(!e || !e[At]) } t(St, Se), St.of = function () { return this(arguments) }, St.prototype.toString = function () { return this.__toString("List [", "]") }, St.prototype.get = function (e, t) { if ((e = k(this, e)) >= 0 && e < this.size) { var n = Bt(this, e += this._origin); return n && n.array[e & y] } return t }, St.prototype.set = function (e, t) { return Rt(this, e, t) }, St.prototype.remove = function (e) { return this.has(e) ? 0 === e ? this.shift() : e === this.size - 1 ? this.pop() : this.splice(e, 1) : this }, St.prototype.insert = function (e, t) { return this.splice(e, 0, t) }, St.prototype.clear = function () { return 0 === this.size ? this : this.__ownerID ? (this.size = this._origin = this._capacity = 0, this._level = v, this._root = this._tail = null, this.__hash = void 0, this.__altered = !0, this) : Mt() }, St.prototype.push = function () { var e = arguments, t = this.size; return this.withMutations((function (n) { Ft(n, 0, t + e.length); for (var r = 0; r < e.length; r++)n.set(t + r, e[r]) })) }, St.prototype.pop = function () { return Ft(this, 0, -1) }, St.prototype.unshift = function () { var e = arguments; return this.withMutations((function (t) { Ft(t, -e.length); for (var n = 0; n < e.length; n++)t.set(n, e[n]) })) }, St.prototype.shift = function () { return Ft(this, 1) }, St.prototype.merge = function () { return Ut(this, void 0, arguments) }, St.prototype.mergeWith = function (t) { return Ut(this, t, e.call(arguments, 1)) }, St.prototype.mergeDeep = function () { return Ut(this, ht, arguments) }, St.prototype.mergeDeepWith = function (t) { var n = e.call(arguments, 1); return Ut(this, dt(t), n) }, St.prototype.setSize = function (e) { return Ft(this, 0, e) }, St.prototype.slice = function (e, t) { var n = this.size; return j(e, t, n) ? this : Ft(this, T(e, n), I(t, n)) }, St.prototype.__iterator = function (e, t) { var n = 0, r = Pt(this, t); return new F((function () { var t = r(); return t === It ? q() : U(e, n++, t) })) }, St.prototype.__iterate = function (e, t) { for (var n, r = 0, o = Pt(this, t); (n = o()) !== It && !1 !== e(n, r++, this);); return r }, St.prototype.__ensureOwner = function (e) { return e === this.__ownerID ? this : e ? Nt(this._origin, this._capacity, this._level, this._root, this._tail, e, this.__hash) : (this.__ownerID = e, this) }, St.isList = Ct; var At = "@@__IMMUTABLE_LIST__@@", kt = St.prototype; function Ot(e, t) { this.array = e, this.ownerID = t } kt[At] = !0, kt[m] = kt.remove, kt.setIn = Ke.setIn, kt.deleteIn = kt.removeIn = Ke.removeIn, kt.update = Ke.update, kt.updateIn = Ke.updateIn, kt.mergeIn = Ke.mergeIn, kt.mergeDeepIn = Ke.mergeDeepIn, kt.withMutations = Ke.withMutations, kt.asMutable = Ke.asMutable, kt.asImmutable = Ke.asImmutable, kt.wasAltered = Ke.wasAltered, Ot.prototype.removeBefore = function (e, t, n) { if (n === t ? 1 << t : 0 === this.array.length) return this; var r = n >>> t & y; if (r >= this.array.length) return new Ot([], e); var o, a = 0 === r; if (t > 0) { var i = this.array[r]; if ((o = i && i.removeBefore(e, t - v, n)) === i && a) return this } if (a && !o) return this; var s = Lt(this, e); if (!a) for (var u = 0; u < r; u++)s.array[u] = void 0; return o && (s.array[r] = o), s }, Ot.prototype.removeAfter = function (e, t, n) { if (n === (t ? 1 << t : 0) || 0 === this.array.length) return this; var r, o = n - 1 >>> t & y; if (o >= this.array.length) return this; if (t > 0) { var a = this.array[o]; if ((r = a && a.removeAfter(e, t - v, n)) === a && o === this.array.length - 1) return this } var i = Lt(this, e); return i.array.splice(o + 1), r && (i.array[o] = r), i }; var jt, Tt, It = {}; function Pt(e, t) { var n = e._origin, r = e._capacity, o = qt(r), a = e._tail; return i(e._root, e._level, 0); function i(e, t, n) { return 0 === t ? s(e, n) : u(e, t, n) } function s(e, i) { var s = i === o ? a && a.array : e && e.array, u = i > n ? 0 : n - i, c = r - i; return c > g && (c = g), function () { if (u === c) return It; var e = t ? --c : u++; return s && s[e] } } function u(e, o, a) { var s, u = e && e.array, c = a > n ? 0 : n - a >> o, l = 1 + (r - a >> o); return l > g && (l = g), function () { for (; ;) { if (s) { var e = s(); if (e !== It) return e; s = null } if (c === l) return It; var n = t ? --l : c++; s = i(u && u[n], o - v, a + (n << o)) } } } } function Nt(e, t, n, r, o, a, i) { var s = Object.create(kt); return s.size = t - e, s._origin = e, s._capacity = t, s._level = n, s._root = r, s._tail = o, s.__ownerID = a, s.__hash = i, s.__altered = !1, s } function Mt() { return jt || (jt = Nt(0, 0, v)) } function Rt(e, t, n) { if ((t = k(e, t)) != t) return e; if (t >= e.size || t < 0) return e.withMutations((function (e) { t < 0 ? Ft(e, t).set(0, n) : Ft(e, 0, t + 1).set(t, n) })); t += e._origin; var r = e._tail, o = e._root, a = x(w); return t >= qt(e._capacity) ? r = Dt(r, e.__ownerID, 0, t, n, a) : o = Dt(o, e.__ownerID, e._level, t, n, a), a.value ? e.__ownerID ? (e._root = o, e._tail = r, e.__hash = void 0, e.__altered = !0, e) : Nt(e._origin, e._capacity, e._level, o, r) : e } function Dt(e, t, n, r, o, a) { var i, s = r >>> n & y, u = e && s < e.array.length; if (!u && void 0 === o) return e; if (n > 0) { var c = e && e.array[s], l = Dt(c, t, n - v, r, o, a); return l === c ? e : ((i = Lt(e, t)).array[s] = l, i) } return u && e.array[s] === o ? e : (E(a), i = Lt(e, t), void 0 === o && s === i.array.length - 1 ? i.array.pop() : i.array[s] = o, i) } function Lt(e, t) { return t && e && t === e.ownerID ? e : new Ot(e ? e.array.slice() : [], t) } function Bt(e, t) { if (t >= qt(e._capacity)) return e._tail; if (t < 1 << e._level + v) { for (var n = e._root, r = e._level; n && r > 0;)n = n.array[t >>> r & y], r -= v; return n } } function Ft(e, t, n) { void 0 !== t && (t |= 0), void 0 !== n && (n |= 0); var r = e.__ownerID || new S, o = e._origin, a = e._capacity, i = o + t, s = void 0 === n ? a : n < 0 ? a + n : o + n; if (i === o && s === a) return e; if (i >= s) return e.clear(); for (var u = e._level, c = e._root, l = 0; i + l < 0;)c = new Ot(c && c.array.length ? [void 0, c] : [], r), l += 1 << (u += v); l && (i += l, o += l, s += l, a += l); for (var p = qt(a), f = qt(s); f >= 1 << u + v;)c = new Ot(c && c.array.length ? [c] : [], r), u += v; var h = e._tail, d = f < p ? Bt(e, s - 1) : f > p ? new Ot([], r) : h; if (h && f > p && i < a && h.array.length) { for (var m = c = Lt(c, r), g = u; g > v; g -= v) { var b = p >>> g & y; m = m.array[b] = Lt(m.array[b], r) } m.array[p >>> v & y] = h } if (s < a && (d = d && d.removeAfter(r, 0, s)), i >= f) i -= f, s -= f, u = v, c = null, d = d && d.removeBefore(r, 0, i); else if (i > o || f < p) { for (l = 0; c;) { var _ = i >>> u & y; if (_ !== f >>> u & y) break; _ && (l += (1 << u) * _), u -= v, c = c.array[_] } c && i > o && (c = c.removeBefore(r, u, i - l)), c && f < p && (c = c.removeAfter(r, u, f - l)), l && (i -= l, s -= l) } return e.__ownerID ? (e.size = s - i, e._origin = i, e._capacity = s, e._level = u, e._root = c, e._tail = d, e.__hash = void 0, e.__altered = !0, e) : Nt(i, s, u, c, d) } function Ut(e, t, n) { for (var r = [], a = 0, s = 0; s < n.length; s++) { var u = n[s], c = o(u); c.size > a && (a = c.size), i(u) || (c = c.map((function (e) { return he(e) }))), r.push(c) } return a > e.size && (e = e.setSize(a)), mt(e, t, r) } function qt(e) { return e < g ? 0 : e - 1 >>> v << v } function zt(e) { return null == e ? Ht() : Vt(e) ? e : Ht().withMutations((function (t) { var n = r(e); Ve(n.size), n.forEach((function (e, n) { return t.set(n, e) })) })) } function Vt(e) { return He(e) && l(e) } function Wt(e, t, n, r) { var o = Object.create(zt.prototype); return o.size = e ? e.size : 0, o._map = e, o._list = t, o.__ownerID = n, o.__hash = r, o } function Ht() { return Tt || (Tt = Wt(ot(), Mt())) } function Jt(e, t, n) { var r, o, a = e._map, i = e._list, s = a.get(t), u = void 0 !== s; if (n === b) { if (!u) return e; i.size >= g && i.size >= 2 * a.size ? (r = (o = i.filter((function (e, t) { return void 0 !== e && s !== t }))).toKeyedSeq().map((function (e) { return e[0] })).flip().toMap(), e.__ownerID && (r.__ownerID = o.__ownerID = e.__ownerID)) : (r = a.remove(t), o = s === i.size - 1 ? i.pop() : i.set(s, void 0)) } else if (u) { if (n === i.get(s)[1]) return e; r = a, o = i.set(s, [t, n]) } else r = a.set(t, i.size), o = i.set(i.size, [t, n]); return e.__ownerID ? (e.size = r.size, e._map = r, e._list = o, e.__hash = void 0, e) : Wt(r, o) } function $t(e, t) { this._iter = e, this._useKeys = t, this.size = e.size } function Kt(e) { this._iter = e, this.size = e.size } function Yt(e) { this._iter = e, this.size = e.size } function Gt(e) { this._iter = e, this.size = e.size } function Zt(e) { var t = bn(e); return t._iter = e, t.size = e.size, t.flip = function () { return e }, t.reverse = function () { var t = e.reverse.apply(this); return t.flip = function () { return e.reverse() }, t }, t.has = function (t) { return e.includes(t) }, t.includes = function (t) { return e.has(t) }, t.cacheResult = _n, t.__iterateUncached = function (t, n) { var r = this; return e.__iterate((function (e, n) { return !1 !== t(n, e, r) }), n) }, t.__iteratorUncached = function (t, n) { if (t === R) { var r = e.__iterator(t, n); return new F((function () { var e = r.next(); if (!e.done) { var t = e.value[0]; e.value[0] = e.value[1], e.value[1] = t } return e })) } return e.__iterator(t === M ? N : M, n) }, t } function Xt(e, t, n) { var r = bn(e); return r.size = e.size, r.has = function (t) { return e.has(t) }, r.get = function (r, o) { var a = e.get(r, b); return a === b ? o : t.call(n, a, r, e) }, r.__iterateUncached = function (r, o) { var a = this; return e.__iterate((function (e, o, i) { return !1 !== r(t.call(n, e, o, i), o, a) }), o) }, r.__iteratorUncached = function (r, o) { var a = e.__iterator(R, o); return new F((function () { var o = a.next(); if (o.done) return o; var i = o.value, s = i[0]; return U(r, s, t.call(n, i[1], s, e), o) })) }, r } function Qt(e, t) { var n = bn(e); return n._iter = e, n.size = e.size, n.reverse = function () { return e }, e.flip && (n.flip = function () { var t = Zt(e); return t.reverse = function () { return e.flip() }, t }), n.get = function (n, r) { return e.get(t ? n : -1 - n, r) }, n.has = function (n) { return e.has(t ? n : -1 - n) }, n.includes = function (t) { return e.includes(t) }, n.cacheResult = _n, n.__iterate = function (t, n) { var r = this; return e.__iterate((function (e, n) { return t(e, n, r) }), !n) }, n.__iterator = function (t, n) { return e.__iterator(t, !n) }, n } function en(e, t, n, r) { var o = bn(e); return r && (o.has = function (r) { var o = e.get(r, b); return o !== b && !!t.call(n, o, r, e) }, o.get = function (r, o) { var a = e.get(r, b); return a !== b && t.call(n, a, r, e) ? a : o }), o.__iterateUncached = function (o, a) { var i = this, s = 0; return e.__iterate((function (e, a, u) { if (t.call(n, e, a, u)) return s++, o(e, r ? a : s - 1, i) }), a), s }, o.__iteratorUncached = function (o, a) { var i = e.__iterator(R, a), s = 0; return new F((function () { for (; ;) { var a = i.next(); if (a.done) return a; var u = a.value, c = u[0], l = u[1]; if (t.call(n, l, c, e)) return U(o, r ? c : s++, l, a) } })) }, o } function tn(e, t, n) { var r = We().asMutable(); return e.__iterate((function (o, a) { r.update(t.call(n, o, a, e), 0, (function (e) { return e + 1 })) })), r.asImmutable() } function nn(e, t, n) { var r = s(e), o = (l(e) ? zt() : We()).asMutable(); e.__iterate((function (a, i) { o.update(t.call(n, a, i, e), (function (e) { return (e = e || []).push(r ? [i, a] : a), e })) })); var a = yn(e); return o.map((function (t) { return mn(e, a(t)) })) } function rn(e, t, n, r) { var o = e.size; if (void 0 !== t && (t |= 0), void 0 !== n && (n === 1 / 0 ? n = o : n |= 0), j(t, n, o)) return e; var a = T(t, o), i = I(n, o); if (a != a || i != i) return rn(e.toSeq().cacheResult(), t, n, r); var s, u = i - a; u == u && (s = u < 0 ? 0 : u); var c = bn(e); return c.size = 0 === s ? s : e.size && s || void 0, !r && ae(e) && s >= 0 && (c.get = function (t, n) { return (t = k(this, t)) >= 0 && t < s ? e.get(t + a, n) : n }), c.__iterateUncached = function (t, n) { var o = this; if (0 === s) return 0; if (n) return this.cacheResult().__iterate(t, n); var i = 0, u = !0, c = 0; return e.__iterate((function (e, n) { if (!u || !(u = i++ < a)) return c++, !1 !== t(e, r ? n : c - 1, o) && c !== s })), c }, c.__iteratorUncached = function (t, n) { if (0 !== s && n) return this.cacheResult().__iterator(t, n); var o = 0 !== s && e.__iterator(t, n), i = 0, u = 0; return new F((function () { for (; i++ < a;)o.next(); if (++u > s) return q(); var e = o.next(); return r || t === M ? e : U(t, u - 1, t === N ? void 0 : e.value[1], e) })) }, c } function on(e, t, n) { var r = bn(e); return r.__iterateUncached = function (r, o) { var a = this; if (o) return this.cacheResult().__iterate(r, o); var i = 0; return e.__iterate((function (e, o, s) { return t.call(n, e, o, s) && ++i && r(e, o, a) })), i }, r.__iteratorUncached = function (r, o) { var a = this; if (o) return this.cacheResult().__iterator(r, o); var i = e.__iterator(R, o), s = !0; return new F((function () { if (!s) return q(); var e = i.next(); if (e.done) return e; var o = e.value, u = o[0], c = o[1]; return t.call(n, c, u, a) ? r === R ? e : U(r, u, c, e) : (s = !1, q()) })) }, r } function an(e, t, n, r) { var o = bn(e); return o.__iterateUncached = function (o, a) { var i = this; if (a) return this.cacheResult().__iterate(o, a); var s = !0, u = 0; return e.__iterate((function (e, a, c) { if (!s || !(s = t.call(n, e, a, c))) return u++, o(e, r ? a : u - 1, i) })), u }, o.__iteratorUncached = function (o, a) { var i = this; if (a) return this.cacheResult().__iterator(o, a); var s = e.__iterator(R, a), u = !0, c = 0; return new F((function () { var e, a, l; do { if ((e = s.next()).done) return r || o === M ? e : U(o, c++, o === N ? void 0 : e.value[1], e); var p = e.value; a = p[0], l = p[1], u && (u = t.call(n, l, a, i)) } while (u); return o === R ? e : U(o, a, l, e) })) }, o } function sn(e, t) { var n = s(e), o = [e].concat(t).map((function (e) { return i(e) ? n && (e = r(e)) : e = n ? se(e) : ue(Array.isArray(e) ? e : [e]), e })).filter((function (e) { return 0 !== e.size })); if (0 === o.length) return e; if (1 === o.length) { var a = o[0]; if (a === e || n && s(a) || u(e) && u(a)) return a } var c = new te(o); return n ? c = c.toKeyedSeq() : u(e) || (c = c.toSetSeq()), (c = c.flatten(!0)).size = o.reduce((function (e, t) { if (void 0 !== e) { var n = t.size; if (void 0 !== n) return e + n } }), 0), c } function un(e, t, n) { var r = bn(e); return r.__iterateUncached = function (r, o) { var a = 0, s = !1; function u(e, c) { var l = this; e.__iterate((function (e, o) { return (!t || c < t) && i(e) ? u(e, c + 1) : !1 === r(e, n ? o : a++, l) && (s = !0), !s }), o) } return u(e, 0), a }, r.__iteratorUncached = function (r, o) { var a = e.__iterator(r, o), s = [], u = 0; return new F((function () { for (; a;) { var e = a.next(); if (!1 === e.done) { var c = e.value; if (r === R && (c = c[1]), t && !(s.length < t) || !i(c)) return n ? e : U(r, u++, c, e); s.push(a), a = c.__iterator(r, o) } else a = s.pop() } return q() })) }, r } function cn(e, t, n) { var r = yn(e); return e.toSeq().map((function (o, a) { return r(t.call(n, o, a, e)) })).flatten(!0) } function ln(e, t) { var n = bn(e); return n.size = e.size && 2 * e.size - 1, n.__iterateUncached = function (n, r) { var o = this, a = 0; return e.__iterate((function (e, r) { return (!a || !1 !== n(t, a++, o)) && !1 !== n(e, a++, o) }), r), a }, n.__iteratorUncached = function (n, r) { var o, a = e.__iterator(M, r), i = 0; return new F((function () { return (!o || i % 2) && (o = a.next()).done ? o : i % 2 ? U(n, i++, t) : U(n, i++, o.value, o) })) }, n } function pn(e, t, n) { t || (t = wn); var r = s(e), o = 0, a = e.toSeq().map((function (t, r) { return [r, t, o++, n ? n(t, r, e) : t] })).toArray(); return a.sort((function (e, n) { return t(e[3], n[3]) || e[2] - n[2] })).forEach(r ? function (e, t) { a[t].length = 2 } : function (e, t) { a[t] = e[1] }), r ? K(a) : u(e) ? Y(a) : G(a) } function fn(e, t, n) { if (t || (t = wn), n) { var r = e.toSeq().map((function (t, r) { return [t, n(t, r, e)] })).reduce((function (e, n) { return hn(t, e[1], n[1]) ? n : e })); return r && r[0] } return e.reduce((function (e, n) { return hn(t, e, n) ? n : e })) } function hn(e, t, n) { var r = e(n, t); return 0 === r && n !== t && (null == n || n != n) || r > 0 } function dn(e, t, r) { var o = bn(e); return o.size = new te(r).map((function (e) { return e.size })).min(), o.__iterate = function (e, t) { for (var n, r = this.__iterator(M, t), o = 0; !(n = r.next()).done && !1 !== e(n.value, o++, this);); return o }, o.__iteratorUncached = function (e, o) { var a = r.map((function (e) { return e = n(e), W(o ? e.reverse() : e) })), i = 0, s = !1; return new F((function () { var n; return s || (n = a.map((function (e) { return e.next() })), s = n.some((function (e) { return e.done }))), s ? q() : U(e, i++, t.apply(null, n.map((function (e) { return e.value })))) })) }, o } function mn(e, t) { return ae(e) ? t : e.constructor(t) } function vn(e) { if (e !== Object(e)) throw new TypeError("Expected [K, V] tuple: " + e) } function gn(e) { return Ve(e.size), A(e) } function yn(e) { return s(e) ? r : u(e) ? o : a } function bn(e) { return Object.create((s(e) ? K : u(e) ? Y : G).prototype) } function _n() { return this._iter.cacheResult ? (this._iter.cacheResult(), this.size = this._iter.size, this) : $.prototype.cacheResult.call(this) } function wn(e, t) { return e > t ? 1 : e < t ? -1 : 0 } function xn(e) { var t = W(e); if (!t) { if (!J(e)) throw new TypeError("Expected iterable or array-like: " + e); t = W(n(e)) } return t } function En(e, t) { var n, r = function (a) { if (a instanceof r) return a; if (!(this instanceof r)) return new r(a); if (!n) { n = !0; var i = Object.keys(e); kn(o, i), o.size = i.length, o._name = t, o._keys = i, o._defaultValues = e } this._map = We(a) }, o = r.prototype = Object.create(Sn); return o.constructor = r, r } t(zt, We), zt.of = function () { return this(arguments) }, zt.prototype.toString = function () { return this.__toString("OrderedMap {", "}") }, zt.prototype.get = function (e, t) { var n = this._map.get(e); return void 0 !== n ? this._list.get(n)[1] : t }, zt.prototype.clear = function () { return 0 === this.size ? this : this.__ownerID ? (this.size = 0, this._map.clear(), this._list.clear(), this) : Ht() }, zt.prototype.set = function (e, t) { return Jt(this, e, t) }, zt.prototype.remove = function (e) { return Jt(this, e, b) }, zt.prototype.wasAltered = function () { return this._map.wasAltered() || this._list.wasAltered() }, zt.prototype.__iterate = function (e, t) { var n = this; return this._list.__iterate((function (t) { return t && e(t[1], t[0], n) }), t) }, zt.prototype.__iterator = function (e, t) { return this._list.fromEntrySeq().__iterator(e, t) }, zt.prototype.__ensureOwner = function (e) { if (e === this.__ownerID) return this; var t = this._map.__ensureOwner(e), n = this._list.__ensureOwner(e); return e ? Wt(t, n, e, this.__hash) : (this.__ownerID = e, this._map = t, this._list = n, this) }, zt.isOrderedMap = Vt, zt.prototype[d] = !0, zt.prototype[m] = zt.prototype.remove, t($t, K), $t.prototype.get = function (e, t) { return this._iter.get(e, t) }, $t.prototype.has = function (e) { return this._iter.has(e) }, $t.prototype.valueSeq = function () { return this._iter.valueSeq() }, $t.prototype.reverse = function () { var e = this, t = Qt(this, !0); return this._useKeys || (t.valueSeq = function () { return e._iter.toSeq().reverse() }), t }, $t.prototype.map = function (e, t) { var n = this, r = Xt(this, e, t); return this._useKeys || (r.valueSeq = function () { return n._iter.toSeq().map(e, t) }), r }, $t.prototype.__iterate = function (e, t) { var n, r = this; return this._iter.__iterate(this._useKeys ? function (t, n) { return e(t, n, r) } : (n = t ? gn(this) : 0, function (o) { return e(o, t ? --n : n++, r) }), t) }, $t.prototype.__iterator = function (e, t) { if (this._useKeys) return this._iter.__iterator(e, t); var n = this._iter.__iterator(M, t), r = t ? gn(this) : 0; return new F((function () { var o = n.next(); return o.done ? o : U(e, t ? --r : r++, o.value, o) })) }, $t.prototype[d] = !0, t(Kt, Y), Kt.prototype.includes = function (e) { return this._iter.includes(e) }, Kt.prototype.__iterate = function (e, t) { var n = this, r = 0; return this._iter.__iterate((function (t) { return e(t, r++, n) }), t) }, Kt.prototype.__iterator = function (e, t) { var n = this._iter.__iterator(M, t), r = 0; return new F((function () { var t = n.next(); return t.done ? t : U(e, r++, t.value, t) })) }, t(Yt, G), Yt.prototype.has = function (e) { return this._iter.includes(e) }, Yt.prototype.__iterate = function (e, t) { var n = this; return this._iter.__iterate((function (t) { return e(t, t, n) }), t) }, Yt.prototype.__iterator = function (e, t) { var n = this._iter.__iterator(M, t); return new F((function () { var t = n.next(); return t.done ? t : U(e, t.value, t.value, t) })) }, t(Gt, K), Gt.prototype.entrySeq = function () { return this._iter.toSeq() }, Gt.prototype.__iterate = function (e, t) { var n = this; return this._iter.__iterate((function (t) { if (t) { vn(t); var r = i(t); return e(r ? t.get(1) : t[1], r ? t.get(0) : t[0], n) } }), t) }, Gt.prototype.__iterator = function (e, t) { var n = this._iter.__iterator(M, t); return new F((function () { for (; ;) { var t = n.next(); if (t.done) return t; var r = t.value; if (r) { vn(r); var o = i(r); return U(e, o ? r.get(0) : r[0], o ? r.get(1) : r[1], t) } } })) }, Kt.prototype.cacheResult = $t.prototype.cacheResult = Yt.prototype.cacheResult = Gt.prototype.cacheResult = _n, t(En, Ee), En.prototype.toString = function () { return this.__toString(An(this) + " {", "}") }, En.prototype.has = function (e) { return this._defaultValues.hasOwnProperty(e) }, En.prototype.get = function (e, t) { if (!this.has(e)) return t; var n = this._defaultValues[e]; return this._map ? this._map.get(e, n) : n }, En.prototype.clear = function () { if (this.__ownerID) return this._map && this._map.clear(), this; var e = this.constructor; return e._empty || (e._empty = Cn(this, ot())) }, En.prototype.set = function (e, t) { if (!this.has(e)) throw new Error('Cannot set unknown key "' + e + '" on ' + An(this)); if (this._map && !this._map.has(e) && t === this._defaultValues[e]) return this; var n = this._map && this._map.set(e, t); return this.__ownerID || n === this._map ? this : Cn(this, n) }, En.prototype.remove = function (e) { if (!this.has(e)) return this; var t = this._map && this._map.remove(e); return this.__ownerID || t === this._map ? this : Cn(this, t) }, En.prototype.wasAltered = function () { return this._map.wasAltered() }, En.prototype.__iterator = function (e, t) { var n = this; return r(this._defaultValues).map((function (e, t) { return n.get(t) })).__iterator(e, t) }, En.prototype.__iterate = function (e, t) { var n = this; return r(this._defaultValues).map((function (e, t) { return n.get(t) })).__iterate(e, t) }, En.prototype.__ensureOwner = function (e) { if (e === this.__ownerID) return this; var t = this._map && this._map.__ensureOwner(e); return e ? Cn(this, t, e) : (this.__ownerID = e, this._map = t, this) }; var Sn = En.prototype; function Cn(e, t, n) { var r = Object.create(Object.getPrototypeOf(e)); return r._map = t, r.__ownerID = n, r } function An(e) { return e._name || e.constructor.name || "Record" } function kn(e, t) { try { t.forEach(On.bind(void 0, e)) } catch (e) { } } function On(e, t) { Object.defineProperty(e, t, { get: function () { return this.get(t) }, set: function (e) { _e(this.__ownerID, "Cannot set on an immutable record."), this.set(t, e) } }) } function jn(e) { return null == e ? Dn() : Tn(e) && !l(e) ? e : Dn().withMutations((function (t) { var n = a(e); Ve(n.size), n.forEach((function (e) { return t.add(e) })) })) } function Tn(e) { return !(!e || !e[Pn]) } Sn[m] = Sn.remove, Sn.deleteIn = Sn.removeIn = Ke.removeIn, Sn.merge = Ke.merge, Sn.mergeWith = Ke.mergeWith, Sn.mergeIn = Ke.mergeIn, Sn.mergeDeep = Ke.mergeDeep, Sn.mergeDeepWith = Ke.mergeDeepWith, Sn.mergeDeepIn = Ke.mergeDeepIn, Sn.setIn = Ke.setIn, Sn.update = Ke.update, Sn.updateIn = Ke.updateIn, Sn.withMutations = Ke.withMutations, Sn.asMutable = Ke.asMutable, Sn.asImmutable = Ke.asImmutable, t(jn, Ce), jn.of = function () { return this(arguments) }, jn.fromKeys = function (e) { return this(r(e).keySeq()) }, jn.prototype.toString = function () { return this.__toString("Set {", "}") }, jn.prototype.has = function (e) { return this._map.has(e) }, jn.prototype.add = function (e) { return Mn(this, this._map.set(e, !0)) }, jn.prototype.remove = function (e) { return Mn(this, this._map.remove(e)) }, jn.prototype.clear = function () { return Mn(this, this._map.clear()) }, jn.prototype.union = function () { var t = e.call(arguments, 0); return 0 === (t = t.filter((function (e) { return 0 !== e.size }))).length ? this : 0 !== this.size || this.__ownerID || 1 !== t.length ? this.withMutations((function (e) { for (var n = 0; n < t.length; n++)a(t[n]).forEach((function (t) { return e.add(t) })) })) : this.constructor(t[0]) }, jn.prototype.intersect = function () { var t = e.call(arguments, 0); if (0 === t.length) return this; t = t.map((function (e) { return a(e) })); var n = this; return this.withMutations((function (e) { n.forEach((function (n) { t.every((function (e) { return e.includes(n) })) || e.remove(n) })) })) }, jn.prototype.subtract = function () { var t = e.call(arguments, 0); if (0 === t.length) return this; t = t.map((function (e) { return a(e) })); var n = this; return this.withMutations((function (e) { n.forEach((function (n) { t.some((function (e) { return e.includes(n) })) && e.remove(n) })) })) }, jn.prototype.merge = function () { return this.union.apply(this, arguments) }, jn.prototype.mergeWith = function (t) { var n = e.call(arguments, 1); return this.union.apply(this, n) }, jn.prototype.sort = function (e) { return Ln(pn(this, e)) }, jn.prototype.sortBy = function (e, t) { return Ln(pn(this, t, e)) }, jn.prototype.wasAltered = function () { return this._map.wasAltered() }, jn.prototype.__iterate = function (e, t) { var n = this; return this._map.__iterate((function (t, r) { return e(r, r, n) }), t) }, jn.prototype.__iterator = function (e, t) { return this._map.map((function (e, t) { return t })).__iterator(e, t) }, jn.prototype.__ensureOwner = function (e) { if (e === this.__ownerID) return this; var t = this._map.__ensureOwner(e); return e ? this.__make(t, e) : (this.__ownerID = e, this._map = t, this) }, jn.isSet = Tn; var In, Pn = "@@__IMMUTABLE_SET__@@", Nn = jn.prototype; function Mn(e, t) { return e.__ownerID ? (e.size = t.size, e._map = t, e) : t === e._map ? e : 0 === t.size ? e.__empty() : e.__make(t) } function Rn(e, t) { var n = Object.create(Nn); return n.size = e ? e.size : 0, n._map = e, n.__ownerID = t, n } function Dn() { return In || (In = Rn(ot())) } function Ln(e) { return null == e ? zn() : Bn(e) ? e : zn().withMutations((function (t) { var n = a(e); Ve(n.size), n.forEach((function (e) { return t.add(e) })) })) } function Bn(e) { return Tn(e) && l(e) } Nn[Pn] = !0, Nn[m] = Nn.remove, Nn.mergeDeep = Nn.merge, Nn.mergeDeepWith = Nn.mergeWith, Nn.withMutations = Ke.withMutations, Nn.asMutable = Ke.asMutable, Nn.asImmutable = Ke.asImmutable, Nn.__empty = Dn, Nn.__make = Rn, t(Ln, jn), Ln.of = function () { return this(arguments) }, Ln.fromKeys = function (e) { return this(r(e).keySeq()) }, Ln.prototype.toString = function () { return this.__toString("OrderedSet {", "}") }, Ln.isOrderedSet = Bn; var Fn, Un = Ln.prototype; function qn(e, t) { var n = Object.create(Un); return n.size = e ? e.size : 0, n._map = e, n.__ownerID = t, n } function zn() { return Fn || (Fn = qn(Ht())) } function Vn(e) { return null == e ? Yn() : Wn(e) ? e : Yn().unshiftAll(e) } function Wn(e) { return !(!e || !e[Jn]) } Un[d] = !0, Un.__empty = zn, Un.__make = qn, t(Vn, Se), Vn.of = function () { return this(arguments) }, Vn.prototype.toString = function () { return this.__toString("Stack [", "]") }, Vn.prototype.get = function (e, t) { var n = this._head; for (e = k(this, e); n && e--;)n = n.next; return n ? n.value : t }, Vn.prototype.peek = function () { return this._head && this._head.value }, Vn.prototype.push = function () { if (0 === arguments.length) return this; for (var e = this.size + arguments.length, t = this._head, n = arguments.length - 1; n >= 0; n--)t = { value: arguments[n], next: t }; return this.__ownerID ? (this.size = e, this._head = t, this.__hash = void 0, this.__altered = !0, this) : Kn(e, t) }, Vn.prototype.pushAll = function (e) { if (0 === (e = o(e)).size) return this; Ve(e.size); var t = this.size, n = this._head; return e.reverse().forEach((function (e) { t++, n = { value: e, next: n } })), this.__ownerID ? (this.size = t, this._head = n, this.__hash = void 0, this.__altered = !0, this) : Kn(t, n) }, Vn.prototype.pop = function () { return this.slice(1) }, Vn.prototype.unshift = function () { return this.push.apply(this, arguments) }, Vn.prototype.unshiftAll = function (e) { return this.pushAll(e) }, Vn.prototype.shift = function () { return this.pop.apply(this, arguments) }, Vn.prototype.clear = function () { return 0 === this.size ? this : this.__ownerID ? (this.size = 0, this._head = void 0, this.__hash = void 0, this.__altered = !0, this) : Yn() }, Vn.prototype.slice = function (e, t) { if (j(e, t, this.size)) return this; var n = T(e, this.size); if (I(t, this.size) !== this.size) return Se.prototype.slice.call(this, e, t); for (var r = this.size - n, o = this._head; n--;)o = o.next; return this.__ownerID ? (this.size = r, this._head = o, this.__hash = void 0, this.__altered = !0, this) : Kn(r, o) }, Vn.prototype.__ensureOwner = function (e) { return e === this.__ownerID ? this : e ? Kn(this.size, this._head, e, this.__hash) : (this.__ownerID = e, this.__altered = !1, this) }, Vn.prototype.__iterate = function (e, t) { if (t) return this.reverse().__iterate(e); for (var n = 0, r = this._head; r && !1 !== e(r.value, n++, this);)r = r.next; return n }, Vn.prototype.__iterator = function (e, t) { if (t) return this.reverse().__iterator(e); var n = 0, r = this._head; return new F((function () { if (r) { var t = r.value; return r = r.next, U(e, n++, t) } return q() })) }, Vn.isStack = Wn; var Hn, Jn = "@@__IMMUTABLE_STACK__@@", $n = Vn.prototype; function Kn(e, t, n, r) { var o = Object.create($n); return o.size = e, o._head = t, o.__ownerID = n, o.__hash = r, o.__altered = !1, o } function Yn() { return Hn || (Hn = Kn(0)) } function Gn(e, t) { var n = function (n) { e.prototype[n] = t[n] }; return Object.keys(t).forEach(n), Object.getOwnPropertySymbols && Object.getOwnPropertySymbols(t).forEach(n), e } $n[Jn] = !0, $n.withMutations = Ke.withMutations, $n.asMutable = Ke.asMutable, $n.asImmutable = Ke.asImmutable, $n.wasAltered = Ke.wasAltered, n.Iterator = F, Gn(n, { toArray: function () { Ve(this.size); var e = new Array(this.size || 0); return this.valueSeq().__iterate((function (t, n) { e[n] = t })), e }, toIndexedSeq: function () { return new Kt(this) }, toJS: function () { return this.toSeq().map((function (e) { return e && "function" == typeof e.toJS ? e.toJS() : e })).__toJS() }, toJSON: function () { return this.toSeq().map((function (e) { return e && "function" == typeof e.toJSON ? e.toJSON() : e })).__toJS() }, toKeyedSeq: function () { return new $t(this, !0) }, toMap: function () { return We(this.toKeyedSeq()) }, toObject: function () { Ve(this.size); var e = {}; return this.__iterate((function (t, n) { e[n] = t })), e }, toOrderedMap: function () { return zt(this.toKeyedSeq()) }, toOrderedSet: function () { return Ln(s(this) ? this.valueSeq() : this) }, toSet: function () { return jn(s(this) ? this.valueSeq() : this) }, toSetSeq: function () { return new Yt(this) }, toSeq: function () { return u(this) ? this.toIndexedSeq() : s(this) ? this.toKeyedSeq() : this.toSetSeq() }, toStack: function () { return Vn(s(this) ? this.valueSeq() : this) }, toList: function () { return St(s(this) ? this.valueSeq() : this) }, toString: function () { return "[Iterable]" }, __toString: function (e, t) { return 0 === this.size ? e + t : e + " " + this.toSeq().map(this.__toStringMapper).join(", ") + " " + t }, concat: function () { return mn(this, sn(this, e.call(arguments, 0))) }, includes: function (e) { return this.some((function (t) { return ge(t, e) })) }, entries: function () { return this.__iterator(R) }, every: function (e, t) { Ve(this.size); var n = !0; return this.__iterate((function (r, o, a) { if (!e.call(t, r, o, a)) return n = !1, !1 })), n }, filter: function (e, t) { return mn(this, en(this, e, t, !0)) }, find: function (e, t, n) { var r = this.findEntry(e, t); return r ? r[1] : n }, forEach: function (e, t) { return Ve(this.size), this.__iterate(t ? e.bind(t) : e) }, join: function (e) { Ve(this.size), e = void 0 !== e ? "" + e : ","; var t = "", n = !0; return this.__iterate((function (r) { n ? n = !1 : t += e, t += null != r ? r.toString() : "" })), t }, keys: function () { return this.__iterator(N) }, map: function (e, t) { return mn(this, Xt(this, e, t)) }, reduce: function (e, t, n) { var r, o; return Ve(this.size), arguments.length < 2 ? o = !0 : r = t, this.__iterate((function (t, a, i) { o ? (o = !1, r = t) : r = e.call(n, r, t, a, i) })), r }, reduceRight: function (e, t, n) { var r = this.toKeyedSeq().reverse(); return r.reduce.apply(r, arguments) }, reverse: function () { return mn(this, Qt(this, !0)) }, slice: function (e, t) { return mn(this, rn(this, e, t, !0)) }, some: function (e, t) { return !this.every(tr(e), t) }, sort: function (e) { return mn(this, pn(this, e)) }, values: function () { return this.__iterator(M) }, butLast: function () { return this.slice(0, -1) }, isEmpty: function () { return void 0 !== this.size ? 0 === this.size : !this.some((function () { return !0 })) }, count: function (e, t) { return A(e ? this.toSeq().filter(e, t) : this) }, countBy: function (e, t) { return tn(this, e, t) }, equals: function (e) { return ye(this, e) }, entrySeq: function () { var e = this; if (e._cache) return new te(e._cache); var t = e.toSeq().map(er).toIndexedSeq(); return t.fromEntrySeq = function () { return e.toSeq() }, t }, filterNot: function (e, t) { return this.filter(tr(e), t) }, findEntry: function (e, t, n) { var r = n; return this.__iterate((function (n, o, a) { if (e.call(t, n, o, a)) return r = [o, n], !1 })), r }, findKey: function (e, t) { var n = this.findEntry(e, t); return n && n[0] }, findLast: function (e, t, n) { return this.toKeyedSeq().reverse().find(e, t, n) }, findLastEntry: function (e, t, n) { return this.toKeyedSeq().reverse().findEntry(e, t, n) }, findLastKey: function (e, t) { return this.toKeyedSeq().reverse().findKey(e, t) }, first: function () { return this.find(O) }, flatMap: function (e, t) { return mn(this, cn(this, e, t)) }, flatten: function (e) { return mn(this, un(this, e, !0)) }, fromEntrySeq: function () { return new Gt(this) }, get: function (e, t) { return this.find((function (t, n) { return ge(n, e) }), void 0, t) }, getIn: function (e, t) { for (var n, r = this, o = xn(e); !(n = o.next()).done;) { var a = n.value; if ((r = r && r.get ? r.get(a, b) : b) === b) return t } return r }, groupBy: function (e, t) { return nn(this, e, t) }, has: function (e) { return this.get(e, b) !== b }, hasIn: function (e) { return this.getIn(e, b) !== b }, isSubset: function (e) { return e = "function" == typeof e.includes ? e : n(e), this.every((function (t) { return e.includes(t) })) }, isSuperset: function (e) { return (e = "function" == typeof e.isSubset ? e : n(e)).isSubset(this) }, keyOf: function (e) { return this.findKey((function (t) { return ge(t, e) })) }, keySeq: function () { return this.toSeq().map(Qn).toIndexedSeq() }, last: function () { return this.toSeq().reverse().first() }, lastKeyOf: function (e) { return this.toKeyedSeq().reverse().keyOf(e) }, max: function (e) { return fn(this, e) }, maxBy: function (e, t) { return fn(this, t, e) }, min: function (e) { return fn(this, e ? nr(e) : ar) }, minBy: function (e, t) { return fn(this, t ? nr(t) : ar, e) }, rest: function () { return this.slice(1) }, skip: function (e) { return this.slice(Math.max(0, e)) }, skipLast: function (e) { return mn(this, this.toSeq().reverse().skip(e).reverse()) }, skipWhile: function (e, t) { return mn(this, an(this, e, t, !0)) }, skipUntil: function (e, t) { return this.skipWhile(tr(e), t) }, sortBy: function (e, t) { return mn(this, pn(this, t, e)) }, take: function (e) { return this.slice(0, Math.max(0, e)) }, takeLast: function (e) { return mn(this, this.toSeq().reverse().take(e).reverse()) }, takeWhile: function (e, t) { return mn(this, on(this, e, t)) }, takeUntil: function (e, t) { return this.takeWhile(tr(e), t) }, valueSeq: function () { return this.toIndexedSeq() }, hashCode: function () { return this.__hash || (this.__hash = ir(this)) } }); var Zn = n.prototype; Zn[p] = !0, Zn[B] = Zn.values, Zn.__toJS = Zn.toArray, Zn.__toStringMapper = rr, Zn.inspect = Zn.toSource = function () { return this.toString() }, Zn.chain = Zn.flatMap, Zn.contains = Zn.includes, Gn(r, { flip: function () { return mn(this, Zt(this)) }, mapEntries: function (e, t) { var n = this, r = 0; return mn(this, this.toSeq().map((function (o, a) { return e.call(t, [a, o], r++, n) })).fromEntrySeq()) }, mapKeys: function (e, t) { var n = this; return mn(this, this.toSeq().flip().map((function (r, o) { return e.call(t, r, o, n) })).flip()) } }); var Xn = r.prototype; function Qn(e, t) { return t } function er(e, t) { return [t, e] } function tr(e) { return function () { return !e.apply(this, arguments) } } function nr(e) { return function () { return -e.apply(this, arguments) } } function rr(e) { return "string" == typeof e ? JSON.stringify(e) : String(e) } function or() { return C(arguments) } function ar(e, t) { return e < t ? 1 : e > t ? -1 : 0 } function ir(e) { if (e.size === 1 / 0) return 0; var t = l(e), n = s(e), r = t ? 1 : 0; return sr(e.__iterate(n ? t ? function (e, t) { r = 31 * r + ur(Oe(e), Oe(t)) | 0 } : function (e, t) { r = r + ur(Oe(e), Oe(t)) | 0 } : t ? function (e) { r = 31 * r + Oe(e) | 0 } : function (e) { r = r + Oe(e) | 0 }), r) } function sr(e, t) { return t = Ae(t, 3432918353), t = Ae(t << 15 | t >>> -15, 461845907), t = Ae(t << 13 | t >>> -13, 5), t = Ae((t = (t + 3864292196 | 0) ^ e) ^ t >>> 16, 2246822507), t = ke((t = Ae(t ^ t >>> 13, 3266489909)) ^ t >>> 16) } function ur(e, t) { return e ^ t + 2654435769 + (e << 6) + (e >> 2) | 0 } return Xn[f] = !0, Xn[B] = Zn.entries, Xn.__toJS = Zn.toObject, Xn.__toStringMapper = function (e, t) { return JSON.stringify(t) + ": " + rr(e) }, Gn(o, { toKeyedSeq: function () { return new $t(this, !1) }, filter: function (e, t) { return mn(this, en(this, e, t, !1)) }, findIndex: function (e, t) { var n = this.findEntry(e, t); return n ? n[0] : -1 }, indexOf: function (e) { var t = this.keyOf(e); return void 0 === t ? -1 : t }, lastIndexOf: function (e) { var t = this.lastKeyOf(e); return void 0 === t ? -1 : t }, reverse: function () { return mn(this, Qt(this, !1)) }, slice: function (e, t) { return mn(this, rn(this, e, t, !1)) }, splice: function (e, t) { var n = arguments.length; if (t = Math.max(0 | t, 0), 0 === n || 2 === n && !t) return this; e = T(e, e < 0 ? this.count() : this.size); var r = this.slice(0, e); return mn(this, 1 === n ? r : r.concat(C(arguments, 2), this.slice(e + t))) }, findLastIndex: function (e, t) { var n = this.findLastEntry(e, t); return n ? n[0] : -1 }, first: function () { return this.get(0) }, flatten: function (e) { return mn(this, un(this, e, !1)) }, get: function (e, t) { return (e = k(this, e)) < 0 || this.size === 1 / 0 || void 0 !== this.size && e > this.size ? t : this.find((function (t, n) { return n === e }), void 0, t) }, has: function (e) { return (e = k(this, e)) >= 0 && (void 0 !== this.size ? this.size === 1 / 0 || e < this.size : -1 !== this.indexOf(e)) }, interpose: function (e) { return mn(this, ln(this, e)) }, interleave: function () { var e = [this].concat(C(arguments)), t = dn(this.toSeq(), Y.of, e), n = t.flatten(!0); return t.size && (n.size = t.size * e.length), mn(this, n) }, keySeq: function () { return we(0, this.size) }, last: function () { return this.get(-1) }, skipWhile: function (e, t) { return mn(this, an(this, e, t, !1)) }, zip: function () { return mn(this, dn(this, or, [this].concat(C(arguments)))) }, zipWith: function (e) { var t = C(arguments); return t[0] = this, mn(this, dn(this, e, t)) } }), o.prototype[h] = !0, o.prototype[d] = !0, Gn(a, { get: function (e, t) { return this.has(e) ? e : t }, includes: function (e) { return this.has(e) }, keySeq: function () { return this.valueSeq() } }), a.prototype.has = Zn.includes, a.prototype.contains = a.prototype.includes, Gn(K, r.prototype), Gn(Y, o.prototype), Gn(G, a.prototype), Gn(Ee, r.prototype), Gn(Se, o.prototype), Gn(Ce, a.prototype), { Iterable: n, Seq: $, Collection: xe, Map: We, OrderedMap: zt, List: St, Stack: Vn, Set: jn, OrderedSet: Ln, Record: En, Range: we, Repeat: be, is: ge, fromJS: he } }() }, function (e, t, n) { e.exports = n(589) }, function (e, t, n) { var r = n(244); e.exports = function (e, t, n) { return t in e ? r(e, t, { value: n, enumerable: !0, configurable: !0, writable: !0 }) : e[t] = n, e } }, function (e, t, n) { e.exports = n(594) }, function (e, t, n) { "use strict"; (function (e) { n.d(t, "t", (function () { return be })), n.d(t, "A", (function () { return _e })), n.d(t, "i", (function () { return we })), n.d(t, "w", (function () { return xe })), n.d(t, "r", (function () { return Ee })), n.d(t, "u", (function () { return Se })), n.d(t, "s", (function () { return Ce })), n.d(t, "p", (function () { return Ae })), n.d(t, "v", (function () { return ke })), n.d(t, "y", (function () { return Oe })), n.d(t, "z", (function () { return je })), n.d(t, "K", (function () { return Te })), n.d(t, "f", (function () { return Ie })), n.d(t, "n", (function () { return Pe })), n.d(t, "h", (function () { return Ne })), n.d(t, "E", (function () { return Me })), n.d(t, "L", (function () { return De })), n.d(t, "o", (function () { return ze })), n.d(t, "D", (function () { return Ve })), n.d(t, "a", (function () { return We })), n.d(t, "I", (function () { return He })), n.d(t, "b", (function () { return Je })), n.d(t, "H", (function () { return $e })), n.d(t, "G", (function () { return Ke })), n.d(t, "F", (function () { return Ye })), n.d(t, "k", (function () { return Ge })), n.d(t, "d", (function () { return Ze })), n.d(t, "g", (function () { return Xe })), n.d(t, "m", (function () { return Qe })), n.d(t, "l", (function () { return et })), n.d(t, "e", (function () { return tt })), n.d(t, "J", (function () { return nt })), n.d(t, "x", (function () { return rt })), n.d(t, "B", (function () { return ot })), n.d(t, "C", (function () { return at })), n.d(t, "j", (function () { return it })), n.d(t, "c", (function () { return st })), n.d(t, "q", (function () { return ct })); var r = n(103), o = n.n(r), a = n(173), i = n.n(a), s = n(67), u = n.n(s), c = n(14), l = n.n(c), p = n(32), f = n.n(p), h = n(15), d = n.n(h), m = (n(38), n(37)), v = n.n(m), g = n(23), y = n.n(g), b = (n(16), n(212)), _ = n.n(b), w = n(22), x = n.n(w), E = n(21), S = n.n(E), C = (n(30), n(17)), A = n.n(C), k = n(12), O = n.n(k), j = n(18), T = n.n(j), I = n(2), P = n.n(I), N = n(51), M = n.n(N), R = n(86), D = n.n(R), L = n(4), B = n.n(L), F = n(13), U = n.n(F), q = n(19), z = n.n(q), V = n(1), W = n.n(V), H = n(514), J = n(515), $ = n.n(J), K = n(286), Y = n.n(K), G = n(287), Z = n.n(G), X = n(213), Q = n.n(X), ee = n(344), te = n.n(ee), ne = n(125), re = n.n(ne), oe = n(66), ae = n.n(oe), ie = n(144), se = n(27), ue = n(517), ce = n.n(ue), le = n(146), pe = n(518), fe = n.n(pe), he = n(519), de = n.n(he), me = n(78), ve = n.n(me), ge = "default", ye = function (e) { return W.a.Iterable.isIterable(e) }; function be(e) { try { var t = JSON.parse(e); if (t && "object" === z()(t)) return t } catch (e) { } return !1 } function _e(e) { return Se(e) ? ye(e) ? e.toJS() : e : {} } function we(e) { var t, n; if (ye(e)) return e; if (e instanceof se.a.File) return e; if (!Se(e)) return e; if (U()(e)) return B()(n = W.a.Seq(e)).call(n, we).toList(); if (ae()(D()(e))) { var r, o = function (e) { if (!ae()(D()(e))) return e; var t, n = {}, r = "_**[]", o = {}, a = M()(D()(e).call(e)); try { for (a.s(); !(t = a.n()).done;) { var i = t.value; if (n[i[0]] || o[i[0]] && o[i[0]].containsMultiple) { var s, u, c, l; if (!o[i[0]]) o[i[0]] = { containsMultiple: !0, length: 1 }, n[P()(c = P()(l = "".concat(i[0])).call(l, r)).call(c, o[i[0]].length)] = n[i[0]], delete n[i[0]]; o[i[0]].length += 1, n[P()(s = P()(u = "".concat(i[0])).call(u, r)).call(s, o[i[0]].length)] = i[1] } else n[i[0]] = i[1] } } catch (e) { a.e(e) } finally { a.f() } return n }(e); return B()(r = W.a.OrderedMap(o)).call(r, we) } return B()(t = W.a.OrderedMap(e)).call(t, we) } function xe(e) { return U()(e) ? e : [e] } function Ee(e) { return "function" == typeof e } function Se(e) { return !!e && "object" === z()(e) } function Ce(e) { return "function" == typeof e } function Ae(e) { return U()(e) } var ke = Z.a; function Oe(e, t) { var n; return S()(n = A()(e)).call(n, (function (n, r) { return n[r] = t(e[r], r), n }), {}) } function je(e, t) { var n; return S()(n = A()(e)).call(n, (function (n, r) { var o = t(e[r], r); return o && "object" === z()(o) && x()(n, o), n }), {}) } function Te(e) { return function (t) { t.dispatch, t.getState; return function (t) { return function (n) { return "function" == typeof n ? n(e()) : t(n) } } } } function Ie(e) { var t, n = e.keySeq(); return n.contains(ge) ? ge : _()(t = O()(n).call(n, (function (e) { return "2" === (e + "")[0] }))).call(t).first() } function Pe(e, t) { if (!W.a.Iterable.isIterable(e)) return W.a.List(); var n = e.getIn(U()(t) ? t : [t]); return W.a.List.isList(n) ? n : W.a.List() } function Ne(e) { var t, n = [/filename\*=[^']+'\w*'"([^"]+)";?/i, /filename\*=[^']+'\w*'([^;]+);?/i, /filename="([^;]*);?"/i, /filename=([^;]*);?/i]; if (v()(n).call(n, (function (n) { return null !== (t = n.exec(e)) })), null !== t && t.length > 1) try { return decodeURIComponent(t[1]) } catch (e) { console.error(e) } return null } function Me(e) { return t = e.replace(/\.[^./]*$/, ""), Y()($()(t)); var t } function Re(e, t, n, r, o) { if (!t) return []; var a = [], i = t.get("nullable"), s = t.get("required"), u = t.get("maximum"), c = t.get("minimum"), l = t.get("type"), p = t.get("format"), f = t.get("maxLength"), h = t.get("minLength"), m = t.get("uniqueItems"), g = t.get("maxItems"), y = t.get("minItems"), b = t.get("pattern"); if (i && null === e) return []; if (l && (n || s || void 0 !== e || "array" === l)) { var _ = "string" === l && e, w = "array" === l && U()(e) && e.length, x = "array" === l && W.a.List.isList(e) && e.count(), E = [_, w, x, "array" === l && "string" == typeof e && e, "file" === l && e instanceof se.a.File, "boolean" === l && (e || !1 === e), "number" === l && (e || 0 === e), "integer" === l && (e || 0 === e), "object" === l && "object" === z()(e) && null !== e, "object" === l && "string" == typeof e && e], S = v()(E).call(E, (function (e) { return !!e })); if ((n || s) && !S && !r) return a.push("Required field is not provided"), a; if ("object" === l && (null === o || "application/json" === o)) { var C, A = e; if ("string" == typeof e) try { A = JSON.parse(e) } catch (e) { return a.push("Parameter string value must be valid JSON"), a } if (t && t.has("required") && Ce(s.isList) && s.isList() && T()(s).call(s, (function (e) { void 0 === A[e] && a.push({ propKey: e, error: "Required property not found" }) })), t && t.has("properties")) T()(C = t.get("properties")).call(C, (function (e, t) { var n = Re(A[t], e, !1, r, o); a.push.apply(a, d()(B()(n).call(n, (function (e) { return { propKey: t, error: e } })))) })) } if (b) { var k = function (e, t) { if (!new RegExp(t).test(e)) return "Value must follow pattern " + t }(e, b); k && a.push(k) } if (y && "array" === l) { var j = function (e, t) { var n; if (!e && t >= 1 || e && e.length < t) return P()(n = "Array must contain at least ".concat(t, " item")).call(n, 1 === t ? "" : "s") }(e, y); j && a.push(j) } if (g && "array" === l) { var I = function (e, t) { var n; if (e && e.length > t) return P()(n = "Array must not contain more then ".concat(t, " item")).call(n, 1 === t ? "" : "s") }(e, g); I && a.push({ needRemove: !0, error: I }) } if (m && "array" === l) { var N = function (e, t) { if (e && ("true" === t || !0 === t)) { var n = Object(V.fromJS)(e), r = n.toSet(); if (e.length > r.size) { var o = Object(V.Set)(); if (T()(n).call(n, (function (e, t) { O()(n).call(n, (function (t) { return Ce(t.equals) ? t.equals(e) : t === e })).size > 1 && (o = o.add(t)) })), 0 !== o.size) return B()(o).call(o, (function (e) { return { index: e, error: "No duplicates allowed." } })).toArray() } } }(e, m); N && a.push.apply(a, d()(N)) } if (f || 0 === f) { var M = function (e, t) { var n; if (e.length > t) return P()(n = "Value must be no longer than ".concat(t, " character")).call(n, 1 !== t ? "s" : "") }(e, f); M && a.push(M) } if (h) { var R = function (e, t) { var n; if (e.length < t) return P()(n = "Value must be at least ".concat(t, " character")).call(n, 1 !== t ? "s" : "") }(e, h); R && a.push(R) } if (u || 0 === u) { var D = function (e, t) { if (e > t) return "Value must be less than ".concat(t) }(e, u); D && a.push(D) } if (c || 0 === c) { var L = function (e, t) { if (e < t) return "Value must be greater than ".concat(t) }(e, c); L && a.push(L) } if ("string" === l) { var F; if (!(F = "date-time" === p ? function (e) { if (isNaN(Date.parse(e))) return "Value must be a DateTime" }(e) : "uuid" === p ? function (e) { if (e = e.toString().toLowerCase(), !/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[)}]?$/.test(e)) return "Value must be a Guid" }(e) : function (e) { if (e && "string" != typeof e) return "Value must be a string" }(e))) return a; a.push(F) } else if ("boolean" === l) { var q = function (e) { if ("true" !== e && "false" !== e && !0 !== e && !1 !== e) return "Value must be a boolean" }(e); if (!q) return a; a.push(q) } else if ("number" === l) { var H = function (e) { if (!/^-?\d+(\.?\d+)?$/.test(e)) return "Value must be a number" }(e); if (!H) return a; a.push(H) } else if ("integer" === l) { var J = function (e) { if (!/^-?\d+$/.test(e)) return "Value must be an integer" }(e); if (!J) return a; a.push(J) } else if ("array" === l) { if (!w && !x) return a; e && T()(e).call(e, (function (e, n) { var i = Re(e, t.get("items"), !1, r, o); a.push.apply(a, d()(B()(i).call(i, (function (e) { return { index: n, error: e } })))) })) } else if ("file" === l) { var $ = function (e) { if (e && !(e instanceof se.a.File)) return "Value must be a file" }(e); if (!$) return a; a.push($) } } return a } var De = function (e, t) { var n = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {}, r = n.isOAS3, o = void 0 !== r && r, a = n.bypassRequiredCheck, i = void 0 !== a && a, s = e.get("required"), u = Object(le.a)(e, { isOAS3: o }), c = u.schema, l = u.parameterContentMediaType; return Re(t, c, s, i, l) }, Le = function (e, t, n) { if (e && (!e.xml || !e.xml.name)) { if (e.xml = e.xml || {}, !e.$$ref) return e.type || e.items || e.properties || e.additionalProperties ? '\n\x3c!-- XML example cannot be generated; root element name is undefined --\x3e' : null; var r = e.$$ref.match(/\S*\/(\S+)$/); e.xml.name = r[1] } return Object(ie.memoizedCreateXMLExample)(e, t, n) }, Be = [{ when: /json/, shouldStringifyTypes: ["string"] }], Fe = ["object"], Ue = function (e, t, n, r) { var o = Object(ie.memoizedSampleFromSchema)(e, t, r), a = z()(o), i = S()(Be).call(Be, (function (e, t) { var r; return t.when.test(n) ? P()(r = []).call(r, d()(e), d()(t.shouldStringifyTypes)) : e }), Fe); return te()(i, (function (e) { return e === a })) ? f()(o, null, 2) : o }, qe = function (e, t, n, r) { var o, a = Ue(e, t, n, r); try { "\n" === (o = ve.a.safeDump(ve.a.safeLoad(a), { lineWidth: -1 }))[o.length - 1] && (o = y()(o).call(o, 0, o.length - 1)) } catch (e) { return console.error(e), "error: could not generate yaml example" } return o.replace(/\t/g, " ") }, ze = function (e) { var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "", n = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {}, r = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : void 0; return e && Ce(e.toJS) && (e = e.toJS()), r && Ce(r.toJS) && (r = r.toJS()), /xml/.test(t) ? Le(e, n, r) : /(yaml|yml)/.test(t) ? qe(e, n, t, r) : Ue(e, n, t, r) }, Ve = function () { var e = {}, t = se.a.location.search; if (!t) return {}; if ("" != t) { var n = t.substr(1).split("&"); for (var r in n) n.hasOwnProperty(r) && (r = n[r].split("="), e[decodeURIComponent(r[0])] = r[1] && decodeURIComponent(r[1]) || "") } return e }, We = function (t) { return (t instanceof e ? t : e.from(t.toString(), "utf-8")).toString("base64") }, He = { operationsSorter: { alpha: function (e, t) { return e.get("path").localeCompare(t.get("path")) }, method: function (e, t) { return e.get("method").localeCompare(t.get("method")) } }, tagsSorter: { alpha: function (e, t) { return e.localeCompare(t) } } }, Je = function (e) { var t = []; for (var n in e) { var r = e[n]; void 0 !== r && "" !== r && t.push([n, "=", encodeURIComponent(r).replace(/%20/g, "+")].join("")) } return t.join("&") }, $e = function (e, t, n) { return !!Q()(n, (function (n) { return re()(e[n], t[n]) })) }; function Ke(e) { return "string" != typeof e || "" === e ? "" : Object(H.sanitizeUrl)(e) } function Ye(e) { return !(!e || l()(e).call(e, "localhost") >= 0 || l()(e).call(e, "127.0.0.1") >= 0 || "none" === e) } function Ge(e) { if (!W.a.OrderedMap.isOrderedMap(e)) return null; if (!e.size) return null; var t = u()(e).call(e, (function (e, t) { return i()(t).call(t, "2") && A()(e.get("content") || {}).length > 0 })), n = e.get("default") || W.a.OrderedMap(), r = (n.get("content") || W.a.OrderedMap()).keySeq().toJS().length ? n : null; return t || r } var Ze = function (e) { return "string" == typeof e || e instanceof String ? o()(e).call(e).replace(/\s/g, "%20") : "" }, Xe = function (e) { return ce()(Ze(e).replace(/%20/g, "_")) }, Qe = function (e) { return O()(e).call(e, (function (e, t) { return /^x-/.test(t) })) }, et = function (e) { return O()(e).call(e, (function (e, t) { return /^pattern|maxLength|minLength|maximum|minimum/.test(t) })) }; function tt(e, t) { var n, r = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : function () { return !0 }; if ("object" !== z()(e) || U()(e) || null === e || !t) return e; var o = x()({}, e); return T()(n = A()(o)).call(n, (function (e) { e === t && r(o[e], e) ? delete o[e] : o[e] = tt(o[e], t, r) })), o } function nt(e) { if ("string" == typeof e) return e; if (e && e.toJS && (e = e.toJS()), "object" === z()(e) && null !== e) try { return f()(e, null, 2) } catch (t) { return String(e) } return null == e ? "" : e.toString() } function rt(e) { return "number" == typeof e ? e.toString() : e } function ot(e) { var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}, n = t.returnAll, r = void 0 !== n && n, o = t.allowHashes, a = void 0 === o || o; if (!W.a.Map.isMap(e)) throw new Error("paramToIdentifier: received a non-Im.Map parameter as input"); var i, s, u, c = e.get("name"), l = e.get("in"), p = []; e && e.hashCode && l && c && a && p.push(P()(i = P()(s = "".concat(l, ".")).call(s, c, ".hash-")).call(i, e.hashCode())); l && c && p.push(P()(u = "".concat(l, ".")).call(u, c)); return p.push(c), r ? p : p[0] || "" } function at(e, t) { var n, r = ot(e, { returnAll: !0 }); return O()(n = B()(r).call(r, (function (e) { return t[e] }))).call(n, (function (e) { return void 0 !== e }))[0] } function it() { return ut(fe()(32).toString("base64")) } function st(e) { return ut(de()("sha256").update(e).digest("base64")) } function ut(e) { return e.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "") } var ct = function (e) { return !e || !(!ye(e) || !e.isEmpty()) } }).call(this, n(77).Buffer) }, function (e, t) { e.exports = function (e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } }, function (e, t, n) { var r = n(244); function o(e, t) { for (var n = 0; n < t.length; n++) { var o = t[n]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), r(e, o.key, o) } } e.exports = function (e, t, n) { return t && o(e.prototype, t), n && o(e, n), e } }, function (e, t, n) { var r = n(920), o = n(923); e.exports = function (e, t) { if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function"); e.prototype = r(t && t.prototype, { constructor: { value: e, writable: !0, configurable: !0 } }), t && o(e, t) } }, function (e, t, n) { var r = n(470), o = n(210), a = n(934), i = n(935); e.exports = function (e) { var t = a(); return function () { var n, a = o(e); if (t) { var s = o(this).constructor; n = r(a, arguments, s) } else n = a.apply(this, arguments); return i(this, n) } } }, function (e, t) { e.exports = function (e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e } }, function (e, t, n) { e.exports = n(1013)() }, function (e, t, n) { e.exports = n(586) }, function (e, t, n) { e.exports = n(603) }, function (e, t, n) { e.exports = n(651) }, function (e, t, n) { var r = n(654), o = n(393), a = n(186), i = n(663); e.exports = function (e) { return r(e) || o(e) || a(e) || i() } }, function (e, t, n) { var r = n(401), o = n(674), a = n(186), i = n(404); e.exports = function (e, t) { return r(e) || o(e, t) || a(e, t) || i() } }, function (e, t, n) { e.exports = n(553) }, function (e, t, n) { e.exports = n(405) }, function (e, t, n) { var r = n(555), o = n(181); function a(t) { return e.exports = a = "function" == typeof o && "symbol" == typeof r ? function (e) { return typeof e } : function (e) { return e && "function" == typeof o && e.constructor === o && e !== o.prototype ? "symbol" : typeof e }, a(t) } e.exports = a }, function (e, t, n) { "use strict"; function r(e, t) { return e === t } function o(e, t, n) { if (null === t || null === n || t.length !== n.length) return !1; for (var r = t.length, o = 0; o < r; o++)if (!e(t[o], n[o])) return !1; return !0 } function a(e) { var t = Array.isArray(e[0]) ? e[0] : e; if (!t.every((function (e) { return "function" == typeof e }))) { var n = t.map((function (e) { return typeof e })).join(", "); throw new Error("Selector creators expect all input-selectors to be functions, instead received the following types: [" + n + "]") } return t } n.d(t, "a", (function () { return i })); var i = function (e) { for (var t = arguments.length, n = Array(t > 1 ? t - 1 : 0), r = 1; r < t; r++)n[r - 1] = arguments[r]; return function () { for (var t = arguments.length, r = Array(t), o = 0; o < t; o++)r[o] = arguments[o]; var i = 0, s = r.pop(), u = a(r), c = e.apply(void 0, [function () { return i++, s.apply(null, arguments) }].concat(n)), l = e((function () { for (var e = [], t = u.length, n = 0; n < t; n++)e.push(u[n].apply(null, arguments)); return c.apply(null, e) })); return l.resultFunc = s, l.dependencies = u, l.recomputations = function () { return i }, l.resetRecomputations = function () { return i = 0 }, l } }((function (e) { var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : r, n = null, a = null; return function () { return o(t, n, arguments) || (a = e.apply(null, arguments)), n = arguments, a } })) }, function (e, t, n) { e.exports = n(598) }, function (e, t, n) { e.exports = n(610) }, function (e, t, n) { e.exports = n(607) }, function (e, t, n) { "use strict"; var r = n(42), o = n(107).f, a = n(358), i = n(33), s = n(109), u = n(70), c = n(52), l = function (e) { var t = function (t, n, r) { if (this instanceof e) { switch (arguments.length) { case 0: return new e; case 1: return new e(t); case 2: return new e(t, n) }return new e(t, n, r) } return e.apply(this, arguments) }; return t.prototype = e.prototype, t }; e.exports = function (e, t) { var n, p, f, h, d, m, v, g, y = e.target, b = e.global, _ = e.stat, w = e.proto, x = b ? r : _ ? r[y] : (r[y] || {}).prototype, E = b ? i : i[y] || (i[y] = {}), S = E.prototype; for (f in t) n = !a(b ? f : y + (_ ? "." : "#") + f, e.forced) && x && c(x, f), d = E[f], n && (m = e.noTargetGet ? (g = o(x, f)) && g.value : x[f]), h = n && m ? m : t[f], n && typeof d == typeof h || (v = e.bind && n ? s(h, r) : e.wrap && n ? l(h) : w && "function" == typeof h ? s(Function.call, h) : h, (e.sham || h && h.sham || d && d.sham) && u(v, "sham", !0), E[f] = v, w && (c(i, p = y + "Prototype") || u(i, p, {}), i[p][f] = h, e.real && S && !S[f] && u(S, f, h))) } }, function (e, t, n) { var r = n(244), o = n(874), a = n(878), i = n(883), s = n(450), u = n(888), c = n(451), l = n(452), p = n(3); function f(e, t) { var n = l(e); if (c) { var r = c(e); t && (r = u(r).call(r, (function (t) { return s(e, t).enumerable }))), n.push.apply(n, r) } return n } e.exports = function (e) { for (var t = 1; t < arguments.length; t++) { var n, u = null != arguments[t] ? arguments[t] : {}; if (t % 2) i(n = f(Object(u), !0)).call(n, (function (t) { p(e, t, u[t]) })); else if (a) o(e, a(u)); else { var c; i(c = f(Object(u))).call(c, (function (t) { r(e, t, s(u, t)) })) } } return e } }, function (e, t, n) { "use strict"; e.exports = function (e, t, n, r, o, a, i, s) { if (!e) { var u; if (void 0 === t) u = new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings."); else { var c = [n, r, o, a, i, s], l = 0; (u = new Error(t.replace(/%s/g, (function () { return c[l++] })))).name = "Invariant Violation" } throw u.framesToPop = 1, u } } }, function (e, t, n) { "use strict"; t.a = function () { var e = { location: {}, history: {}, open: function () { }, close: function () { }, File: function () { } }; if ("undefined" == typeof window) return e; try { e = window; for (var t = 0, n = ["File", "Blob", "FormData"]; t < n.length; t++) { var r = n[t]; r in window && (e[r] = window[r]) } } catch (e) { console.error(e) } return e }() }, function (e, t, n) { "use strict"; var r, o = n(1), a = "<>", i = function () { invariant(!1, "ImmutablePropTypes type checking code is stripped in production.") }; i.isRequired = i; var s = function () { return i }; function u(e) { var t = typeof e; return Array.isArray(e) ? "array" : e instanceof RegExp ? "object" : e instanceof o.Iterable ? "Immutable." + e.toSource().split(" ")[0] : t } function c(e) { function t(t, n, r, o, i, s) { for (var u = arguments.length, c = Array(u > 6 ? u - 6 : 0), l = 6; l < u; l++)c[l - 6] = arguments[l]; if (s = s || r, o = o || a, null != n[r]) return e.apply(void 0, [n, r, o, i, s].concat(c)); var p = i; return t ? new Error("Required " + p + " `" + s + "` was not specified in `" + o + "`.") : void 0 } var n = t.bind(null, !1); return n.isRequired = t.bind(null, !0), n } function l(e, t) { return n = "Iterable." + e, r = function (e) { return o.Iterable.isIterable(e) && t(e) }, c((function (e, t, o, a, i) { var s = e[t]; if (!r(s)) { var c = u(s); return new Error("Invalid " + a + " `" + i + "` of type `" + c + "` supplied to `" + o + "`, expected `" + n + "`.") } return null })); var n, r } (r = { listOf: s, mapOf: s, orderedMapOf: s, setOf: s, orderedSetOf: s, stackOf: s, iterableOf: s, recordOf: s, shape: s, contains: s, mapContains: s, orderedMapContains: s, list: i, map: i, orderedMap: i, set: i, orderedSet: i, stack: i, seq: i, record: i, iterable: i }).iterable.indexed = l("Indexed", o.Iterable.isIndexed), r.iterable.keyed = l("Keyed", o.Iterable.isKeyed), e.exports = r }, function (e, t, n) { var r = n(918); function o() { return e.exports = o = r || function (e) { for (var t = 1; t < arguments.length; t++) { var n = arguments[t]; for (var r in n) Object.prototype.hasOwnProperty.call(n, r) && (e[r] = n[r]) } return e }, o.apply(this, arguments) } e.exports = o }, function (e, t, n) { e.exports = n(612) }, function (e, t, n) { "use strict"; e.exports = function (e) { for (var t = arguments.length - 1, n = "Minified React error #" + e + "; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=" + e, r = 0; r < t; r++)n += "&args[]=" + encodeURIComponent(arguments[r + 1]); n += " for the full message or use the non-minified dev environment for full errors and additional helpful warnings."; var o = new Error(n); throw o.name = "Invariant Violation", o.framesToPop = 1, o } }, function (e, t, n) { e.exports = n(550) }, function (e, t) { e.exports = {} }, function (e, t, n) { "use strict"; var r = n(83); e.exports = r }, function (e, t) { e.exports = function (e) { try { return !!e() } catch (e) { return !0 } } }, function (e, t, n) { "use strict"; n.r(t), n.d(t, "isOAS3", (function () { return c })), n.d(t, "isSwagger2", (function () { return l })), n.d(t, "OAS3ComponentWrapFactory", (function () { return p })); var r = n(29), o = n.n(r), a = n(173), i = n.n(a), s = n(0), u = n.n(s); function c(e) { var t = e.get("openapi"); return "string" == typeof t && (i()(t).call(t, "3.0.") && t.length > 4) } function l(e) { var t = e.get("swagger"); return "string" == typeof t && i()(t).call(t, "2.0") } function p(e) { return function (t, n) { return function (r) { return n && n.specSelectors && n.specSelectors.specJson ? c(n.specSelectors.specJson()) ? u.a.createElement(e, o()({}, r, n, { Ori: t })) : u.a.createElement(t, r) : (console.warn("OAS3 wrapper: couldn't get spec"), null) } } } }, function (e, t, n) { e.exports = n(670) }, function (e, t, n) { e.exports = n(664) }, function (e, t, n) { var r = n(42), o = n(233), a = n(52), i = n(179), s = n(235), u = n(362), c = o("wks"), l = r.Symbol, p = u ? l : l && l.withoutSetter || i; e.exports = function (e) { return a(c, e) || (s && a(l, e) ? c[e] = l[e] : c[e] = p("Symbol." + e)), c[e] } }, function (e, t, n) { "use strict"; var r = Object.getOwnPropertySymbols, o = Object.prototype.hasOwnProperty, a = Object.prototype.propertyIsEnumerable; function i(e) { if (null == e) throw new TypeError("Object.assign cannot be called with null or undefined"); return Object(e) } e.exports = function () { try { if (!Object.assign) return !1; var e = new String("abc"); if (e[5] = "de", "5" === Object.getOwnPropertyNames(e)[0]) return !1; for (var t = {}, n = 0; n < 10; n++)t["_" + String.fromCharCode(n)] = n; if ("0123456789" !== Object.getOwnPropertyNames(t).map((function (e) { return t[e] })).join("")) return !1; var r = {}; return "abcdefghijklmnopqrst".split("").forEach((function (e) { r[e] = e })), "abcdefghijklmnopqrst" === Object.keys(Object.assign({}, r)).join("") } catch (e) { return !1 } }() ? Object.assign : function (e, t) { for (var n, s, u = i(e), c = 1; c < arguments.length; c++) { for (var l in n = Object(arguments[c])) o.call(n, l) && (u[l] = n[l]); if (r) { s = r(n); for (var p = 0; p < s.length; p++)a.call(n, s[p]) && (u[s[p]] = n[s[p]]) } } return u } }, function (e, t, n) { e.exports = n(898) }, function (e, t, n) { (function (t) { var n = function (e) { return e && e.Math == Math && e }; e.exports = n("object" == typeof globalThis && globalThis) || n("object" == typeof window && window) || n("object" == typeof self && self) || n("object" == typeof t && t) || function () { return this }() || Function("return this")() }).call(this, n(55)) }, function (e, t, n) { var r = n(33); e.exports = function (e) { return r[e + "Prototype"] } }, function (e, t, n) { "use strict"; var r = n(31), o = n(139), a = n(471), i = (n(26), o.ID_ATTRIBUTE_NAME), s = a, u = "__reactInternalInstance$" + Math.random().toString(36).slice(2); function c(e, t) { return 1 === e.nodeType && e.getAttribute(i) === String(t) || 8 === e.nodeType && e.nodeValue === " react-text: " + t + " " || 8 === e.nodeType && e.nodeValue === " react-empty: " + t + " " } function l(e) { for (var t; t = e._renderedComponent;)e = t; return e } function p(e, t) { var n = l(e); n._hostNode = t, t[u] = n } function f(e, t) { if (!(e._flags & s.hasCachedChildNodes)) { var n = e._renderedChildren, o = t.firstChild; e: for (var a in n) if (n.hasOwnProperty(a)) { var i = n[a], u = l(i)._domID; if (0 !== u) { for (; null !== o; o = o.nextSibling)if (c(o, u)) { p(i, o); continue e } r("32", u) } } e._flags |= s.hasCachedChildNodes } } function h(e) { if (e[u]) return e[u]; for (var t, n, r = []; !e[u];) { if (r.push(e), !e.parentNode) return null; e = e.parentNode } for (; e && (n = e[u]); e = r.pop())t = n, r.length && f(n, e); return t } var d = { getClosestInstanceFromNode: h, getInstanceFromNode: function (e) { var t = h(e); return null != t && t._hostNode === e ? t : null }, getNodeFromInstance: function (e) { if (void 0 === e._hostNode && r("33"), e._hostNode) return e._hostNode; for (var t = []; !e._hostNode;)t.push(e), e._hostParent || r("34"), e = e._hostParent; for (; t.length; e = t.pop())f(e, e._hostNode); return e._hostNode }, precacheChildNodes: f, precacheNode: p, uncacheNode: function (e) { var t = e._hostNode; t && (delete t[u], e._hostNode = null) } }; e.exports = d }, function (e, t) { e.exports = function (e) { return "object" == typeof e ? null !== e : "function" == typeof e } }, function (e, t, n) { var r = n(197); e.exports = function (e, t, n) { var o = null == e ? void 0 : r(e, t); return void 0 === o ? n : o } }, function (e, t, n) { "use strict"; n.r(t), n.d(t, "UPDATE_SPEC", (function () { return te })), n.d(t, "UPDATE_URL", (function () { return ne })), n.d(t, "UPDATE_JSON", (function () { return re })), n.d(t, "UPDATE_PARAM", (function () { return oe })), n.d(t, "UPDATE_EMPTY_PARAM_INCLUSION", (function () { return ae })), n.d(t, "VALIDATE_PARAMS", (function () { return ie })), n.d(t, "SET_RESPONSE", (function () { return se })), n.d(t, "SET_REQUEST", (function () { return ue })), n.d(t, "SET_MUTATED_REQUEST", (function () { return ce })), n.d(t, "LOG_REQUEST", (function () { return le })), n.d(t, "CLEAR_RESPONSE", (function () { return pe })), n.d(t, "CLEAR_REQUEST", (function () { return fe })), n.d(t, "CLEAR_VALIDATE_PARAMS", (function () { return he })), n.d(t, "UPDATE_OPERATION_META_VALUE", (function () { return de })), n.d(t, "UPDATE_RESOLVED", (function () { return me })), n.d(t, "UPDATE_RESOLVED_SUBTREE", (function () { return ve })), n.d(t, "SET_SCHEME", (function () { return ge })), n.d(t, "updateSpec", (function () { return ye })), n.d(t, "updateResolved", (function () { return be })), n.d(t, "updateUrl", (function () { return _e })), n.d(t, "updateJsonSpec", (function () { return we })), n.d(t, "parseToJson", (function () { return xe })), n.d(t, "resolveSpec", (function () { return Se })), n.d(t, "requestResolvedSubtree", (function () { return ke })), n.d(t, "changeParam", (function () { return Oe })), n.d(t, "changeParamByIdentity", (function () { return je })), n.d(t, "updateResolvedSubtree", (function () { return Te })), n.d(t, "invalidateResolvedSubtreeCache", (function () { return Ie })), n.d(t, "validateParams", (function () { return Pe })), n.d(t, "updateEmptyParamInclusion", (function () { return Ne })), n.d(t, "clearValidateParams", (function () { return Me })), n.d(t, "changeConsumesValue", (function () { return Re })), n.d(t, "changeProducesValue", (function () { return De })), n.d(t, "setResponse", (function () { return Le })), n.d(t, "setRequest", (function () { return Be })), n.d(t, "setMutatedRequest", (function () { return Fe })), n.d(t, "logRequest", (function () { return Ue })), n.d(t, "executeRequest", (function () { return qe })), n.d(t, "execute", (function () { return ze })), n.d(t, "clearResponse", (function () { return Ve })), n.d(t, "clearRequest", (function () { return We })), n.d(t, "setScheme", (function () { return He })); var r = n(25), o = n.n(r), a = n(56), i = n.n(a), s = n(346), u = n.n(s), c = n(22), l = n.n(c), p = n(17), f = n.n(p), h = n(2), d = n.n(h), m = n(18), v = n.n(m), g = n(14), y = n.n(g), b = n(41), _ = n.n(b), w = n(214), x = n.n(w), E = n(12), S = n.n(E), C = n(68), A = n.n(C), k = n(104), O = n.n(k), j = n(21), T = n.n(j), I = n(79), P = n.n(I), N = n(347), M = n.n(N), R = n(4), D = n.n(R), L = n(13), B = n.n(L), F = n(19), U = n.n(F), q = n(78), z = n.n(q), V = n(1), W = n(98), H = n.n(W), J = n(143), $ = n.n(J), K = n(215), Y = n.n(K), G = n(521), Z = n.n(G), X = n(348), Q = n.n(X), ee = n(5), te = "spec_update_spec", ne = "spec_update_url", re = "spec_update_json", oe = "spec_update_param", ae = "spec_update_empty_param_inclusion", ie = "spec_validate_param", se = "spec_set_response", ue = "spec_set_request", ce = "spec_set_mutated_request", le = "spec_log_request", pe = "spec_clear_response", fe = "spec_clear_request", he = "spec_clear_validate_param", de = "spec_update_operation_meta_value", me = "spec_update_resolved", ve = "spec_update_resolved_subtree", ge = "set_scheme"; function ye(e) { var t, n = (t = e, Y()(t) ? t : "").replace(/\t/g, " "); if ("string" == typeof e) return { type: te, payload: n } } function be(e) { return { type: me, payload: e } } function _e(e) { return { type: ne, payload: e } } function we(e) { return { type: re, payload: e } } var xe = function (e) { return function (t) { var n = t.specActions, r = t.specSelectors, o = t.errActions, a = r.specStr, i = null; try { e = e || a(), o.clear({ source: "parser" }), i = z.a.safeLoad(e) } catch (e) { return console.error(e), o.newSpecErr({ source: "parser", level: "error", message: e.reason, line: e.mark && e.mark.line ? e.mark.line + 1 : void 0 }) } return i && "object" === U()(i) ? n.updateJsonSpec(i) : {} } }, Ee = !1, Se = function (e, t) { return function (n) { var r = n.specActions, o = n.specSelectors, a = n.errActions, i = n.fn, s = i.fetch, u = i.resolve, c = i.AST, l = void 0 === c ? {} : c, p = n.getConfigs; Ee || (console.warn("specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!"), Ee = !0); var f = p(), h = f.modelPropertyMacro, d = f.parameterMacro, m = f.requestInterceptor, v = f.responseInterceptor; void 0 === e && (e = o.specJson()), void 0 === t && (t = o.url()); var g = l.getLineNumberForPath ? l.getLineNumberForPath : function () { }, y = o.specStr(); return u({ fetch: s, spec: e, baseDoc: t, modelPropertyMacro: h, parameterMacro: d, requestInterceptor: m, responseInterceptor: v }).then((function (e) { var t = e.spec, n = e.errors; if (a.clear({ type: "thrown" }), B()(n) && n.length > 0) { var o = D()(n).call(n, (function (e) { return console.error(e), e.line = e.fullPath ? g(y, e.fullPath) : null, e.path = e.fullPath ? e.fullPath.join(".") : null, e.level = "error", e.type = "thrown", e.source = "resolver", M()(e, "message", { enumerable: !0, value: e.message }), e })); a.newThrownErrBatch(o) } return r.updateResolved(t) })) } }, Ce = [], Ae = Z()(P()(_.a.mark((function e() { var t, n, r, o, a, i, s, u, c, l, p, f, h, d, m, v, g, y; return _.a.wrap((function (e) { for (; ;)switch (e.prev = e.next) { case 0: if (t = Ce.system) { e.next = 4; break } return console.error("debResolveSubtrees: don't have a system to operate on, aborting."), e.abrupt("return"); case 4: if (n = t.errActions, r = t.errSelectors, o = t.fn, a = o.resolveSubtree, i = o.fetch, s = o.AST, u = void 0 === s ? {} : s, c = t.specSelectors, l = t.specActions, a) { e.next = 8; break } return console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing."), e.abrupt("return"); case 8: return p = u.getLineNumberForPath ? u.getLineNumberForPath : function () { }, f = c.specStr(), h = t.getConfigs(), d = h.modelPropertyMacro, m = h.parameterMacro, v = h.requestInterceptor, g = h.responseInterceptor, e.prev = 11, e.next = 14, T()(Ce).call(Ce, function () { var e = P()(_.a.mark((function e(t, o) { var s, u, l, h, y, b, w, E, C; return _.a.wrap((function (e) { for (; ;)switch (e.prev = e.next) { case 0: return e.next = 2, t; case 2: return s = e.sent, u = s.resultMap, l = s.specWithCurrentSubtrees, e.next = 7, a(l, o, { baseDoc: c.url(), modelPropertyMacro: d, parameterMacro: m, requestInterceptor: v, responseInterceptor: g }); case 7: if (h = e.sent, y = h.errors, b = h.spec, r.allErrors().size && n.clearBy((function (e) { var t; return "thrown" !== e.get("type") || "resolver" !== e.get("source") || !O()(t = e.get("fullPath")).call(t, (function (e, t) { return e === o[t] || void 0 === o[t] })) })), B()(y) && y.length > 0 && (w = D()(y).call(y, (function (e) { return e.line = e.fullPath ? p(f, e.fullPath) : null, e.path = e.fullPath ? e.fullPath.join(".") : null, e.level = "error", e.type = "thrown", e.source = "resolver", M()(e, "message", { enumerable: !0, value: e.message }), e })), n.newThrownErrBatch(w)), !b || !c.isOAS3() || "components" !== o[0] || "securitySchemes" !== o[1]) { e.next = 15; break } return e.next = 15, A.a.all(D()(E = S()(C = x()(b)).call(C, (function (e) { return "openIdConnect" === e.type }))).call(E, function () { var e = P()(_.a.mark((function e(t) { var n, r; return _.a.wrap((function (e) { for (; ;)switch (e.prev = e.next) { case 0: return n = { url: t.openIdConnectUrl, requestInterceptor: v, responseInterceptor: g }, e.prev = 1, e.next = 4, i(n); case 4: (r = e.sent) instanceof Error || r.status >= 400 ? console.error(r.statusText + " " + n.url) : t.openIdConnectData = JSON.parse(r.text), e.next = 11; break; case 8: e.prev = 8, e.t0 = e.catch(1), console.error(e.t0); case 11: case "end": return e.stop() } }), e, null, [[1, 8]]) }))); return function (t) { return e.apply(this, arguments) } }())); case 15: return Q()(u, o, b), Q()(l, o, b), e.abrupt("return", { resultMap: u, specWithCurrentSubtrees: l }); case 18: case "end": return e.stop() } }), e) }))); return function (t, n) { return e.apply(this, arguments) } }(), A.a.resolve({ resultMap: (c.specResolvedSubtree([]) || Object(V.Map)()).toJS(), specWithCurrentSubtrees: c.specJson().toJS() })); case 14: y = e.sent, delete Ce.system, Ce = [], e.next = 22; break; case 19: e.prev = 19, e.t0 = e.catch(11), console.error(e.t0); case 22: l.updateResolvedSubtree([], y.resultMap); case 23: case "end": return e.stop() } }), e, null, [[11, 19]]) }))), 35), ke = function (e) { return function (t) { var n; y()(n = D()(Ce).call(Ce, (function (e) { return e.join("@@") }))).call(n, e.join("@@")) > -1 || (Ce.push(e), Ce.system = t, Ae()) } }; function Oe(e, t, n, r, o) { return { type: oe, payload: { path: e, value: r, paramName: t, paramIn: n, isXml: o } } } function je(e, t, n, r) { return { type: oe, payload: { path: e, param: t, value: n, isXml: r } } } var Te = function (e, t) { return { type: ve, payload: { path: e, value: t } } }, Ie = function () { return { type: ve, payload: { path: [], value: Object(V.Map)() } } }, Pe = function (e, t) { return { type: ie, payload: { pathMethod: e, isOAS3: t } } }, Ne = function (e, t, n, r) { return { type: ae, payload: { pathMethod: e, paramName: t, paramIn: n, includeEmptyValue: r } } }; function Me(e) { return { type: he, payload: { pathMethod: e } } } function Re(e, t) { return { type: de, payload: { path: e, value: t, key: "consumes_value" } } } function De(e, t) { return { type: de, payload: { path: e, value: t, key: "produces_value" } } } var Le = function (e, t, n) { return { payload: { path: e, method: t, res: n }, type: se } }, Be = function (e, t, n) { return { payload: { path: e, method: t, req: n }, type: ue } }, Fe = function (e, t, n) { return { payload: { path: e, method: t, req: n }, type: ce } }, Ue = function (e) { return { payload: e, type: le } }, qe = function (e) { return function (t) { var n, r, o = t.fn, a = t.specActions, i = t.specSelectors, s = t.getConfigs, c = t.oas3Selectors, p = e.pathName, h = e.method, m = e.operation, g = s(), y = g.requestInterceptor, b = g.responseInterceptor, w = m.toJS(); m && m.get("parameters") && v()(n = S()(r = m.get("parameters")).call(r, (function (e) { return e && !0 === e.get("allowEmptyValue") }))).call(n, (function (t) { if (i.parameterInclusionSettingFor([p, h], t.get("name"), t.get("in"))) { e.parameters = e.parameters || {}; var n = Object(ee.C)(t, e.parameters); (!n || n && 0 === n.size) && (e.parameters[t.get("name")] = "") } })); if (e.contextUrl = H()(i.url()).toString(), w && w.operationId ? e.operationId = w.operationId : w && p && h && (e.operationId = o.opId(w, p, h)), i.isOAS3()) { var x, E = d()(x = "".concat(p, ":")).call(x, h); e.server = c.selectedServer(E) || c.selectedServer(); var C = c.serverVariables({ server: e.server, namespace: E }).toJS(), A = c.serverVariables({ server: e.server }).toJS(); e.serverVariables = f()(C).length ? C : A, e.requestContentType = c.requestContentType(p, h), e.responseContentType = c.responseContentType(p, h) || "*/*"; var k = c.requestBodyValue(p, h), O = c.requestBodyInclusionSetting(p, h); if (Object(ee.t)(k)) e.requestBody = JSON.parse(k); else if (k && k.toJS) { var j; e.requestBody = S()(j = D()(k).call(k, (function (e) { return V.Map.isMap(e) ? e.get("value") : e }))).call(j, (function (e, t) { return (B()(e) ? 0 !== e.length : !Object(ee.q)(e)) || O.get(t) })).toJS() } else e.requestBody = k } var T = l()({}, e); T = o.buildRequest(T), a.setRequest(e.pathName, e.method, T); var I = function () { var t = P()(_.a.mark((function t(n) { var r, o; return _.a.wrap((function (t) { for (; ;)switch (t.prev = t.next) { case 0: return t.next = 2, y.apply(undefined, [n]); case 2: return r = t.sent, o = l()({}, r), a.setMutatedRequest(e.pathName, e.method, o), t.abrupt("return", r); case 6: case "end": return t.stop() } }), t) }))); return function (e) { return t.apply(this, arguments) } }(); e.requestInterceptor = I, e.responseInterceptor = b; var N = u()(); return o.execute(e).then((function (t) { t.duration = u()() - N, a.setResponse(e.pathName, e.method, t) })).catch((function (t) { console.error(t), a.setResponse(e.pathName, e.method, { error: !0, err: $()(t) }) })) } }, ze = function () { var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, t = e.path, n = e.method, r = i()(e, ["path", "method"]); return function (e) { var a = e.fn.fetch, i = e.specSelectors, s = e.specActions, u = i.specJsonWithResolvedSubtrees().toJS(), c = i.operationScheme(t, n), l = i.contentTypeValues([t, n]).toJS(), p = l.requestContentType, f = l.responseContentType, h = /xml/i.test(p), d = i.parameterValues([t, n], h).toJS(); return s.executeRequest(o()(o()({}, r), {}, { fetch: a, spec: u, pathName: t, method: n, parameters: d, requestContentType: p, scheme: c, responseContentType: f })) } }; function Ve(e, t) { return { type: pe, payload: { path: e, method: t } } } function We(e, t) { return { type: fe, payload: { path: e, method: t } } } function He(e, t, n) { return { type: ge, payload: { scheme: e, path: t, method: n } } } }, function (e, t, n) { var r = n(33), o = n(52), a = n(232), i = n(71).f; e.exports = function (e) { var t = r.Symbol || (r.Symbol = {}); o(t, e) || i(t, e, { value: a.f(e) }) } }, function (e, t, n) { var r = n(35); e.exports = !r((function () { return 7 != Object.defineProperty({}, 1, { get: function () { return 7 } })[1] })) }, function (e, t, n) { "use strict"; var r = n(163), o = ["kind", "resolve", "construct", "instanceOf", "predicate", "represent", "defaultStyle", "styleAliases"], a = ["scalar", "sequence", "mapping"]; e.exports = function (e, t) { var n, i; if (t = t || {}, Object.keys(t).forEach((function (t) { if (-1 === o.indexOf(t)) throw new r('Unknown option "' + t + '" is met in definition of "' + e + '" YAML type.') })), this.tag = e, this.kind = t.kind || null, this.resolve = t.resolve || function () { return !0 }, this.construct = t.construct || function (e) { return e }, this.instanceOf = t.instanceOf || null, this.predicate = t.predicate || null, this.represent = t.represent || null, this.defaultStyle = t.defaultStyle || null, this.styleAliases = (n = t.styleAliases || null, i = {}, null !== n && Object.keys(n).forEach((function (e) { n[e].forEach((function (t) { i[String(t)] = e })) })), i), -1 === a.indexOf(this.kind)) throw new r('Unknown kind "' + this.kind + '" is specified for "' + e + '" YAML type.') } }, function (e, t, n) { var r = n(402), o = n(246), a = n(684), i = n(181), s = n(186); e.exports = function (e, t) { var n; if (void 0 === i || null == a(e)) { if (o(e) || (n = s(e)) || t && e && "number" == typeof e.length) { n && (e = n); var u = 0, c = function () { }; return { s: c, n: function () { return u >= e.length ? { done: !0 } : { done: !1, value: e[u++] } }, e: function (e) { throw e }, f: c } } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.") } var l, p = !0, f = !1; return { s: function () { n = r(e) }, n: function () { var e = n.next(); return p = e.done, e }, e: function (e) { f = !0, l = e }, f: function () { try { p || null == n.return || n.return() } finally { if (f) throw l } } } } }, function (e, t) { var n = {}.hasOwnProperty; e.exports = function (e, t) { return n.call(e, t) } }, function (e, t, n) { var r = n(45); e.exports = function (e) { if (!r(e)) throw TypeError(String(e) + " is not an object"); return e } }, function (e, t) { var n = Array.isArray; e.exports = n }, function (e, t) { var n; n = function () { return this }(); try { n = n || new Function("return this")() } catch (e) { "object" == typeof window && (n = window) } e.exports = n }, function (e, t, n) { var r = n(453), o = n(451), a = n(894); e.exports = function (e, t) { if (null == e) return {}; var n, i, s = a(e, t); if (o) { var u = o(e); for (i = 0; i < u.length; i++)n = u[i], r(t).call(t, n) >= 0 || Object.prototype.propertyIsEnumerable.call(e, n) && (s[n] = e[n]) } return s } }, function (e, t, n) { "use strict"; n.r(t), n.d(t, "UPDATE_SELECTED_SERVER", (function () { return r })), n.d(t, "UPDATE_REQUEST_BODY_VALUE", (function () { return o })), n.d(t, "UPDATE_REQUEST_BODY_VALUE_RETAIN_FLAG", (function () { return a })), n.d(t, "UPDATE_REQUEST_BODY_INCLUSION", (function () { return i })), n.d(t, "UPDATE_ACTIVE_EXAMPLES_MEMBER", (function () { return s })), n.d(t, "UPDATE_REQUEST_CONTENT_TYPE", (function () { return u })), n.d(t, "UPDATE_RESPONSE_CONTENT_TYPE", (function () { return c })), n.d(t, "UPDATE_SERVER_VARIABLE_VALUE", (function () { return l })), n.d(t, "SET_REQUEST_BODY_VALIDATE_ERROR", (function () { return p })), n.d(t, "CLEAR_REQUEST_BODY_VALIDATE_ERROR", (function () { return f })), n.d(t, "CLEAR_REQUEST_BODY_VALUE", (function () { return h })), n.d(t, "setSelectedServer", (function () { return d })), n.d(t, "setRequestBodyValue", (function () { return m })), n.d(t, "setRetainRequestBodyValueFlag", (function () { return v })), n.d(t, "setRequestBodyInclusion", (function () { return g })), n.d(t, "setActiveExamplesMember", (function () { return y })), n.d(t, "setRequestContentType", (function () { return b })), n.d(t, "setResponseContentType", (function () { return _ })), n.d(t, "setServerVariableValue", (function () { return w })), n.d(t, "setRequestBodyValidateError", (function () { return x })), n.d(t, "clearRequestBodyValidateError", (function () { return E })), n.d(t, "initRequestBodyValidateError", (function () { return S })), n.d(t, "clearRequestBodyValue", (function () { return C })); var r = "oas3_set_servers", o = "oas3_set_request_body_value", a = "oas3_set_request_body_retain_flag", i = "oas3_set_request_body_inclusion", s = "oas3_set_active_examples_member", u = "oas3_set_request_content_type", c = "oas3_set_response_content_type", l = "oas3_set_server_variable_value", p = "oas3_set_request_body_validate_error", f = "oas3_clear_request_body_validate_error", h = "oas3_clear_request_body_value"; function d(e, t) { return { type: r, payload: { selectedServerUrl: e, namespace: t } } } function m(e) { var t = e.value, n = e.pathMethod; return { type: o, payload: { value: t, pathMethod: n } } } var v = function (e) { var t = e.value, n = e.pathMethod; return { type: a, payload: { value: t, pathMethod: n } } }; function g(e) { var t = e.value, n = e.pathMethod, r = e.name; return { type: i, payload: { value: t, pathMethod: n, name: r } } } function y(e) { var t = e.name, n = e.pathMethod, r = e.contextType, o = e.contextName; return { type: s, payload: { name: t, pathMethod: n, contextType: r, contextName: o } } } function b(e) { var t = e.value, n = e.pathMethod; return { type: u, payload: { value: t, pathMethod: n } } } function _(e) { var t = e.value, n = e.path, r = e.method; return { type: c, payload: { value: t, path: n, method: r } } } function w(e) { var t = e.server, n = e.namespace, r = e.key, o = e.val; return { type: l, payload: { server: t, namespace: n, key: r, val: o } } } var x = function (e) { var t = e.path, n = e.method, r = e.validationErrors; return { type: p, payload: { path: t, method: n, validationErrors: r } } }, E = function (e) { var t = e.path, n = e.method; return { type: f, payload: { path: t, method: n } } }, S = function (e) { var t = e.pathMethod; return { type: f, payload: { path: t[0], method: t[1] } } }, C = function (e) { var t = e.pathMethod; return { type: h, payload: { pathMethod: t } } } }, function (e, t, n) { "use strict"; var r = !("undefined" == typeof window || !window.document || !window.document.createElement), o = { canUseDOM: r, canUseWorkers: "undefined" != typeof Worker, canUseEventListeners: r && !(!window.addEventListener && !window.attachEvent), canUseViewport: r && !!window.screen, isInWorker: !r }; e.exports = o }, function (e, t) { e.exports = function (e) { var t = typeof e; return null != e && ("object" == t || "function" == t) } }, function (e, t, n) { "use strict"; n.d(t, "b", (function () { return b })), n.d(t, "e", (function () { return _ })), n.d(t, "c", (function () { return x })), n.d(t, "a", (function () { return E })), n.d(t, "d", (function () { return S })); var r = n(51), o = n.n(r), a = n(18), i = n.n(a), s = n(37), u = n.n(s), c = n(2), l = n.n(c), p = n(19), f = n.n(p), h = n(59), d = n.n(h), m = n(353), v = n.n(m), g = function (e) { return String.prototype.toLowerCase.call(e) }, y = function (e) { return e.replace(/[^\w]/gi, "_") }; function b(e) { var t = e.openapi; return !!t && v()(t, "3") } function _(e, t) { var n = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : "", r = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : {}, o = r.v2OperationIdCompatibilityMode; if (!e || "object" !== f()(e)) return null; var a = (e.operationId || "").replace(/\s/g, ""); return a.length ? y(e.operationId) : w(t, n, { v2OperationIdCompatibilityMode: o }) } function w(e, t) { var n, r = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {}, o = r.v2OperationIdCompatibilityMode; if (o) { var a, i, s = l()(a = "".concat(t.toLowerCase(), "_")).call(a, e).replace(/[\s!@#$%^&*()_+=[{\]};:<>|./?,\\'""-]/g, "_"); return (s = s || l()(i = "".concat(e.substring(1), "_")).call(i, t)).replace(/((_){2,})/g, "_").replace(/^(_)*/g, "").replace(/([_])*$/g, "") } return l()(n = "".concat(g(t))).call(n, y(e)) } function x(e, t) { var n; return l()(n = "".concat(g(t), "-")).call(n, e) } function E(e, t) { return e && e.paths ? function (e, t) { return function (e, t, n) { if (!e || "object" !== f()(e) || !e.paths || "object" !== f()(e.paths)) return null; var r = e.paths; for (var o in r) for (var a in r[o]) if ("PARAMETERS" !== a.toUpperCase()) { var i = r[o][a]; if (i && "object" === f()(i)) { var s = { spec: e, pathName: o, method: a.toUpperCase(), operation: i }, u = t(s); if (n && u) return s } } return }(e, t, !0) || null }(e, (function (e) { var n, r = e.pathName, o = e.method, a = e.operation; if (!a || "object" !== f()(a)) return !1; var i = a.operationId, s = _(a, r, o), c = x(r, o); return u()(n = [s, c, i]).call(n, (function (e) { return e && e === t })) })) : null } function S(e) { var t = e.spec, n = t.paths, r = {}; if (!n || t.$$normalized) return e; for (var a in n) { var s = n[a]; if (d()(s)) { var c = s.parameters, p = function (e) { var n = s[e]; if (!d()(n)) return "continue"; var p = _(n, a, e); if (p) { r[p] ? r[p].push(n) : r[p] = [n]; var f = r[p]; if (f.length > 1) i()(f).call(f, (function (e, t) { var n; e.__originalOperationId = e.__originalOperationId || e.operationId, e.operationId = l()(n = "".concat(p)).call(n, t + 1) })); else if (void 0 !== n.operationId) { var h = f[0]; h.__originalOperationId = h.__originalOperationId || n.operationId, h.operationId = p } } if ("parameters" !== e) { var m = [], v = {}; for (var g in t) "produces" !== g && "consumes" !== g && "security" !== g || (v[g] = t[g], m.push(v)); if (c && (v.parameters = c, m.push(v)), m.length) { var y, b = o()(m); try { for (b.s(); !(y = b.n()).done;) { var w = y.value; for (var x in w) if (n[x]) { if ("parameters" === x) { var E, S = o()(w[x]); try { var C = function () { var e, t = E.value; u()(e = n[x]).call(e, (function (e) { return e.name && e.name === t.name || e.$ref && e.$ref === t.$ref || e.$$ref && e.$$ref === t.$$ref || e === t })) || n[x].push(t) }; for (S.s(); !(E = S.n()).done;)C() } catch (e) { S.e(e) } finally { S.f() } } } else n[x] = w[x] } } catch (e) { b.e(e) } finally { b.f() } } } }; for (var f in s) p(f) } } return t.$$normalized = !0, e } }, function (e, t, n) { "use strict"; n.r(t), n.d(t, "NEW_THROWN_ERR", (function () { return a })), n.d(t, "NEW_THROWN_ERR_BATCH", (function () { return i })), n.d(t, "NEW_SPEC_ERR", (function () { return s })), n.d(t, "NEW_SPEC_ERR_BATCH", (function () { return u })), n.d(t, "NEW_AUTH_ERR", (function () { return c })), n.d(t, "CLEAR", (function () { return l })), n.d(t, "CLEAR_BY", (function () { return p })), n.d(t, "newThrownErr", (function () { return f })), n.d(t, "newThrownErrBatch", (function () { return h })), n.d(t, "newSpecErr", (function () { return d })), n.d(t, "newSpecErrBatch", (function () { return m })), n.d(t, "newAuthErr", (function () { return v })), n.d(t, "clear", (function () { return g })), n.d(t, "clearBy", (function () { return y })); var r = n(143), o = n.n(r), a = "err_new_thrown_err", i = "err_new_thrown_err_batch", s = "err_new_spec_err", u = "err_new_spec_err_batch", c = "err_new_auth_err", l = "err_clear", p = "err_clear_by"; function f(e) { return { type: a, payload: o()(e) } } function h(e) { return { type: i, payload: e } } function d(e) { return { type: s, payload: e } } function m(e) { return { type: u, payload: e } } function v(e) { return { type: c, payload: e } } function g() { var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; return { type: l, payload: e } } function y() { var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : function () { return !0 }; return { type: p, payload: e } } }, function (e, t, n) { var r = n(49), o = n(35), a = n(52), i = Object.defineProperty, s = {}, u = function (e) { throw e }; e.exports = function (e, t) { if (a(s, e)) return s[e]; t || (t = {}); var n = [][e], c = !!a(t, "ACCESSORS") && t.ACCESSORS, l = a(t, 0) ? t[0] : u, p = a(t, 1) ? t[1] : void 0; return s[e] = !!n && !o((function () { if (c && !r) return !0; var e = { length: -1 }; c ? i(e, 1, { enumerable: !0, get: u }) : e[1] = 1, n.call(e, l, p) })) } }, function (e, t) { "function" == typeof Object.create ? e.exports = function (e, t) { e.super_ = t, e.prototype = Object.create(t.prototype, { constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } }) } : e.exports = function (e, t) { e.super_ = t; var n = function () { }; n.prototype = t.prototype, e.prototype = new n, e.prototype.constructor = e } }, function (e, t, n) { var r = n(77), o = r.Buffer; function a(e, t) { for (var n in e) t[n] = e[n] } function i(e, t, n) { return o(e, t, n) } o.from && o.alloc && o.allocUnsafe && o.allocUnsafeSlow ? e.exports = r : (a(r, t), t.Buffer = i), a(o, i), i.from = function (e, t, n) { if ("number" == typeof e) throw new TypeError("Argument must not be a number"); return o(e, t, n) }, i.alloc = function (e, t, n) { if ("number" != typeof e) throw new TypeError("Argument must be a number"); var r = o(e); return void 0 !== t ? "string" == typeof n ? r.fill(t, n) : r.fill(t) : r.fill(0), r }, i.allocUnsafe = function (e) { if ("number" != typeof e) throw new TypeError("Argument must be a number"); return o(e) }, i.allocUnsafeSlow = function (e) { if ("number" != typeof e) throw new TypeError("Argument must be a number"); return r.SlowBuffer(e) } }, function (e, t, n) { var r; !function () { "use strict"; var n = {}.hasOwnProperty; function o() { for (var e = [], t = 0; t < arguments.length; t++) { var r = arguments[t]; if (r) { var a = typeof r; if ("string" === a || "number" === a) e.push(r); else if (Array.isArray(r) && r.length) { var i = o.apply(null, r); i && e.push(i) } else if ("object" === a) for (var s in r) n.call(r, s) && r[s] && e.push(s) } } return e.join(" ") } e.exports ? (o.default = o, e.exports = o) : void 0 === (r = function () { return o }.apply(t, [])) || (e.exports = r) }() }, function (e, t, n) { var r = n(114), o = n(59); e.exports = function (e) { if (!o(e)) return !1; var t = r(e); return "[object Function]" == t || "[object GeneratorFunction]" == t || "[object AsyncFunction]" == t || "[object Proxy]" == t } }, function (e, t, n) { e.exports = n(647) }, function (e, t, n) { e.exports = n(902) }, function (e, t, n) { var r = n(177), o = n(128); e.exports = function (e) { return r(o(e)) } }, function (e, t, n) { var r = n(49), o = n(71), a = n(108); e.exports = r ? function (e, t, n) { return o.f(e, t, a(1, n)) } : function (e, t, n) { return e[t] = n, e } }, function (e, t, n) { var r = n(49), o = n(357), a = n(53), i = n(178), s = Object.defineProperty; t.f = r ? s : function (e, t, n) { if (a(e), t = i(t, !0), a(n), o) try { return s(e, t, n) } catch (e) { } if ("get" in n || "set" in n) throw TypeError("Accessors not supported"); return "value" in n && (e[t] = n.value), e } }, function (e, t, n) { var r = n(33), o = n(42), a = function (e) { return "function" == typeof e ? e : void 0 }; e.exports = function (e, t) { return arguments.length < 2 ? a(r[e]) || a(o[e]) : r[e] && r[e][t] || o[e] && o[e][t] } }, function (e, t, n) { var r = n(128); e.exports = function (e) { return Object(r(e)) } }, function (e, t, n) { n(154); var r = n(561), o = n(42), a = n(90), i = n(70), s = n(111), u = n(39)("toStringTag"); for (var c in r) { var l = o[c], p = l && l.prototype; p && a(p) !== u && i(p, u, c), s[c] = s.Array } }, function (e, t, n) { var r = n(407), o = "object" == typeof self && self && self.Object === Object && self, a = r || o || Function("return this")(); e.exports = a }, function (e, t, n) { "use strict"; e.exports = { debugTool: null } }, function (e, t, n) { "use strict"; (function (e) { var r = n(592), o = n(593), a = n(373); function i() { return u.TYPED_ARRAY_SUPPORT ? 2147483647 : 1073741823 } function s(e, t) { if (i() < t) throw new RangeError("Invalid typed array length"); return u.TYPED_ARRAY_SUPPORT ? (e = new Uint8Array(t)).__proto__ = u.prototype : (null === e && (e = new u(t)), e.length = t), e } function u(e, t, n) { if (!(u.TYPED_ARRAY_SUPPORT || this instanceof u)) return new u(e, t, n); if ("number" == typeof e) { if ("string" == typeof t) throw new Error("If encoding is specified then the first argument must be a string"); return p(this, e) } return c(this, e, t, n) } function c(e, t, n, r) { if ("number" == typeof t) throw new TypeError('"value" argument must not be a number'); return "undefined" != typeof ArrayBuffer && t instanceof ArrayBuffer ? function (e, t, n, r) { if (t.byteLength, n < 0 || t.byteLength < n) throw new RangeError("'offset' is out of bounds"); if (t.byteLength < n + (r || 0)) throw new RangeError("'length' is out of bounds"); t = void 0 === n && void 0 === r ? new Uint8Array(t) : void 0 === r ? new Uint8Array(t, n) : new Uint8Array(t, n, r); u.TYPED_ARRAY_SUPPORT ? (e = t).__proto__ = u.prototype : e = f(e, t); return e }(e, t, n, r) : "string" == typeof t ? function (e, t, n) { "string" == typeof n && "" !== n || (n = "utf8"); if (!u.isEncoding(n)) throw new TypeError('"encoding" must be a valid string encoding'); var r = 0 | d(t, n), o = (e = s(e, r)).write(t, n); o !== r && (e = e.slice(0, o)); return e }(e, t, n) : function (e, t) { if (u.isBuffer(t)) { var n = 0 | h(t.length); return 0 === (e = s(e, n)).length || t.copy(e, 0, 0, n), e } if (t) { if ("undefined" != typeof ArrayBuffer && t.buffer instanceof ArrayBuffer || "length" in t) return "number" != typeof t.length || (r = t.length) != r ? s(e, 0) : f(e, t); if ("Buffer" === t.type && a(t.data)) return f(e, t.data) } var r; throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.") }(e, t) } function l(e) { if ("number" != typeof e) throw new TypeError('"size" argument must be a number'); if (e < 0) throw new RangeError('"size" argument must not be negative') } function p(e, t) { if (l(t), e = s(e, t < 0 ? 0 : 0 | h(t)), !u.TYPED_ARRAY_SUPPORT) for (var n = 0; n < t; ++n)e[n] = 0; return e } function f(e, t) { var n = t.length < 0 ? 0 : 0 | h(t.length); e = s(e, n); for (var r = 0; r < n; r += 1)e[r] = 255 & t[r]; return e } function h(e) { if (e >= i()) throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x" + i().toString(16) + " bytes"); return 0 | e } function d(e, t) { if (u.isBuffer(e)) return e.length; if ("undefined" != typeof ArrayBuffer && "function" == typeof ArrayBuffer.isView && (ArrayBuffer.isView(e) || e instanceof ArrayBuffer)) return e.byteLength; "string" != typeof e && (e = "" + e); var n = e.length; if (0 === n) return 0; for (var r = !1; ;)switch (t) { case "ascii": case "latin1": case "binary": return n; case "utf8": case "utf-8": case void 0: return q(e).length; case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return 2 * n; case "hex": return n >>> 1; case "base64": return z(e).length; default: if (r) return q(e).length; t = ("" + t).toLowerCase(), r = !0 } } function m(e, t, n) { var r = !1; if ((void 0 === t || t < 0) && (t = 0), t > this.length) return ""; if ((void 0 === n || n > this.length) && (n = this.length), n <= 0) return ""; if ((n >>>= 0) <= (t >>>= 0)) return ""; for (e || (e = "utf8"); ;)switch (e) { case "hex": return T(this, t, n); case "utf8": case "utf-8": return A(this, t, n); case "ascii": return O(this, t, n); case "latin1": case "binary": return j(this, t, n); case "base64": return C(this, t, n); case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return I(this, t, n); default: if (r) throw new TypeError("Unknown encoding: " + e); e = (e + "").toLowerCase(), r = !0 } } function v(e, t, n) { var r = e[t]; e[t] = e[n], e[n] = r } function g(e, t, n, r, o) { if (0 === e.length) return -1; if ("string" == typeof n ? (r = n, n = 0) : n > 2147483647 ? n = 2147483647 : n < -2147483648 && (n = -2147483648), n = +n, isNaN(n) && (n = o ? 0 : e.length - 1), n < 0 && (n = e.length + n), n >= e.length) { if (o) return -1; n = e.length - 1 } else if (n < 0) { if (!o) return -1; n = 0 } if ("string" == typeof t && (t = u.from(t, r)), u.isBuffer(t)) return 0 === t.length ? -1 : y(e, t, n, r, o); if ("number" == typeof t) return t &= 255, u.TYPED_ARRAY_SUPPORT && "function" == typeof Uint8Array.prototype.indexOf ? o ? Uint8Array.prototype.indexOf.call(e, t, n) : Uint8Array.prototype.lastIndexOf.call(e, t, n) : y(e, [t], n, r, o); throw new TypeError("val must be string, number or Buffer") } function y(e, t, n, r, o) { var a, i = 1, s = e.length, u = t.length; if (void 0 !== r && ("ucs2" === (r = String(r).toLowerCase()) || "ucs-2" === r || "utf16le" === r || "utf-16le" === r)) { if (e.length < 2 || t.length < 2) return -1; i = 2, s /= 2, u /= 2, n /= 2 } function c(e, t) { return 1 === i ? e[t] : e.readUInt16BE(t * i) } if (o) { var l = -1; for (a = n; a < s; a++)if (c(e, a) === c(t, -1 === l ? 0 : a - l)) { if (-1 === l && (l = a), a - l + 1 === u) return l * i } else -1 !== l && (a -= a - l), l = -1 } else for (n + u > s && (n = s - u), a = n; a >= 0; a--) { for (var p = !0, f = 0; f < u; f++)if (c(e, a + f) !== c(t, f)) { p = !1; break } if (p) return a } return -1 } function b(e, t, n, r) { n = Number(n) || 0; var o = e.length - n; r ? (r = Number(r)) > o && (r = o) : r = o; var a = t.length; if (a % 2 != 0) throw new TypeError("Invalid hex string"); r > a / 2 && (r = a / 2); for (var i = 0; i < r; ++i) { var s = parseInt(t.substr(2 * i, 2), 16); if (isNaN(s)) return i; e[n + i] = s } return i } function _(e, t, n, r) { return V(q(t, e.length - n), e, n, r) } function w(e, t, n, r) { return V(function (e) { for (var t = [], n = 0; n < e.length; ++n)t.push(255 & e.charCodeAt(n)); return t }(t), e, n, r) } function x(e, t, n, r) { return w(e, t, n, r) } function E(e, t, n, r) { return V(z(t), e, n, r) } function S(e, t, n, r) { return V(function (e, t) { for (var n, r, o, a = [], i = 0; i < e.length && !((t -= 2) < 0); ++i)r = (n = e.charCodeAt(i)) >> 8, o = n % 256, a.push(o), a.push(r); return a }(t, e.length - n), e, n, r) } function C(e, t, n) { return 0 === t && n === e.length ? r.fromByteArray(e) : r.fromByteArray(e.slice(t, n)) } function A(e, t, n) { n = Math.min(e.length, n); for (var r = [], o = t; o < n;) { var a, i, s, u, c = e[o], l = null, p = c > 239 ? 4 : c > 223 ? 3 : c > 191 ? 2 : 1; if (o + p <= n) switch (p) { case 1: c < 128 && (l = c); break; case 2: 128 == (192 & (a = e[o + 1])) && (u = (31 & c) << 6 | 63 & a) > 127 && (l = u); break; case 3: a = e[o + 1], i = e[o + 2], 128 == (192 & a) && 128 == (192 & i) && (u = (15 & c) << 12 | (63 & a) << 6 | 63 & i) > 2047 && (u < 55296 || u > 57343) && (l = u); break; case 4: a = e[o + 1], i = e[o + 2], s = e[o + 3], 128 == (192 & a) && 128 == (192 & i) && 128 == (192 & s) && (u = (15 & c) << 18 | (63 & a) << 12 | (63 & i) << 6 | 63 & s) > 65535 && u < 1114112 && (l = u) }null === l ? (l = 65533, p = 1) : l > 65535 && (l -= 65536, r.push(l >>> 10 & 1023 | 55296), l = 56320 | 1023 & l), r.push(l), o += p } return function (e) { var t = e.length; if (t <= k) return String.fromCharCode.apply(String, e); var n = "", r = 0; for (; r < t;)n += String.fromCharCode.apply(String, e.slice(r, r += k)); return n }(r) } t.Buffer = u, t.SlowBuffer = function (e) { +e != e && (e = 0); return u.alloc(+e) }, t.INSPECT_MAX_BYTES = 50, u.TYPED_ARRAY_SUPPORT = void 0 !== e.TYPED_ARRAY_SUPPORT ? e.TYPED_ARRAY_SUPPORT : function () { try { var e = new Uint8Array(1); return e.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } }, 42 === e.foo() && "function" == typeof e.subarray && 0 === e.subarray(1, 1).byteLength } catch (e) { return !1 } }(), t.kMaxLength = i(), u.poolSize = 8192, u._augment = function (e) { return e.__proto__ = u.prototype, e }, u.from = function (e, t, n) { return c(null, e, t, n) }, u.TYPED_ARRAY_SUPPORT && (u.prototype.__proto__ = Uint8Array.prototype, u.__proto__ = Uint8Array, "undefined" != typeof Symbol && Symbol.species && u[Symbol.species] === u && Object.defineProperty(u, Symbol.species, { value: null, configurable: !0 })), u.alloc = function (e, t, n) { return function (e, t, n, r) { return l(t), t <= 0 ? s(e, t) : void 0 !== n ? "string" == typeof r ? s(e, t).fill(n, r) : s(e, t).fill(n) : s(e, t) }(null, e, t, n) }, u.allocUnsafe = function (e) { return p(null, e) }, u.allocUnsafeSlow = function (e) { return p(null, e) }, u.isBuffer = function (e) { return !(null == e || !e._isBuffer) }, u.compare = function (e, t) { if (!u.isBuffer(e) || !u.isBuffer(t)) throw new TypeError("Arguments must be Buffers"); if (e === t) return 0; for (var n = e.length, r = t.length, o = 0, a = Math.min(n, r); o < a; ++o)if (e[o] !== t[o]) { n = e[o], r = t[o]; break } return n < r ? -1 : r < n ? 1 : 0 }, u.isEncoding = function (e) { switch (String(e).toLowerCase()) { case "hex": case "utf8": case "utf-8": case "ascii": case "latin1": case "binary": case "base64": case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return !0; default: return !1 } }, u.concat = function (e, t) { if (!a(e)) throw new TypeError('"list" argument must be an Array of Buffers'); if (0 === e.length) return u.alloc(0); var n; if (void 0 === t) for (t = 0, n = 0; n < e.length; ++n)t += e[n].length; var r = u.allocUnsafe(t), o = 0; for (n = 0; n < e.length; ++n) { var i = e[n]; if (!u.isBuffer(i)) throw new TypeError('"list" argument must be an Array of Buffers'); i.copy(r, o), o += i.length } return r }, u.byteLength = d, u.prototype._isBuffer = !0, u.prototype.swap16 = function () { var e = this.length; if (e % 2 != 0) throw new RangeError("Buffer size must be a multiple of 16-bits"); for (var t = 0; t < e; t += 2)v(this, t, t + 1); return this }, u.prototype.swap32 = function () { var e = this.length; if (e % 4 != 0) throw new RangeError("Buffer size must be a multiple of 32-bits"); for (var t = 0; t < e; t += 4)v(this, t, t + 3), v(this, t + 1, t + 2); return this }, u.prototype.swap64 = function () { var e = this.length; if (e % 8 != 0) throw new RangeError("Buffer size must be a multiple of 64-bits"); for (var t = 0; t < e; t += 8)v(this, t, t + 7), v(this, t + 1, t + 6), v(this, t + 2, t + 5), v(this, t + 3, t + 4); return this }, u.prototype.toString = function () { var e = 0 | this.length; return 0 === e ? "" : 0 === arguments.length ? A(this, 0, e) : m.apply(this, arguments) }, u.prototype.equals = function (e) { if (!u.isBuffer(e)) throw new TypeError("Argument must be a Buffer"); return this === e || 0 === u.compare(this, e) }, u.prototype.inspect = function () { var e = "", n = t.INSPECT_MAX_BYTES; return this.length > 0 && (e = this.toString("hex", 0, n).match(/.{2}/g).join(" "), this.length > n && (e += " ... ")), "" }, u.prototype.compare = function (e, t, n, r, o) { if (!u.isBuffer(e)) throw new TypeError("Argument must be a Buffer"); if (void 0 === t && (t = 0), void 0 === n && (n = e ? e.length : 0), void 0 === r && (r = 0), void 0 === o && (o = this.length), t < 0 || n > e.length || r < 0 || o > this.length) throw new RangeError("out of range index"); if (r >= o && t >= n) return 0; if (r >= o) return -1; if (t >= n) return 1; if (this === e) return 0; for (var a = (o >>>= 0) - (r >>>= 0), i = (n >>>= 0) - (t >>>= 0), s = Math.min(a, i), c = this.slice(r, o), l = e.slice(t, n), p = 0; p < s; ++p)if (c[p] !== l[p]) { a = c[p], i = l[p]; break } return a < i ? -1 : i < a ? 1 : 0 }, u.prototype.includes = function (e, t, n) { return -1 !== this.indexOf(e, t, n) }, u.prototype.indexOf = function (e, t, n) { return g(this, e, t, n, !0) }, u.prototype.lastIndexOf = function (e, t, n) { return g(this, e, t, n, !1) }, u.prototype.write = function (e, t, n, r) { if (void 0 === t) r = "utf8", n = this.length, t = 0; else if (void 0 === n && "string" == typeof t) r = t, n = this.length, t = 0; else { if (!isFinite(t)) throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported"); t |= 0, isFinite(n) ? (n |= 0, void 0 === r && (r = "utf8")) : (r = n, n = void 0) } var o = this.length - t; if ((void 0 === n || n > o) && (n = o), e.length > 0 && (n < 0 || t < 0) || t > this.length) throw new RangeError("Attempt to write outside buffer bounds"); r || (r = "utf8"); for (var a = !1; ;)switch (r) { case "hex": return b(this, e, t, n); case "utf8": case "utf-8": return _(this, e, t, n); case "ascii": return w(this, e, t, n); case "latin1": case "binary": return x(this, e, t, n); case "base64": return E(this, e, t, n); case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return S(this, e, t, n); default: if (a) throw new TypeError("Unknown encoding: " + r); r = ("" + r).toLowerCase(), a = !0 } }, u.prototype.toJSON = function () { return { type: "Buffer", data: Array.prototype.slice.call(this._arr || this, 0) } }; var k = 4096; function O(e, t, n) { var r = ""; n = Math.min(e.length, n); for (var o = t; o < n; ++o)r += String.fromCharCode(127 & e[o]); return r } function j(e, t, n) { var r = ""; n = Math.min(e.length, n); for (var o = t; o < n; ++o)r += String.fromCharCode(e[o]); return r } function T(e, t, n) { var r = e.length; (!t || t < 0) && (t = 0), (!n || n < 0 || n > r) && (n = r); for (var o = "", a = t; a < n; ++a)o += U(e[a]); return o } function I(e, t, n) { for (var r = e.slice(t, n), o = "", a = 0; a < r.length; a += 2)o += String.fromCharCode(r[a] + 256 * r[a + 1]); return o } function P(e, t, n) { if (e % 1 != 0 || e < 0) throw new RangeError("offset is not uint"); if (e + t > n) throw new RangeError("Trying to access beyond buffer length") } function N(e, t, n, r, o, a) { if (!u.isBuffer(e)) throw new TypeError('"buffer" argument must be a Buffer instance'); if (t > o || t < a) throw new RangeError('"value" argument is out of bounds'); if (n + r > e.length) throw new RangeError("Index out of range") } function M(e, t, n, r) { t < 0 && (t = 65535 + t + 1); for (var o = 0, a = Math.min(e.length - n, 2); o < a; ++o)e[n + o] = (t & 255 << 8 * (r ? o : 1 - o)) >>> 8 * (r ? o : 1 - o) } function R(e, t, n, r) { t < 0 && (t = 4294967295 + t + 1); for (var o = 0, a = Math.min(e.length - n, 4); o < a; ++o)e[n + o] = t >>> 8 * (r ? o : 3 - o) & 255 } function D(e, t, n, r, o, a) { if (n + r > e.length) throw new RangeError("Index out of range"); if (n < 0) throw new RangeError("Index out of range") } function L(e, t, n, r, a) { return a || D(e, 0, n, 4), o.write(e, t, n, r, 23, 4), n + 4 } function B(e, t, n, r, a) { return a || D(e, 0, n, 8), o.write(e, t, n, r, 52, 8), n + 8 } u.prototype.slice = function (e, t) { var n, r = this.length; if ((e = ~~e) < 0 ? (e += r) < 0 && (e = 0) : e > r && (e = r), (t = void 0 === t ? r : ~~t) < 0 ? (t += r) < 0 && (t = 0) : t > r && (t = r), t < e && (t = e), u.TYPED_ARRAY_SUPPORT) (n = this.subarray(e, t)).__proto__ = u.prototype; else { var o = t - e; n = new u(o, void 0); for (var a = 0; a < o; ++a)n[a] = this[a + e] } return n }, u.prototype.readUIntLE = function (e, t, n) { e |= 0, t |= 0, n || P(e, t, this.length); for (var r = this[e], o = 1, a = 0; ++a < t && (o *= 256);)r += this[e + a] * o; return r }, u.prototype.readUIntBE = function (e, t, n) { e |= 0, t |= 0, n || P(e, t, this.length); for (var r = this[e + --t], o = 1; t > 0 && (o *= 256);)r += this[e + --t] * o; return r }, u.prototype.readUInt8 = function (e, t) { return t || P(e, 1, this.length), this[e] }, u.prototype.readUInt16LE = function (e, t) { return t || P(e, 2, this.length), this[e] | this[e + 1] << 8 }, u.prototype.readUInt16BE = function (e, t) { return t || P(e, 2, this.length), this[e] << 8 | this[e + 1] }, u.prototype.readUInt32LE = function (e, t) { return t || P(e, 4, this.length), (this[e] | this[e + 1] << 8 | this[e + 2] << 16) + 16777216 * this[e + 3] }, u.prototype.readUInt32BE = function (e, t) { return t || P(e, 4, this.length), 16777216 * this[e] + (this[e + 1] << 16 | this[e + 2] << 8 | this[e + 3]) }, u.prototype.readIntLE = function (e, t, n) { e |= 0, t |= 0, n || P(e, t, this.length); for (var r = this[e], o = 1, a = 0; ++a < t && (o *= 256);)r += this[e + a] * o; return r >= (o *= 128) && (r -= Math.pow(2, 8 * t)), r }, u.prototype.readIntBE = function (e, t, n) { e |= 0, t |= 0, n || P(e, t, this.length); for (var r = t, o = 1, a = this[e + --r]; r > 0 && (o *= 256);)a += this[e + --r] * o; return a >= (o *= 128) && (a -= Math.pow(2, 8 * t)), a }, u.prototype.readInt8 = function (e, t) { return t || P(e, 1, this.length), 128 & this[e] ? -1 * (255 - this[e] + 1) : this[e] }, u.prototype.readInt16LE = function (e, t) { t || P(e, 2, this.length); var n = this[e] | this[e + 1] << 8; return 32768 & n ? 4294901760 | n : n }, u.prototype.readInt16BE = function (e, t) { t || P(e, 2, this.length); var n = this[e + 1] | this[e] << 8; return 32768 & n ? 4294901760 | n : n }, u.prototype.readInt32LE = function (e, t) { return t || P(e, 4, this.length), this[e] | this[e + 1] << 8 | this[e + 2] << 16 | this[e + 3] << 24 }, u.prototype.readInt32BE = function (e, t) { return t || P(e, 4, this.length), this[e] << 24 | this[e + 1] << 16 | this[e + 2] << 8 | this[e + 3] }, u.prototype.readFloatLE = function (e, t) { return t || P(e, 4, this.length), o.read(this, e, !0, 23, 4) }, u.prototype.readFloatBE = function (e, t) { return t || P(e, 4, this.length), o.read(this, e, !1, 23, 4) }, u.prototype.readDoubleLE = function (e, t) { return t || P(e, 8, this.length), o.read(this, e, !0, 52, 8) }, u.prototype.readDoubleBE = function (e, t) { return t || P(e, 8, this.length), o.read(this, e, !1, 52, 8) }, u.prototype.writeUIntLE = function (e, t, n, r) { (e = +e, t |= 0, n |= 0, r) || N(this, e, t, n, Math.pow(2, 8 * n) - 1, 0); var o = 1, a = 0; for (this[t] = 255 & e; ++a < n && (o *= 256);)this[t + a] = e / o & 255; return t + n }, u.prototype.writeUIntBE = function (e, t, n, r) { (e = +e, t |= 0, n |= 0, r) || N(this, e, t, n, Math.pow(2, 8 * n) - 1, 0); var o = n - 1, a = 1; for (this[t + o] = 255 & e; --o >= 0 && (a *= 256);)this[t + o] = e / a & 255; return t + n }, u.prototype.writeUInt8 = function (e, t, n) { return e = +e, t |= 0, n || N(this, e, t, 1, 255, 0), u.TYPED_ARRAY_SUPPORT || (e = Math.floor(e)), this[t] = 255 & e, t + 1 }, u.prototype.writeUInt16LE = function (e, t, n) { return e = +e, t |= 0, n || N(this, e, t, 2, 65535, 0), u.TYPED_ARRAY_SUPPORT ? (this[t] = 255 & e, this[t + 1] = e >>> 8) : M(this, e, t, !0), t + 2 }, u.prototype.writeUInt16BE = function (e, t, n) { return e = +e, t |= 0, n || N(this, e, t, 2, 65535, 0), u.TYPED_ARRAY_SUPPORT ? (this[t] = e >>> 8, this[t + 1] = 255 & e) : M(this, e, t, !1), t + 2 }, u.prototype.writeUInt32LE = function (e, t, n) { return e = +e, t |= 0, n || N(this, e, t, 4, 4294967295, 0), u.TYPED_ARRAY_SUPPORT ? (this[t + 3] = e >>> 24, this[t + 2] = e >>> 16, this[t + 1] = e >>> 8, this[t] = 255 & e) : R(this, e, t, !0), t + 4 }, u.prototype.writeUInt32BE = function (e, t, n) { return e = +e, t |= 0, n || N(this, e, t, 4, 4294967295, 0), u.TYPED_ARRAY_SUPPORT ? (this[t] = e >>> 24, this[t + 1] = e >>> 16, this[t + 2] = e >>> 8, this[t + 3] = 255 & e) : R(this, e, t, !1), t + 4 }, u.prototype.writeIntLE = function (e, t, n, r) { if (e = +e, t |= 0, !r) { var o = Math.pow(2, 8 * n - 1); N(this, e, t, n, o - 1, -o) } var a = 0, i = 1, s = 0; for (this[t] = 255 & e; ++a < n && (i *= 256);)e < 0 && 0 === s && 0 !== this[t + a - 1] && (s = 1), this[t + a] = (e / i >> 0) - s & 255; return t + n }, u.prototype.writeIntBE = function (e, t, n, r) { if (e = +e, t |= 0, !r) { var o = Math.pow(2, 8 * n - 1); N(this, e, t, n, o - 1, -o) } var a = n - 1, i = 1, s = 0; for (this[t + a] = 255 & e; --a >= 0 && (i *= 256);)e < 0 && 0 === s && 0 !== this[t + a + 1] && (s = 1), this[t + a] = (e / i >> 0) - s & 255; return t + n }, u.prototype.writeInt8 = function (e, t, n) { return e = +e, t |= 0, n || N(this, e, t, 1, 127, -128), u.TYPED_ARRAY_SUPPORT || (e = Math.floor(e)), e < 0 && (e = 255 + e + 1), this[t] = 255 & e, t + 1 }, u.prototype.writeInt16LE = function (e, t, n) { return e = +e, t |= 0, n || N(this, e, t, 2, 32767, -32768), u.TYPED_ARRAY_SUPPORT ? (this[t] = 255 & e, this[t + 1] = e >>> 8) : M(this, e, t, !0), t + 2 }, u.prototype.writeInt16BE = function (e, t, n) { return e = +e, t |= 0, n || N(this, e, t, 2, 32767, -32768), u.TYPED_ARRAY_SUPPORT ? (this[t] = e >>> 8, this[t + 1] = 255 & e) : M(this, e, t, !1), t + 2 }, u.prototype.writeInt32LE = function (e, t, n) { return e = +e, t |= 0, n || N(this, e, t, 4, 2147483647, -2147483648), u.TYPED_ARRAY_SUPPORT ? (this[t] = 255 & e, this[t + 1] = e >>> 8, this[t + 2] = e >>> 16, this[t + 3] = e >>> 24) : R(this, e, t, !0), t + 4 }, u.prototype.writeInt32BE = function (e, t, n) { return e = +e, t |= 0, n || N(this, e, t, 4, 2147483647, -2147483648), e < 0 && (e = 4294967295 + e + 1), u.TYPED_ARRAY_SUPPORT ? (this[t] = e >>> 24, this[t + 1] = e >>> 16, this[t + 2] = e >>> 8, this[t + 3] = 255 & e) : R(this, e, t, !1), t + 4 }, u.prototype.writeFloatLE = function (e, t, n) { return L(this, e, t, !0, n) }, u.prototype.writeFloatBE = function (e, t, n) { return L(this, e, t, !1, n) }, u.prototype.writeDoubleLE = function (e, t, n) { return B(this, e, t, !0, n) }, u.prototype.writeDoubleBE = function (e, t, n) { return B(this, e, t, !1, n) }, u.prototype.copy = function (e, t, n, r) { if (n || (n = 0), r || 0 === r || (r = this.length), t >= e.length && (t = e.length), t || (t = 0), r > 0 && r < n && (r = n), r === n) return 0; if (0 === e.length || 0 === this.length) return 0; if (t < 0) throw new RangeError("targetStart out of bounds"); if (n < 0 || n >= this.length) throw new RangeError("sourceStart out of bounds"); if (r < 0) throw new RangeError("sourceEnd out of bounds"); r > this.length && (r = this.length), e.length - t < r - n && (r = e.length - t + n); var o, a = r - n; if (this === e && n < t && t < r) for (o = a - 1; o >= 0; --o)e[o + t] = this[o + n]; else if (a < 1e3 || !u.TYPED_ARRAY_SUPPORT) for (o = 0; o < a; ++o)e[o + t] = this[o + n]; else Uint8Array.prototype.set.call(e, this.subarray(n, n + a), t); return a }, u.prototype.fill = function (e, t, n, r) { if ("string" == typeof e) { if ("string" == typeof t ? (r = t, t = 0, n = this.length) : "string" == typeof n && (r = n, n = this.length), 1 === e.length) { var o = e.charCodeAt(0); o < 256 && (e = o) } if (void 0 !== r && "string" != typeof r) throw new TypeError("encoding must be a string"); if ("string" == typeof r && !u.isEncoding(r)) throw new TypeError("Unknown encoding: " + r) } else "number" == typeof e && (e &= 255); if (t < 0 || this.length < t || this.length < n) throw new RangeError("Out of range index"); if (n <= t) return this; var a; if (t >>>= 0, n = void 0 === n ? this.length : n >>> 0, e || (e = 0), "number" == typeof e) for (a = t; a < n; ++a)this[a] = e; else { var i = u.isBuffer(e) ? e : q(new u(e, r).toString()), s = i.length; for (a = 0; a < n - t; ++a)this[a + t] = i[a % s] } return this }; var F = /[^+\/0-9A-Za-z-_]/g; function U(e) { return e < 16 ? "0" + e.toString(16) : e.toString(16) } function q(e, t) { var n; t = t || 1 / 0; for (var r = e.length, o = null, a = [], i = 0; i < r; ++i) { if ((n = e.charCodeAt(i)) > 55295 && n < 57344) { if (!o) { if (n > 56319) { (t -= 3) > -1 && a.push(239, 191, 189); continue } if (i + 1 === r) { (t -= 3) > -1 && a.push(239, 191, 189); continue } o = n; continue } if (n < 56320) { (t -= 3) > -1 && a.push(239, 191, 189), o = n; continue } n = 65536 + (o - 55296 << 10 | n - 56320) } else o && (t -= 3) > -1 && a.push(239, 191, 189); if (o = null, n < 128) { if ((t -= 1) < 0) break; a.push(n) } else if (n < 2048) { if ((t -= 2) < 0) break; a.push(n >> 6 | 192, 63 & n | 128) } else if (n < 65536) { if ((t -= 3) < 0) break; a.push(n >> 12 | 224, n >> 6 & 63 | 128, 63 & n | 128) } else { if (!(n < 1114112)) throw new Error("Invalid code point"); if ((t -= 4) < 0) break; a.push(n >> 18 | 240, n >> 12 & 63 | 128, n >> 6 & 63 | 128, 63 & n | 128) } } return a } function z(e) { return r.toByteArray(function (e) { if ((e = function (e) { return e.trim ? e.trim() : e.replace(/^\s+|\s+$/g, "") }(e).replace(F, "")).length < 2) return ""; for (; e.length % 4 != 0;)e += "="; return e }(e)) } function V(e, t, n, r) { for (var o = 0; o < r && !(o + n >= t.length || o >= e.length); ++o)t[o + n] = e[o]; return o } }).call(this, n(55)) }, function (e, t, n) { "use strict"; var r = n(848); e.exports = r }, function (e, t, n) { var r = n(908); function o(e, t, n, o, a, i, s) { try { var u = e[i](s), c = u.value } catch (e) { return void n(e) } u.done ? t(c) : r.resolve(c).then(o, a) } e.exports = function (e) { return function () { var t = this, n = arguments; return new r((function (r, a) { var i = e.apply(t, n); function s(e) { o(i, r, a, s, u, "next", e) } function u(e) { o(i, r, a, s, u, "throw", e) } s(void 0) })) } } }, function (e, t) { e.exports = function (e) { if ("function" != typeof e) throw TypeError(String(e) + " is not a function"); return e } }, function (e, t, n) { var r = n(151), o = Math.min; e.exports = function (e) { return e > 0 ? o(r(e), 9007199254740991) : 0 } }, function (e, t, n) { var r, o, a, i = n(364), s = n(42), u = n(45), c = n(70), l = n(52), p = n(234), f = n(180), h = n(152), d = s.WeakMap; if (i) { var m = p.state || (p.state = new d), v = m.get, g = m.has, y = m.set; r = function (e, t) { return t.facade = e, y.call(m, e, t), t }, o = function (e) { return v.call(m, e) || {} }, a = function (e) { return g.call(m, e) } } else { var b = f("state"); h[b] = !0, r = function (e, t) { return t.facade = e, c(e, b, t), t }, o = function (e) { return l(e, b) ? e[b] : {} }, a = function (e) { return l(e, b) } } e.exports = { set: r, get: o, has: a, enforce: function (e) { return a(e) ? o(e) : r(e, {}) }, getterFor: function (e) { return function (t) { var n; if (!u(t) || (n = o(t)).type !== e) throw TypeError("Incompatible receiver, " + e + " required"); return n } } } }, function (e, t, n) { "use strict"; function r(e) { return function () { return e } } var o = function () { }; o.thatReturns = r, o.thatReturnsFalse = r(!1), o.thatReturnsTrue = r(!0), o.thatReturnsNull = r(null), o.thatReturnsThis = function () { return this }, o.thatReturnsArgument = function (e) { return e }, e.exports = o }, function (e, t, n) { "use strict"; var r = n(31), o = n(40), a = n(475), i = n(124), s = n(476), u = n(140), c = n(204), l = n(26), p = [], f = 0, h = a.getPooled(), d = !1, m = null; function v() { x.ReactReconcileTransaction && m || r("123") } var g = [{ initialize: function () { this.dirtyComponentsLength = p.length }, close: function () { this.dirtyComponentsLength !== p.length ? (p.splice(0, this.dirtyComponentsLength), w()) : p.length = 0 } }, { initialize: function () { this.callbackQueue.reset() }, close: function () { this.callbackQueue.notifyAll() } }]; function y() { this.reinitializeTransaction(), this.dirtyComponentsLength = null, this.callbackQueue = a.getPooled(), this.reconcileTransaction = x.ReactReconcileTransaction.getPooled(!0) } function b(e, t) { return e._mountOrder - t._mountOrder } function _(e) { var t = e.dirtyComponentsLength; t !== p.length && r("124", t, p.length), p.sort(b), f++; for (var n = 0; n < t; n++) { var o, a = p[n], i = a._pendingCallbacks; if (a._pendingCallbacks = null, s.logTopLevelRenders) { var c = a; a._currentElement.type.isReactTopLevelWrapper && (c = a._renderedComponent), o = "React update: " + c.getName(), console.time(o) } if (u.performUpdateIfNecessary(a, e.reconcileTransaction, f), o && console.timeEnd(o), i) for (var l = 0; l < i.length; l++)e.callbackQueue.enqueue(i[l], a.getPublicInstance()) } } o(y.prototype, c, { getTransactionWrappers: function () { return g }, destructor: function () { this.dirtyComponentsLength = null, a.release(this.callbackQueue), this.callbackQueue = null, x.ReactReconcileTransaction.release(this.reconcileTransaction), this.reconcileTransaction = null }, perform: function (e, t, n) { return c.perform.call(this, this.reconcileTransaction.perform, this.reconcileTransaction, e, t, n) } }), i.addPoolingTo(y); var w = function () { for (; p.length || d;) { if (p.length) { var e = y.getPooled(); e.perform(_, null, e), y.release(e) } if (d) { d = !1; var t = h; h = a.getPooled(), t.notifyAll(), a.release(t) } } }; var x = { ReactReconcileTransaction: null, batchedUpdates: function (e, t, n, r, o, a) { return v(), m.batchedUpdates(e, t, n, r, o, a) }, enqueueUpdate: function e(t) { v(), m.isBatchingUpdates ? (p.push(t), null == t._updateBatchNumber && (t._updateBatchNumber = f + 1)) : m.batchedUpdates(e, t) }, flushBatchedUpdates: w, injection: { injectReconcileTransaction: function (e) { e || r("126"), x.ReactReconcileTransaction = e }, injectBatchingStrategy: function (e) { e || r("127"), "function" != typeof e.batchedUpdates && r("128"), "boolean" != typeof e.isBatchingUpdates && r("129"), m = e } }, asap: function (e, t) { l(m.isBatchingUpdates, "ReactUpdates.asap: Can't enqueue an asap callback in a context whereupdates are not being batched."), h.enqueue(e, t), d = !0 } }; e.exports = x }, function (e, t, n) { "use strict"; (function (t) { function n(e) { return e instanceof t || e instanceof Date || e instanceof RegExp } function r(e) { if (e instanceof t) { var n = t.alloc ? t.alloc(e.length) : new t(e.length); return e.copy(n), n } if (e instanceof Date) return new Date(e.getTime()); if (e instanceof RegExp) return new RegExp(e); throw new Error("Unexpected situation") } function o(e) { var t = []; return e.forEach((function (e, a) { "object" == typeof e && null !== e ? Array.isArray(e) ? t[a] = o(e) : n(e) ? t[a] = r(e) : t[a] = i({}, e) : t[a] = e })), t } function a(e, t) { return "__proto__" === t ? void 0 : e[t] } var i = e.exports = function () { if (arguments.length < 1 || "object" != typeof arguments[0]) return !1; if (arguments.length < 2) return arguments[0]; var e, t, s = arguments[0], u = Array.prototype.slice.call(arguments, 1); return u.forEach((function (u) { "object" != typeof u || null === u || Array.isArray(u) || Object.keys(u).forEach((function (c) { return t = a(s, c), (e = a(u, c)) === s ? void 0 : "object" != typeof e || null === e ? void (s[c] = e) : Array.isArray(e) ? void (s[c] = o(e)) : n(e) ? void (s[c] = r(e)) : "object" != typeof t || null === t || Array.isArray(t) ? void (s[c] = i({}, e)) : void (s[c] = i(t, e)) })) })), s } }).call(this, n(77).Buffer) }, function (e, t, n) { e.exports = n(686) }, function (e, t, n) { "use strict"; var r = n(1075), o = n(1076); function a() { this.protocol = null, this.slashes = null, this.auth = null, this.host = null, this.port = null, this.hostname = null, this.hash = null, this.search = null, this.query = null, this.pathname = null, this.path = null, this.href = null } t.parse = b, t.resolve = function (e, t) { return b(e, !1, !0).resolve(t) }, t.resolveObject = function (e, t) { return e ? b(e, !1, !0).resolveObject(t) : t }, t.format = function (e) { o.isString(e) && (e = b(e)); return e instanceof a ? e.format() : a.prototype.format.call(e) }, t.Url = a; var i = /^([a-z0-9.+-]+:)/i, s = /:[0-9]*$/, u = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/, c = ["{", "}", "|", "\\", "^", "`"].concat(["<", ">", '"', "`", " ", "\r", "\n", "\t"]), l = ["'"].concat(c), p = ["%", "/", "?", ";", "#"].concat(l), f = ["/", "?", "#"], h = /^[+a-z0-9A-Z_-]{0,63}$/, d = /^([+a-z0-9A-Z_-]{0,63})(.*)$/, m = { javascript: !0, "javascript:": !0 }, v = { javascript: !0, "javascript:": !0 }, g = { http: !0, https: !0, ftp: !0, gopher: !0, file: !0, "http:": !0, "https:": !0, "ftp:": !0, "gopher:": !0, "file:": !0 }, y = n(1077); function b(e, t, n) { if (e && o.isObject(e) && e instanceof a) return e; var r = new a; return r.parse(e, t, n), r } a.prototype.parse = function (e, t, n) { if (!o.isString(e)) throw new TypeError("Parameter 'url' must be a string, not " + typeof e); var a = e.indexOf("?"), s = -1 !== a && a < e.indexOf("#") ? "?" : "#", c = e.split(s); c[0] = c[0].replace(/\\/g, "/"); var b = e = c.join(s); if (b = b.trim(), !n && 1 === e.split("#").length) { var _ = u.exec(b); if (_) return this.path = b, this.href = b, this.pathname = _[1], _[2] ? (this.search = _[2], this.query = t ? y.parse(this.search.substr(1)) : this.search.substr(1)) : t && (this.search = "", this.query = {}), this } var w = i.exec(b); if (w) { var x = (w = w[0]).toLowerCase(); this.protocol = x, b = b.substr(w.length) } if (n || w || b.match(/^\/\/[^@\/]+@[^@\/]+/)) { var E = "//" === b.substr(0, 2); !E || w && v[w] || (b = b.substr(2), this.slashes = !0) } if (!v[w] && (E || w && !g[w])) { for (var S, C, A = -1, k = 0; k < f.length; k++) { -1 !== (O = b.indexOf(f[k])) && (-1 === A || O < A) && (A = O) } -1 !== (C = -1 === A ? b.lastIndexOf("@") : b.lastIndexOf("@", A)) && (S = b.slice(0, C), b = b.slice(C + 1), this.auth = decodeURIComponent(S)), A = -1; for (k = 0; k < p.length; k++) { var O; -1 !== (O = b.indexOf(p[k])) && (-1 === A || O < A) && (A = O) } -1 === A && (A = b.length), this.host = b.slice(0, A), b = b.slice(A), this.parseHost(), this.hostname = this.hostname || ""; var j = "[" === this.hostname[0] && "]" === this.hostname[this.hostname.length - 1]; if (!j) for (var T = this.hostname.split(/\./), I = (k = 0, T.length); k < I; k++) { var P = T[k]; if (P && !P.match(h)) { for (var N = "", M = 0, R = P.length; M < R; M++)P.charCodeAt(M) > 127 ? N += "x" : N += P[M]; if (!N.match(h)) { var D = T.slice(0, k), L = T.slice(k + 1), B = P.match(d); B && (D.push(B[1]), L.unshift(B[2])), L.length && (b = "/" + L.join(".") + b), this.hostname = D.join("."); break } } } this.hostname.length > 255 ? this.hostname = "" : this.hostname = this.hostname.toLowerCase(), j || (this.hostname = r.toASCII(this.hostname)); var F = this.port ? ":" + this.port : "", U = this.hostname || ""; this.host = U + F, this.href += this.host, j && (this.hostname = this.hostname.substr(1, this.hostname.length - 2), "/" !== b[0] && (b = "/" + b)) } if (!m[x]) for (k = 0, I = l.length; k < I; k++) { var q = l[k]; if (-1 !== b.indexOf(q)) { var z = encodeURIComponent(q); z === q && (z = escape(q)), b = b.split(q).join(z) } } var V = b.indexOf("#"); -1 !== V && (this.hash = b.substr(V), b = b.slice(0, V)); var W = b.indexOf("?"); if (-1 !== W ? (this.search = b.substr(W), this.query = b.substr(W + 1), t && (this.query = y.parse(this.query)), b = b.slice(0, W)) : t && (this.search = "", this.query = {}), b && (this.pathname = b), g[x] && this.hostname && !this.pathname && (this.pathname = "/"), this.pathname || this.search) { F = this.pathname || ""; var H = this.search || ""; this.path = F + H } return this.href = this.format(), this }, a.prototype.format = function () { var e = this.auth || ""; e && (e = (e = encodeURIComponent(e)).replace(/%3A/i, ":"), e += "@"); var t = this.protocol || "", n = this.pathname || "", r = this.hash || "", a = !1, i = ""; this.host ? a = e + this.host : this.hostname && (a = e + (-1 === this.hostname.indexOf(":") ? this.hostname : "[" + this.hostname + "]"), this.port && (a += ":" + this.port)), this.query && o.isObject(this.query) && Object.keys(this.query).length && (i = y.stringify(this.query)); var s = this.search || i && "?" + i || ""; return t && ":" !== t.substr(-1) && (t += ":"), this.slashes || (!t || g[t]) && !1 !== a ? (a = "//" + (a || ""), n && "/" !== n.charAt(0) && (n = "/" + n)) : a || (a = ""), r && "#" !== r.charAt(0) && (r = "#" + r), s && "?" !== s.charAt(0) && (s = "?" + s), t + a + (n = n.replace(/[?#]/g, (function (e) { return encodeURIComponent(e) }))) + (s = s.replace("#", "%23")) + r }, a.prototype.resolve = function (e) { return this.resolveObject(b(e, !1, !0)).format() }, a.prototype.resolveObject = function (e) { if (o.isString(e)) { var t = new a; t.parse(e, !1, !0), e = t } for (var n = new a, r = Object.keys(this), i = 0; i < r.length; i++) { var s = r[i]; n[s] = this[s] } if (n.hash = e.hash, "" === e.href) return n.href = n.format(), n; if (e.slashes && !e.protocol) { for (var u = Object.keys(e), c = 0; c < u.length; c++) { var l = u[c]; "protocol" !== l && (n[l] = e[l]) } return g[n.protocol] && n.hostname && !n.pathname && (n.path = n.pathname = "/"), n.href = n.format(), n } if (e.protocol && e.protocol !== n.protocol) { if (!g[e.protocol]) { for (var p = Object.keys(e), f = 0; f < p.length; f++) { var h = p[f]; n[h] = e[h] } return n.href = n.format(), n } if (n.protocol = e.protocol, e.host || v[e.protocol]) n.pathname = e.pathname; else { for (var d = (e.pathname || "").split("/"); d.length && !(e.host = d.shift());); e.host || (e.host = ""), e.hostname || (e.hostname = ""), "" !== d[0] && d.unshift(""), d.length < 2 && d.unshift(""), n.pathname = d.join("/") } if (n.search = e.search, n.query = e.query, n.host = e.host || "", n.auth = e.auth, n.hostname = e.hostname || e.host, n.port = e.port, n.pathname || n.search) { var m = n.pathname || "", y = n.search || ""; n.path = m + y } return n.slashes = n.slashes || e.slashes, n.href = n.format(), n } var b = n.pathname && "/" === n.pathname.charAt(0), _ = e.host || e.pathname && "/" === e.pathname.charAt(0), w = _ || b || n.host && e.pathname, x = w, E = n.pathname && n.pathname.split("/") || [], S = (d = e.pathname && e.pathname.split("/") || [], n.protocol && !g[n.protocol]); if (S && (n.hostname = "", n.port = null, n.host && ("" === E[0] ? E[0] = n.host : E.unshift(n.host)), n.host = "", e.protocol && (e.hostname = null, e.port = null, e.host && ("" === d[0] ? d[0] = e.host : d.unshift(e.host)), e.host = null), w = w && ("" === d[0] || "" === E[0])), _) n.host = e.host || "" === e.host ? e.host : n.host, n.hostname = e.hostname || "" === e.hostname ? e.hostname : n.hostname, n.search = e.search, n.query = e.query, E = d; else if (d.length) E || (E = []), E.pop(), E = E.concat(d), n.search = e.search, n.query = e.query; else if (!o.isNullOrUndefined(e.search)) { if (S) n.hostname = n.host = E.shift(), (j = !!(n.host && n.host.indexOf("@") > 0) && n.host.split("@")) && (n.auth = j.shift(), n.host = n.hostname = j.shift()); return n.search = e.search, n.query = e.query, o.isNull(n.pathname) && o.isNull(n.search) || (n.path = (n.pathname ? n.pathname : "") + (n.search ? n.search : "")), n.href = n.format(), n } if (!E.length) return n.pathname = null, n.search ? n.path = "/" + n.search : n.path = null, n.href = n.format(), n; for (var C = E.slice(-1)[0], A = (n.host || e.host || E.length > 1) && ("." === C || ".." === C) || "" === C, k = 0, O = E.length; O >= 0; O--)"." === (C = E[O]) ? E.splice(O, 1) : ".." === C ? (E.splice(O, 1), k++) : k && (E.splice(O, 1), k--); if (!w && !x) for (; k--; k)E.unshift(".."); !w || "" === E[0] || E[0] && "/" === E[0].charAt(0) || E.unshift(""), A && "/" !== E.join("/").substr(-1) && E.push(""); var j, T = "" === E[0] || E[0] && "/" === E[0].charAt(0); S && (n.hostname = n.host = T ? "" : E.length ? E.shift() : "", (j = !!(n.host && n.host.indexOf("@") > 0) && n.host.split("@")) && (n.auth = j.shift(), n.host = n.hostname = j.shift())); return (w = w || n.host && E.length) && !T && E.unshift(""), E.length ? n.pathname = E.join("/") : (n.pathname = null, n.path = null), o.isNull(n.pathname) && o.isNull(n.search) || (n.path = (n.pathname ? n.pathname : "") + (n.search ? n.search : "")), n.auth = e.auth || n.auth, n.slashes = n.slashes || e.slashes, n.href = n.format(), n }, a.prototype.parseHost = function () { var e = this.host, t = s.exec(e); t && (":" !== (t = t[0]) && (this.port = t.substr(1)), e = e.substr(0, e.length - t.length)), e && (this.hostname = e) } }, function (e, t, n) { "use strict"; n.r(t), n.d(t, "SHOW_AUTH_POPUP", (function () { return h })), n.d(t, "AUTHORIZE", (function () { return d })), n.d(t, "LOGOUT", (function () { return m })), n.d(t, "PRE_AUTHORIZE_OAUTH2", (function () { return v })), n.d(t, "AUTHORIZE_OAUTH2", (function () { return g })), n.d(t, "VALIDATE", (function () { return y })), n.d(t, "CONFIGURE_AUTH", (function () { return b })), n.d(t, "RESTORE_AUTHORIZATION", (function () { return _ })), n.d(t, "showDefinitions", (function () { return w })), n.d(t, "authorize", (function () { return x })), n.d(t, "authorizeWithPersistOption", (function () { return E })), n.d(t, "logout", (function () { return S })), n.d(t, "logoutWithPersistOption", (function () { return C })), n.d(t, "preAuthorizeImplicit", (function () { return A })), n.d(t, "authorizeOauth2", (function () { return k })), n.d(t, "authorizeOauth2WithPersistOption", (function () { return O })), n.d(t, "authorizePassword", (function () { return j })), n.d(t, "authorizeApplication", (function () { return T })), n.d(t, "authorizeAccessCodeWithFormParams", (function () { return I })), n.d(t, "authorizeAccessCodeWithBasicAuthentication", (function () { return P })), n.d(t, "authorizeRequest", (function () { return N })), n.d(t, "configureAuth", (function () { return M })), n.d(t, "restoreAuthorization", (function () { return R })), n.d(t, "persistAuthorizationIfNeeded", (function () { return D })); var r = n(19), o = n.n(r), a = n(22), i = n.n(a), s = n(32), u = n.n(s), c = n(98), l = n.n(c), p = n(27), f = n(5), h = "show_popup", d = "authorize", m = "logout", v = "pre_authorize_oauth2", g = "authorize_oauth2", y = "validate", b = "configure_auth", _ = "restore_authorization"; function w(e) { return { type: h, payload: e } } function x(e) { return { type: d, payload: e } } var E = function (e) { return function (t) { var n = t.authActions; n.authorize(e), n.persistAuthorizationIfNeeded() } }; function S(e) { return { type: m, payload: e } } var C = function (e) { return function (t) { var n = t.authActions; n.logout(e), n.persistAuthorizationIfNeeded() } }, A = function (e) { return function (t) { var n = t.authActions, r = t.errActions, o = e.auth, a = e.token, i = e.isValid, s = o.schema, c = o.name, l = s.get("flow"); delete p.a.swaggerUIRedirectOauth2, "accessCode" === l || i || r.newAuthErr({ authId: c, source: "auth", level: "warning", message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server" }), a.error ? r.newAuthErr({ authId: c, source: "auth", level: "error", message: u()(a) }) : n.authorizeOauth2WithPersistOption({ auth: o, token: a }) } }; function k(e) { return { type: g, payload: e } } var O = function (e) { return function (t) { var n = t.authActions; n.authorizeOauth2(e), n.persistAuthorizationIfNeeded() } }, j = function (e) { return function (t) { var n = t.authActions, r = e.schema, o = e.name, a = e.username, s = e.password, u = e.passwordType, c = e.clientId, l = e.clientSecret, p = { grant_type: "password", scope: e.scopes.join(" "), username: a, password: s }, h = {}; switch (u) { case "request-body": !function (e, t, n) { t && i()(e, { client_id: t }); n && i()(e, { client_secret: n }) }(p, c, l); break; case "basic": h.Authorization = "Basic " + Object(f.a)(c + ":" + l); break; default: console.warn("Warning: invalid passwordType ".concat(u, " was passed, not including client id and secret")) }return n.authorizeRequest({ body: Object(f.b)(p), url: r.get("tokenUrl"), name: o, headers: h, query: {}, auth: e }) } }; var T = function (e) { return function (t) { var n = t.authActions, r = e.schema, o = e.scopes, a = e.name, i = e.clientId, s = e.clientSecret, u = { Authorization: "Basic " + Object(f.a)(i + ":" + s) }, c = { grant_type: "client_credentials", scope: o.join(" ") }; return n.authorizeRequest({ body: Object(f.b)(c), name: a, url: r.get("tokenUrl"), auth: e, headers: u }) } }, I = function (e) { var t = e.auth, n = e.redirectUrl; return function (e) { var r = e.authActions, o = t.schema, a = t.name, i = t.clientId, s = t.clientSecret, u = t.codeVerifier, c = { grant_type: "authorization_code", code: t.code, client_id: i, client_secret: s, redirect_uri: n, code_verifier: u }; return r.authorizeRequest({ body: Object(f.b)(c), name: a, url: o.get("tokenUrl"), auth: t }) } }, P = function (e) { var t = e.auth, n = e.redirectUrl; return function (e) { var r = e.authActions, o = t.schema, a = t.name, i = t.clientId, s = t.clientSecret, u = { Authorization: "Basic " + Object(f.a)(i + ":" + s) }, c = { grant_type: "authorization_code", code: t.code, client_id: i, redirect_uri: n }; return r.authorizeRequest({ body: Object(f.b)(c), name: a, url: o.get("tokenUrl"), auth: t, headers: u }) } }, N = function (e) { return function (t) { var n, r = t.fn, a = t.getConfigs, s = t.authActions, c = t.errActions, p = t.oas3Selectors, f = t.specSelectors, h = t.authSelectors, d = e.body, m = e.query, v = void 0 === m ? {} : m, g = e.headers, y = void 0 === g ? {} : g, b = e.name, _ = e.url, w = e.auth, x = (h.getConfigs() || {}).additionalQueryStringParams; if (f.isOAS3()) { var E = p.serverEffectiveValue(p.selectedServer()); n = l()(_, E, !0) } else n = l()(_, f.url(), !0); "object" === o()(x) && (n.query = i()({}, n.query, x)); var S = n.toString(), C = i()({ Accept: "application/json, text/plain, */*", "Content-Type": "application/x-www-form-urlencoded", "X-Requested-With": "XMLHttpRequest" }, y); r.fetch({ url: S, method: "post", headers: C, query: v, body: d, requestInterceptor: a().requestInterceptor, responseInterceptor: a().responseInterceptor }).then((function (e) { var t = JSON.parse(e.data), n = t && (t.error || ""), r = t && (t.parseError || ""); e.ok ? n || r ? c.newAuthErr({ authId: b, level: "error", source: "auth", message: u()(t) }) : s.authorizeOauth2WithPersistOption({ auth: w, token: t }) : c.newAuthErr({ authId: b, level: "error", source: "auth", message: e.statusText }) })).catch((function (e) { var t = new Error(e).message; if (e.response && e.response.data) { var n = e.response.data; try { var r = "string" == typeof n ? JSON.parse(n) : n; r.error && (t += ", error: ".concat(r.error)), r.error_description && (t += ", description: ".concat(r.error_description)) } catch (e) { } } c.newAuthErr({ authId: b, level: "error", source: "auth", message: t }) })) } }; function M(e) { return { type: b, payload: e } } function R(e) { return { type: _, payload: e } } var D = function () { return function (e) { var t = e.authSelectors; if ((0, e.getConfigs)().persistAuthorization) { var n = t.authorized(); localStorage.setItem("authorized", u()(n.toJS())) } } } }, function (e, t, n) { var r = n(1105); e.exports = function (e) { for (var t = 1; t < arguments.length; t++) { var n = null != arguments[t] ? Object(arguments[t]) : {}, o = Object.keys(n); "function" == typeof Object.getOwnPropertySymbols && (o = o.concat(Object.getOwnPropertySymbols(n).filter((function (e) { return Object.getOwnPropertyDescriptor(n, e).enumerable })))), o.forEach((function (t) { r(e, t, n[t]) })) } return e } }, function (e, t, n) { var r = n(238), o = n(149), a = n(39)("toStringTag"), i = "Arguments" == o(function () { return arguments }()); e.exports = r ? o : function (e) { var t, n, r; return void 0 === e ? "Undefined" : null === e ? "Null" : "string" == typeof (n = function (e, t) { try { return e[t] } catch (e) { } }(t = Object(e), a)) ? n : i ? o(t) : "Object" == (r = o(t)) && "function" == typeof t.callee ? "Arguments" : r } }, function (e, t, n) { var r = n(109), o = n(177), a = n(73), i = n(81), s = n(241), u = [].push, c = function (e) { var t = 1 == e, n = 2 == e, c = 3 == e, l = 4 == e, p = 6 == e, f = 7 == e, h = 5 == e || p; return function (d, m, v, g) { for (var y, b, _ = a(d), w = o(_), x = r(m, v, 3), E = i(w.length), S = 0, C = g || s, A = t ? C(d, E) : n || f ? C(d, 0) : void 0; E > S; S++)if ((h || S in w) && (b = x(y = w[S], S, _), e)) if (t) A[S] = b; else if (b) switch (e) { case 3: return !0; case 5: return y; case 6: return S; case 2: u.call(A, y) } else switch (e) { case 4: return !1; case 7: u.call(A, y) }return p ? -1 : c || l ? l : A } }; e.exports = { forEach: c(0), map: c(1), filter: c(2), some: c(3), every: c(4), find: c(5), findIndex: c(6), filterOut: c(7) } }, function (e, t, n) { "use strict"; e.exports = { current: null } }, function (e, t) { e.exports = function (e) { return null != e && "object" == typeof e } }, function (e, t) { var n, r, o = e.exports = {}; function a() { throw new Error("setTimeout has not been defined") } function i() { throw new Error("clearTimeout has not been defined") } function s(e) { if (n === setTimeout) return setTimeout(e, 0); if ((n === a || !n) && setTimeout) return n = setTimeout, setTimeout(e, 0); try { return n(e, 0) } catch (t) { try { return n.call(null, e, 0) } catch (t) { return n.call(this, e, 0) } } } !function () { try { n = "function" == typeof setTimeout ? setTimeout : a } catch (e) { n = a } try { r = "function" == typeof clearTimeout ? clearTimeout : i } catch (e) { r = i } }(); var u, c = [], l = !1, p = -1; function f() { l && u && (l = !1, u.length ? c = u.concat(c) : p = -1, c.length && h()) } function h() { if (!l) { var e = s(f); l = !0; for (var t = c.length; t;) { for (u = c, c = []; ++p < t;)u && u[p].run(); p = -1, t = c.length } u = null, l = !1, function (e) { if (r === clearTimeout) return clearTimeout(e); if ((r === i || !r) && clearTimeout) return r = clearTimeout, clearTimeout(e); try { r(e) } catch (t) { try { return r.call(null, e) } catch (t) { return r.call(this, e) } } }(e) } } function d(e, t) { this.fun = e, this.array = t } function m() { } o.nextTick = function (e) { var t = new Array(arguments.length - 1); if (arguments.length > 1) for (var n = 1; n < arguments.length; n++)t[n - 1] = arguments[n]; c.push(new d(e, t)), 1 !== c.length || l || s(h) }, d.prototype.run = function () { this.fun.apply(null, this.array) }, o.title = "browser", o.browser = !0, o.env = {}, o.argv = [], o.version = "", o.versions = {}, o.on = m, o.addListener = m, o.once = m, o.off = m, o.removeListener = m, o.removeAllListeners = m, o.emit = m, o.prependListener = m, o.prependOnceListener = m, o.listeners = function (e) { return [] }, o.binding = function (e) { throw new Error("process.binding is not supported") }, o.cwd = function () { return "/" }, o.chdir = function (e) { throw new Error("process.chdir is not supported") }, o.umask = function () { return 0 } }, function (e, t, n) { "use strict"; var r = n(40), o = n(124), a = n(83), i = (n(34), ["dispatchConfig", "_targetInst", "nativeEvent", "isDefaultPrevented", "isPropagationStopped", "_dispatchListeners", "_dispatchInstances"]), s = { type: null, target: null, currentTarget: a.thatReturnsNull, eventPhase: null, bubbles: null, cancelable: null, timeStamp: function (e) { return e.timeStamp || Date.now() }, defaultPrevented: null, isTrusted: null }; function u(e, t, n, r) { this.dispatchConfig = e, this._targetInst = t, this.nativeEvent = n; var o = this.constructor.Interface; for (var i in o) if (o.hasOwnProperty(i)) { 0; var s = o[i]; s ? this[i] = s(n) : "target" === i ? this.target = r : this[i] = n[i] } var u = null != n.defaultPrevented ? n.defaultPrevented : !1 === n.returnValue; return this.isDefaultPrevented = u ? a.thatReturnsTrue : a.thatReturnsFalse, this.isPropagationStopped = a.thatReturnsFalse, this } r(u.prototype, { preventDefault: function () { this.defaultPrevented = !0; var e = this.nativeEvent; e && (e.preventDefault ? e.preventDefault() : "unknown" != typeof e.returnValue && (e.returnValue = !1), this.isDefaultPrevented = a.thatReturnsTrue) }, stopPropagation: function () { var e = this.nativeEvent; e && (e.stopPropagation ? e.stopPropagation() : "unknown" != typeof e.cancelBubble && (e.cancelBubble = !0), this.isPropagationStopped = a.thatReturnsTrue) }, persist: function () { this.isPersistent = a.thatReturnsTrue }, isPersistent: a.thatReturnsFalse, destructor: function () { var e = this.constructor.Interface; for (var t in e) this[t] = null; for (var n = 0; n < i.length; n++)this[i[n]] = null } }), u.Interface = s, u.augmentClass = function (e, t) { var n = this, a = function () { }; a.prototype = n.prototype; var i = new a; r(i, e.prototype), e.prototype = i, e.prototype.constructor = e, e.Interface = r({}, n.Interface, t), e.augmentClass = n.augmentClass, o.addPoolingTo(e, o.fourArgumentPooler) }, o.addPoolingTo(u, o.fourArgumentPooler), e.exports = u }, function (e, t, n) { var r = n(406); e.exports = function (e) { return null == e ? "" : r(e) } }, function (e, t, n) { "use strict"; n.r(t), n.d(t, "lastError", (function () { return M })), n.d(t, "url", (function () { return R })), n.d(t, "specStr", (function () { return D })), n.d(t, "specSource", (function () { return L })), n.d(t, "specJson", (function () { return B })), n.d(t, "specResolved", (function () { return F })), n.d(t, "specResolvedSubtree", (function () { return U })), n.d(t, "specJsonWithResolvedSubtrees", (function () { return z })), n.d(t, "spec", (function () { return V })), n.d(t, "isOAS3", (function () { return W })), n.d(t, "info", (function () { return H })), n.d(t, "externalDocs", (function () { return J })), n.d(t, "version", (function () { return $ })), n.d(t, "semver", (function () { return K })), n.d(t, "paths", (function () { return Y })), n.d(t, "operations", (function () { return G })), n.d(t, "consumes", (function () { return Z })), n.d(t, "produces", (function () { return X })), n.d(t, "security", (function () { return Q })), n.d(t, "securityDefinitions", (function () { return ee })), n.d(t, "findDefinition", (function () { return te })), n.d(t, "definitions", (function () { return ne })), n.d(t, "basePath", (function () { return re })), n.d(t, "host", (function () { return oe })), n.d(t, "schemes", (function () { return ae })), n.d(t, "operationsWithRootInherited", (function () { return ie })), n.d(t, "tags", (function () { return se })), n.d(t, "tagDetails", (function () { return ue })), n.d(t, "operationsWithTags", (function () { return ce })), n.d(t, "taggedOperations", (function () { return le })), n.d(t, "responses", (function () { return pe })), n.d(t, "requests", (function () { return fe })), n.d(t, "mutatedRequests", (function () { return he })), n.d(t, "responseFor", (function () { return de })), n.d(t, "requestFor", (function () { return me })), n.d(t, "mutatedRequestFor", (function () { return ve })), n.d(t, "allowTryItOutFor", (function () { return ge })), n.d(t, "parameterWithMetaByIdentity", (function () { return ye })), n.d(t, "parameterInclusionSettingFor", (function () { return be })), n.d(t, "parameterWithMeta", (function () { return _e })), n.d(t, "operationWithMeta", (function () { return we })), n.d(t, "getParameter", (function () { return xe })), n.d(t, "hasHost", (function () { return Ee })), n.d(t, "parameterValues", (function () { return Se })), n.d(t, "parametersIncludeIn", (function () { return Ce })), n.d(t, "parametersIncludeType", (function () { return Ae })), n.d(t, "contentTypeValues", (function () { return ke })), n.d(t, "currentProducesFor", (function () { return Oe })), n.d(t, "producesOptionsFor", (function () { return je })), n.d(t, "consumesOptionsFor", (function () { return Te })), n.d(t, "operationScheme", (function () { return Ie })), n.d(t, "canExecuteScheme", (function () { return Pe })), n.d(t, "validateBeforeExecute", (function () { return Ne })), n.d(t, "getOAS3RequiredRequestBodyContentType", (function () { return Me })), n.d(t, "isMediaTypeSchemaPropertiesEqual", (function () { return Re })); var r = n(13), o = n.n(r), a = n(16), i = n.n(a), s = n(37), u = n.n(s), c = n(212), l = n.n(c), p = n(21), f = n.n(p), h = n(67), d = n.n(h), m = n(12), v = n.n(m), g = n(4), y = n.n(g), b = n(14), _ = n.n(b), w = n(18), x = n.n(w), E = n(23), S = n.n(E), C = n(2), A = n.n(C), k = n(15), O = n.n(k), j = n(20), T = n(5), I = n(1), P = ["get", "put", "post", "delete", "options", "head", "patch", "trace"], N = function (e) { return e || Object(I.Map)() }, M = Object(j.a)(N, (function (e) { return e.get("lastError") })), R = Object(j.a)(N, (function (e) { return e.get("url") })), D = Object(j.a)(N, (function (e) { return e.get("spec") || "" })), L = Object(j.a)(N, (function (e) { return e.get("specSource") || "not-editor" })), B = Object(j.a)(N, (function (e) { return e.get("json", Object(I.Map)()) })), F = Object(j.a)(N, (function (e) { return e.get("resolved", Object(I.Map)()) })), U = function (e, t) { var n; return e.getIn(A()(n = ["resolvedSubtrees"]).call(n, O()(t)), void 0) }, q = function e(t, n) { return I.Map.isMap(t) && I.Map.isMap(n) ? n.get("$$ref") ? n : Object(I.OrderedMap)().mergeWith(e, t, n) : n }, z = Object(j.a)(N, (function (e) { return Object(I.OrderedMap)().mergeWith(q, e.get("json"), e.get("resolvedSubtrees")) })), V = function (e) { return B(e) }, W = Object(j.a)(V, (function () { return !1 })), H = Object(j.a)(V, (function (e) { return De(e && e.get("info")) })), J = Object(j.a)(V, (function (e) { return De(e && e.get("externalDocs")) })), $ = Object(j.a)(H, (function (e) { return e && e.get("version") })), K = Object(j.a)($, (function (e) { var t; return S()(t = /v?([0-9]*)\.([0-9]*)\.([0-9]*)/i.exec(e)).call(t, 1) })), Y = Object(j.a)(z, (function (e) { return e.get("paths") })), G = Object(j.a)(Y, (function (e) { if (!e || e.size < 1) return Object(I.List)(); var t = Object(I.List)(); return e && x()(e) ? (x()(e).call(e, (function (e, n) { if (!e || !x()(e)) return {}; x()(e).call(e, (function (e, r) { var o; _()(P).call(P, r) < 0 || (t = t.push(Object(I.fromJS)({ path: n, method: r, operation: e, id: A()(o = "".concat(r, "-")).call(o, n) }))) })) })), t) : Object(I.List)() })), Z = Object(j.a)(V, (function (e) { return Object(I.Set)(e.get("consumes")) })), X = Object(j.a)(V, (function (e) { return Object(I.Set)(e.get("produces")) })), Q = Object(j.a)(V, (function (e) { return e.get("security", Object(I.List)()) })), ee = Object(j.a)(V, (function (e) { return e.get("securityDefinitions") })), te = function (e, t) { var n = e.getIn(["resolvedSubtrees", "definitions", t], null), r = e.getIn(["json", "definitions", t], null); return n || r || null }, ne = Object(j.a)(V, (function (e) { var t = e.get("definitions"); return I.Map.isMap(t) ? t : Object(I.Map)() })), re = Object(j.a)(V, (function (e) { return e.get("basePath") })), oe = Object(j.a)(V, (function (e) { return e.get("host") })), ae = Object(j.a)(V, (function (e) { return e.get("schemes", Object(I.Map)()) })), ie = Object(j.a)(G, Z, X, (function (e, t, n) { return y()(e).call(e, (function (e) { return e.update("operation", (function (e) { if (e) { if (!I.Map.isMap(e)) return; return e.withMutations((function (e) { return e.get("consumes") || e.update("consumes", (function (e) { return Object(I.Set)(e).merge(t) })), e.get("produces") || e.update("produces", (function (e) { return Object(I.Set)(e).merge(n) })), e })) } return Object(I.Map)() })) })) })), se = Object(j.a)(V, (function (e) { var t = e.get("tags", Object(I.List)()); return I.List.isList(t) ? v()(t).call(t, (function (e) { return I.Map.isMap(e) })) : Object(I.List)() })), ue = function (e, t) { var n, r = se(e) || Object(I.List)(); return d()(n = v()(r).call(r, I.Map.isMap)).call(n, (function (e) { return e.get("name") === t }), Object(I.Map)()) }, ce = Object(j.a)(ie, se, (function (e, t) { return f()(e).call(e, (function (e, t) { var n = Object(I.Set)(t.getIn(["operation", "tags"])); return n.count() < 1 ? e.update("default", Object(I.List)(), (function (e) { return e.push(t) })) : f()(n).call(n, (function (e, n) { return e.update(n, Object(I.List)(), (function (e) { return e.push(t) })) }), e) }), f()(t).call(t, (function (e, t) { return e.set(t.get("name"), Object(I.List)()) }), Object(I.OrderedMap)())) })), le = function (e) { return function (t) { var n, r = (0, t.getConfigs)(), o = r.tagsSorter, a = r.operationsSorter; return y()(n = ce(e).sortBy((function (e, t) { return t }), (function (e, t) { var n = "function" == typeof o ? o : T.I.tagsSorter[o]; return n ? n(e, t) : null }))).call(n, (function (t, n) { var r = "function" == typeof a ? a : T.I.operationsSorter[a], o = r ? l()(t).call(t, r) : t; return Object(I.Map)({ tagDetails: ue(e, n), operations: o }) })) } }, pe = Object(j.a)(N, (function (e) { return e.get("responses", Object(I.Map)()) })), fe = Object(j.a)(N, (function (e) { return e.get("requests", Object(I.Map)()) })), he = Object(j.a)(N, (function (e) { return e.get("mutatedRequests", Object(I.Map)()) })), de = function (e, t, n) { return pe(e).getIn([t, n], null) }, me = function (e, t, n) { return fe(e).getIn([t, n], null) }, ve = function (e, t, n) { return he(e).getIn([t, n], null) }, ge = function () { return !0 }, ye = function (e, t, n) { var r, o, a = z(e).getIn(A()(r = ["paths"]).call(r, O()(t), ["parameters"]), Object(I.OrderedMap)()), i = e.getIn(A()(o = ["meta", "paths"]).call(o, O()(t), ["parameters"]), Object(I.OrderedMap)()), s = y()(a).call(a, (function (e) { var t, r, o, a = i.get(A()(t = "".concat(n.get("in"), ".")).call(t, n.get("name"))), s = i.get(A()(r = A()(o = "".concat(n.get("in"), ".")).call(o, n.get("name"), ".hash-")).call(r, n.hashCode())); return Object(I.OrderedMap)().merge(e, a, s) })); return d()(s).call(s, (function (e) { return e.get("in") === n.get("in") && e.get("name") === n.get("name") }), Object(I.OrderedMap)()) }, be = function (e, t, n, r) { var o, a, i = A()(o = "".concat(r, ".")).call(o, n); return e.getIn(A()(a = ["meta", "paths"]).call(a, O()(t), ["parameter_inclusions", i]), !1) }, _e = function (e, t, n, r) { var o, a = z(e).getIn(A()(o = ["paths"]).call(o, O()(t), ["parameters"]), Object(I.OrderedMap)()), i = d()(a).call(a, (function (e) { return e.get("in") === r && e.get("name") === n }), Object(I.OrderedMap)()); return ye(e, t, i) }, we = function (e, t, n) { var r, o = z(e).getIn(["paths", t, n], Object(I.OrderedMap)()), a = e.getIn(["meta", "paths", t, n], Object(I.OrderedMap)()), i = y()(r = o.get("parameters", Object(I.List)())).call(r, (function (r) { return ye(e, [t, n], r) })); return Object(I.OrderedMap)().merge(o, a).set("parameters", i) }; function xe(e, t, n, r) { var o; t = t || []; var a = e.getIn(A()(o = ["meta", "paths"]).call(o, O()(t), ["parameters"]), Object(I.fromJS)([])); return d()(a).call(a, (function (e) { return I.Map.isMap(e) && e.get("name") === n && e.get("in") === r })) || Object(I.Map)() } var Ee = Object(j.a)(V, (function (e) { var t = e.get("host"); return "string" == typeof t && t.length > 0 && "/" !== t[0] })); function Se(e, t, n) { var r; t = t || []; var o = we.apply(void 0, A()(r = [e]).call(r, O()(t))).get("parameters", Object(I.List)()); return f()(o).call(o, (function (e, t) { var r = n && "body" === t.get("in") ? t.get("value_xml") : t.get("value"); return e.set(Object(T.B)(t, { allowHashes: !1 }), r) }), Object(I.fromJS)({})) } function Ce(e) { var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : ""; if (I.List.isList(e)) return u()(e).call(e, (function (e) { return I.Map.isMap(e) && e.get("in") === t })) } function Ae(e) { var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : ""; if (I.List.isList(e)) return u()(e).call(e, (function (e) { return I.Map.isMap(e) && e.get("type") === t })) } function ke(e, t) { var n, r; t = t || []; var o = z(e).getIn(A()(n = ["paths"]).call(n, O()(t)), Object(I.fromJS)({})), a = e.getIn(A()(r = ["meta", "paths"]).call(r, O()(t)), Object(I.fromJS)({})), i = Oe(e, t), s = o.get("parameters") || new I.List, u = a.get("consumes_value") ? a.get("consumes_value") : Ae(s, "file") ? "multipart/form-data" : Ae(s, "formData") ? "application/x-www-form-urlencoded" : void 0; return Object(I.fromJS)({ requestContentType: u, responseContentType: i }) } function Oe(e, t) { var n, r; t = t || []; var o = z(e).getIn(A()(n = ["paths"]).call(n, O()(t)), null); if (null !== o) { var a = e.getIn(A()(r = ["meta", "paths"]).call(r, O()(t), ["produces_value"]), null), i = o.getIn(["produces", 0], null); return a || i || "application/json" } } function je(e, t) { var n; t = t || []; var r = z(e), o = r.getIn(A()(n = ["paths"]).call(n, O()(t)), null); if (null !== o) { var a = t, s = i()(a, 1)[0], u = o.get("produces", null), c = r.getIn(["paths", s, "produces"], null), l = r.getIn(["produces"], null); return u || c || l } } function Te(e, t) { var n; t = t || []; var r = z(e), o = r.getIn(A()(n = ["paths"]).call(n, O()(t)), null); if (null !== o) { var a = t, s = i()(a, 1)[0], u = o.get("consumes", null), c = r.getIn(["paths", s, "consumes"], null), l = r.getIn(["consumes"], null); return u || c || l } } var Ie = function (e, t, n) { var r = e.get("url").match(/^([a-z][a-z0-9+\-.]*):/), a = o()(r) ? r[1] : null; return e.getIn(["scheme", t, n]) || e.getIn(["scheme", "_defaultScheme"]) || a || "" }, Pe = function (e, t, n) { var r; return _()(r = ["http", "https"]).call(r, Ie(e, t, n)) > -1 }, Ne = function (e, t) { var n; t = t || []; var r = e.getIn(A()(n = ["meta", "paths"]).call(n, O()(t), ["parameters"]), Object(I.fromJS)([])), o = !0; return x()(r).call(r, (function (e) { var t = e.get("errors"); t && t.count() && (o = !1) })), o }, Me = function (e, t) { var n, r, o = { requestBody: !1, requestContentType: {} }, a = e.getIn(A()(n = ["resolvedSubtrees", "paths"]).call(n, O()(t), ["requestBody"]), Object(I.fromJS)([])); return a.size < 1 || (a.getIn(["required"]) && (o.requestBody = a.getIn(["required"])), x()(r = a.getIn(["content"]).entrySeq()).call(r, (function (e) { var t = e[0]; if (e[1].getIn(["schema", "required"])) { var n = e[1].getIn(["schema", "required"]).toJS(); o.requestContentType[t] = n } }))), o }, Re = function (e, t, n, r) { var o; if ((n || r) && n === r) return !0; var a = e.getIn(A()(o = ["resolvedSubtrees", "paths"]).call(o, O()(t), ["requestBody", "content"]), Object(I.fromJS)([])); if (a.size < 2 || !n || !r) return !1; var i = a.getIn([n, "schema", "properties"], Object(I.fromJS)([])), s = a.getIn([r, "schema", "properties"], Object(I.fromJS)([])); return !!i.equals(s) }; function De(e) { return I.Map.isMap(e) ? e : new I.Map } }, function (e, t, n) { "use strict"; (function (t) { var r = n(915), o = n(916), a = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//, i = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\S\s]*)/i, s = new RegExp("^[\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\xA0\\u1680\\u180E\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200A\\u202F\\u205F\\u3000\\u2028\\u2029\\uFEFF]+"); function u(e) { return (e || "").toString().replace(s, "") } var c = [["#", "hash"], ["?", "query"], function (e) { return e.replace("\\", "/") }, ["/", "pathname"], ["@", "auth", 1], [NaN, "host", void 0, 1, 1], [/:(\d+)$/, "port", void 0, 1], [NaN, "hostname", void 0, 1, 1]], l = { hash: 1, query: 1 }; function p(e) { var n, r = ("undefined" != typeof window ? window : void 0 !== t ? t : "undefined" != typeof self ? self : {}).location || {}, o = {}, i = typeof (e = e || r); if ("blob:" === e.protocol) o = new h(unescape(e.pathname), {}); else if ("string" === i) for (n in o = new h(e, {}), l) delete o[n]; else if ("object" === i) { for (n in e) n in l || (o[n] = e[n]); void 0 === o.slashes && (o.slashes = a.test(e.href)) } return o } function f(e) { e = u(e); var t = i.exec(e); return { protocol: t[1] ? t[1].toLowerCase() : "", slashes: !!t[2], rest: t[3] } } function h(e, t, n) { if (e = u(e), !(this instanceof h)) return new h(e, t, n); var a, i, s, l, d, m, v = c.slice(), g = typeof t, y = this, b = 0; for ("object" !== g && "string" !== g && (n = t, t = null), n && "function" != typeof n && (n = o.parse), t = p(t), a = !(i = f(e || "")).protocol && !i.slashes, y.slashes = i.slashes || a && t.slashes, y.protocol = i.protocol || t.protocol || "", e = i.rest, i.slashes || (v[3] = [/(.*)/, "pathname"]); b < v.length; b++)"function" != typeof (l = v[b]) ? (s = l[0], m = l[1], s != s ? y[m] = e : "string" == typeof s ? ~(d = e.indexOf(s)) && ("number" == typeof l[2] ? (y[m] = e.slice(0, d), e = e.slice(d + l[2])) : (y[m] = e.slice(d), e = e.slice(0, d))) : (d = s.exec(e)) && (y[m] = d[1], e = e.slice(0, d.index)), y[m] = y[m] || a && l[3] && t[m] || "", l[4] && (y[m] = y[m].toLowerCase())) : e = l(e); n && (y.query = n(y.query)), a && t.slashes && "/" !== y.pathname.charAt(0) && ("" !== y.pathname || "" !== t.pathname) && (y.pathname = function (e, t) { if ("" === e) return t; for (var n = (t || "/").split("/").slice(0, -1).concat(e.split("/")), r = n.length, o = n[r - 1], a = !1, i = 0; r--;)"." === n[r] ? n.splice(r, 1) : ".." === n[r] ? (n.splice(r, 1), i++) : i && (0 === r && (a = !0), n.splice(r, 1), i--); return a && n.unshift(""), "." !== o && ".." !== o || n.push(""), n.join("/") }(y.pathname, t.pathname)), r(y.port, y.protocol) || (y.host = y.hostname, y.port = ""), y.username = y.password = "", y.auth && (l = y.auth.split(":"), y.username = l[0] || "", y.password = l[1] || ""), y.origin = y.protocol && y.host && "file:" !== y.protocol ? y.protocol + "//" + y.host : "null", y.href = y.toString() } h.prototype = { set: function (e, t, n) { var a = this; switch (e) { case "query": "string" == typeof t && t.length && (t = (n || o.parse)(t)), a[e] = t; break; case "port": a[e] = t, r(t, a.protocol) ? t && (a.host = a.hostname + ":" + t) : (a.host = a.hostname, a[e] = ""); break; case "hostname": a[e] = t, a.port && (t += ":" + a.port), a.host = t; break; case "host": a[e] = t, /:\d+$/.test(t) ? (t = t.split(":"), a.port = t.pop(), a.hostname = t.join(":")) : (a.hostname = t, a.port = ""); break; case "protocol": a.protocol = t.toLowerCase(), a.slashes = !n; break; case "pathname": case "hash": if (t) { var i = "pathname" === e ? "/" : "#"; a[e] = t.charAt(0) !== i ? i + t : t } else a[e] = t; break; default: a[e] = t }for (var s = 0; s < c.length; s++) { var u = c[s]; u[4] && (a[u[1]] = a[u[1]].toLowerCase()) } return a.origin = a.protocol && a.host && "file:" !== a.protocol ? a.protocol + "//" + a.host : "null", a.href = a.toString(), a }, toString: function (e) { e && "function" == typeof e || (e = o.stringify); var t, n = this, r = n.protocol; r && ":" !== r.charAt(r.length - 1) && (r += ":"); var a = r + (n.slashes ? "//" : ""); return n.username && (a += n.username, n.password && (a += ":" + n.password), a += "@"), a += n.host + n.pathname, (t = "object" == typeof n.query ? e(n.query) : n.query) && (a += "?" !== t.charAt(0) ? "?" + t : t), n.hash && (a += n.hash), a } }, h.extractProtocol = f, h.location = p, h.trimLeft = u, h.qs = o, e.exports = h }).call(this, n(55)) }, function (e, t) { e.exports = !0 }, function (e, t, n) { "use strict"; var r = n(363).charAt, o = n(82), a = n(236), i = "String Iterator", s = o.set, u = o.getterFor(i); a(String, "String", (function (e) { s(this, { type: i, string: String(e), index: 0 }) }), (function () { var e, t = u(this), n = t.string, o = t.index; return o >= n.length ? { value: void 0, done: !0 } : (e = r(n, o), t.index += e.length, { value: e, done: !1 }) })) }, function (e, t, n) { var r = n(238), o = n(71).f, a = n(70), i = n(52), s = n(559), u = n(39)("toStringTag"); e.exports = function (e, t, n, c) { if (e) { var l = n ? e : e.prototype; i(l, u) || o(l, u, { configurable: !0, value: t }), c && !r && a(l, "toString", s) } } }, function (e, t, n) { "use strict"; e.exports = function (e) { if ("function" != typeof e) throw new TypeError(e + " is not a function"); return e } }, function (e, t, n) { e.exports = n(636) }, function (e, t, n) { e.exports = n(869) }, function (e, t, n) { "use strict"; n.r(t), n.d(t, "UPDATE_LAYOUT", (function () { return o })), n.d(t, "UPDATE_FILTER", (function () { return a })), n.d(t, "UPDATE_MODE", (function () { return i })), n.d(t, "SHOW", (function () { return s })), n.d(t, "updateLayout", (function () { return u })), n.d(t, "updateFilter", (function () { return c })), n.d(t, "show", (function () { return l })), n.d(t, "changeMode", (function () { return p })); var r = n(5), o = "layout_update_layout", a = "layout_update_filter", i = "layout_update_mode", s = "layout_show"; function u(e) { return { type: o, payload: e } } function c(e) { return { type: a, payload: e } } function l(e) { var t = !(arguments.length > 1 && void 0 !== arguments[1]) || arguments[1]; return e = Object(r.w)(e), { type: s, payload: { thing: e, shown: t } } } function p(e) { var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : ""; return e = Object(r.w)(e), { type: i, payload: { thing: e, mode: t } } } }, function (e, t, n) { var r = n(421), o = n(161), a = n(193), i = n(54), s = n(117), u = n(194), c = n(160), l = n(252), p = Object.prototype.hasOwnProperty; e.exports = function (e) { if (null == e) return !0; if (s(e) && (i(e) || "string" == typeof e || "function" == typeof e.splice || u(e) || l(e) || a(e))) return !e.length; var t = o(e); if ("[object Map]" == t || "[object Set]" == t) return !e.size; if (c(e)) return !r(e).length; for (var n in e) if (p.call(e, n)) return !1; return !0 } }, function (e, t, n) { var r = n(49), o = n(176), a = n(108), i = n(69), s = n(178), u = n(52), c = n(357), l = Object.getOwnPropertyDescriptor; t.f = r ? l : function (e, t) { if (e = i(e), t = s(t, !0), c) try { return l(e, t) } catch (e) { } if (u(e, t)) return a(!o.f.call(e, t), e[t]) } }, function (e, t) { e.exports = function (e, t) { return { enumerable: !(1 & e), configurable: !(2 & e), writable: !(4 & e), value: t } } }, function (e, t, n) { var r = n(80); e.exports = function (e, t, n) { if (r(e), void 0 === t) return e; switch (n) { case 0: return function () { return e.call(t) }; case 1: return function (n) { return e.call(t, n) }; case 2: return function (n, r) { return e.call(t, n, r) }; case 3: return function (n, r, o) { return e.call(t, n, r, o) } }return function () { return e.apply(t, arguments) } } }, function (e, t, n) { var r, o = n(53), a = n(237), i = n(231), s = n(152), u = n(369), c = n(228), l = n(180), p = l("IE_PROTO"), f = function () { }, h = function (e) { return "