diff --git a/modules/MTConnect.NET-HttpServer-Module/MTConnect.NET-HttpServer-Module.csproj b/modules/MTConnect.NET-HttpServer-Module/MTConnect.NET-HttpServer-Module.csproj index 39ca5e08..a596bb81 100644 --- a/modules/MTConnect.NET-HttpServer-Module/MTConnect.NET-HttpServer-Module.csproj +++ b/modules/MTConnect.NET-HttpServer-Module/MTConnect.NET-HttpServer-Module.csproj @@ -59,6 +59,7 @@ + diff --git a/modules/MTConnect.NET-HttpServer-Module/MTConnectHttpServerModule.cs b/modules/MTConnect.NET-HttpServer-Module/MTConnectHttpServerModule.cs index ad380c50..e3de6e3d 100644 --- a/modules/MTConnect.NET-HttpServer-Module/MTConnectHttpServerModule.cs +++ b/modules/MTConnect.NET-HttpServer-Module/MTConnectHttpServerModule.cs @@ -40,7 +40,7 @@ public void StartBeforeLoad() { } public void StartAfterLoad() { // Intialize the Http Server - _httpServer = new MTConnectHttpAgentServer(_configuration, _mtconnectAgent); + _httpServer = new MTConnectShdrHttpAgentServer(_configuration, _mtconnectAgent); _httpServer.ServerStarted += HttpListenerStarted; _httpServer.ServerStopped += HttpListenerStopped; diff --git a/modules/MTConnect.NET-HttpServer-Module/MTConnectShdrHttpAgentServer.cs b/modules/MTConnect.NET-HttpServer-Module/MTConnectShdrHttpAgentServer.cs new file mode 100644 index 00000000..05de7021 --- /dev/null +++ b/modules/MTConnect.NET-HttpServer-Module/MTConnectShdrHttpAgentServer.cs @@ -0,0 +1,130 @@ +// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. +// TrakHound Inc. licenses this file to you under the MIT license. + +using MTConnect.Agents; +using MTConnect.Assets; +using MTConnect.Configurations; +using MTConnect.Devices; +using MTConnect.Devices.DataItems; +using MTConnect.Formatters; +using MTConnect.Servers; +using MTConnect.Shdr; +using System; +using System.Linq; +using System.Text; + +namespace MTConnect.Modules.Http +{ + /// + /// An Http Web Server for processing MTConnect REST Api Requests that supports SHDR Put and Post requests + /// + public class MTConnectShdrHttpAgentServer : MTConnectHttpAgentServer + { + public MTConnectShdrHttpAgentServer(HttpModuleConfiguration configuration, IMTConnectAgentBroker mtconnectAgent) : base(configuration, mtconnectAgent) { } + + + //protected override bool OnObservationInput(string deviceKey, string dataItemKey, string input) + protected override bool OnObservationInput(MTConnectObservationInputArgs args) + { + // Get the Devices Document from the Agent + var devicesDocument = _mtconnectAgent.GetDevicesResponseDocument(args.DeviceKey); + if (devicesDocument != null && !devicesDocument.Devices.IsNullOrEmpty()) + { + // Get the first Device (should only be one Device) + var device = devicesDocument.Devices.FirstOrDefault(); + if (device != null) + { + // Get the DataItem based on the Key + var dataItem = device.GetDataItemByKey(args.DataItemKey); + if (dataItem != null) + { + // Construct an SHDR Line using the DataItemId and the Input string from Http + var shdrLine = $"|{dataItem.Id}|{args.Value}"; + + if (dataItem.Category == DataItemCategory.CONDITION) + { + var condition = ShdrFaultState.FromString(shdrLine); + if (condition != null) _mtconnectAgent.AddObservation(device.Uuid, condition); + } + else if (dataItem.Type == Devices.DataItems.MessageDataItem.TypeId) + { + var message = ShdrMessage.FromString(shdrLine); + if (message != null) _mtconnectAgent.AddObservation(device.Uuid, message); + } + else if (dataItem.Representation == DataItemRepresentation.TABLE) + { + var table = ShdrTable.FromString(shdrLine); + if (table != null) _mtconnectAgent.AddObservation(device.Uuid, table); + } + else if (dataItem.Representation == DataItemRepresentation.DATA_SET) + { + var dataSet = ShdrDataSet.FromString(shdrLine); + if (dataSet != null) _mtconnectAgent.AddObservation(device.Uuid, dataSet); + } + else if (dataItem.Representation == DataItemRepresentation.TIME_SERIES) + { + var timeSeries = ShdrTimeSeries.FromString(shdrLine); + if (timeSeries != null) _mtconnectAgent.AddObservation(device.Uuid, timeSeries); + } + else + { + var dataItems = ShdrDataItem.FromString(shdrLine); + if (!dataItems.IsNullOrEmpty()) _mtconnectAgent.AddObservations(device.Uuid, dataItems); + } + } + else + { + //if (_mtconnectAgent.InvalidObservationAdded != null) + //{ + // _mtconnectAgent.InvalidObservationAdded.Invoke(deviceKey, dataItemKey, new ValidationResult(false, $"DataItemKey \"{dataItemKey}\" not Found in Device")); + //} + } + + return true; + } + else + { + //if (_mtconnectAgent.InvalidObservationAdded != null) + //{ + // _mtconnectAgent.InvalidObservationAdded.Invoke(deviceKey, dataItemKey, new ValidationResult(false, $"Device \"{deviceKey}\" not Found")); + //} + } + } + + return false; + } + + //protected override bool OnAssetInput(string assetId, string deviceKey, string assetType, byte[] requestBytes, string documentFormat = DocumentFormat.XML) + 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)); + if (result.Success) + { + var asset = (Asset)result.Entity; + asset.AssetId = args.AssetId; + asset.Timestamp = asset.Timestamp > DateTime.MinValue ? asset.Timestamp : DateTime.Now; + return _mtconnectAgent.AddAsset(args.DeviceKey, asset); + } + } + + return false; + } + + 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/src/MTConnect.NET-Applications-Agents/MTConnectAgentApplication.cs b/src/MTConnect.NET-Applications-Agents/MTConnectAgentApplication.cs index 21f1234c..646727ef 100644 --- a/src/MTConnect.NET-Applications-Agents/MTConnectAgentApplication.cs +++ b/src/MTConnect.NET-Applications-Agents/MTConnectAgentApplication.cs @@ -328,6 +328,7 @@ public void StartAgent(IAgentApplicationConfiguration configuration, bool verbos // Initialize Agent Modules _modules = new MTConnectAgentModules(configuration, _mtconnectAgent); + _modules.ModuleLoaded += ModuleLoaded; _modules.Load(); // Read Indexes for Buffer @@ -392,6 +393,7 @@ public void StartAgent(IAgentApplicationConfiguration configuration, bool verbos // Initilialize Processors _processors = new MTConnectAgentProcessors(configuration); + _processors.ProcessorLoaded += ProcessorLoaded; _processors.Load(); _mtconnectAgent.ProcessObservationFunction = _processors.Process; @@ -619,6 +621,16 @@ protected virtual void OnPrintHelpArguments() { } #region "Logging" + private void ModuleLoaded(object sender, IMTConnectAgentModule module) + { + _applicationLogger.Debug($"[Application] : Module Loaded : " + module.GetType().Name); + } + + private void ProcessorLoaded(object sender, IMTConnectAgentProcessor processor) + { + _applicationLogger.Debug($"[Application] : Processor Loaded : " + processor.GetType().Name); + } + private void DevicesRequested(string deviceName) { _agentLogger.Debug($"[Agent] : MTConnectDevices Requested : " + deviceName); diff --git a/src/MTConnect.NET-Common/Agents/MTConnectAgentModules.cs b/src/MTConnect.NET-Common/Agents/MTConnectAgentModules.cs index 1135e382..c27a1b9b 100644 --- a/src/MTConnect.NET-Common/Agents/MTConnectAgentModules.cs +++ b/src/MTConnect.NET-Common/Agents/MTConnectAgentModules.cs @@ -17,6 +17,9 @@ public class MTConnectAgentModules private readonly IMTConnectAgentBroker _mtconnectAgent; + public event EventHandler ModuleLoaded; + + public MTConnectAgentModules(IAgentApplicationConfiguration configuration, IMTConnectAgentBroker mtconnectAgent) { _configuration = configuration; @@ -48,6 +51,8 @@ public void Load() var moduleId = Guid.NewGuid().ToString(); + if (ModuleLoaded != null) ModuleLoaded.Invoke(this, module); + lock (_lock) _modules.Add(moduleId, module); } catch { } diff --git a/src/MTConnect.NET-Common/Agents/MTConnectAgentProcessors.cs b/src/MTConnect.NET-Common/Agents/MTConnectAgentProcessors.cs index 508c8002..cdf7ce6b 100644 --- a/src/MTConnect.NET-Common/Agents/MTConnectAgentProcessors.cs +++ b/src/MTConnect.NET-Common/Agents/MTConnectAgentProcessors.cs @@ -19,6 +19,9 @@ public class MTConnectAgentProcessors private readonly IAgentApplicationConfiguration _configuration; + public event EventHandler ProcessorLoaded; + + public MTConnectAgentProcessors(IAgentApplicationConfiguration configuration) { _configuration = configuration; @@ -49,6 +52,8 @@ public void Load() var processorId = Guid.NewGuid().ToString(); + if (ProcessorLoaded != null) ProcessorLoaded.Invoke(this, processor); + lock (_lock) _processors.Add(processorId, processor); } catch { } diff --git a/src/MTConnect.NET-Common/IMTConnectController.cs b/src/MTConnect.NET-Common/IMTConnectController.cs deleted file mode 100644 index 36a75152..00000000 --- a/src/MTConnect.NET-Common/IMTConnectController.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. -// TrakHound Inc. licenses this file to you under the MIT license. - -namespace MTConnect -{ - public interface IMTConnectController - { - string Id { get; } - - string Description { get; } - - - void Start(); - - void Stop(); - } -} diff --git a/src/MTConnect.NET-Common/IMTConnectDataSource.cs b/src/MTConnect.NET-Common/IMTConnectDataSource.cs deleted file mode 100644 index d1edee4c..00000000 --- a/src/MTConnect.NET-Common/IMTConnectDataSource.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2023 TrakHound Inc., All Rights Reserved. -// TrakHound Inc. licenses this file to you under the MIT license. - -namespace MTConnect -{ - public interface IMTConnectDataSource - { - string Id { get; } - - string Description { get; } - - - void Start(); - - void Stop(); - } -} diff --git a/src/MTConnect.NET-Common/MTConnect.NET-Common.csproj b/src/MTConnect.NET-Common/MTConnect.NET-Common.csproj index 611151b2..4a8db66f 100644 --- a/src/MTConnect.NET-Common/MTConnect.NET-Common.csproj +++ b/src/MTConnect.NET-Common/MTConnect.NET-Common.csproj @@ -108,14 +108,10 @@ - - - - diff --git a/src/MTConnect.NET-Python-Processor/MTConnect.NET-Python-Processor.csproj b/src/MTConnect.NET-Python-Processor/MTConnect.NET-Python-Processor.csproj index 4d40d9e5..e9309fb6 100644 --- a/src/MTConnect.NET-Python-Processor/MTConnect.NET-Python-Processor.csproj +++ b/src/MTConnect.NET-Python-Processor/MTConnect.NET-Python-Processor.csproj @@ -55,6 +55,7 @@ + diff --git a/src/MTConnect.NET-Python-Processor/MTConnectPythonProcessor.cs b/src/MTConnect.NET-Python-Processor/MTConnectPythonProcessor.cs index 900ed22c..f7ca19c2 100644 --- a/src/MTConnect.NET-Python-Processor/MTConnectPythonProcessor.cs +++ b/src/MTConnect.NET-Python-Processor/MTConnectPythonProcessor.cs @@ -5,6 +5,7 @@ using MTConnect.Assets; using MTConnect.Configurations; using MTConnect.Observations.Input; +using NLog; using System; using System.Collections.Generic; using System.IO; @@ -14,16 +15,22 @@ namespace MTConnect.Processors public class MTConnectPythonProcessor : IMTConnectAgentProcessor { public const string ConfigurationTypeId = "python"; + private const int DefaultUpdateInterval = 2000; private const string _functionName = "process"; private const string _defaultDirectory = "processors"; private const string _defaultExtension = ".py"; + private readonly Logger _logger = LogManager.GetLogger("python-processor-logger"); private readonly Microsoft.Scripting.Hosting.ScriptEngine _pythonEngine; private readonly Dictionary> _functions = new Dictionary>(); private readonly PythonProcessorConfiguration _configuration; private readonly object _lock = new object(); + private FileSystemWatcher _watcher; + private System.Timers.Timer _watcherTimer; + private bool _update = false; + public MTConnectPythonProcessor(object configuration) { @@ -32,16 +39,16 @@ public MTConnectPythonProcessor(object configuration) if (_configuration == null) _configuration = new PythonProcessorConfiguration(); Load(); + + StartWatcher(); } + private void Load() { lock (_lock) _functions.Clear(); - var dir = _configuration.Directory; - if (string.IsNullOrEmpty(dir)) dir = _defaultDirectory; - if (!Path.IsPathRooted(dir)) dir = Path.Combine(AppContext.BaseDirectory, dir); - + var dir = GetDirectory(); var files = Directory.GetFiles(dir, $"*{_defaultExtension}"); if (!files.IsNullOrEmpty()) { @@ -64,14 +71,14 @@ private void LoadEngine(string file) var process = scope.GetVariable>(_functionName); if (process != null) { - var key = file.ToMD5Hash(); + _logger.Info($"[Python-Processor] : Script Loaded : {file}"); - AddFunction(key, process); + AddFunction(file, process); } } catch (Exception ex) { - + _logger.Error($"[Python-Processor] : Error Loading Script : {file} : {ex.Message}"); } } @@ -100,7 +107,7 @@ public IObservationInput Process(ProcessObservation observation) } catch (Exception ex) { - Console.WriteLine(ex.Message); + _logger.Error($"[Python-Processor] : Process Error : {functionKey} : {ex.Message}"); } } } @@ -110,7 +117,7 @@ public IObservationInput Process(ProcessObservation observation) } catch (Exception ex) { - Console.WriteLine(ex.Message); + _logger.Error($"[Python-Processor] : Error During Process : {ex.Message}"); } if (outputObservation != null) @@ -134,27 +141,27 @@ public IAsset Process(IAsset asset) } - private void AddFunction(string key, Func function) + private void AddFunction(string filePath, Func function) { - if (key != null && function != null) + if (filePath != null && function != null) { lock (_lock) { - _functions.Remove(key); - _functions.Add(key, function); + _functions.Remove(filePath); + _functions.Add(filePath, function); } } } - private Func GetFunction(string key) + private Func GetFunction(string filePath) { - if (key != null) + if (filePath != null) { lock (_lock) { - if (_functions.ContainsKey(key)) + if (_functions.ContainsKey(filePath)) { - return _functions[key]; + return _functions[filePath]; } } } @@ -172,5 +179,57 @@ private void RemoveFunction(string key) } } } + + + private void StartWatcher() + { + _watcher = new FileSystemWatcher(); + _watcher.Path = GetDirectory(); + _watcher.Filter = $"*{_defaultExtension}"; + _watcher.Created += FileWatcherUpdated; + _watcher.Changed += FileWatcherUpdated; + _watcher.Deleted += FileWatcherUpdated; + _watcher.EnableRaisingEvents = true; + + _watcherTimer = new System.Timers.Timer(); + _watcherTimer.Interval = DefaultUpdateInterval; + _watcherTimer.Elapsed += WatcherTimerElapsed; + _watcherTimer.Enabled = true; + } + + private void StopWatcher() + { + if (_watcher != null) _watcher.Dispose(); + if (_watcherTimer != null) _watcherTimer.Dispose(); + } + + private void FileWatcherUpdated(object sender, FileSystemEventArgs e) + { + _update = true; + } + + private void WatcherTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) + { + Update(); + } + + private void Update() + { + if (_update) + { + Load(); + + _update = false; + } + } + + + private string GetDirectory() + { + var dir = _configuration.Directory; + if (string.IsNullOrEmpty(dir)) dir = _defaultDirectory; + if (!Path.IsPathRooted(dir)) dir = Path.Combine(AppContext.BaseDirectory, dir); + return dir; + } } } \ No newline at end of file