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