diff --git a/agent/MTConnect.NET-Agent/MTConnect.NET-Agent.csproj b/agent/MTConnect.NET-Agent/MTConnect.NET-Agent.csproj index d467bd6c..9fa145e6 100644 --- a/agent/MTConnect.NET-Agent/MTConnect.NET-Agent.csproj +++ b/agent/MTConnect.NET-Agent/MTConnect.NET-Agent.csproj @@ -35,7 +35,7 @@ - + Always diff --git a/agent/MTConnect.NET-Agent/agent.config.default.yaml b/agent/MTConnect.NET-Agent/agent.config.default.yaml deleted file mode 100644 index 89649e10..00000000 --- a/agent/MTConnect.NET-Agent/agent.config.default.yaml +++ /dev/null @@ -1,112 +0,0 @@ -# - Device Configuration - -# The Path to look for the file(s) that represent the Device Information Models to load into the Agent. -# The path can either be a single file or a directory. -# The path can be absolute or relative to the executable's directory -devices: devices - -# - SHDR Adapter Configuration - -# The Agent is able to receive data through a TCP port using the SHDR protocol -adapters: -- hostname: localhost - port: 7878 - reconnectInterval: 1000 - -# Sets whether a Device Model can be sent from an SHDR Adapter -allowShdrDevice: false - - -# - Windows Service Configuration - -# Changes the service name when installing or removing the service. This allows multiple agents to run as services on the same machine. -serviceName: MTConnect-Agent-HTTP - -# Sets the Service Start Type. True = Auto | False = Manual -serviceAutoStart: true - - -# - HTTP Server Configuration - -# The Agent is able to respond to requests using the Http REST protocol described in the MTConnect Standard. -# The server supports response compression and serving of Static files (Files configuration) - -# The server Hostname to bind to. -# Change this to the server's IP Address or hostname -#server: localhost - -# The port number the agent binds to for requests. -port: 5000 - -# List of Encodings (ex. gzip, br, deflate) to pass to the Accept-Encoding HTTP Header -responseCompression: -- gzip -- br - -# Allow HTTP PUT or POST of data item values or assets. -allowPut: true - -# The maximum number of Threads to use for the Http Stream Requests -# If client receives Service Unavailable : 503 HTTP Status Code, this value can be increased as needed. CPU and Memory resource usage increases as this value increases -maxStreamingThreads: 5 - -# Sets the default response document indendation -indentOutput: true - -# Sets the default response document comments output. Comments contain descriptions from the MTConnect standard -# This is typically just used for debugging or for demo purposes -outputComments: false - -# Configuration for Static Files that can be served from the Http Server -files: -- path: schemas - location: schemas -#- path: styles -# location: styles -#- path: styles/favicon.ico -# location: favicon.ico - -# Configuration for XML Stylesheets for MTConnectStreamsResponse documents (Current and Samples requests) -#streamsStyle: -# location: styles/stylesheet-streams.xsl - - -# - Buffer Configuration - -# The Agent has an internal buffer that retains the information that the Agent can respond with according to the MTConnect Standard. -# There is also a Durable File backed buffer that can retain the information in the Agent between Agent restarts - -# The maximum number of Observations the agent can hold in its buffer -observationBufferSize: 150000 - -# The maximum number of assets the agent can hold in its buffer -assetBufferSize: 1000 - -# Sets whether the Agent buffers are durable and retain state after restart -durable: false - -# Sets whether the durable Agent buffers use Compression -useBufferCompression: false - - -# - Agent Configuration - - -# Sets the default MTConnect version to output response documents for. -defaultVersion: 2.1 - -# Overwrite timestamps with the agent time. This will correct clock drift but will not give as accurate relative time since it will not take into consideration network latencies. This can be overridden on a per adapter basis. -ignoreTimestamps: false - -# Sets the default for Converting Units when adding Observations -convertUnits: true - -# Sets the default for Ignoring the case of Observation values. Applicable values will be converted to uppercase -ignoreObservationCase: true - -# Sets the default input validation level when new Observations are added to the Agent. 0 = Ignore, 1 = Warning, 2 = Strict -inputValidationLevel: Ignore - -# Sets whether Configuration files are monitored. If enabled and a configuration file is changed, the Agent will restart -monitorConfigurationFiles: true - -# Sets the minimum time (in seconds) between Agent restarts when MonitorConfigurationFiles is enabled -configurationFileRestartInterval: 2 - -# Sets whether Agent Metrics are captured (ex. ObserationUpdateRate, AssetUpdateRate) -enableMetrics: true diff --git a/agent/MTConnect.NET-Agent/agent.config.yaml b/agent/MTConnect.NET-Agent/agent.config.yaml new file mode 100644 index 00000000..ee94e38e --- /dev/null +++ b/agent/MTConnect.NET-Agent/agent.config.yaml @@ -0,0 +1,71 @@ +# - Device Configuration - +devices: devices + +# - Processors - +processors: +- python: # - Add Python Processor + directory: processors + +# - Modules - +modules: + +- http-server: # - Add HTTP Server module + # hostname: localhost + port: 5000 + # allowPut: true + indentOutput: true + documentFormat: xml + accept: + text/xml: xml + application/json: json + # responseCompression: + # - gzip + # - br + # files: + # - path: schemas + # location: schemas + # - path: styles + # location: styles + # - path: styles/favicon.ico + # location: favicon.ico + +# - mqtt-relay: # - Add MQTT Relay module +# server: localhost +# port: 1883 +# currentInterval: 5000 +# sampleInterval: 500 + +# - shdr-adapter: # - Add SHDR Adapter module for Device = M12346 and Port = 7878 +# deviceKey: M12346 +# hostname: localhost +# port: 7878 +# heartbeat: 1000 +# reconnectInterval: 1000 +# connectionTimeout: 1000 + +- shdr-adapter: # - Add SHDR Adapter module for Device = OKUMA-Lathe and Port = 7879 + deviceKey: OKUMA-Lathe + hostname: localhost + port: 7878 + heartbeat: 1000 + reconnectInterval: 1000 + connectionTimeout: 1000 + +# - mqtt-adapter: # - Add MQTT Adapter module for Device = M12346 and Topic = cnc-01 +# deviceKey: M12346 +# server: localhost +# port: 1883 +# topic: cnc-01 + + +# The maximum number of Observations the agent can hold in its buffer +observationBufferSize: 150000 + +# The maximum number of assets the agent can hold in its buffer +assetBufferSize: 1000 + +# Sets whether the Agent buffers are durable and retain state after restart +durable: false + +# Sets the default MTConnect version to output response documents for. +defaultVersion: 2.2 \ No newline at end of file diff --git a/agent/MTConnect.NET-Applications-Agents/MTConnect.NET-Applications-Agents.csproj b/agent/MTConnect.NET-Applications-Agents/MTConnect.NET-Applications-Agents.csproj index 4aaa288c..906674f6 100644 --- a/agent/MTConnect.NET-Applications-Agents/MTConnect.NET-Applications-Agents.csproj +++ b/agent/MTConnect.NET-Applications-Agents/MTConnect.NET-Applications-Agents.csproj @@ -64,18 +64,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + @@ -83,16 +83,22 @@ - - Always - True - \ - - - Always - True - \ - + + Always + true + true + True + contentFiles\any\any;content + true + + + Always + true + true + True + contentFiles\any\any;content + true + diff --git a/agent/MTConnect.NET-Applications-Agents/MTConnectAgentApplication.cs b/agent/MTConnect.NET-Applications-Agents/MTConnectAgentApplication.cs index 26cc40ee..17bb37b7 100644 --- a/agent/MTConnect.NET-Applications-Agents/MTConnectAgentApplication.cs +++ b/agent/MTConnect.NET-Applications-Agents/MTConnectAgentApplication.cs @@ -21,10 +21,10 @@ namespace MTConnect.Applications { - /// - /// An MTConnect Agent Application base class supporting Command line arguments, Device management, Buffer management, Logging, Windows Service, and Configuration File management - /// - public class MTConnectAgentApplication : IMTConnectAgentApplication + /// + /// An MTConnect Agent Application base class supporting Command line arguments, Device management, Buffer management, Logging, Windows Service, and Configuration File management + /// + public class MTConnectAgentApplication : IMTConnectAgentApplication { private const string DefaultServiceName = "MTConnect.NET-Agent"; private const string DefaultServiceDisplayName = "MTConnect.NET Agent"; @@ -47,6 +47,7 @@ public class MTConnectAgentApplication : IMTConnectAgentApplication private MTConnectAssetFileBuffer _assetBuffer; private MTConnectAgentModules _modules; private MTConnectAgentProcessors _processors; + private IAgentApplicationConfiguration _initialAgentConfiguration; protected IAgentConfigurationFileWatcher _agentConfigurationWatcher; private System.Timers.Timer _metricsTimer; private bool _started = false; @@ -65,11 +66,12 @@ public class MTConnectAgentApplication : IMTConnectAgentApplication public event EventHandler OnRestart; - public MTConnectAgentApplication() + public MTConnectAgentApplication(IAgentApplicationConfiguration agentConfiguration = null) { ServiceName = DefaultServiceName; ServiceDisplayName = DefaultServiceDisplayName; ServiceDescription = DefaultServiceDescription; + _initialAgentConfiguration = agentConfiguration; } @@ -103,6 +105,18 @@ public void Run(string[] args, bool isBlocking = false) OnCommandLineArgumentsRead(args); + // Copy Default NLog Configuration File + string logConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NLog.config"); + string defaultLogConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NLog.default.config"); + if (!File.Exists(logConfigPath) && File.Exists(defaultLogConfigPath)) + { + File.Copy(defaultLogConfigPath, logConfigPath); + + LogManager.Configuration = LogManager.Configuration.Reload(); + LogManager.ReconfigExistingLoggers(); + } + + // Convert Json Configuration File to YAML string jsonConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AgentConfiguration.JsonFilename); if (File.Exists(jsonConfigPath)) @@ -113,18 +127,21 @@ public void Run(string[] args, bool isBlocking = false) // Copy Default Configuration File string yamlConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AgentConfiguration.YamlFilename); - string defaultPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AgentConfiguration.DefaultYamlFilename); - if (!File.Exists(yamlConfigPath) && !File.Exists(jsonConfigPath) && File.Exists(defaultPath)) + string defaultConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AgentConfiguration.DefaultYamlFilename); + if (!File.Exists(yamlConfigPath) && !File.Exists(jsonConfigPath) && File.Exists(defaultConfigPath)) { - File.Copy(defaultPath, yamlConfigPath); + File.Copy(defaultConfigPath, yamlConfigPath); } // Read the Agent Configuation File - var configuration = OnConfigurationFileRead(configFile); + var configuration = _initialAgentConfiguration; + if (configuration == null) + { + configuration = OnConfigurationFileRead(configFile); + if (configuration != null) _applicationLogger.Info($"Configuration File Read Successfully from: {configuration.Path}"); + } if (configuration != null) { - _applicationLogger.Info($"Configuration File Read Successfully from: {configuration.Path}"); - // Set Service Name if (!string.IsNullOrEmpty(configuration.ServiceName)) serviceDisplayName = configuration.ServiceName; @@ -395,37 +412,41 @@ public void StartAgent(IAgentApplicationConfiguration configuration, bool verbos _modules.StartBeforeLoad(initializeDataItems); // Read Device Configuration Files + IEnumerable deviceConfigurations = null; var devicesPath = configuration.Devices; - //if (string.IsNullOrEmpty(devicesPath)) devicesPath = "devices"; - var devices = DeviceConfiguration.FromFiles(devicesPath, DocumentFormat.XML); - if (!devices.IsNullOrEmpty()) + if (!string.IsNullOrEmpty(devicesPath)) { - // Add Device(s) to Agent - foreach (var device in devices) + deviceConfigurations = DeviceConfiguration.FromFiles(devicesPath, DocumentFormat.XML); + if (!deviceConfigurations.IsNullOrEmpty()) { - _agentLogger.Info($"Device ({device.Name}) Read From File : {device.Path}"); + // Add Device(s) to Agent + foreach (var device in deviceConfigurations) + { + _agentLogger.Info($"Device ({device.Name}) Read From File : {device.Path}"); - _mtconnectAgent.AddDevice(device, initializeDataItems); - } + _mtconnectAgent.AddDevice(device, initializeDataItems); + } - if (configuration.MonitorConfigurationFiles) - { - // Set Device Configuration File Watcher - var paths = devices.Select(o => o.Path).Distinct(); - foreach (var path in paths) + if (configuration.MonitorConfigurationFiles) { - // Create a Device Configuration File Watcher - var deviceConfigurationWatcher = new DeviceConfigurationFileWatcher(path, configuration.ConfigurationFileRestartInterval * 1000); - deviceConfigurationWatcher.ConfigurationUpdated += DeviceConfigurationFileUpdated; - deviceConfigurationWatcher.ErrorReceived += DeviceConfigurationFileError; - _deviceConfigurationWatchers.Add(deviceConfigurationWatcher); + // Set Device Configuration File Watcher + var paths = deviceConfigurations.Select(o => o.Path).Distinct(); + foreach (var path in paths) + { + // Create a Device Configuration File Watcher + var deviceConfigurationWatcher = new DeviceConfigurationFileWatcher(path, configuration.ConfigurationFileRestartInterval * 1000); + deviceConfigurationWatcher.ConfigurationUpdated += DeviceConfigurationFileUpdated; + deviceConfigurationWatcher.ErrorReceived += DeviceConfigurationFileError; + _deviceConfigurationWatchers.Add(deviceConfigurationWatcher); + } } } + else + { + _agentLogger.Warn($"No Devices Found : Reading from : {configuration.Devices}"); + } } - else - { - _agentLogger.Warn($"No Devices Found : Reading from : {configuration.Devices}"); - } + // Initilialize Processors _processors = new MTConnectAgentProcessors(configuration); @@ -435,12 +456,6 @@ public void StartAgent(IAgentApplicationConfiguration configuration, bool verbos _mtconnectAgent.ProcessObservationFunction = _processors.Process; - - - //OnStartAgentBeforeLoad(devices, initializeDataItems); - //_modules.StartBeforeLoad(); - - // Initialize Agent Current Observations/Conditions // This updates the MTConnectAgent's cache used to determine duplicate observations if (_observationBuffer != null) @@ -450,7 +465,7 @@ public void StartAgent(IAgentApplicationConfiguration configuration, bool verbos } _modules.StartAfterLoad(initializeDataItems); - OnStartAgentAfterLoad(devices, initializeDataItems); + OnStartAgentAfterLoad(deviceConfigurations, initializeDataItems); // Save Indexes for Buffer if (configuration.Durable) diff --git a/agent/MTConnect.NET-Applications-Agents/NLog.config b/agent/MTConnect.NET-Applications-Agents/NLog.default.config similarity index 81% rename from agent/MTConnect.NET-Applications-Agents/NLog.config rename to agent/MTConnect.NET-Applications-Agents/NLog.default.config index 37080dc1..2cfe9cfa 100644 --- a/agent/MTConnect.NET-Applications-Agents/NLog.config +++ b/agent/MTConnect.NET-Applications-Agents/NLog.default.config @@ -36,23 +36,23 @@ - + - + - + - + - - + + \ No newline at end of file diff --git a/agent/MTConnect.NET-Applications-Agents/agent.config.default.yaml b/agent/MTConnect.NET-Applications-Agents/agent.config.default.yaml index deab9106..7077797b 100644 --- a/agent/MTConnect.NET-Applications-Agents/agent.config.default.yaml +++ b/agent/MTConnect.NET-Applications-Agents/agent.config.default.yaml @@ -7,6 +7,9 @@ modules: # allowPut: true indentOutput: true documentFormat: xml + accept: + text/xml: xml + application/json: json responseCompression: - gzip - br diff --git a/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/DeviceMappingConfiguration.cs b/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/DeviceMappingConfiguration.cs index 5933b95e..a8292fe3 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/DeviceMappingConfiguration.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/DeviceMappingConfiguration.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. namespace MTConnect diff --git a/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/ModuleConfiguration.cs b/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/HttpAdapterModuleConfiguration.cs similarity index 64% rename from agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/ModuleConfiguration.cs rename to agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/HttpAdapterModuleConfiguration.cs index 050e28da..b5152a08 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/ModuleConfiguration.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/HttpAdapterModuleConfiguration.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using System.Collections.Generic; namespace MTConnect.Configurations { - public class ModuleConfiguration : HttpClientConfiguration + public class HttpAdapterModuleConfiguration : HttpClientConfiguration { public Dictionary Devices { get; set; } } diff --git a/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/Module.cs b/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/Module.cs index be9e4c84..4bd57f58 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/Module.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/Module.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Agents; @@ -20,7 +20,7 @@ public class Module : MTConnectAgentModule public const string ConfigurationTypeId = "http-client"; private const string ModuleId = "HTTP Adapter"; - private readonly ModuleConfiguration _configuration; + private readonly HttpAdapterModuleConfiguration _configuration; private readonly IMTConnectAgentBroker _mtconnectAgent; private readonly List _clients = new List(); private readonly Dictionary _clientInformations = new Dictionary(); @@ -32,7 +32,7 @@ public Module(IMTConnectAgentBroker mtconnectAgent, object configuration) : base Id = ModuleId; _mtconnectAgent = mtconnectAgent; - _configuration = AgentApplicationConfiguration.GetConfiguration(configuration); + _configuration = AgentApplicationConfiguration.GetConfiguration(configuration); } diff --git a/agent/Modules/MTConnect.NET-AgentModule-HttpServer/ModuleConfiguration.cs b/agent/Modules/MTConnect.NET-AgentModule-HttpServer/HttpServerModuleConfiguration.cs similarity index 77% rename from agent/Modules/MTConnect.NET-AgentModule-HttpServer/ModuleConfiguration.cs rename to agent/Modules/MTConnect.NET-AgentModule-HttpServer/HttpServerModuleConfiguration.cs index 03366dc7..ad31b003 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-HttpServer/ModuleConfiguration.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-HttpServer/HttpServerModuleConfiguration.cs @@ -1,14 +1,11 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using System.Collections.Generic; namespace MTConnect.Configurations { - /// - /// Configuration for an MTConnect Http Agent Module - /// - public class ModuleConfiguration : HttpServerConfiguration + public class HttpServerModuleConfiguration : HttpServerConfiguration { public IEnumerable DevicesNamespaces { get; set; } diff --git a/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnectHttpAgentServer.cs b/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnectHttpAgentServer.cs index a06d9801..06b59cda 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnectHttpAgentServer.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnectHttpAgentServer.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using Ceen.Httpd; @@ -27,10 +27,10 @@ public class MTConnectHttpAgentServer : MTConnectHttpServer private static readonly Dictionary _commonSchemas = new Dictionary(); private static readonly object _lock = new object(); - private readonly ModuleConfiguration _moduleConfiguration; + private readonly HttpServerModuleConfiguration _moduleConfiguration; - public MTConnectHttpAgentServer(ModuleConfiguration configuration, IMTConnectAgentBroker mtconnectAgent) : base(configuration, mtconnectAgent) + public MTConnectHttpAgentServer(HttpServerModuleConfiguration configuration, IMTConnectAgentBroker mtconnectAgent) : base(configuration, mtconnectAgent) { _moduleConfiguration = configuration; } @@ -41,7 +41,7 @@ protected override void OnConfigureServer(ServerConfig serverConfig) base.OnConfigureServer(serverConfig); } - protected override byte[] OnProcessStatic(MTConnectStaticFileRequest request) + protected override Stream OnProcessStatic(MTConnectStaticFileRequest request) { if (request.LocalPath != null) { @@ -142,7 +142,7 @@ protected override List> OnCreateFormatOptions(MTCo #region "Stylesheets" - private static byte[] ReadDevicesStylesheet(string filePath, Version mtconnectVersion) + private static Stream ReadDevicesStylesheet(string filePath, Version mtconnectVersion) { if (filePath != null) { @@ -173,7 +173,7 @@ private static byte[] ReadDevicesStylesheet(string filePath, Version mtconnectVe } catch { } - return Encoding.UTF8.GetBytes(s); + return new MemoryStream(Encoding.UTF8.GetBytes(s)); } } catch { } @@ -182,7 +182,7 @@ private static byte[] ReadDevicesStylesheet(string filePath, Version mtconnectVe return null; } - private static byte[] ReadStreamsStylesheet(string filePath, Version mtconnectVersion) + private static Stream ReadStreamsStylesheet(string filePath, Version mtconnectVersion) { if (filePath != null) { @@ -199,7 +199,7 @@ private static byte[] ReadStreamsStylesheet(string filePath, Version mtconnectVe fileContents = Regex.Replace(fileContents, pattern, replace); } - return Encoding.UTF8.GetBytes(fileContents); + return new MemoryStream(Encoding.UTF8.GetBytes(fileContents)); } } catch { } diff --git a/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnectShdrHttpAgentServer.cs b/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnectShdrHttpAgentServer.cs index c4bd11c6..2c857712 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnectShdrHttpAgentServer.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnectShdrHttpAgentServer.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Agents; @@ -9,6 +9,7 @@ using MTConnect.Servers; using MTConnect.Shdr; using System; +using System.IO; using System.Linq; using System.Text; @@ -19,7 +20,7 @@ namespace MTConnect.Modules.Http /// public class MTConnectShdrHttpAgentServer : MTConnectHttpAgentServer { - public MTConnectShdrHttpAgentServer(ModuleConfiguration configuration, IMTConnectAgentBroker mtconnectAgent) : base(configuration, mtconnectAgent) { } + public MTConnectShdrHttpAgentServer(HttpServerModuleConfiguration configuration, IMTConnectAgentBroker mtconnectAgent) : base(configuration, mtconnectAgent) { } protected override bool OnObservationInput(MTConnectObservationInputArgs args) @@ -96,8 +97,8 @@ protected override bool OnAssetInput(MTConnectAssetInputArgs args) { if (!string.IsNullOrEmpty(args.DeviceKey) && !string.IsNullOrEmpty(args.AssetType)) { - //var asset = Assets.Xml.XmlAsset.FromXml(assetType, ); - var result = EntityFormatter.CreateAsset(args.DocumentFormat, args.AssetType, ReadRequestBody(args.RequestBody)); + var stream = new MemoryStream(args.RequestBody); + var result = EntityFormatter.CreateAsset(args.DocumentFormat, args.AssetType, ReadRequestBody(stream)); if (result.Success) { var asset = (Asset)result.Content; @@ -110,18 +111,41 @@ protected override bool OnAssetInput(MTConnectAssetInputArgs args) return false; } - private byte[] ReadRequestBody(byte[] bytes) + private Stream ReadRequestBody(Stream inputStream) { - if (bytes != null) + if (inputStream != null) { try { - return Encoding.Convert(Encoding.ASCII, Encoding.UTF8, bytes); + using (var memoryStream = new MemoryStream()) + { + // Probably a more efficient way to do this but this probably won't get called at a high frequency + + inputStream.CopyTo(memoryStream); + var inputBytes = memoryStream.ToArray(); + var outputBytes = Encoding.Convert(Encoding.ASCII, Encoding.UTF8, inputBytes); + return new MemoryStream(outputBytes); + } + } catch { } } return null; } + + //private byte[] ReadRequestBody(byte[] bytes) + //{ + // if (bytes != null) + // { + // try + // { + // return Encoding.Convert(Encoding.ASCII, Encoding.UTF8, bytes); + // } + // catch { } + // } + + // return null; + //} } } \ No newline at end of file diff --git a/agent/Modules/MTConnect.NET-AgentModule-HttpServer/Module.cs b/agent/Modules/MTConnect.NET-AgentModule-HttpServer/Module.cs index 8276af90..197ede0a 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-HttpServer/Module.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-HttpServer/Module.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using Ceen; @@ -19,7 +19,7 @@ public class Module : MTConnectAgentModule private const string HttpServerLogId = "HTTP-Server"; private const string ValidationLogId = "Agent-Validation"; - private readonly ModuleConfiguration _configuration; + private readonly HttpServerModuleConfiguration _configuration; private readonly IMTConnectAgentBroker _mtconnectAgent; private MTConnectHttpServer _httpServer; @@ -28,7 +28,7 @@ public Module(IMTConnectAgentBroker mtconnectAgent, object controllerConfigurati Id = ModuleId; _mtconnectAgent = mtconnectAgent; - _configuration = AgentApplicationConfiguration.GetConfiguration(controllerConfiguration); + _configuration = AgentApplicationConfiguration.GetConfiguration(controllerConfiguration); } diff --git a/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/Module.cs b/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/Module.cs index a95e4e7e..6545e176 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/Module.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/Module.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MQTTnet; @@ -29,7 +29,7 @@ public class Module : MTConnectAgentModule private const string AssetTopic = "assets"; private const string DeviceTopic = "device"; - private readonly ModuleConfiguration _configuration; + private readonly MqttAdapterModuleConfiguration _configuration; private readonly IMTConnectAgentBroker _mtconnectAgent; private readonly MqttFactory _mqttFactory; private readonly IMqttClient _mqttClient; @@ -45,7 +45,7 @@ public Module(IMTConnectAgentBroker mtconnectAgent, object configuration) : base _mqttClient = _mqttFactory.CreateMqttClient(); _mqttClient.ApplicationMessageReceivedAsync += MessageReceived; - _configuration = AgentApplicationConfiguration.GetConfiguration(configuration); + _configuration = AgentApplicationConfiguration.GetConfiguration(configuration); } diff --git a/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/ModuleConfiguration.cs b/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/MqttAdapterModuleConfiguration.cs similarity index 95% rename from agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/ModuleConfiguration.cs rename to agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/MqttAdapterModuleConfiguration.cs index 95ef3f76..61f633c3 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/ModuleConfiguration.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/MqttAdapterModuleConfiguration.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. namespace MTConnect.Configurations { - public class ModuleConfiguration + public class MqttAdapterModuleConfiguration { /// /// The MQTT broker hostname @@ -94,7 +94,7 @@ public class ModuleConfiguration public string DocumentFormat { get; set; } - public ModuleConfiguration() + public MqttAdapterModuleConfiguration() { Server = "localhost"; Port = 1883; diff --git a/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/Module.cs b/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/Module.cs index a4f22b2b..2b9cce0e 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/Module.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/Module.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MQTTnet; @@ -26,7 +26,7 @@ public class Module : MTConnectAgentModule public const string ConfigurationTypeId = "mqtt-broker"; private const string ModuleId = "MQTT Broker"; - private readonly ModuleConfiguration _configuration; + private readonly MqttBrokerModuleConfiguration _configuration; private readonly MTConnectMqttDocumentServer _server; private MqttServer _mqttServer; private CancellationTokenSource _stop; @@ -36,7 +36,7 @@ public Module(IMTConnectAgentBroker mtconnectAgent, object configuration) : base { Id = ModuleId; - _configuration = AgentApplicationConfiguration.GetConfiguration(configuration); + _configuration = AgentApplicationConfiguration.GetConfiguration(configuration); _server = new MTConnectMqttDocumentServer(mtconnectAgent, _configuration); _server.ProbeReceived += ProbeReceived; @@ -162,11 +162,18 @@ private async void ProbeReceived(IDevice device, IDevicesResponseDocument respon { var topic = $"{_configuration.TopicPrefix}/{MTConnectMqttDocumentServer.ProbeTopic}/{device.Uuid}"; - var message = new MqttApplicationMessage(); - message.Retain = true; - message.Topic = topic; - message.QualityOfServiceLevel = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce; - message.Payload = formatResult.Content; + //var message = new MqttApplicationMessage(); + //message.Retain = true; + //message.Topic = topic; + //message.QualityOfServiceLevel = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce; + //message.Payload = formatResult.Content; + + var messageBuilder = new MqttApplicationMessageBuilder(); + messageBuilder.WithRetainFlag(true); + messageBuilder.WithTopic(topic); + messageBuilder.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce); + messageBuilder.WithPayload(formatResult.Content); + var message = messageBuilder.Build(); var injectMessage = new InjectedMqttApplicationMessage(message); @@ -184,11 +191,18 @@ private async void CurrentReceived(IDevice device, IStreamsResponseOutputDocumen { var topic = $"{_configuration.TopicPrefix}/{MTConnectMqttDocumentServer.CurrentTopic}/{device.Uuid}"; - var message = new MqttApplicationMessage(); - message.Retain = true; - message.Topic = topic; - message.QualityOfServiceLevel = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce; - message.Payload = formatResult.Content; + //var message = new MqttApplicationMessage(); + //message.Retain = true; + //message.Topic = topic; + //message.QualityOfServiceLevel = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce; + //message.Payload = formatResult.Content; + + var messageBuilder = new MqttApplicationMessageBuilder(); + messageBuilder.WithRetainFlag(true); + messageBuilder.WithTopic(topic); + messageBuilder.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce); + messageBuilder.WithPayload(formatResult.Content); + var message = messageBuilder.Build(); var injectMessage = new InjectedMqttApplicationMessage(message); @@ -206,11 +220,18 @@ private async void SampleReceived(IDevice device, IStreamsResponseOutputDocument { var topic = $"{_configuration.TopicPrefix}/{MTConnectMqttDocumentServer.SampleTopic}/{device.Uuid}"; - var message = new MqttApplicationMessage(); - //message.Retain = true; - message.Topic = topic; - message.QualityOfServiceLevel = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce; - message.Payload = formatResult.Content; + //var message = new MqttApplicationMessage(); + ////message.Retain = true; + //message.Topic = topic; + //message.QualityOfServiceLevel = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce; + //message.Payload = formatResult.Content; + + var messageBuilder = new MqttApplicationMessageBuilder(); + messageBuilder.WithRetainFlag(true); + messageBuilder.WithTopic(topic); + messageBuilder.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce); + messageBuilder.WithPayload(formatResult.Content); + var message = messageBuilder.Build(); var injectMessage = new InjectedMqttApplicationMessage(message); @@ -233,11 +254,18 @@ private async void AssetReceived(IDevice device, IAssetsResponseDocument respons { var topic = $"{_configuration.TopicPrefix}/{MTConnectMqttDocumentServer.AssetTopic}/{device.Uuid}/{asset.AssetId}"; - var message = new MqttApplicationMessage(); - message.Retain = true; - message.Topic = topic; - message.QualityOfServiceLevel = (MQTTnet.Protocol.MqttQualityOfServiceLevel)_configuration.QoS; - message.Payload = formatResult.Content; + //var message = new MqttApplicationMessage(); + //message.Retain = true; + //message.Topic = topic; + //message.QualityOfServiceLevel = (MQTTnet.Protocol.MqttQualityOfServiceLevel)_configuration.QoS; + //message.Payload = formatResult.Content; + + var messageBuilder = new MqttApplicationMessageBuilder(); + messageBuilder.WithRetainFlag(true); + messageBuilder.WithTopic(topic); + messageBuilder.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce); + messageBuilder.WithPayload(formatResult.Content); + var message = messageBuilder.Build(); var injectMessage = new InjectedMqttApplicationMessage(message); diff --git a/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/ModuleConfiguration.cs b/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/MqttBrokerModuleConfiguration.cs similarity index 93% rename from agent/Modules/MTConnect.NET-AgentModule-MqttBroker/ModuleConfiguration.cs rename to agent/Modules/MTConnect.NET-AgentModule-MqttBroker/MqttBrokerModuleConfiguration.cs index 5a567f79..62e258d7 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/ModuleConfiguration.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/MqttBrokerModuleConfiguration.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. namespace MTConnect.Configurations { - public class ModuleConfiguration : IMTConnectMqttServerConfiguration + public class MqttBrokerModuleConfiguration : IMTConnectMqttServerConfiguration { /// /// The MQTT broker hostname to bind to @@ -86,7 +86,7 @@ public class ModuleConfiguration : IMTConnectMqttServerConfiguration public int SampleInterval { get; set; } - public ModuleConfiguration() + public MqttBrokerModuleConfiguration() { Port = 1883; InitialDelay = 500; diff --git a/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/Module.cs b/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/Module.cs index e467f3bd..cc31b5c4 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/Module.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/Module.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MQTTnet; @@ -24,7 +24,7 @@ public class Module : MTConnectAgentModule public const string ConfigurationTypeId = "mqtt-relay"; private const string ModuleId = "MQTT Relay"; - private readonly ModuleConfiguration _configuration; + private readonly MqttRelayModuleConfiguration _configuration; private readonly MTConnectMqttDocumentServer _server; private readonly MqttFactory _mqttFactory; private readonly IMqttClient _mqttClient; @@ -35,7 +35,7 @@ public Module(IMTConnectAgentBroker mtconnectAgent, object configuration) : base { Id = ModuleId; - _configuration = AgentApplicationConfiguration.GetConfiguration(configuration); + _configuration = AgentApplicationConfiguration.GetConfiguration(configuration); _server = new MTConnectMqttDocumentServer(mtconnectAgent, _configuration); _server.ProbeReceived += ProbeReceived; @@ -181,11 +181,18 @@ private async void ProbeReceived(IDevice device, IDevicesResponseDocument respon { var topic = $"{_configuration.TopicPrefix}/{MTConnectMqttDocumentServer.ProbeTopic}/{device.Uuid}"; - var message = new MqttApplicationMessage(); - message.Retain = true; - message.Topic = topic; - message.QualityOfServiceLevel = (MQTTnet.Protocol.MqttQualityOfServiceLevel)_configuration.QoS; - message.Payload = formatResult.Content; + //var message = new MqttApplicationMessage(); + //message.Retain = true; + //message.Topic = topic; + //message.QualityOfServiceLevel = (MQTTnet.Protocol.MqttQualityOfServiceLevel)_configuration.QoS; + //message.Payload = formatResult.Content; + + var messageBuilder = new MqttApplicationMessageBuilder(); + messageBuilder.WithRetainFlag(true); + messageBuilder.WithTopic(topic); + messageBuilder.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce); + messageBuilder.WithPayload(formatResult.Content); + var message = messageBuilder.Build(); try { @@ -219,11 +226,18 @@ private async void CurrentReceived(IDevice device, IStreamsResponseOutputDocumen { var topic = $"{_configuration.TopicPrefix}/{MTConnectMqttDocumentServer.CurrentTopic}/{device.Uuid}"; - var message = new MqttApplicationMessage(); - //message.Retain = true; - message.Topic = topic; - message.QualityOfServiceLevel = (MQTTnet.Protocol.MqttQualityOfServiceLevel)_configuration.QoS; - message.Payload = formatResult.Content; + //var message = new MqttApplicationMessage(); + ////message.Retain = true; + //message.Topic = topic; + //message.QualityOfServiceLevel = (MQTTnet.Protocol.MqttQualityOfServiceLevel)_configuration.QoS; + //message.Payload = formatResult.Content; + + var messageBuilder = new MqttApplicationMessageBuilder(); + //messageBuilder.WithRetainFlag(true); + messageBuilder.WithTopic(topic); + messageBuilder.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce); + messageBuilder.WithPayload(formatResult.Content); + var message = messageBuilder.Build(); try { @@ -257,11 +271,18 @@ private async void SampleReceived(IDevice device, IStreamsResponseOutputDocument { var topic = $"{_configuration.TopicPrefix}/{MTConnectMqttDocumentServer.SampleTopic}/{device.Uuid}"; - var message = new MqttApplicationMessage(); - //message.Retain = true; - message.Topic = topic; - message.QualityOfServiceLevel = (MQTTnet.Protocol.MqttQualityOfServiceLevel)_configuration.QoS; - message.Payload = formatResult.Content; + //var message = new MqttApplicationMessage(); + ////message.Retain = true; + //message.Topic = topic; + //message.QualityOfServiceLevel = (MQTTnet.Protocol.MqttQualityOfServiceLevel)_configuration.QoS; + //message.Payload = formatResult.Content; + + var messageBuilder = new MqttApplicationMessageBuilder(); + //messageBuilder.WithRetainFlag(true); + messageBuilder.WithTopic(topic); + messageBuilder.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce); + messageBuilder.WithPayload(formatResult.Content); + var message = messageBuilder.Build(); try { @@ -297,11 +318,18 @@ private async void AssetReceived(IDevice device, IAssetsResponseDocument respons { var topic = $"{_configuration.TopicPrefix}/{MTConnectMqttDocumentServer.AssetTopic}/{device.Uuid}/{asset.AssetId}"; - var message = new MqttApplicationMessage(); - message.Retain = true; - message.Topic = topic; - message.QualityOfServiceLevel = (MQTTnet.Protocol.MqttQualityOfServiceLevel)_configuration.QoS; - message.Payload = formatResult.Content; + //var message = new MqttApplicationMessage(); + //message.Retain = true; + //message.Topic = topic; + //message.QualityOfServiceLevel = (MQTTnet.Protocol.MqttQualityOfServiceLevel)_configuration.QoS; + //message.Payload = formatResult.Content; + + var messageBuilder = new MqttApplicationMessageBuilder(); + messageBuilder.WithRetainFlag(true); + messageBuilder.WithTopic(topic); + messageBuilder.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce); + messageBuilder.WithPayload(formatResult.Content); + var message = messageBuilder.Build(); try { diff --git a/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/ModuleConfiguration.cs b/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/MqttRelayModuleConfiguration.cs similarity index 94% rename from agent/Modules/MTConnect.NET-AgentModule-MqttRelay/ModuleConfiguration.cs rename to agent/Modules/MTConnect.NET-AgentModule-MqttRelay/MqttRelayModuleConfiguration.cs index f92475bb..882eec96 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/ModuleConfiguration.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/MqttRelayModuleConfiguration.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. namespace MTConnect.Configurations { - public class ModuleConfiguration : IMTConnectMqttServerConfiguration + public class MqttRelayModuleConfiguration : IMTConnectMqttServerConfiguration { /// /// The MQTT broker hostname @@ -105,7 +105,7 @@ public class ModuleConfiguration : IMTConnectMqttServerConfiguration public int SampleInterval { get; set; } - public ModuleConfiguration() + public MqttRelayModuleConfiguration() { Server = "localhost"; Port = 7878; diff --git a/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/Module.cs b/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/Module.cs index 5d93b7d3..5b1c025c 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/Module.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/Module.cs @@ -20,7 +20,7 @@ public class Module : MTConnectAgentModule public const string ConfigurationTypeId = "shdr-adapter"; private const string ModuleId = "SHDR Adapter"; - private readonly ModuleConfiguration _configuration; + private readonly ShdrAdapterModuleConfiguration _configuration; private readonly IMTConnectAgentBroker _mtconnectAgent; private readonly List _adapters = new List(); @@ -30,7 +30,7 @@ public Module(IMTConnectAgentBroker mtconnectAgent, object configuration) : base Id = ModuleId; _mtconnectAgent = mtconnectAgent; - _configuration = AgentApplicationConfiguration.GetConfiguration(configuration); + _configuration = AgentApplicationConfiguration.GetConfiguration(configuration); } diff --git a/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/ModuleConfiguration.cs b/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/ShdrAdapterModuleConfiguration.cs similarity index 65% rename from agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/ModuleConfiguration.cs rename to agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/ShdrAdapterModuleConfiguration.cs index 8122e5be..5b1fd627 100644 --- a/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/ModuleConfiguration.cs +++ b/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/ShdrAdapterModuleConfiguration.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. namespace MTConnect.Configurations { - public class ModuleConfiguration : ShdrAdapterClientConfiguration + public class ShdrAdapterModuleConfiguration : ShdrAdapterClientConfiguration { /// /// Gets or Sets whether a Device Model can be sent from an SHDR Adapter @@ -11,7 +11,7 @@ public class ModuleConfiguration : ShdrAdapterClientConfiguration public bool AllowShdrDevice { get; set; } - public ModuleConfiguration() + public ShdrAdapterModuleConfiguration() { AllowShdrDevice = false; } diff --git a/build/AssemblyInfo.cs b/build/AssemblyInfo.cs index e1e68fb9..23e2506e 100644 --- a/build/AssemblyInfo.cs +++ b/build/AssemblyInfo.cs @@ -1,6 +1,6 @@ using System.Reflection; -[assembly: AssemblyVersion("6.0.12")] -[assembly: AssemblyFileVersion("6.0.12")] +[assembly: AssemblyVersion("6.0.14")] +[assembly: AssemblyFileVersion("6.0.14")] [assembly: AssemblyCompany("TrakHound Inc.")] [assembly: AssemblyCopyright("Copyright (c) 2024 TrakHound Inc., All Rights Reserved.")] diff --git a/libraries/MTConnect.NET-Common/Agents/MTConnectAgentModules.cs b/libraries/MTConnect.NET-Common/Agents/MTConnectAgentModules.cs index 58e5be32..816fdfdd 100644 --- a/libraries/MTConnect.NET-Common/Agents/MTConnectAgentModules.cs +++ b/libraries/MTConnect.NET-Common/Agents/MTConnectAgentModules.cs @@ -66,19 +66,26 @@ public void Load() { if (_configuration.IsModuleConfigured(configurationTypeId)) { - try - { - // Create new Instance of the Controller and add to cached dictionary - var module = (IMTConnectAgentModule)Activator.CreateInstance(moduleType, new object[] { _mtconnectAgent, null }); - module.LogReceived += HandleModuleLogReceived; - - var moduleId = Guid.NewGuid().ToString(); - - if (ModuleLoaded != null) ModuleLoaded.Invoke(this, module); - - lock (_lock) _modules.Add(moduleId, module); - } - catch { } + var moduleCount = _configuration.GetModuleCount(configurationTypeId); + if (moduleCount > 0) + { + for (int i = 0; i < moduleCount; i++) + { + try + { + // Create new Instance of the Controller and add to cached dictionary + var module = (IMTConnectAgentModule)Activator.CreateInstance(moduleType, new object[] { _mtconnectAgent, null }); + module.LogReceived += HandleModuleLogReceived; + + var moduleId = Guid.NewGuid().ToString(); + + if (ModuleLoaded != null) ModuleLoaded.Invoke(this, module); + + lock (_lock) _modules.Add(moduleId, module); + } + catch { } + } + } } } } diff --git a/libraries/MTConnect.NET-Common/Agents/MTConnectInputAgentModule.cs b/libraries/MTConnect.NET-Common/Agents/MTConnectInputAgentModule.cs index a96454c0..7e512322 100644 --- a/libraries/MTConnect.NET-Common/Agents/MTConnectInputAgentModule.cs +++ b/libraries/MTConnect.NET-Common/Agents/MTConnectInputAgentModule.cs @@ -461,7 +461,7 @@ public void AddTimeSeriesObservation( public void AddObservation(IObservationInput observation) { - Agent.AddObservation(observation); + Agent.AddObservation(_device.Uuid, observation); } diff --git a/libraries/MTConnect.NET-Common/Configurations/AdapterApplicationConfiguration.cs b/libraries/MTConnect.NET-Common/Configurations/AdapterApplicationConfiguration.cs index 4b57549e..e10737d1 100644 --- a/libraries/MTConnect.NET-Common/Configurations/AdapterApplicationConfiguration.cs +++ b/libraries/MTConnect.NET-Common/Configurations/AdapterApplicationConfiguration.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using System; diff --git a/libraries/MTConnect.NET-Common/Configurations/AgentApplicationConfiguration.cs b/libraries/MTConnect.NET-Common/Configurations/AgentApplicationConfiguration.cs index 3f554039..24a940ef 100644 --- a/libraries/MTConnect.NET-Common/Configurations/AgentApplicationConfiguration.cs +++ b/libraries/MTConnect.NET-Common/Configurations/AgentApplicationConfiguration.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. -using MTConnect.Devices.Configurations; +using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json.Serialization; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; @@ -132,6 +133,31 @@ public IEnumerable GetModules(string key) return null; } + public int GetModuleCount(string key) + { + if (!string.IsNullOrEmpty(key) && !Modules.IsNullOrEmpty()) + { + var count = 0; + + foreach (var configurationObj in Modules) + { + try + { + var rootDictionary = (Dictionary)configurationObj; + if (rootDictionary.ContainsKey(key)) + { + count++; + } + } + catch { } + } + + return count; + } + + return 0; + } + public IEnumerable GetModules(string key) { if (!string.IsNullOrEmpty(key) && !Modules.IsNullOrEmpty()) @@ -168,6 +194,15 @@ public IEnumerable GetModules(string key) } } } + else + { + var constructor = typeof(TConfiguration).GetConstructor(new Type[] { }); + var configuration = constructor.Invoke(new object[] { }); + if (configuration != null) + { + configurations.Add((TConfiguration)configuration); + } + } } } catch { } @@ -179,7 +214,21 @@ public IEnumerable GetModules(string key) return null; } - public bool IsModuleConfigured(string key) + public void AddModule(string key, object moduleConfiguration) + { + if (!string.IsNullOrEmpty(key)) + { + var rootDictionary = new Dictionary(); + rootDictionary.Add(key, moduleConfiguration); + + var modules = Modules?.ToList(); + if (modules == null) modules = new List(); + modules.Add(rootDictionary); + Modules = modules; + } + } + + public bool IsModuleConfigured(string key) { if (!string.IsNullOrEmpty(key) && !Modules.IsNullOrEmpty()) { @@ -327,6 +376,19 @@ public static TConfiguration GetConfiguration(object controllerC } catch { } } + else + { + try + { + var constructor = typeof(TConfiguration).GetConstructor(new Type[] { }); + var configuration = constructor.Invoke(new object[] { }); + if (configuration != null) + { + return (TConfiguration)configuration; + } + } + catch { } + } return default; } diff --git a/libraries/MTConnect.NET-Common/Configurations/DeviceConfiguration.cs b/libraries/MTConnect.NET-Common/Configurations/DeviceConfiguration.cs index 7136eaa3..5dc3cb7e 100644 --- a/libraries/MTConnect.NET-Common/Configurations/DeviceConfiguration.cs +++ b/libraries/MTConnect.NET-Common/Configurations/DeviceConfiguration.cs @@ -74,29 +74,35 @@ public static IEnumerable FromFile(string filePath, string { if (File.Exists(rootPath)) { - var contents = File.ReadAllBytes(rootPath); - if (contents != null) + using (var contents = File.OpenRead(rootPath)) { - // Read ResponseDocument Format - var devicesDocument = Formatters.ResponseDocumentFormatter.CreateDevicesResponseDocument(documentFormatterId, contents).Content; - if (devicesDocument != null && devicesDocument.Devices != null && devicesDocument.Devices.Count() > 0) + if (contents != null) { - var devices = new List(); + if (contents.Position > 0) contents.Seek(0, SeekOrigin.Begin); - foreach (var device in devicesDocument.Devices) + // Read ResponseDocument Format + var devicesDocument = Formatters.ResponseDocumentFormatter.CreateDevicesResponseDocument(documentFormatterId, contents).Content; + if (devicesDocument != null && devicesDocument.Devices != null && devicesDocument.Devices.Count() > 0) { - devices.Add(new DeviceConfiguration(device, rootPath)); - } + var devices = new List(); - return devices; - } - else - { - // Read Single Entity Format - var device = Formatters.EntityFormatter.CreateDevice(documentFormatterId, contents).Content; - if (device != null) + foreach (var device in devicesDocument.Devices) + { + devices.Add(new DeviceConfiguration(device, rootPath)); + } + + return devices; + } + else { - return new List { new DeviceConfiguration(device, rootPath) }; + if (contents.Position > 0) contents.Seek(0, SeekOrigin.Begin); + + // Read Single Entity Format + var device = Formatters.EntityFormatter.CreateDevice(documentFormatterId, contents).Content; + if (device != null) + { + return new List { new DeviceConfiguration(device, rootPath) }; + } } } } @@ -132,29 +138,35 @@ public static async Task> FromFileAsync(string { if (File.Exists(rootPath)) { - var contents = await File.ReadAllBytesAsync(rootPath); - if (contents != null) + using (var contents = File.OpenRead(rootPath)) { - // Read ResponseDocument Format - var devicesDocument = Formatters.ResponseDocumentFormatter.CreateDevicesResponseDocument(documentFormatterId, contents).Content; - if (devicesDocument != null && devicesDocument.Devices != null && devicesDocument.Devices.Count() > 0) + if (contents != null) { - var devices = new List(); + if (contents.Position > 0) contents.Seek(0, SeekOrigin.Begin); - foreach (var device in devicesDocument.Devices) + // Read ResponseDocument Format + var devicesDocument = Formatters.ResponseDocumentFormatter.CreateDevicesResponseDocument(documentFormatterId, contents).Content; + if (devicesDocument != null && devicesDocument.Devices != null && devicesDocument.Devices.Count() > 0) { - devices.Add(new DeviceConfiguration(device, rootPath)); - } + var devices = new List(); - return devices; - } - else - { - // Read Single Entity Format - var device = Formatters.EntityFormatter.CreateDevice(documentFormatterId, contents).Content; - if (device != null) + foreach (var device in devicesDocument.Devices) + { + devices.Add(new DeviceConfiguration(device, rootPath)); + } + + return devices; + } + else { - return new List { new DeviceConfiguration(device, rootPath) }; + if (contents.Position > 0) contents.Seek(0, SeekOrigin.Begin); + + // Read Single Entity Format + var device = Formatters.EntityFormatter.CreateDevice(documentFormatterId, contents).Content; + if (device != null) + { + return new List { new DeviceConfiguration(device, rootPath) }; + } } } } diff --git a/libraries/MTConnect.NET-Common/Configurations/IAgentApplicationConfiguration.cs b/libraries/MTConnect.NET-Common/Configurations/IAgentApplicationConfiguration.cs index bcfe2721..5464be5e 100644 --- a/libraries/MTConnect.NET-Common/Configurations/IAgentApplicationConfiguration.cs +++ b/libraries/MTConnect.NET-Common/Configurations/IAgentApplicationConfiguration.cs @@ -59,6 +59,8 @@ public interface IAgentApplicationConfiguration : IAgentConfiguration IEnumerable GetModules(string key); + int GetModuleCount(string key); + IEnumerable GetModules(string key); bool IsModuleConfigured(string key); diff --git a/libraries/MTConnect.NET-Common/Formatters/EntityFormatter.cs b/libraries/MTConnect.NET-Common/Formatters/EntityFormatter.cs index 0be33427..b73a1dcc 100644 --- a/libraries/MTConnect.NET-Common/Formatters/EntityFormatter.cs +++ b/libraries/MTConnect.NET-Common/Formatters/EntityFormatter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -9,6 +9,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; namespace MTConnect.Formatters @@ -113,7 +114,7 @@ public static FormatWriteResult Format(string documentFormatterId, IAsset asset, } - public static FormatReadResult CreateDevice(string documentFormatterId, byte[] content) + public static FormatReadResult CreateDevice(string documentFormatterId, Stream content) { // Get the Formatter with the specified ID var formatter = GetFormatter(documentFormatterId); @@ -126,7 +127,7 @@ public static FormatReadResult CreateDevice(string documentFormatterId, return FormatReadResult.Error(null, $"Entity Formatter Not found for \"{documentFormatterId}\""); } - public static FormatReadResult CreateComponent(string documentFormatterId, byte[] content) + public static FormatReadResult CreateComponent(string documentFormatterId, Stream content) { // Get the Formatter with the specified ID var formatter = GetFormatter(documentFormatterId); @@ -139,7 +140,7 @@ public static FormatReadResult CreateComponent(string documentFormat return FormatReadResult.Error(null, $"Entity Formatter Not found for \"{documentFormatterId}\""); } - public static FormatReadResult CreateComposition(string documentFormatterId, byte[] content) + public static FormatReadResult CreateComposition(string documentFormatterId, Stream content) { // Get the Formatter with the specified ID var formatter = GetFormatter(documentFormatterId); @@ -152,7 +153,7 @@ public static FormatReadResult CreateComposition(string documentFo return FormatReadResult.Error(null, $"Entity Formatter Not found for \"{documentFormatterId}\""); } - public static FormatReadResult CreateDataItem(string documentFormatterId, byte[] content) + public static FormatReadResult CreateDataItem(string documentFormatterId, Stream content) { // Get the Formatter with the specified ID var formatter = GetFormatter(documentFormatterId); @@ -165,7 +166,7 @@ public static FormatReadResult CreateDataItem(string documentFormatte return FormatReadResult.Error(null, $"Entity Formatter Not found for \"{documentFormatterId}\""); } - public static FormatReadResult CreateAsset(string documentFormatterId, string assetType, byte[] content) + public static FormatReadResult CreateAsset(string documentFormatterId, string assetType, Stream content) { // Get the Formatter with the specified ID var formatter = GetFormatter(documentFormatterId); diff --git a/libraries/MTConnect.NET-Common/Formatters/FormatWriteResult.cs b/libraries/MTConnect.NET-Common/Formatters/FormatWriteResult.cs index 8ba6bfe6..5d6f886f 100644 --- a/libraries/MTConnect.NET-Common/Formatters/FormatWriteResult.cs +++ b/libraries/MTConnect.NET-Common/Formatters/FormatWriteResult.cs @@ -1,13 +1,14 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using System.Collections.Generic; +using System.IO; namespace MTConnect.Formatters { public struct FormatWriteResult { - public byte[] Content { get; set; } + public Stream Content { get; set; } public string ContentType { get; set; } @@ -22,7 +23,7 @@ public struct FormatWriteResult public double ResponseDuration { get; set; } - public FormatWriteResult(byte[] content, string contentType, bool success = true, IEnumerable messages = null, IEnumerable warnings = null, IEnumerable errors = null) + public FormatWriteResult(Stream content, string contentType, bool success = true, IEnumerable messages = null, IEnumerable warnings = null, IEnumerable errors = null) { Content = content; ContentType = contentType; @@ -34,7 +35,7 @@ public FormatWriteResult(byte[] content, string contentType, bool success = true } - public static FormatWriteResult Successful(byte[] content, string contentType, string message = null) + public static FormatWriteResult Successful(Stream content, string contentType, string message = null) { var messages = new List(); if (!string.IsNullOrEmpty(message)) messages = new List { message }; @@ -42,13 +43,13 @@ public static FormatWriteResult Successful(byte[] content, string contentType, s return new FormatWriteResult(content, contentType, true, messages); } - public static FormatWriteResult Successful(byte[] content, string contentType, IEnumerable messages) + public static FormatWriteResult Successful(Stream content, string contentType, IEnumerable messages) { return new FormatWriteResult(content, contentType, true, messages); } - public static FormatWriteResult Warning(byte[] content, string contentType, string warning = null) + public static FormatWriteResult Warning(Stream content, string contentType, string warning = null) { var warnings = new List(); if (!string.IsNullOrEmpty(warning)) warnings = new List { warning }; @@ -56,13 +57,13 @@ public static FormatWriteResult Warning(byte[] content, string contentType, stri return new FormatWriteResult(content, contentType, true, null, warnings); } - public static FormatWriteResult Warning(byte[] content, string contentType, IEnumerable warnings) + public static FormatWriteResult Warning(Stream content, string contentType, IEnumerable warnings) { return new FormatWriteResult(content, contentType, true, null, warnings); } - public static FormatWriteResult Error(byte[] content, string contentType, string error = null) + public static FormatWriteResult Error(Stream content, string contentType, string error = null) { var errors = new List(); if (!string.IsNullOrEmpty(error)) errors = new List { error }; @@ -70,7 +71,7 @@ public static FormatWriteResult Error(byte[] content, string contentType, string return new FormatWriteResult(content, contentType, false, null, null, errors); } - public static FormatWriteResult Error(byte[] content, string contentType, IEnumerable errors) + public static FormatWriteResult Error(Stream content, string contentType, IEnumerable errors) { return new FormatWriteResult(content, contentType, false, null, null, errors); } diff --git a/libraries/MTConnect.NET-Common/Formatters/IEntityFormatter.cs b/libraries/MTConnect.NET-Common/Formatters/IEntityFormatter.cs index d47101a1..65a0b61a 100644 --- a/libraries/MTConnect.NET-Common/Formatters/IEntityFormatter.cs +++ b/libraries/MTConnect.NET-Common/Formatters/IEntityFormatter.cs @@ -1,10 +1,11 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; using MTConnect.Devices; using MTConnect.Observations; using System.Collections.Generic; +using System.IO; namespace MTConnect.Formatters { @@ -24,14 +25,14 @@ public interface IEntityFormatter FormatWriteResult Format(IAsset asset, IEnumerable> options = null); - FormatReadResult CreateDevice(byte[] content, IEnumerable> options = null); + FormatReadResult CreateDevice(Stream content, IEnumerable> options = null); - FormatReadResult CreateComponent(byte[] content, IEnumerable> options = null); + FormatReadResult CreateComponent(Stream content, IEnumerable> options = null); - FormatReadResult CreateComposition(byte[] content, IEnumerable> options = null); + FormatReadResult CreateComposition(Stream content, IEnumerable> options = null); - FormatReadResult CreateDataItem(byte[] content, IEnumerable> options = null); + FormatReadResult CreateDataItem(Stream content, IEnumerable> options = null); - FormatReadResult CreateAsset(string assetType, byte[] content, IEnumerable> options = null); + FormatReadResult CreateAsset(string assetType, Stream content, IEnumerable> options = null); } } \ No newline at end of file diff --git a/libraries/MTConnect.NET-Common/Formatters/IResponseDocumentFormatter.cs b/libraries/MTConnect.NET-Common/Formatters/IResponseDocumentFormatter.cs index 0e95ddce..8f82fa4f 100644 --- a/libraries/MTConnect.NET-Common/Formatters/IResponseDocumentFormatter.cs +++ b/libraries/MTConnect.NET-Common/Formatters/IResponseDocumentFormatter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -7,6 +7,7 @@ using MTConnect.Streams; using MTConnect.Streams.Output; using System.Collections.Generic; +using System.IO; namespace MTConnect.Formatters { @@ -26,12 +27,12 @@ public interface IResponseDocumentFormatter FormatWriteResult Format(IErrorResponseDocument document, IEnumerable> options = null); - FormatReadResult CreateDevicesResponseDocument(byte[] content, IEnumerable> options = null); + FormatReadResult CreateDevicesResponseDocument(Stream content, IEnumerable> options = null); - FormatReadResult CreateStreamsResponseDocument(byte[] content, IEnumerable> options = null); + FormatReadResult CreateStreamsResponseDocument(Stream content, IEnumerable> options = null); - FormatReadResult CreateAssetsResponseDocument(byte[] content, IEnumerable> options = null); + FormatReadResult CreateAssetsResponseDocument(Stream content, IEnumerable> options = null); - FormatReadResult CreateErrorResponseDocument(byte[] content, IEnumerable> options = null); + FormatReadResult CreateErrorResponseDocument(Stream content, IEnumerable> options = null); } } \ No newline at end of file diff --git a/libraries/MTConnect.NET-Common/Formatters/ResponseDocumentFormatter.cs b/libraries/MTConnect.NET-Common/Formatters/ResponseDocumentFormatter.cs index 9571227b..04863c8d 100644 --- a/libraries/MTConnect.NET-Common/Formatters/ResponseDocumentFormatter.cs +++ b/libraries/MTConnect.NET-Common/Formatters/ResponseDocumentFormatter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -10,6 +10,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.IO; namespace MTConnect.Formatters { @@ -113,7 +114,7 @@ public static FormatWriteResult Format(string documentFormatterId, IErrorRespons } - public static FormatReadResult CreateDevicesResponseDocument(string documentFormatterId, byte[] content) + public static FormatReadResult CreateDevicesResponseDocument(string documentFormatterId, Stream content) { // Get the Formatter with the specified ID var formatter = GetFormatter(documentFormatterId); @@ -126,7 +127,7 @@ public static FormatReadResult CreateDevicesResponseDo return FormatReadResult.Error(null, $"Document Formatter Not found for \"{documentFormatterId}\""); } - public static FormatReadResult CreateStreamsResponseDocument(string documentFormatterId, byte[] content) + public static FormatReadResult CreateStreamsResponseDocument(string documentFormatterId, Stream content) { // Get the Formatter with the specified ID var formatter = GetFormatter(documentFormatterId); @@ -139,7 +140,7 @@ public static FormatReadResult CreateStreamsResponseDo return FormatReadResult.Error(null, $"Document Formatter Not found for \"{documentFormatterId}\""); } - public static FormatReadResult CreateAssetsResponseDocument(string documentFormatterId, byte[] content) + public static FormatReadResult CreateAssetsResponseDocument(string documentFormatterId, Stream content) { // Get the Formatter with the specified ID var formatter = GetFormatter(documentFormatterId); @@ -152,7 +153,7 @@ public static FormatReadResult CreateAssetsResponseDocu return FormatReadResult.Error(null, $"Document Formatter Not found for \"{documentFormatterId}\""); } - public static FormatReadResult CreateErrorResponseDocument(string documentFormatterId, byte[] content) + public static FormatReadResult CreateErrorResponseDocument(string documentFormatterId, Stream content) { // Get the Formatter with the specified ID var formatter = GetFormatter(documentFormatterId); diff --git a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpAssetClient.cs b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpAssetClient.cs index 400020f9..c0f48da8 100644 --- a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpAssetClient.cs +++ b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpAssetClient.cs @@ -6,6 +6,7 @@ using MTConnect.Http; using System; using System.Collections.Generic; +using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -343,8 +344,8 @@ private IAssetsResponseDocument HandleResponse(HttpResponseMessage response) } else if (response.Content != null) { - var documentString = response.Content.ReadAsByteArrayAsync().Result; - return ReadDocument(response, documentString); + var documentStream = response.Content.ReadAsStreamAsync().Result; + return ReadDocument(response, documentStream); } } @@ -362,12 +363,12 @@ private async Task HandleResponseAsync(HttpResponseMess else if (response.Content != null) { #if NET5_0_OR_GREATER - var documentString = await response.Content.ReadAsByteArrayAsync(cancel); + var documentStream = await response.Content.ReadAsStreamAsync(cancel); #else - var documentString = await response.Content.ReadAsByteArrayAsync(); + var documentStream = await response.Content.ReadAsStreamAsync(); #endif - return ReadDocument(response, documentString); + return ReadDocument(response, documentStream); } } @@ -375,16 +376,17 @@ private async Task HandleResponseAsync(HttpResponseMess } - private IAssetsResponseDocument ReadDocument(HttpResponseMessage response, byte[] documentBytes) + private IAssetsResponseDocument ReadDocument(HttpResponseMessage response, Stream documentStream) { - if (documentBytes != null && documentBytes.Length > 0) + if (documentStream != null && documentStream.Length > 0) { // Handle Compression Encoding var contentEncoding = MTConnectHttpResponse.GetContentHeaderValue(response, HttpHeaders.ContentEncoding); - var bytes = MTConnectHttpResponse.HandleContentEncoding(contentEncoding, documentBytes); + var stream = MTConnectHttpResponse.HandleContentEncoding(contentEncoding, documentStream); + if (stream != null && stream.Position > 0) stream.Seek(0, SeekOrigin.Begin); // Process MTConnectAssets Document - var document = Formatters.ResponseDocumentFormatter.CreateAssetsResponseDocument(DocumentFormat.ToString(), bytes).Content; + var document = Formatters.ResponseDocumentFormatter.CreateAssetsResponseDocument(DocumentFormat.ToString(), stream).Content; if (document != null) { return document; @@ -392,7 +394,7 @@ private IAssetsResponseDocument ReadDocument(HttpResponseMessage response, byte[ else { // Process MTConnectError Document (if MTConnectAssets fails) - var errorDocument = Formatters.ResponseDocumentFormatter.CreateErrorResponseDocument(DocumentFormat.ToString(), bytes).Content; + var errorDocument = Formatters.ResponseDocumentFormatter.CreateErrorResponseDocument(DocumentFormat.ToString(), stream).Content; if (errorDocument != null) { MTConnectError?.Invoke(this, errorDocument); diff --git a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClient.cs b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClient.cs index 180f22d0..5c1ba37a 100644 --- a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClient.cs +++ b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClient.cs @@ -21,6 +21,8 @@ namespace MTConnect.Clients public class MTConnectHttpClient : IMTConnectClient, IMTConnectEntityClient { private readonly Dictionary _devices = new Dictionary(); + private readonly Dictionary _cachedComponents = new Dictionary(); + private readonly Dictionary _cachedDataItems = new Dictionary(); private readonly object _lock = new object(); private CancellationTokenSource _stop; @@ -622,7 +624,6 @@ public async Task GetAssetAsync(string assetId, Cancell private async Task Worker() { var initialRequest = true; - //_lastInstanceId = 0; ClientStarted?.Invoke(this, new EventArgs()); @@ -652,6 +653,13 @@ private async Task Worker() } } + // Clear Cached DataItems and Components + lock (_lock) + { + _cachedComponents.Clear(); + _cachedDataItems.Clear(); + } + // Add to cached list if (!probe.Devices.IsNullOrEmpty()) { @@ -1106,13 +1114,36 @@ private IComponent GetCachedComponent(string deviceUuid, string componentId) { if (!string.IsNullOrEmpty(deviceUuid) && !string.IsNullOrEmpty(componentId)) { + var key = $"{deviceUuid}:{componentId}"; + lock (_lock) { - _devices.TryGetValue(deviceUuid, out var device); - if (device != null && !device.Components.IsNullOrEmpty()) + _cachedComponents.TryGetValue(key, out var component); + if (component == null) { - return device.Components.FirstOrDefault(o => o.Id == componentId); + _devices.TryGetValue(deviceUuid, out var device); + if (device != null) + { + if (device.Id == componentId) + { + _cachedComponents.Add(key, device); + } + else + { + var components = device.GetComponents(); + if (!components.IsNullOrEmpty()) + { + component = components.FirstOrDefault(o => o.Id == componentId); + if (component != null) + { + _cachedComponents.Add(key, component); + } + } + } + } } + + return component; } } @@ -1123,17 +1154,29 @@ private IDataItem GetCachedDataItem(string deviceUuid, string dataItemId) { if (!string.IsNullOrEmpty(deviceUuid) && !string.IsNullOrEmpty(dataItemId)) { + var key = $"{deviceUuid}:{dataItemId}"; + lock (_lock) { - _devices.TryGetValue(deviceUuid, out var device); - if (device != null) + _cachedDataItems.TryGetValue(key, out var dataItem); + if (dataItem == null) { - var dataItems = device.GetDataItems(); - if (!dataItems.IsNullOrEmpty()) + _devices.TryGetValue(deviceUuid, out var device); + if (device != null) { - return dataItems.FirstOrDefault(o => o.Id == dataItemId); + var dataItems = device.GetDataItems(); + if (!dataItems.IsNullOrEmpty()) + { + dataItem = dataItems.FirstOrDefault(o => o.Id == dataItemId); + if (dataItem != null) + { + _cachedDataItems.Add(key, dataItem); + } + } } } + + return dataItem; } } diff --git a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClientStream.cs b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClientStream.cs index 9e6766c4..bf77e365 100644 --- a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClientStream.cs +++ b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClientStream.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Errors; @@ -298,7 +298,7 @@ private static string GetHeaderValue(string s, string name) return null; } - private static byte[] ReadBody(Stream stream, int length) + private static Stream ReadBody(Stream stream, int length) { if (stream != null && length > 0) { @@ -307,7 +307,7 @@ private static byte[] ReadBody(Stream stream, int length) // Create a buffer to contain body of the response // based on the size of the content-length received in the Http Headers - var body = new byte[size]; + var body = new MemoryStream(size); // Read New Line characters // These always appear after the Http Header @@ -327,7 +327,7 @@ private static byte[] ReadBody(Stream stream, int length) if (j > size - i) j = size - i; // Add the chunk bytes to the body buffer - Array.Copy(chunk, 0, body, i, j); + body.Write(chunk, 0, j); // Increment the index of the body buffer based on the number of bytes read in this chunk i += j; @@ -339,15 +339,16 @@ private static byte[] ReadBody(Stream stream, int length) return null; } - protected virtual void ProcessResponseBody(byte[] responseBody, string contentEncoding = null) + protected virtual void ProcessResponseBody(Stream responseBody, string contentEncoding = null) { if (responseBody != null && responseBody.Length > 0) { // Handle Compression Encoding - var bytes = MTConnectHttpResponse.HandleContentEncoding(contentEncoding, responseBody); + var stream = MTConnectHttpResponse.HandleContentEncoding(contentEncoding, responseBody); + if (stream.Position > 0) stream.Seek(0, SeekOrigin.Begin); // Process MTConnectDevices Document - var document = Formatters.ResponseDocumentFormatter.CreateStreamsResponseDocument(_documentFormat, bytes).Content; + var document = Formatters.ResponseDocumentFormatter.CreateStreamsResponseDocument(_documentFormat, stream).Content; if (document != null) { DocumentReceived?.Invoke(this, document); @@ -355,11 +356,10 @@ protected virtual void ProcessResponseBody(byte[] responseBody, string contentEn else { // Process MTConnectError Document (if MTConnectStreams fails) - var errorDocument = Formatters.ResponseDocumentFormatter.CreateErrorResponseDocument(_documentFormat, bytes).Content; + var errorDocument = Formatters.ResponseDocumentFormatter.CreateErrorResponseDocument(_documentFormat, stream).Content; if (errorDocument != null) ErrorReceived?.Invoke(this, errorDocument); } } } - } } \ No newline at end of file diff --git a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpCurrentClient.cs b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpCurrentClient.cs index 2e784b64..73ffd24b 100644 --- a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpCurrentClient.cs +++ b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpCurrentClient.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Errors; @@ -6,6 +6,7 @@ using MTConnect.Streams; using System; using System.Collections.Generic; +using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -332,8 +333,8 @@ private IStreamsResponseDocument HandleResponse(HttpResponseMessage response) } else if (response.Content != null) { - var documentBytes = response.Content.ReadAsByteArrayAsync().Result; - return ReadDocument(response, documentBytes); + var documentStream = response.Content.ReadAsStreamAsync().Result; + return ReadDocument(response, documentStream); } } @@ -351,28 +352,29 @@ private async Task HandleResponseAsync(HttpResponseMes else if (response.Content != null) { #if NET5_0_OR_GREATER - var documentBytes = await response.Content.ReadAsByteArrayAsync(cancel); + var documentStream = await response.Content.ReadAsStreamAsync(cancel); #else - var documentBytes = await response.Content.ReadAsByteArrayAsync(); + var documentStream = await response.Content.ReadAsStreamAsync(); #endif - return ReadDocument(response, documentBytes); + return ReadDocument(response, documentStream); } } return null; } - private IStreamsResponseDocument ReadDocument(HttpResponseMessage response, byte[] documentBytes) + private IStreamsResponseDocument ReadDocument(HttpResponseMessage response, Stream documentStream) { - if (documentBytes != null && documentBytes.Length > 0) + if (documentStream != null && documentStream.Length > 0) { // Handle Compression Encoding var contentEncoding = MTConnectHttpResponse.GetContentHeaderValue(response, HttpHeaders.ContentEncoding); - var bytes = MTConnectHttpResponse.HandleContentEncoding(contentEncoding, documentBytes); + var stream = MTConnectHttpResponse.HandleContentEncoding(contentEncoding, documentStream); + if (stream != null && stream.Position > 0) stream.Seek(0, SeekOrigin.Begin); // Process MTConnectStreams Document - var document = Formatters.ResponseDocumentFormatter.CreateStreamsResponseDocument(DocumentFormat.ToString(), bytes).Content; + var document = Formatters.ResponseDocumentFormatter.CreateStreamsResponseDocument(DocumentFormat.ToString(), stream).Content; if (document != null) { return document; @@ -380,7 +382,7 @@ private IStreamsResponseDocument ReadDocument(HttpResponseMessage response, byte else { // Process MTConnectError Document (if MTConnectDevices fails) - var errorDocument = Formatters.ResponseDocumentFormatter.CreateErrorResponseDocument(DocumentFormat.ToString(), bytes).Content; + var errorDocument = Formatters.ResponseDocumentFormatter.CreateErrorResponseDocument(DocumentFormat.ToString(), stream).Content; if (errorDocument != null) { MTConnectError?.Invoke(this, errorDocument); diff --git a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpProbeClient.cs b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpProbeClient.cs index d5f31f82..69f306d5 100644 --- a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpProbeClient.cs +++ b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpProbeClient.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Devices; @@ -6,6 +6,7 @@ using MTConnect.Http; using System; using System.Collections.Generic; +using System.IO; using System.Net; using System.Net.Http; using System.Net.Sockets; @@ -324,7 +325,6 @@ public static Uri CreateUri(string hostname, int port, string device = null, str // Replace 'localhost' with '127.0.0.1' (This is due to a performance issue with .NET Core's System.Net.Http.HttpClient) if (url.Contains("localhost")) url = url.Replace("localhost", "127.0.0.1"); - //if (url.Contains(Environment.MachineName)) url = url.Replace(Environment.MachineName, "127.0.0.1"); // Check for http if (!url.StartsWith("http://") && !url.StartsWith("https://")) url = "http://" + url; @@ -366,8 +366,8 @@ private IDevicesResponseDocument HandleResponse(HttpResponseMessage response) } else if (response.Content != null) { - var documentBytes = response.Content.ReadAsByteArrayAsync().Result; - return ReadDocument(response, documentBytes); + var documentStream = response.Content.ReadAsStreamAsync().Result; + return ReadDocument(response, documentStream); } } @@ -385,28 +385,29 @@ private async Task HandleResponseAsync(HttpResponseMes else if (response.Content != null) { #if NET5_0_OR_GREATER - var documentBytes = await response.Content.ReadAsByteArrayAsync(cancel); + var documentStream = await response.Content.ReadAsStreamAsync(cancel); #else - var documentBytes = await response.Content.ReadAsByteArrayAsync(); + var documentStream = await response.Content.ReadAsStreamAsync(); #endif - return ReadDocument(response, documentBytes); + return ReadDocument(response, documentStream); } } return null; } - private IDevicesResponseDocument ReadDocument(HttpResponseMessage response, byte[] documentBytes) + private IDevicesResponseDocument ReadDocument(HttpResponseMessage response, Stream documentStream) { - if (documentBytes != null && documentBytes.Length > 0) + if (documentStream != null && documentStream.Length > 0) { // Handle Compression Encoding var contentEncoding = MTConnectHttpResponse.GetContentHeaderValue(response, HttpHeaders.ContentEncoding); - var bytes = MTConnectHttpResponse.HandleContentEncoding(contentEncoding, documentBytes); + var stream = MTConnectHttpResponse.HandleContentEncoding(contentEncoding, documentStream); + if (stream != null && stream.Position > 0) stream.Seek(0, SeekOrigin.Begin); // Process MTConnectDevices Document - var document = Formatters.ResponseDocumentFormatter.CreateDevicesResponseDocument(DocumentFormat.ToString(), bytes).Content; + var document = Formatters.ResponseDocumentFormatter.CreateDevicesResponseDocument(DocumentFormat.ToString(), stream).Content; if (document != null) { return document; @@ -414,7 +415,7 @@ private IDevicesResponseDocument ReadDocument(HttpResponseMessage response, byte else { // Process MTConnectError Document (if MTConnectDevices fails) - var errorDocument = Formatters.ResponseDocumentFormatter.CreateErrorResponseDocument(DocumentFormat.ToString(), bytes).Content; + var errorDocument = Formatters.ResponseDocumentFormatter.CreateErrorResponseDocument(DocumentFormat.ToString(), stream).Content; if (errorDocument != null) { MTConnectError?.Invoke(this, errorDocument); diff --git a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpResponse.cs b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpResponse.cs index 9f0d6819..8055d450 100644 --- a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpResponse.cs +++ b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpResponse.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using System.IO; @@ -24,51 +24,51 @@ public static string GetContentHeaderValue(HttpResponseMessage response, string return null; } - public static byte[] HandleContentEncoding(string contentEncoding, byte[] bytes) + public static Stream HandleContentEncoding(string contentEncoding, Stream inputStream) { - if (bytes != null && bytes.Length > 0) + if (inputStream != null && inputStream.Length > 0) { + MemoryStream outputStream; + if (inputStream.CanSeek && inputStream.Position > 0) inputStream.Seek(0, SeekOrigin.Begin); + try { switch (contentEncoding) { case Http.HttpContentEncodings.Gzip: - using (var inputStream = new MemoryStream(bytes)) - using (var outputStream = new MemoryStream()) using (var encodingStream = new GZipStream(inputStream, CompressionMode.Decompress, true)) { + outputStream = new MemoryStream(); encodingStream.CopyTo(outputStream); - return outputStream.ToArray(); + return outputStream; } #if NET5_0_OR_GREATER case Http.HttpContentEncodings.Brotli: - using (var inputStream = new MemoryStream(bytes)) - using (var outputStream = new MemoryStream()) using (var encodingStream = new BrotliStream(inputStream, CompressionMode.Decompress, true)) { + outputStream = new MemoryStream(); encodingStream.CopyTo(outputStream); - return outputStream.ToArray(); + return outputStream; } #endif case Http.HttpContentEncodings.Deflate: - using (var inputStream = new MemoryStream(bytes)) - using (var outputStream = new MemoryStream()) using (var encodingStream = new DeflateStream(inputStream, CompressionMode.Decompress, true)) { + outputStream = new MemoryStream(); encodingStream.CopyTo(outputStream); - return outputStream.ToArray(); + return outputStream; } } } catch { } } - return bytes; + return inputStream; } } } \ No newline at end of file diff --git a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpSampleClient.cs b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpSampleClient.cs index 8558ce0e..3cfce697 100644 --- a/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpSampleClient.cs +++ b/libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpSampleClient.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Errors; @@ -6,6 +6,7 @@ using MTConnect.Streams; using System; using System.Collections.Generic; +using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -363,8 +364,8 @@ private IStreamsResponseDocument HandleResponse(HttpResponseMessage response) } else if (response.Content != null) { - var xml = response.Content.ReadAsByteArrayAsync().Result; - return ReadDocument(response, xml); + var documentStream = response.Content.ReadAsStreamAsync().Result; + return ReadDocument(response, documentStream); } } @@ -382,28 +383,29 @@ private async Task HandleResponseAsync(HttpResponseMes else if (response.Content != null) { #if NET5_0_OR_GREATER - var documentString = await response.Content.ReadAsByteArrayAsync(cancel); + var documentStream = await response.Content.ReadAsStreamAsync(cancel); #else - var documentString = await response.Content.ReadAsByteArrayAsync(); + var documentStream = await response.Content.ReadAsStreamAsync(); #endif - return ReadDocument(response, documentString); + return ReadDocument(response, documentStream); } } return null; } - private IStreamsResponseDocument ReadDocument(HttpResponseMessage response, byte[] documentBytes) + private IStreamsResponseDocument ReadDocument(HttpResponseMessage response, Stream documentStream) { - if (documentBytes != null && documentBytes.Length > 0) + if (documentStream != null && documentStream.Length > 0) { // Handle Compression Encoding var contentEncoding = MTConnectHttpResponse.GetContentHeaderValue(response, HttpHeaders.ContentEncoding); - var bytes = MTConnectHttpResponse.HandleContentEncoding(contentEncoding, documentBytes); + var stream = MTConnectHttpResponse.HandleContentEncoding(contentEncoding, documentStream); + if (stream != null && stream.Position > 0) stream.Seek(0, SeekOrigin.Begin); // Process MTConnectDevices Document - var document = Formatters.ResponseDocumentFormatter.CreateStreamsResponseDocument(DocumentFormat.ToString(), bytes).Content; + var document = Formatters.ResponseDocumentFormatter.CreateStreamsResponseDocument(DocumentFormat.ToString(), stream).Content; if (document != null) { return document; @@ -411,7 +413,7 @@ private IStreamsResponseDocument ReadDocument(HttpResponseMessage response, byte else { // Process MTConnectError Document (if MTConnectDevices fails) - var errorDocument = Formatters.ResponseDocumentFormatter.CreateErrorResponseDocument(DocumentFormat.ToString(), bytes).Content; + var errorDocument = Formatters.ResponseDocumentFormatter.CreateErrorResponseDocument(DocumentFormat.ToString(), stream).Content; if (errorDocument != null) { MTConnectError?.Invoke(this, errorDocument); diff --git a/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpResponse.cs b/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpResponse.cs index d079922d..e73eb7db 100644 --- a/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpResponse.cs +++ b/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpResponse.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -7,6 +7,7 @@ using MTConnect.Formatters; using MTConnect.Streams.Output; using System.Collections.Generic; +using System.IO; namespace MTConnect.Servers.Http { @@ -14,7 +15,7 @@ public struct MTConnectHttpResponse { public bool Success { get; set; } - public byte[] Content { get; set; } + public Stream Content { get; set; } public string ContentType { get; set; } diff --git a/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpResponseHandler.cs b/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpResponseHandler.cs index a4fa45de..3529baef 100644 --- a/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpResponseHandler.cs +++ b/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpResponseHandler.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using Ceen; @@ -127,85 +127,86 @@ protected async Task WriteResponse(string content, IHttpResponse httpResponse, C try { httpResponse.ContentType = contentType; - httpResponse.StatusCode = (Ceen.HttpStatusCode)statusCode; + httpResponse.StatusCode = statusCode; - var bytes = Encoding.UTF8.GetBytes(content); - await WriteToStream(bytes, httpResponse, acceptEncodings); + var contentStream = new MemoryStream(Encoding.UTF8.GetBytes(content)); + await WriteToStream(contentStream, httpResponse, acceptEncodings); } catch { } } } - protected async Task WriteToStream(byte[] bytes, IHttpResponse httpResponse, IEnumerable acceptEncodings = null) + protected async Task WriteToStream(Stream inputStream, IHttpResponse httpResponse, IEnumerable acceptEncodings = null) { - if (httpResponse != null && !bytes.IsNullOrEmpty()) + if (httpResponse != null && inputStream != null && inputStream.Length > 0) { + if (inputStream.Position > 0) inputStream.Seek(0, SeekOrigin.Begin); + MemoryStream outputStream; + try { // Gzip if (!_serverConfiguration.ResponseCompression.IsNullOrEmpty() && - _serverConfiguration.ResponseCompression.Contains(HttpResponseCompression.Gzip) && + _serverConfiguration.ResponseCompression.Contains(HttpResponseCompression.Gzip) && !acceptEncodings.IsNullOrEmpty() && acceptEncodings.Contains("gzip")) { httpResponse.AddHeader("Content-Encoding", "gzip"); - using (var ms = new MemoryStream()) + outputStream = new MemoryStream(); + using (var zip = new GZipStream(outputStream, CompressionMode.Compress, true)) { - using (var zip = new GZipStream(ms, CompressionMode.Compress, true)) - { - zip.Write(bytes, 0, bytes.Length); - } - bytes = ms.ToArray(); + inputStream.CopyTo(zip); } + outputStream.Seek(0, SeekOrigin.Begin); + await httpResponse.WriteAllAsync(outputStream); } #if NET5_0_OR_GREATER else if (!_serverConfiguration.ResponseCompression.IsNullOrEmpty() && - _serverConfiguration.ResponseCompression.Contains(HttpResponseCompression.Br) && + _serverConfiguration.ResponseCompression.Contains(HttpResponseCompression.Br) && !acceptEncodings.IsNullOrEmpty() && acceptEncodings.Contains("br")) { httpResponse.AddHeader("Content-Encoding", "br"); - using (var ms = new MemoryStream()) + outputStream = new MemoryStream(); + using (var zip = new BrotliStream(inputStream, CompressionMode.Compress, true)) { - using (var zip = new BrotliStream(ms, CompressionMode.Compress, true)) - { - zip.Write(bytes, 0, bytes.Length); - } - bytes = ms.ToArray(); + inputStream.CopyTo(zip); } + outputStream.Seek(0, SeekOrigin.Begin); + await httpResponse.WriteAllAsync(outputStream); } #endif else if (!_serverConfiguration.ResponseCompression.IsNullOrEmpty() && - _serverConfiguration.ResponseCompression.Contains(HttpResponseCompression.Deflate) && + _serverConfiguration.ResponseCompression.Contains(HttpResponseCompression.Deflate) && !acceptEncodings.IsNullOrEmpty() && acceptEncodings.Contains("deflate")) { httpResponse.AddHeader("Content-Encoding", "deflate"); - using (var ms = new MemoryStream()) + outputStream = new MemoryStream(); + using (var zip = new DeflateStream(inputStream, CompressionMode.Compress, true)) { - using (var zip = new DeflateStream(ms, CompressionMode.Compress, true)) - { - zip.Write(bytes, 0, bytes.Length); - } - bytes = ms.ToArray(); + inputStream.CopyTo(zip); } + outputStream.Seek(0, SeekOrigin.Begin); + await httpResponse.WriteAllAsync(outputStream); } - - await httpResponse.WriteAllAsync(bytes); + else + { + await httpResponse.WriteAllAsync(inputStream); + } } catch { } } } - protected static async Task WriteToResponseStream(Stream responseStream, MTConnectHttpStreamArgs args) { if (responseStream != null) { - await responseStream.WriteAsync(args.Message, 0, args.Message.Length); + await args.Message.CopyToAsync(responseStream); } } @@ -250,7 +251,6 @@ protected IEnumerable ProcessAcceptEncodings(IEnumerable acceptE { var output = new List(); - // Gzip if (_serverConfiguration.ResponseCompression.Contains(HttpResponseCompression.Gzip) && !acceptEncodings.IsNullOrEmpty() && acceptEncodings.Contains(HttpContentEncodings.Gzip)) { diff --git a/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpServer.cs b/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpServer.cs index 875a056f..388ebdb6 100644 --- a/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpServer.cs +++ b/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpServer.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using Ceen; @@ -7,6 +7,7 @@ using MTConnect.Configurations; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -97,7 +98,7 @@ protected virtual void OnConfigureServer(ServerConfig serverConfig) { } /// /// Method run on a Static File request /// - protected virtual byte[] OnProcessStatic(MTConnectStaticFileRequest request) { return null; } + protected virtual Stream OnProcessStatic(MTConnectStaticFileRequest request) { return null; } /// /// Method run when creating the Format Options after an MTConnect REST response is processed and before it is returned diff --git a/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpServerStream.cs b/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpServerStream.cs index 546664cb..3711263e 100644 --- a/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpServerStream.cs +++ b/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpServerStream.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Agents; @@ -10,7 +10,6 @@ using System.IO; using System.IO.Compression; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -34,7 +33,6 @@ public class MTConnectHttpServerStream private readonly string _documentFormat; private readonly IEnumerable _acceptEncodings; private readonly IEnumerable> _formatOptions; - private readonly StringBuilder _multipartBuilder = new StringBuilder(); private CancellationTokenSource _stop; private bool _isConnected; @@ -164,8 +162,6 @@ private void Worker() long lastHeartbeatSent = 0; long now = UnixDateTime.Now; IStreamsResponseOutputDocument document = null; - byte[] chunk = null; - byte[] multipartChunk = null; while (!_stop.IsCancellationRequested) { @@ -194,10 +190,12 @@ private void Worker() if (document != null) { now = UnixDateTime.Now; - chunk = CreateChunk(document, _documentFormat, contentEncoding, _formatOptions); + + var chunkStream = CreateChunk(document, _documentFormat, contentEncoding, _formatOptions); // Create the HTTP Multipart Chunk (with boundary) - multipartChunk = CreateMultipartChunk(chunk, _boundary, contentType, contentEncoding); + var outputStream = CreateMultipartChunk(ref chunkStream, _boundary, contentType, contentEncoding); + if (outputStream.Position > 0) outputStream.Seek(0, SeekOrigin.Begin); if (document.Streams.IsNullOrEmpty() || document.ObservationCount < 1) { @@ -207,7 +205,7 @@ private void Worker() stpw.Stop(); // Raise heartbeat event and include the Multipart Chunk - HeartbeatReceived?.Invoke(this, new MTConnectHttpStreamArgs(_id, multipartChunk, stpw.ElapsedMilliseconds)); + HeartbeatReceived?.Invoke(this, new MTConnectHttpStreamArgs(_id, outputStream, stpw.ElapsedMilliseconds)); // Reset the heartbeat timestamp lastHeartbeatSent = now; @@ -218,7 +216,7 @@ private void Worker() stpw.Stop(); // Raise heartbeat event and include the Multipart Chunk - DocumentReceived?.Invoke(this, new MTConnectHttpStreamArgs(_id, multipartChunk, stpw.ElapsedMilliseconds)); + DocumentReceived?.Invoke(this, new MTConnectHttpStreamArgs(_id, outputStream, stpw.ElapsedMilliseconds)); // Reset the document timestamp lastDocumentSent = now; @@ -232,9 +230,6 @@ private void Worker() } document = null; - chunk = null; - multipartChunk = null; - // Pause the stream for the specified Interval Thread.Sleep(_interval); @@ -281,7 +276,7 @@ private string GetContentEncoding() return null; } - private static byte[] CreateChunk(IStreamsResponseOutputDocument document, string documentFormat, string contentEncoding, IEnumerable> formatOptions) + private static Stream CreateChunk(IStreamsResponseOutputDocument document, string documentFormat, string contentEncoding, IEnumerable> formatOptions) { if (document != null) { @@ -290,6 +285,7 @@ private static byte[] CreateChunk(IStreamsResponseOutputDocument document, strin if (result.Success) { var bytes = result.Content; + MemoryStream outputStream; try { @@ -297,41 +293,32 @@ private static byte[] CreateChunk(IStreamsResponseOutputDocument document, strin { case HttpContentEncodings.Gzip: - using (var ms = new MemoryStream()) + outputStream = new MemoryStream(); + using (var zip = new GZipStream(outputStream, CompressionMode.Compress, true)) { - using (var zip = new GZipStream(ms, CompressionMode.Compress, true)) - { - zip.Write(bytes, 0, bytes.Length); - } - bytes = ms.ToArray(); + zip.CopyTo(bytes); } - break; + return outputStream; #if NET5_0_OR_GREATER case HttpContentEncodings.Brotli: - using (var ms = new MemoryStream()) + outputStream = new MemoryStream(); + using (var zip = new BrotliStream(outputStream, CompressionMode.Compress, true)) { - using (var zip = new BrotliStream(ms, CompressionMode.Compress, true)) - { - zip.Write(bytes, 0, bytes.Length); - } - bytes = ms.ToArray(); + zip.CopyTo(bytes); } - break; + return outputStream; #endif case HttpContentEncodings.Deflate: - using (var ms = new MemoryStream()) + outputStream = new MemoryStream(); + using (var zip = new DeflateStream(outputStream, CompressionMode.Compress, true)) { - using (var zip = new DeflateStream(ms, CompressionMode.Compress, true)) - { - zip.Write(bytes, 0, bytes.Length); - } - bytes = ms.ToArray(); + zip.CopyTo(bytes); } - break; + return outputStream; } } catch { } @@ -343,57 +330,50 @@ private static byte[] CreateChunk(IStreamsResponseOutputDocument document, strin return null; } - private byte[] CreateMultipartChunk(byte[] body, string boundary, string contentType, string contentEncoding) + private static Stream CreateMultipartChunk(ref Stream body, string boundary, string contentType, string contentEncoding) { - _multipartBuilder.Clear(); - var newLine = Encoding.UTF8.GetString(new byte[] { 13, 10 }); - - // Add Boundary Line - _multipartBuilder.Append("--"); - _multipartBuilder.Append(boundary); - _multipartBuilder.Append(newLine); - - // Add Content Type Line - _multipartBuilder.Append("Content-type: "); - _multipartBuilder.Append(contentType); - _multipartBuilder.Append(newLine); - - if (!string.IsNullOrEmpty(contentEncoding)) + var outputStream = new MemoryStream(); + using (var writer = new StreamWriter(outputStream, leaveOpen: true)) { - // Add Content Encoding Line - _multipartBuilder.Append("Content-encoding: "); - _multipartBuilder.Append(contentEncoding); - _multipartBuilder.Append(newLine); - } + writer.NewLine = "\r\n"; - // Add Content Length Line - _multipartBuilder.Append("Content-length: "); - _multipartBuilder.Append(body.Length + 3); // Set Content-length to body.Length + 3 (newline) - _multipartBuilder.Append(newLine); - _multipartBuilder.Append(newLine); + // Add Boundary Line + writer.Write("--"); + writer.Write(boundary); + writer.WriteLine(); - // Get Bytes from StringBuilder - char[] a = new char[_multipartBuilder.Length]; - _multipartBuilder.CopyTo(0, a, 0, _multipartBuilder.Length); + // Add Content Type Line + writer.Write("Content-type: "); + writer.Write(contentType); + writer.WriteLine(); - // Convert StringBuilder result to UTF8 Bytes - var headerBytes = Encoding.UTF8.GetBytes(a); - - // Create Array to return that is the size of the Header + body + newline - byte[] bytes = new byte[headerBytes.Length + body.Length + 3]; - - // Add Header Bytes - Array.Copy(headerBytes, 0, bytes, 0, headerBytes.Length); - - // Add Body bytes - Array.Copy(body, 0, bytes, headerBytes.Length, body.Length); + // Add Content Encoding + if (!string.IsNullOrEmpty(contentEncoding)) + { + // Add Content Encoding Line + writer.Write("Content-encoding: "); + writer.Write(contentEncoding); + writer.WriteLine(); + } - // Add NewLine - bytes[bytes.Length - 3] = 10; - bytes[bytes.Length - 2] = 13; - bytes[bytes.Length - 1] = 10; + // Add Content Length Line + writer.Write("Content-length: "); + writer.Write(body.Length + 3); // Set Content-length to body.Length + 3 (newline) + writer.WriteLine(); + writer.WriteLine(); + writer.Flush(); + + // Copy Body to OutputStream + if (body.Position > 0) body.Seek(0, SeekOrigin.Begin); + body.CopyTo(outputStream); + + // Add NewLine + outputStream.WriteByte(10); + outputStream.WriteByte(13); + outputStream.WriteByte(10); + } - return bytes; + return outputStream; } } } \ No newline at end of file diff --git a/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpStreamArgs.cs b/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpStreamArgs.cs index 0e5b4d6e..12ceb978 100644 --- a/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpStreamArgs.cs +++ b/libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpStreamArgs.cs @@ -1,18 +1,20 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. +using System.IO; + namespace MTConnect.Servers.Http { public struct MTConnectHttpStreamArgs { public string StreamId { get; set; } - public byte[] Message { get; set; } + public Stream Message { get; set; } public double ResponseDuration { get; set; } - public MTConnectHttpStreamArgs(string streamId, byte[] message, double responseDuration) + public MTConnectHttpStreamArgs(string streamId, Stream message, double responseDuration) { StreamId = streamId; Message = message; diff --git a/libraries/MTConnect.NET-HTTP/Servers/MTConnectStaticResponseHandler.cs b/libraries/MTConnect.NET-HTTP/Servers/MTConnectStaticResponseHandler.cs index 4ff0ef4b..f560fa98 100644 --- a/libraries/MTConnect.NET-HTTP/Servers/MTConnectStaticResponseHandler.cs +++ b/libraries/MTConnect.NET-HTTP/Servers/MTConnectStaticResponseHandler.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using Ceen; @@ -15,7 +15,7 @@ namespace MTConnect.Servers { class MTConnectStaticResponseHandler : MTConnectHttpResponseHandler { - public Func ProcessFunction { get; set; } + public Func ProcessFunction { get; set; } public MTConnectStaticResponseHandler(IHttpServerConfiguration serverConfiguration, IMTConnectAgentBroker mtconnectAgent) : base(serverConfiguration, mtconnectAgent) { } @@ -34,7 +34,7 @@ protected async override Task OnRequestReceived(IHttpCont { var statusCode = 404; var contentType = "text/plain"; - byte[] fileContents = null; + Stream fileContents = null; var requestedPath = httpRequest.Path.Trim('/'); var localPath = httpRequest.Path.TrimEnd('/'); @@ -109,7 +109,7 @@ protected async override Task OnRequestReceived(IHttpCont // If nothing found in the overridden method, then read directly from filePath if (fileContents == null) { - fileContents = File.ReadAllBytes(filePath); + fileContents = File.OpenRead(filePath); } statusCode = fileContents != null ? 200 : 500; diff --git a/libraries/MTConnect.NET-JSON-cppagent/Devices/JsonDevice.cs b/libraries/MTConnect.NET-JSON-cppagent/Devices/JsonDevice.cs index 338eaf5a..8203912f 100644 --- a/libraries/MTConnect.NET-JSON-cppagent/Devices/JsonDevice.cs +++ b/libraries/MTConnect.NET-JSON-cppagent/Devices/JsonDevice.cs @@ -1,7 +1,8 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using System; +using System.IO; using System.Text.Json.Serialization; namespace MTConnect.Devices.Json @@ -104,6 +105,7 @@ public JsonDevice(IDevice device) public string ToString(bool indent = false) => JsonFunctions.Convert(this, indented: indent); public byte[] ToBytes(bool indent = false) => JsonFunctions.ConvertBytes(this, indented: indent); + public Stream ToStream(bool indent = false) => JsonFunctions.ConvertStream(this, indented: indent); public Device ToDevice() { diff --git a/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonHttpEntityFormatter.cs b/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonHttpEntityFormatter.cs index 82ec9b46..181febca 100644 --- a/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonHttpEntityFormatter.cs +++ b/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonHttpEntityFormatter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -15,6 +15,7 @@ using MTConnect.Observations; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Text.Json; @@ -34,7 +35,7 @@ public FormatWriteResult Format(IDevice device, IEnumerable(options, "indentOutput"); - var bytes = new JsonDevice(device).ToBytes(indentOuput); + var bytes = new JsonDevice(device).ToStream(indentOuput); if (bytes != null) { return FormatWriteResult.Successful(bytes, ContentType); @@ -88,17 +89,7 @@ public FormatWriteResult Format(IAsset asset, IEnumerable CreateDevice(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateDevice(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -128,7 +119,7 @@ public FormatReadResult CreateDevice(byte[] content, IEnumerable(); } - public FormatReadResult CreateComponent(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateComponent(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -147,7 +138,7 @@ public FormatReadResult CreateComponent(byte[] content, IEnumerable< return new FormatReadResult(); } - public FormatReadResult CreateComposition(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateComposition(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -166,7 +157,7 @@ public FormatReadResult CreateComposition(byte[] content, IEnumera return new FormatReadResult(); } - public FormatReadResult CreateDataItem(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateDataItem(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -185,7 +176,7 @@ public FormatReadResult CreateDataItem(byte[] content, IEnumerable(); } - public FormatReadResult CreateAsset(string assetType, byte[] content, IEnumerable> options = null) + public FormatReadResult CreateAsset(string assetType, Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -198,8 +189,15 @@ public FormatReadResult CreateAsset(string assetType, byte[] content, IE { try { + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Convert from UTF8 bytes - var json = Encoding.UTF8.GetString(content); + var json = Encoding.UTF8.GetString(bytes); if (!string.IsNullOrEmpty(json)) { switch (assetType) diff --git a/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonHttpResponseDocumentFormatter.cs b/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonHttpResponseDocumentFormatter.cs index c6241c6b..f6c621b3 100644 --- a/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonHttpResponseDocumentFormatter.cs +++ b/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonHttpResponseDocumentFormatter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -11,6 +11,7 @@ using MTConnect.Streams.Output; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.Json; @@ -29,10 +30,11 @@ public FormatWriteResult Format(IDevicesResponseDocument document, IEnumerable(options, "indentOutput"); var jsonOptions = indentOutput ? JsonFunctions.IndentOptions : JsonFunctions.DefaultOptions; - var json = JsonSerializer.SerializeToUtf8Bytes(new JsonDevicesResponseDocument(document), jsonOptions); - if (!json.IsNullOrEmpty()) + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, new JsonDevicesResponseDocument(document), jsonOptions); + if (outputStream != null && outputStream.Length > 0) { - return FormatWriteResult.Successful(json, ContentType); + return FormatWriteResult.Successful(outputStream, ContentType); } return FormatWriteResult.Error(); @@ -44,10 +46,11 @@ public FormatWriteResult Format(ref IStreamsResponseOutputDocument document, IEn var indentOutput = GetFormatterOption(options, "indentOutput"); var jsonOptions = indentOutput ? JsonFunctions.IndentOptions : JsonFunctions.DefaultOptions; - var json = JsonSerializer.SerializeToUtf8Bytes(new JsonStreamsResponseDocument(document), jsonOptions); - if (!json.IsNullOrEmpty()) + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, new JsonStreamsResponseDocument(document), jsonOptions); + if (outputStream != null && outputStream.Length > 0) { - return FormatWriteResult.Successful(json, ContentType); + return FormatWriteResult.Successful(outputStream, ContentType); } return FormatWriteResult.Error(); @@ -59,10 +62,11 @@ public virtual FormatWriteResult Format(IAssetsResponseDocument document, IEnume var indentOutput = GetFormatterOption(options, "indentOutput"); var jsonOptions = indentOutput ? JsonFunctions.IndentOptions : JsonFunctions.DefaultOptions; - var json = JsonSerializer.SerializeToUtf8Bytes(new JsonAssetsResponseDocument(document), jsonOptions); - if (!json.IsNullOrEmpty()) + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, new JsonAssetsResponseDocument(document), jsonOptions); + if (outputStream != null && outputStream.Length > 0) { - return FormatWriteResult.Successful(json, ContentType); + return FormatWriteResult.Successful(outputStream, ContentType); } return FormatWriteResult.Error(); @@ -74,28 +78,27 @@ public FormatWriteResult Format(IErrorResponseDocument document, IEnumerable(options, "indentOutput"); var jsonOptions = indentOutput ? JsonFunctions.IndentOptions : JsonFunctions.DefaultOptions; - var json = JsonSerializer.SerializeToUtf8Bytes(document, jsonOptions); - if (!json.IsNullOrEmpty()) + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, document, jsonOptions); + if (outputStream != null && outputStream.Length > 0) { - return FormatWriteResult.Successful(json, ContentType); + return FormatWriteResult.Successful(outputStream, ContentType); } return FormatWriteResult.Error(); } - public FormatReadResult CreateDevicesResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateDevicesResponseDocument(Stream content, IEnumerable> options = null) { - var json = System.Text.Encoding.UTF8.GetString(content); - // Read Document - var document = JsonSerializer.Deserialize(json); + var document = JsonSerializer.Deserialize(content); var success = document != null; return new FormatReadResult(document.ToDocument(), success); } - public FormatReadResult CreateStreamsResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateStreamsResponseDocument(Stream content, IEnumerable> options = null) { // Read Document var document = JsonSerializer.Deserialize(content); @@ -104,7 +107,7 @@ public FormatReadResult CreateStreamsResponseDocument( return new FormatReadResult(document.ToStreamsDocument(), success); } - public virtual FormatReadResult CreateAssetsResponseDocument(byte[] content, IEnumerable> options = null) + public virtual FormatReadResult CreateAssetsResponseDocument(Stream content, IEnumerable> options = null) { // Read Document var document = JsonSerializer.Deserialize(content); @@ -113,7 +116,7 @@ public virtual FormatReadResult CreateAssetsResponseDoc return new FormatReadResult(document.ToAssetsDocument(), success); } - public FormatReadResult CreateErrorResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateErrorResponseDocument(Stream content, IEnumerable> options = null) { // Read Document var document = JsonSerializer.Deserialize(content); diff --git a/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonMqttResponseDocumentFormatter.cs b/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonMqttResponseDocumentFormatter.cs index 2ec70d0e..914f0dc6 100644 --- a/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonMqttResponseDocumentFormatter.cs +++ b/libraries/MTConnect.NET-JSON-cppagent/Formatters/JsonMqttResponseDocumentFormatter.cs @@ -1,9 +1,10 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; using MTConnect.Assets.Json; using System.Collections.Generic; +using System.IO; using System.Text.Json; namespace MTConnect.Formatters @@ -21,16 +22,17 @@ public override FormatWriteResult Format(IAssetsResponseDocument document, IEnum var indentOutput = GetFormatterOption(options, "indentOutput"); var jsonOptions = indentOutput ? JsonFunctions.IndentOptions : JsonFunctions.DefaultOptions; - var json = JsonSerializer.SerializeToUtf8Bytes(new JsonAssetsResponseDocument(document), jsonOptions); - if (!json.IsNullOrEmpty()) + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, new JsonAssetsResponseDocument(document), jsonOptions); + if (outputStream != null && outputStream.Length > 0) { - return FormatWriteResult.Successful(json, ContentType); + return FormatWriteResult.Successful(outputStream, ContentType); } return FormatWriteResult.Error(); } - public override FormatReadResult CreateAssetsResponseDocument(byte[] content, IEnumerable> options = null) + public override FormatReadResult CreateAssetsResponseDocument(Stream content, IEnumerable> options = null) { // Read Document var document = JsonSerializer.Deserialize(content); diff --git a/libraries/MTConnect.NET-JSON-cppagent/JsonFunctions.cs b/libraries/MTConnect.NET-JSON-cppagent/JsonFunctions.cs index 6b9e8d12..bdcaf9d0 100644 --- a/libraries/MTConnect.NET-JSON-cppagent/JsonFunctions.cs +++ b/libraries/MTConnect.NET-JSON-cppagent/JsonFunctions.cs @@ -1,6 +1,7 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. +using System.IO; using System.Text.Json; using System.Text.Json.Serialization; @@ -96,5 +97,34 @@ public static byte[] ConvertBytes(object obj, JsonConverter converter = null, bo return null; } + + public static Stream ConvertStream(object obj, JsonConverter converter = null, bool indented = false) + { + if (obj != null) + { + try + { + var options = new JsonSerializerOptions + { + WriteIndented = indented, +#if NET5_0_OR_GREATER + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, + NumberHandling = JsonNumberHandling.AllowReadingFromString, +#endif + PropertyNameCaseInsensitive = true, + MaxDepth = 1000 + }; + + if (converter != null) options.Converters.Add(converter); + + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, obj, options); + return outputStream; + } + catch { } + } + + return null; + } } } \ No newline at end of file diff --git a/libraries/MTConnect.NET-JSON/Devices/JsonDevice.cs b/libraries/MTConnect.NET-JSON/Devices/JsonDevice.cs index 48fab58a..7a1b4fd7 100644 --- a/libraries/MTConnect.NET-JSON/Devices/JsonDevice.cs +++ b/libraries/MTConnect.NET-JSON/Devices/JsonDevice.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. -using MTConnect.Devices; -using MTConnect.Devices.References; using System; using System.Collections.Generic; +using System.IO; using System.Text.Json.Serialization; namespace MTConnect.Devices.Json @@ -128,6 +127,8 @@ public JsonDevice(IDevice device) public byte[] ToBytes(bool indent = false) => JsonFunctions.ConvertBytes(this, indented: indent); + public Stream ToStream(bool indent = false) => JsonFunctions.ConvertStream(this, indented: indent); + public Device ToDevice() { diff --git a/libraries/MTConnect.NET-JSON/Formatters/JsonEntityFormatter.cs b/libraries/MTConnect.NET-JSON/Formatters/JsonEntityFormatter.cs index e7a2e028..5dc3452e 100644 --- a/libraries/MTConnect.NET-JSON/Formatters/JsonEntityFormatter.cs +++ b/libraries/MTConnect.NET-JSON/Formatters/JsonEntityFormatter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -16,6 +16,7 @@ using MTConnect.Streams.Json; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Text.Json; @@ -35,7 +36,7 @@ public FormatWriteResult Format(IDevice device, IEnumerable(options, "indentOutput"); - var bytes = new JsonDevice(device).ToBytes(indentOuput); + var bytes = new JsonDevice(device).ToStream(indentOuput); if (bytes != null) { return FormatWriteResult.Successful(bytes, ContentType); @@ -55,7 +56,7 @@ public FormatWriteResult Format(IObservation observation, IEnumerable(options, "instanceIdOutput"); - byte[] bytes = null; + Stream bytes = null; switch (observation.Category) { @@ -64,7 +65,7 @@ public FormatWriteResult Format(IObservation observation, IEnumerable observations, IEnumera } - var bytes = JsonFunctions.ConvertBytes(x); + var bytes = JsonFunctions.ConvertStream(x); if (bytes != null) { return FormatWriteResult.Successful(bytes, ContentType); @@ -156,16 +157,16 @@ public FormatWriteResult Format(IAsset asset, IEnumerable CreateDevice(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateDevice(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -197,7 +198,7 @@ public FormatReadResult CreateDevice(byte[] content, IEnumerable(); } - public FormatReadResult CreateComponent(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateComponent(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -216,7 +217,7 @@ public FormatReadResult CreateComponent(byte[] content, IEnumerable< return new FormatReadResult(); } - public FormatReadResult CreateComposition(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateComposition(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -235,7 +236,7 @@ public FormatReadResult CreateComposition(byte[] content, IEnumera return new FormatReadResult(); } - public FormatReadResult CreateDataItem(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateDataItem(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -254,7 +255,7 @@ public FormatReadResult CreateDataItem(byte[] content, IEnumerable(); } - public FormatReadResult CreateAsset(string assetType, byte[] content, IEnumerable> options = null) + public FormatReadResult CreateAsset(string assetType, Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -267,8 +268,15 @@ public FormatReadResult CreateAsset(string assetType, byte[] content, IE { try { + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Convert from UTF8 bytes - var json = Encoding.UTF8.GetString(content); + var json = Encoding.UTF8.GetString(bytes); if (!string.IsNullOrEmpty(json)) { switch (assetType) diff --git a/libraries/MTConnect.NET-JSON/Formatters/JsonInputFormatter.cs b/libraries/MTConnect.NET-JSON/Formatters/JsonInputFormatter.cs index 4b77ee56..58f0d158 100644 --- a/libraries/MTConnect.NET-JSON/Formatters/JsonInputFormatter.cs +++ b/libraries/MTConnect.NET-JSON/Formatters/JsonInputFormatter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -24,7 +24,7 @@ public FormatWriteResult Format(IDeviceInput device, IEnumerable observations, IEn { if (!observations.IsNullOrEmpty()) { - var bytes = JsonFunctions.ConvertBytes(JsonInputObservationGroup.Create(observations)); + var bytes = JsonFunctions.ConvertStream(JsonInputObservationGroup.Create(observations)); if (bytes != null) { return FormatWriteResult.Successful(bytes, ContentType); @@ -55,7 +55,7 @@ public FormatWriteResult Format(IEnumerable assets, IEnumerable> CreateAssets(byte[] content, IEnume assets = JsonInputAssetGroup.ToAssets(jsonAssetGroup); } } - catch (Exception ex) - { - - } + catch { } } var success = assets != null; diff --git a/libraries/MTConnect.NET-JSON/Formatters/JsonResponseDocumentFormatter.cs b/libraries/MTConnect.NET-JSON/Formatters/JsonResponseDocumentFormatter.cs index fc8b4e38..a3c20d54 100644 --- a/libraries/MTConnect.NET-JSON/Formatters/JsonResponseDocumentFormatter.cs +++ b/libraries/MTConnect.NET-JSON/Formatters/JsonResponseDocumentFormatter.cs @@ -11,6 +11,7 @@ using MTConnect.Streams.Output; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.Json; @@ -29,10 +30,11 @@ public FormatWriteResult Format(IDevicesResponseDocument document, IEnumerable(options, "indentOutput"); var jsonOptions = indentOutput ? JsonFunctions.IndentOptions : JsonFunctions.DefaultOptions; - var json = JsonSerializer.SerializeToUtf8Bytes(new JsonDevicesDocument(document), jsonOptions); - if (!json.IsNullOrEmpty()) + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, new JsonDevicesDocument(document), jsonOptions); + if (outputStream != null && outputStream.Length > 0) { - return FormatWriteResult.Successful(json, ContentType); + return FormatWriteResult.Successful(outputStream, ContentType); } return FormatWriteResult.Error(); @@ -44,10 +46,11 @@ public FormatWriteResult Format(ref IStreamsResponseOutputDocument document, IEn var indentOutput = GetFormatterOption(options, "indentOutput"); var jsonOptions = indentOutput ? JsonFunctions.IndentOptions : JsonFunctions.DefaultOptions; - var json = JsonSerializer.SerializeToUtf8Bytes(new JsonStreamsDocument(document), jsonOptions); - if (!json.IsNullOrEmpty()) + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, new JsonStreamsDocument(document), jsonOptions); + if (outputStream != null && outputStream.Length > 0) { - return FormatWriteResult.Successful(json, ContentType); + return FormatWriteResult.Successful(outputStream, ContentType); } return FormatWriteResult.Error(); @@ -59,10 +62,11 @@ public FormatWriteResult Format(IAssetsResponseDocument document, IEnumerable(options, "indentOutput"); var jsonOptions = indentOutput ? JsonFunctions.IndentOptions : JsonFunctions.DefaultOptions; - var json = JsonSerializer.SerializeToUtf8Bytes(new JsonAssetsDocument(document), jsonOptions); - if (!json.IsNullOrEmpty()) + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, new JsonAssetsDocument(document), jsonOptions); + if (outputStream != null && outputStream.Length > 0) { - return FormatWriteResult.Successful(json, ContentType); + return FormatWriteResult.Successful(outputStream, ContentType); } return FormatWriteResult.Error(); @@ -74,17 +78,18 @@ public FormatWriteResult Format(IErrorResponseDocument document, IEnumerable(options, "indentOutput"); var jsonOptions = indentOutput ? JsonFunctions.IndentOptions : JsonFunctions.DefaultOptions; - var json = JsonSerializer.SerializeToUtf8Bytes(document, jsonOptions); - if (!json.IsNullOrEmpty()) + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, document, jsonOptions); + if (outputStream != null && outputStream.Length > 0) { - return FormatWriteResult.Successful(json, ContentType); + return FormatWriteResult.Successful(outputStream, ContentType); } return FormatWriteResult.Error(); } - public FormatReadResult CreateDevicesResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateDevicesResponseDocument(Stream content, IEnumerable> options = null) { // Read Document var document = JsonSerializer.Deserialize(content); @@ -93,7 +98,7 @@ public FormatReadResult CreateDevicesResponseDocument( return new FormatReadResult(document.ToDocument(), success); } - public FormatReadResult CreateStreamsResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateStreamsResponseDocument(Stream content, IEnumerable> options = null) { // Read Document var document = JsonSerializer.Deserialize(content); @@ -102,7 +107,7 @@ public FormatReadResult CreateStreamsResponseDocument( return new FormatReadResult(document.ToStreamsDocument(), success); } - public FormatReadResult CreateAssetsResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateAssetsResponseDocument(Stream content, IEnumerable> options = null) { // Read Document var document = JsonSerializer.Deserialize(content); @@ -111,7 +116,7 @@ public FormatReadResult CreateAssetsResponseDocument(by return new FormatReadResult(document, success); } - public FormatReadResult CreateErrorResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateErrorResponseDocument(Stream content, IEnumerable> options = null) { // Read Document var document = JsonSerializer.Deserialize(content); diff --git a/libraries/MTConnect.NET-JSON/JsonFunctions.cs b/libraries/MTConnect.NET-JSON/JsonFunctions.cs index 6b9e8d12..bdcaf9d0 100644 --- a/libraries/MTConnect.NET-JSON/JsonFunctions.cs +++ b/libraries/MTConnect.NET-JSON/JsonFunctions.cs @@ -1,6 +1,7 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. +using System.IO; using System.Text.Json; using System.Text.Json.Serialization; @@ -96,5 +97,34 @@ public static byte[] ConvertBytes(object obj, JsonConverter converter = null, bo return null; } + + public static Stream ConvertStream(object obj, JsonConverter converter = null, bool indented = false) + { + if (obj != null) + { + try + { + var options = new JsonSerializerOptions + { + WriteIndented = indented, +#if NET5_0_OR_GREATER + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, + NumberHandling = JsonNumberHandling.AllowReadingFromString, +#endif + PropertyNameCaseInsensitive = true, + MaxDepth = 1000 + }; + + if (converter != null) options.Converters.Add(converter); + + var outputStream = new MemoryStream(); + JsonSerializer.Serialize(outputStream, obj, options); + return outputStream; + } + catch { } + } + + return null; + } } } \ No newline at end of file diff --git a/libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttClient.cs b/libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttClient.cs index 2325ae6b..2a8164dd 100644 --- a/libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttClient.cs +++ b/libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttClient.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MQTTnet; @@ -431,7 +431,7 @@ private bool IsAssetTopic(string topic) private void ProcessProbeMessage(MqttApplicationMessage message) { - var result = EntityFormatter.CreateDevice(_documentFormat, message.Payload); + var result = EntityFormatter.CreateDevice(_documentFormat, new MemoryStream(message.Payload)); if (result.Success) { var device = result.Content; @@ -453,37 +453,29 @@ private void ProcessCurrentMessage(MqttApplicationMessage message) { if (!message.Retain) { - var result = ResponseDocumentFormatter.CreateStreamsResponseDocument(_documentFormat, message.Payload); + var result = ResponseDocumentFormatter.CreateStreamsResponseDocument(_documentFormat, new MemoryStream(message.Payload)); if (result.Success) { ProcessCurrentDocument(result.Content); } } - //else - //{ - // Console.WriteLine("STALE CURRENT!!!"); - //} } private void ProcessSampleMessage(MqttApplicationMessage message) { if (!message.Retain) { - var result = ResponseDocumentFormatter.CreateStreamsResponseDocument(_documentFormat, message.Payload); + var result = ResponseDocumentFormatter.CreateStreamsResponseDocument(_documentFormat, new MemoryStream(message.Payload)); if (result.Success) { ProcessSampleDocument(result.Content); } } - //else - //{ - // Console.WriteLine("STALE SAMPLE!!!"); - //} } private void ProcessAssetMessage(MqttApplicationMessage message) { - var result = ResponseDocumentFormatter.CreateAssetsResponseDocument(_documentFormat, message.Payload); + var result = ResponseDocumentFormatter.CreateAssetsResponseDocument(_documentFormat, new MemoryStream(message.Payload)); if (result.Success) { ProcessAssetsDocument(result.Content); @@ -622,8 +614,7 @@ private void ProcessSampleDocument(IStreamsResponseDocument document) } } - //// Save the most recent Sequence that was read - //_lastSequence = observations.Max(o => o.Sequence); + // Save the most recent Sequence that was read var maxSequence = observations.Max(o => o.Sequence); // Save the most recent Sequence that was read diff --git a/libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttExpandedClient.cs b/libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttExpandedClient.cs index 4ade8598..eef4d259 100644 --- a/libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttExpandedClient.cs +++ b/libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttExpandedClient.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MQTTnet; @@ -672,11 +672,13 @@ private void ProcessAsset(MqttApplicationMessage message) // Read Device UUID var deviceUuid = _deviceUuidRegex.Match(message.Topic).Groups[1].Value; + var stream = new MemoryStream(message.Payload); + // Deserialize JSON to Device - var jsonAsset = JsonSerializer.Deserialize(message.Payload); + var jsonAsset = JsonSerializer.Deserialize(stream); if (jsonAsset != null) { - var response = EntityFormatter.CreateAsset(DocumentFormat.JSON, jsonAsset.Type, message.Payload); + var response = EntityFormatter.CreateAsset(DocumentFormat.JSON, jsonAsset.Type, stream); if (response.Success) { if (AssetReceived != null) diff --git a/libraries/MTConnect.NET-MQTT/MTConnectMqttExpander.cs b/libraries/MTConnect.NET-MQTT/MTConnectMqttExpander.cs index 6ce3410b..74d18214 100644 --- a/libraries/MTConnect.NET-MQTT/MTConnectMqttExpander.cs +++ b/libraries/MTConnect.NET-MQTT/MTConnectMqttExpander.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MQTTnet; @@ -11,7 +11,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; @@ -238,10 +237,11 @@ public async Task PublishDevice(IDevice device) var formatResponse = EntityFormatter.Format(_configuration.DocumentFormat, device); if (formatResponse.Success && formatResponse.Content != null) { - var message = new MqttApplicationMessage(); - message.Topic = topic; - message.Payload = formatResponse.Content; - message.Retain = true; + var messageBuilder = new MqttApplicationMessageBuilder(); + messageBuilder.WithTopic(topic); + messageBuilder.WithPayload(formatResponse.Content); + messageBuilder.WithRetainFlag(true); + var message = messageBuilder.Build(); var result = await _mqttClient.PublishAsync(message); if (result.IsSuccess) diff --git a/libraries/MTConnect.NET-MQTT/MTConnectMqttMessage.cs b/libraries/MTConnect.NET-MQTT/MTConnectMqttMessage.cs index 508d10a1..f5f631e6 100644 --- a/libraries/MTConnect.NET-MQTT/MTConnectMqttMessage.cs +++ b/libraries/MTConnect.NET-MQTT/MTConnectMqttMessage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MQTTnet; @@ -7,6 +7,7 @@ using MTConnect.Devices; using MTConnect.Observations; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.Json; @@ -29,7 +30,7 @@ private static MqttApplicationMessage CreateMessage(string topic, string payload return null; } - private static MqttApplicationMessage CreateMessage(string topic, byte[] payload, bool retain = false) + private static MqttApplicationMessage CreateMessage(string topic, Stream payload, bool retain = false) { try { diff --git a/libraries/MTConnect.NET-SHDR/Shdr/ShdrAsset.cs b/libraries/MTConnect.NET-SHDR/Shdr/ShdrAsset.cs index 31de9b33..7b2f2091 100644 --- a/libraries/MTConnect.NET-SHDR/Shdr/ShdrAsset.cs +++ b/libraries/MTConnect.NET-SHDR/Shdr/ShdrAsset.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -88,7 +88,7 @@ public byte[] ChangeId var xmlBytes = XmlAsset.ToXml(asset.Asset); if (xmlBytes != null) { - asset.Xml = Encoding.UTF8.GetString(xmlBytes); + //asset.Xml = Encoding.UTF8.GetString(xmlBytes); } } @@ -139,7 +139,7 @@ public ShdrAsset(IAsset asset) var xmlBytes = XmlAsset.ToXml(asset); if (xmlBytes != null) { - Xml = Encoding.UTF8.GetString(xmlBytes); + //Xml = Encoding.UTF8.GetString(xmlBytes); } } } diff --git a/libraries/MTConnect.NET-XML/Assets/XmlAsset.cs b/libraries/MTConnect.NET-XML/Assets/XmlAsset.cs index fff7fbbc..65c1229e 100644 --- a/libraries/MTConnect.NET-XML/Assets/XmlAsset.cs +++ b/libraries/MTConnect.NET-XML/Assets/XmlAsset.cs @@ -1,9 +1,6 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. -using MTConnect.Assets.CuttingTools; -using MTConnect.Devices; -using MTConnect.Devices.Configurations; using MTConnect.Extensions; using System; using System.Collections.Generic; @@ -54,7 +51,6 @@ public static IAsset FromXml(string type, byte[] xmlBytes) if (xmlType == null) xmlType = typeof(XmlUnknownAsset); return FromXml(xmlType, xmlBytes); - //return FromXml(asset.GetType(), xmlBytes); } return default; @@ -140,7 +136,7 @@ public static void WriteCommonXml(XmlWriter writer, IAsset asset) } - public static byte[] ToXml(IAsset asset, bool indent = false) + public static Stream ToXml(IAsset asset, bool indent = false) { try { @@ -171,10 +167,7 @@ public static byte[] ToXml(IAsset asset, bool indent = false) xmlWriter.Flush(); - //var bytes = stream.ToArray(); - //return Encoding.UTF8.GetString(bytes); - - return stream.ToArray(); + return stream; } } } @@ -183,33 +176,6 @@ public static byte[] ToXml(IAsset asset, bool indent = false) return null; } - - //public static IAsset Create(string type) - //{ - // if (!string.IsNullOrEmpty(type)) - // { - // if (_types == null) _types = GetAllTypes(); - - // if (!_types.IsNullOrEmpty()) - // { - // if (_types.TryGetValue(type, out Type t)) - // { - // var constructor = t.GetConstructor(System.Type.EmptyTypes); - // if (constructor != null) - // { - // try - // { - // return (IAsset)Activator.CreateInstance(t); - // } - // catch { } - // } - // } - // } - // } - - // return null; - //} - public static Type GetAssetType(string type) { if (!string.IsNullOrEmpty(type)) diff --git a/libraries/MTConnect.NET-XML/Assets/XmlAssetCollection.cs b/libraries/MTConnect.NET-XML/Assets/XmlAssetCollection.cs index 038852a3..9d33bdec 100644 --- a/libraries/MTConnect.NET-XML/Assets/XmlAssetCollection.cs +++ b/libraries/MTConnect.NET-XML/Assets/XmlAssetCollection.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets.Xml.CuttingTools; @@ -9,7 +9,6 @@ namespace MTConnect.Assets.Xml { - //public class XmlAssetCollection : IXmlSerializable public class XmlAssetCollection { private readonly bool _indentOutput = false; @@ -19,9 +18,6 @@ public class XmlAssetCollection [XmlArrayItem(typeof(XmlCuttingToolAsset), ElementName = "CuttingTool")] public List Assets { get; set; } - //[XmlIgnore] - //public List Assets { get; set; } - public XmlAssetCollection() { Assets = new List(); } diff --git a/libraries/MTConnect.NET-XML/Assets/XmlAssetsDocument.cs b/libraries/MTConnect.NET-XML/Assets/XmlAssetsDocument.cs index cf67c43b..570036e4 100644 --- a/libraries/MTConnect.NET-XML/Assets/XmlAssetsDocument.cs +++ b/libraries/MTConnect.NET-XML/Assets/XmlAssetsDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets.ComponentConfigurationParameters; @@ -36,9 +36,6 @@ public class XmlAssetsResponseDocument [XmlArrayItem(typeof(XmlRawMaterialAsset), ElementName = RawMaterialAsset.TypeId)] public List Assets { get; set; } - //[XmlElement("Assets")] - //public XmlAssetCollection AssetCollection { get; set; } - [XmlIgnore] public Version Version { get; set; } @@ -48,7 +45,6 @@ public AssetsResponseDocument ToAssetsDocument() var assetsDocument = new AssetsResponseDocument(); assetsDocument.Header = Header.ToErrorHeader(); assetsDocument.Version = Version; - //assetsDocument.Assets = Assets; if (!Assets.IsNullOrEmpty()) { @@ -95,13 +91,6 @@ public AssetsResponseDocument ToAssetsDocument() assetsDocument.Assets = assets; } - //// Add Assets - //if (AssetCollection != null && !AssetCollection.Assets.IsNullOrEmpty()) - //{ - // assetsDocument.Assets = AssetCollection.Assets.ToList(); - //} - //else assetsDocument.Assets = new List(); - return assetsDocument; } @@ -139,7 +128,7 @@ public static AssetsResponseDocument FromXml(byte[] xmlBytes) return null; } - public static byte[] ToXmlBytes( + public static Stream ToXmlStream( IAssetsResponseDocument document, bool indentOutput = false, bool outputComments = false, @@ -152,17 +141,16 @@ public static byte[] ToXmlBytes( { var mtconnectStreamsNamespace = Namespaces.GetStreams(document.Version.Major, document.Version.Minor); - using (var stream = new MemoryStream()) - { - // Set the XmlWriterSettings to use - var xmlWriterSettings = indentOutput ? XmlFunctions.XmlWriterSettingsIndent : XmlFunctions.XmlWriterSettings; + var outputStream = new MemoryStream(); - // Use XmlWriter to write XML to stream - using (var xmlWriter = XmlWriter.Create(stream, xmlWriterSettings)) - { - WriteXml(xmlWriter, document, indentOutput, outputComments, stylesheet); - return stream.ToArray(); - } + // Set the XmlWriterSettings to use + var xmlWriterSettings = indentOutput ? XmlFunctions.XmlWriterSettingsIndent : XmlFunctions.XmlWriterSettings; + + // Use XmlWriter to write XML to stream + using (var xmlWriter = XmlWriter.Create(outputStream, xmlWriterSettings)) + { + WriteXml(xmlWriter, document, indentOutput, outputComments, stylesheet); + return outputStream; } } catch { } diff --git a/libraries/MTConnect.NET-XML/Devices/XmlDevicesResponseDocument.cs b/libraries/MTConnect.NET-XML/Devices/XmlDevicesResponseDocument.cs index 9a732bad..7991b48f 100644 --- a/libraries/MTConnect.NET-XML/Devices/XmlDevicesResponseDocument.cs +++ b/libraries/MTConnect.NET-XML/Devices/XmlDevicesResponseDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Configurations; @@ -123,6 +123,36 @@ public static byte[] ToXmlBytes( return null; } + public static Stream ToXmlStream( + IDevicesResponseDocument document, + IEnumerable extendedSchemas = null, + string styleSheet = null, + bool indent = true, + bool outputComments = false + ) + { + if (document != null && document.Header != null) + { + try + { + var outputStream = new MemoryStream(); + + // Set the XmlWriterSettings to use + var xmlWriterSettings = indent ? XmlFunctions.XmlWriterSettingsIndent : XmlFunctions.XmlWriterSettings; + + // Use XmlWriter to write XML to stream + using (var xmlWriter = XmlWriter.Create(outputStream, xmlWriterSettings)) + { + WriteXml(xmlWriter, document, indent, outputComments, styleSheet, extendedSchemas); + return outputStream; + } + } + catch { } + } + + return null; + } + public static void WriteXml( XmlWriter writer, IDevicesResponseDocument document, diff --git a/libraries/MTConnect.NET-XML/Errors/XmlErrorDocument.cs b/libraries/MTConnect.NET-XML/Errors/XmlErrorDocument.cs index 26539f19..10e46edb 100644 --- a/libraries/MTConnect.NET-XML/Errors/XmlErrorDocument.cs +++ b/libraries/MTConnect.NET-XML/Errors/XmlErrorDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using System; @@ -80,7 +80,7 @@ public static IErrorResponseDocument FromXml(byte[] xmlBytes) } - public static byte[] ToXmlBytes( + public static Stream ToXmlStream( IErrorResponseDocument document, bool indentOutput = true, bool outputComments = false, @@ -93,17 +93,16 @@ public static byte[] ToXmlBytes( { var mtconnectStreamsNamespace = Namespaces.GetStreams(document.Version.Major, document.Version.Minor); - using (var stream = new MemoryStream()) - { - // Set the XmlWriterSettings to use - var xmlWriterSettings = indentOutput ? XmlFunctions.XmlWriterSettingsIndent : XmlFunctions.XmlWriterSettings; + var outputStream = new MemoryStream(); - // Use XmlWriter to write XML to stream - using (var xmlWriter = XmlWriter.Create(stream, xmlWriterSettings)) - { - WriteXml(xmlWriter, document, indentOutput, outputComments, stylesheet); - return stream.ToArray(); - } + // Set the XmlWriterSettings to use + var xmlWriterSettings = indentOutput ? XmlFunctions.XmlWriterSettingsIndent : XmlFunctions.XmlWriterSettings; + + // Use XmlWriter to write XML to stream + using (var xmlWriter = XmlWriter.Create(outputStream, xmlWriterSettings)) + { + WriteXml(xmlWriter, document, indentOutput, outputComments, stylesheet); + return outputStream; } } catch { } diff --git a/libraries/MTConnect.NET-XML/Formatters/XmlEntityFormatter.cs b/libraries/MTConnect.NET-XML/Formatters/XmlEntityFormatter.cs index 68be8e1b..f6ef8176 100644 --- a/libraries/MTConnect.NET-XML/Formatters/XmlEntityFormatter.cs +++ b/libraries/MTConnect.NET-XML/Formatters/XmlEntityFormatter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -38,12 +38,8 @@ public FormatWriteResult Format(IDevice device, IEnumerable observations, IEnumera XmlObservation.WriteXml(xmlWriter, observation); } - var bytes = outputStream.ToArray(); - if (bytes != null) - { - return FormatWriteResult.Successful(bytes, ContentType); - } + return FormatWriteResult.Successful(outputStream, ContentType); } } catch { } @@ -110,77 +98,109 @@ public FormatWriteResult Format(IAsset asset, IEnumerable CreateDevice(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateDevice(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); var errors = new List(); + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Read Entity - var entity = XmlDevice.FromXml(content); + var entity = XmlDevice.FromXml(bytes); var success = entity != null; return new FormatReadResult(entity, success, messages, warnings, errors); } - public FormatReadResult CreateComponent(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateComponent(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); var errors = new List(); + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Read Entity - var entity = XmlComponent.FromXml(content); + var entity = XmlComponent.FromXml(bytes); var success = entity != null; return new FormatReadResult(entity, success, messages, warnings, errors); } - public FormatReadResult CreateComposition(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateComposition(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); var errors = new List(); + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Read Entity - var entity = XmlComposition.FromXml(content); + var entity = XmlComposition.FromXml(bytes); var success = entity != null; return new FormatReadResult(entity, success, messages, warnings, errors); } - public FormatReadResult CreateDataItem(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateDataItem(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); var errors = new List(); + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Read Entity - var entity = XmlDataItem.FromXml(content); + var entity = XmlDataItem.FromXml(bytes); var success = entity != null; return new FormatReadResult(entity, success, messages, warnings, errors); } - public FormatReadResult CreateAsset(string assetType, byte[] content, IEnumerable> options = null) + public FormatReadResult CreateAsset(string assetType, Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); var errors = new List(); + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Read Entity - var entity = XmlAsset.FromXml(assetType, content); + var entity = XmlAsset.FromXml(assetType, bytes); var success = entity != null; return new FormatReadResult(entity, success, messages, warnings, errors); diff --git a/libraries/MTConnect.NET-XML/Formatters/XmlResponseDocumentFormatter.cs b/libraries/MTConnect.NET-XML/Formatters/XmlResponseDocumentFormatter.cs index 15294e58..797b522e 100644 --- a/libraries/MTConnect.NET-XML/Formatters/XmlResponseDocumentFormatter.cs +++ b/libraries/MTConnect.NET-XML/Formatters/XmlResponseDocumentFormatter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Assets; @@ -12,6 +12,7 @@ using MTConnect.Streams.Xml; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.Json; @@ -41,7 +42,7 @@ public FormatWriteResult Format(IDevicesResponseDocument document, IEnumerable(options, "validationLevel"); - var xml = XmlDevicesResponseDocument.ToXmlBytes(document, null, stylesheet, indentOutput, outputComments); + var xml = XmlDevicesResponseDocument.ToXmlStream(document, null, stylesheet, indentOutput, outputComments); if (xml != null && xml.Length > 0) { if (validationLevel > 0) @@ -88,7 +89,7 @@ public FormatWriteResult Format(ref IStreamsResponseOutputDocument document, IEn // Read Validation Level Option passed to Formatter (0 = Ignore, 1 = Warning, 2 = Strict) var validationLevel = GetFormatterOption(options, "validationLevel"); - var xml = XmlStreamsResponseDocument.ToXmlBytes(ref document, extendedNamespaces, stylesheet, indentOutput, outputComments); + var xml = XmlStreamsResponseDocument.ToXmlStream(ref document, extendedNamespaces, stylesheet, indentOutput, outputComments); if (xml != null && xml.Length > 0) { if (validationLevel > 0) @@ -132,7 +133,7 @@ public FormatWriteResult Format(IAssetsResponseDocument document, IEnumerable(options, "validationLevel"); - var xml = XmlAssetsResponseDocument.ToXmlBytes(document, indentOutput, outputComments, stylesheet); + var xml = XmlAssetsResponseDocument.ToXmlStream(document, indentOutput, outputComments, stylesheet); if (xml != null && xml.Length > 0) { if (validationLevel > 0) @@ -176,7 +177,7 @@ public FormatWriteResult Format(IErrorResponseDocument document, IEnumerable(options, "validationLevel"); - var xml = XmlErrorResponseDocument.ToXmlBytes(document, indentOutput, outputComments, stylesheet); + var xml = XmlErrorResponseDocument.ToXmlStream(document, indentOutput, outputComments, stylesheet); if (xml != null) { if (validationLevel > 0) @@ -203,7 +204,7 @@ public FormatWriteResult Format(IErrorResponseDocument document, IEnumerable CreateDevicesResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateDevicesResponseDocument(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -236,14 +237,21 @@ public FormatReadResult CreateDevicesResponseDocument( } } + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Read Document - var document = XmlDevicesResponseDocument.FromXml(content); + var document = XmlDevicesResponseDocument.FromXml(bytes); var success = document != null; return new FormatReadResult(document, success, messages, warnings, errors); } - public FormatReadResult CreateStreamsResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateStreamsResponseDocument(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -276,14 +284,21 @@ public FormatReadResult CreateStreamsResponseDocument( } } + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Read Document - var document = XmlStreamsResponseDocument.FromXml(content); + var document = XmlStreamsResponseDocument.FromXml(bytes); var success = document != null; return new FormatReadResult(document, success, messages, warnings, errors); } - public FormatReadResult CreateAssetsResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateAssetsResponseDocument(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -294,7 +309,7 @@ public FormatReadResult CreateAssetsResponseDocument(by // Read Validation Level Option passed to Formatter (0 = Ignore, 1 = Warning, 2 = Strict) var validationLevel = GetFormatterOption(options, "validationLevel"); - + if (validationLevel > 0) { // Validate XML against XSD Schema @@ -316,14 +331,21 @@ public FormatReadResult CreateAssetsResponseDocument(by } } + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Read Document - var document = XmlAssetsResponseDocument.FromXml(content); + var document = XmlAssetsResponseDocument.FromXml(bytes); var success = document != null; return new FormatReadResult(document, success, messages, warnings, errors); } - public FormatReadResult CreateErrorResponseDocument(byte[] content, IEnumerable> options = null) + public FormatReadResult CreateErrorResponseDocument(Stream content, IEnumerable> options = null) { var messages = new List(); var warnings = new List(); @@ -356,8 +378,15 @@ public FormatReadResult CreateErrorResponseDocument(byte } } + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + content.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + // Read Document - var document = XmlErrorResponseDocument.FromXml(content); + var document = XmlErrorResponseDocument.FromXml(bytes); var success = document != null; return new FormatReadResult(document, success, messages, warnings, errors); diff --git a/libraries/MTConnect.NET-XML/Streams/XmlStreamsResponseDocument.cs b/libraries/MTConnect.NET-XML/Streams/XmlStreamsResponseDocument.cs index 12af7812..bbe0e8d7 100644 --- a/libraries/MTConnect.NET-XML/Streams/XmlStreamsResponseDocument.cs +++ b/libraries/MTConnect.NET-XML/Streams/XmlStreamsResponseDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using MTConnect.Configurations; @@ -427,7 +427,7 @@ private static IEnumerable ReadTimeSeries(string text) #region "Write" - public static byte[] ToXmlBytes( + public static Stream ToXmlStream( ref IStreamsResponseOutputDocument document, IEnumerable extendedSchemas = null, string styleSheet = null, @@ -441,17 +441,16 @@ public static byte[] ToXmlBytes( { var mtconnectStreamsNamespace = Namespaces.GetStreams(document.Version.Major, document.Version.Minor); - using (var stream = new MemoryStream()) - { - // Set the XmlWriterSettings to use - var xmlWriterSettings = indent ? XmlFunctions.XmlWriterSettingsIndent : XmlFunctions.XmlWriterSettings; + var outputStream = new MemoryStream(); - // Use XmlWriter to write XML to stream - using (var xmlWriter = XmlWriter.Create(stream, xmlWriterSettings)) - { - WriteXml(xmlWriter, ref document, extendedSchemas, styleSheet, indent, outputComments); - return stream.ToArray(); - } + // Set the XmlWriterSettings to use + var xmlWriterSettings = indent ? XmlFunctions.XmlWriterSettingsIndent : XmlFunctions.XmlWriterSettings; + + // Use XmlWriter to write XML to stream + using (var xmlWriter = XmlWriter.Create(outputStream, xmlWriterSettings)) + { + WriteXml(xmlWriter, ref document, extendedSchemas, styleSheet, indent, outputComments); + return outputStream; } } catch { } diff --git a/libraries/MTConnect.NET-XML/XmlValidator.cs b/libraries/MTConnect.NET-XML/XmlValidator.cs index caced9d9..df1b4e7b 100644 --- a/libraries/MTConnect.NET-XML/XmlValidator.cs +++ b/libraries/MTConnect.NET-XML/XmlValidator.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// Copyright (c) 2024 TrakHound Inc., All Rights Reserved. // TrakHound Inc. licenses this file to you under the MIT license. using System; @@ -11,7 +11,7 @@ namespace MTConnect { internal static class XmlValidator { - public static XmlValidationResponse Validate(byte[] documentXml, IEnumerable schemaXmls = null) + public static XmlValidationResponse Validate(Stream documentXml, IEnumerable schemaXmls = null) { var success = false; var errors = new List(); @@ -63,8 +63,7 @@ public static XmlValidationResponse Validate(byte[] documentXml, IEnumerable