From fdae50a9721f5ecd6432f96903d587c3a3afc870 Mon Sep 17 00:00:00 2001 From: Nick Genovese Date: Mon, 31 Aug 2020 15:34:12 -0400 Subject: [PATCH] Adds a generic queue and string/byte implementations. Also adds a class that processes string responses from a IBasicCommunication or Gather --- .../PepperDash_Essentials_Core.csproj | 11 +- .../Queues/BytesQueue.cs | 67 ++++++++ .../Queues/GenericQueue.cs | 156 ++++++++++++++++++ .../PepperDashEssentialsBase/Queues/IQueue.cs | 15 ++ .../Queues/StringQueue.cs | 66 ++++++++ .../Queues/StringResponseProcessor.cs | 104 ++++++++++++ .../PepperDash_Essentials_DM.csproj | 6 +- .../Essentials Devices Common.csproj | 6 +- 8 files changed, 422 insertions(+), 9 deletions(-) create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/BytesQueue.cs create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/IQueue.cs create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/StringQueue.cs create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/StringResponseProcessor.cs diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index c31a46b8..fad95abf 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -98,12 +98,12 @@ False - ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll False False - ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll False @@ -113,7 +113,7 @@ False - ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe False @@ -158,6 +158,7 @@ + @@ -222,8 +223,10 @@ + + @@ -313,6 +316,8 @@ + + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/BytesQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/BytesQueue.cs new file mode 100644 index 00000000..aad96cf1 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/BytesQueue.cs @@ -0,0 +1,67 @@ +using System; +using PepperDash.Core; + +namespace PepperDash_Essentials_Core.Queues +{ + /// + /// Byte implementation of Action queue + /// + public class BytesQueue : IQueue + { + private readonly IQueue _queue; + + /// + /// Constructor for BytesQueue + /// + /// Key + /// Action to process queued bytes + public BytesQueue(string key, Action processBytesAction) + { + _queue = new GenericQueue(key, processBytesAction); + } + + /// + /// Constructor for BytesQueue + /// + /// Key + /// Action to process queued bytes + /// Delay in ms between actions being invoked + public BytesQueue(string key, Action processBytesAction, int pacing) + { + _queue = new GenericQueue(key, processBytesAction, pacing); + } + + /// + /// Enqueue a byte array to be processed + /// + /// Byte array to be processed + public void Enqueue(byte[] item) + { + _queue.Enqueue(item); + } + + /// + /// If the instance has been disposed + /// + public bool Disposed + { + get { return _queue.Disposed; } + } + + /// + /// Key + /// + public string Key + { + get { return _queue.Key; } + } + + /// + /// Disposes of resources + /// + public void Dispose() + { + _queue.Dispose(); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs new file mode 100644 index 00000000..6a06cab8 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs @@ -0,0 +1,156 @@ +using System; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.CrestronThread; +using PepperDash.Core; + +namespace PepperDash_Essentials_Core.Queues +{ + /// + /// Threadsafe processing of queued items with pacing if required + /// + /// Type of item to be processed + public class GenericQueue : IQueue where T : class + { + private readonly string _key; + protected readonly CrestronQueue _queue; + protected readonly Thread _worker; + protected readonly CEvent _waitHandle = new CEvent(); + + private readonly bool _delayEnabled; + private readonly int _delayTime; + + /// + /// If the instance has been disposed. + /// + public bool Disposed { get; private set; } + + /// + /// Constructor for generic queue with no pacing + /// + /// Key + /// Action to process items in the queue + public GenericQueue(string key, Action processQueueAction) + { + _key = key; + _queue = new CrestronQueue(); + _worker = new Thread(ProcessQueue, processQueueAction, Thread.eThreadStartOptions.Running); + + CrestronEnvironment.ProgramStatusEventHandler += programEvent => + { + if (programEvent != eProgramStatusEventType.Stopping) + return; + + Dispose(); + }; + } + + /// + /// Constructor for generic queue with no pacing + /// + /// Key + /// Action to process items in the queue + /// Pacing in ms between actions + public GenericQueue(string key, Action processQueueAction, int pacing) + : this(key, processQueueAction) + { + _delayEnabled = pacing > 0; + _delayTime = pacing; + } + + /// + /// Thread callback + /// + /// The action used to process dequeued items + /// Null when the thread is exited + private object ProcessQueue(object obj) + { + var action = obj as Action; + if (action == null) + throw new ArgumentNullException("obj"); + + while (true) + { + T item = null; + + if (_queue.Count > 0) + { + item = _queue.Dequeue(); + if (item == null) + break; + } + if (item != null) + { + try + { + Debug.Console(2, this, "Processing queue item: '{0}'", item.ToString()); + action(item); + + if (_delayEnabled) + Thread.Sleep(_delayTime); + } + catch (Exception ex) + { + Debug.ConsoleWithLog(0, this, "Caught an exception in the ComsQueue {0}\r{1}\r{2}", ex.Message, ex.InnerException, ex.StackTrace); + } + } + else _waitHandle.Wait(); + } + + return null; + } + + /// + /// Enqueues an item and processes the queue. If 'null' is enqueued the thread will close and + /// the class must be reinstantiated. + /// + /// Item to be processed + public virtual void Enqueue(T item) + { + _queue.Enqueue(item); + _waitHandle.Set(); + } + + /// + /// Disposes the thread and cleans up resources. Thread cannot be restarted once + /// disposed. + /// + public void Dispose() + { + Dispose(true); + CrestronEnvironment.GC.SuppressFinalize(this); + } + + /// + /// Actually does the disposing. If you override this method, be sure to either call the base implementation + /// or clean up all the resources yourself. + /// + /// set to true unless called from finalizer + protected void Dispose(bool disposing) + { + if (Disposed) + return; + + if (disposing) + { + Enqueue(null); + _worker.Join(); + _waitHandle.Close(); + } + + Disposed = true; + } + + ~GenericQueue() + { + Dispose(false); + } + + /// + /// Key + /// + public string Key + { + get { return _key; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/IQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/IQueue.cs new file mode 100644 index 00000000..9d69f7fa --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/IQueue.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; + +namespace PepperDash_Essentials_Core.Queues +{ + public interface IQueue : IKeyed, IDisposable where T : class + { + void Enqueue(T item); + bool Disposed { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/StringQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/StringQueue.cs new file mode 100644 index 00000000..2be49f74 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/StringQueue.cs @@ -0,0 +1,66 @@ +using System; + +namespace PepperDash_Essentials_Core.Queues +{ + /// + /// String implementation of Action Queue + /// + public class StringQueue : IQueue + { + private readonly IQueue _queue; + + /// + /// Constructor for BytesQueue + /// + /// Key + /// Action to process queued strings + public StringQueue(string key, Action processStringAction) + { + _queue = new GenericQueue(key, processStringAction); + } + + /// + /// Constructor for StringQueue + /// + /// Key + /// Action to process queued strings + /// Delay in ms between actions being invoked + public StringQueue(string key, Action processStringAction, int pacing) + { + _queue = new GenericQueue(key, processStringAction, pacing); + } + + /// + /// Enqueue a byte array to be processed + /// + /// Byte array to be processed + public void Enqueue(string item) + { + _queue.Enqueue(item); + } + + /// + /// Key + /// + public string Key + { + get { return _queue.Key; } + } + + /// + /// Disposes of resources + /// + public void Dispose() + { + _queue.Dispose(); + } + + /// + /// If the instance has been disposed + /// + public bool Disposed + { + get { return _queue.Disposed; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/StringResponseProcessor.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/StringResponseProcessor.cs new file mode 100644 index 00000000..a3c90689 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/StringResponseProcessor.cs @@ -0,0 +1,104 @@ +using System; +using Crestron.SimplSharp; +using PepperDash.Core; + +namespace PepperDash_Essentials_Core.Queues +{ + public sealed class StringResponseProcessor : IKeyed, IDisposable + { + private readonly IQueue _queue; + private readonly IBasicCommunication _coms; + private readonly CommunicationGather _gather; + + private StringResponseProcessor(string key, Action processStringAction) + { + _queue = new StringQueue(key, processStringAction); + + CrestronEnvironment.ProgramStatusEventHandler += programEvent => + { + if (programEvent != eProgramStatusEventType.Stopping) + return; + + Dispose(); + }; + } + + /// + /// Constructor that builds an instance and subscribes to coms TextReceived for processing + /// + /// Com port to process strings from + /// Action to process the incoming strings + public StringResponseProcessor(IBasicCommunication coms, Action processStringAction) + : this(coms.Key, processStringAction) + { + _coms = coms; + coms.TextReceived += OnResponseReceived; + } + + /// + /// Constructor that builds an instance and subscribes to gather Line Received for processing + /// + /// Gather to process strings from + /// Action to process the incoming strings + public StringResponseProcessor(CommunicationGather gather, Action processStringAction) + : this(gather.Port.Key, processStringAction) + { + _gather = gather; + gather.LineReceived += OnResponseReceived; + } + + private void OnResponseReceived(object sender, GenericCommMethodReceiveTextArgs args) + { + _queue.Enqueue(args.Text); + } + + /// + /// Key + /// + public string Key + { + get { return _queue.Key; } + } + + /// + /// Disposes the instance and cleans up resources. + /// + public void Dispose() + { + Dispose(true); + CrestronEnvironment.GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (Disposed) + return; + + if (disposing) + { + if (_coms != null) + _coms.TextReceived -= OnResponseReceived; + + if (_gather != null) + { + _gather.LineReceived -= OnResponseReceived; + _gather.Stop(); + } + + _queue.Dispose(); + } + + Disposed = true; + } + + /// + /// If the instance has been disposed or not. If it has, you can not use it anymore + /// + public bool Disposed { get; private set; } + + ~StringResponseProcessor() + { + Dispose(false); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj index a8843496..7c8b163a 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj +++ b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj @@ -68,12 +68,12 @@ False - ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll False False - ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll False @@ -83,7 +83,7 @@ False - ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe False diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj index 7c978bd8..22a1dc1d 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj @@ -77,12 +77,12 @@ False - ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll False False - ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll False @@ -92,7 +92,7 @@ False - ..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe False