mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-08-31 19:08:29 +00:00
refactor: Refactor DeviceManager and related classes to improve thread safety and performance
- Replaced CCriticalSection with lock statements in DeviceManager for better thread management. - Updated AddDevice and RemoveDevice methods to use Monitor for locking. - Enhanced event handling for device activation and registration. - Modified FileIO class to utilize Task for asynchronous file operations instead of CrestronInvoke. - Improved feedback mechanisms in FeedbackBase and SystemMonitorController using Task.Run. - Refactored GenericQueue to remove Crestron threading dependencies and utilize System.Threading. - Updated BlueJeansPc and VideoCodecBase classes to use Task for asynchronous operations. - Cleaned up unnecessary critical sections and improved code documentation across various files.
This commit is contained in:
parent
426ef4ad6b
commit
346a5e9e57
23 changed files with 998 additions and 912 deletions
|
|
@ -1,10 +1,9 @@
|
|||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using System.Threading;
|
||||
using Timer = System.Timers.Timer;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
|
@ -18,12 +17,27 @@ namespace PepperDash.Essentials.Core.Config;
|
|||
/// </summary>
|
||||
public class ConfigWriter
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the subfolder where the config file will be written
|
||||
/// </summary>
|
||||
public const string LocalConfigFolder = "LocalConfig";
|
||||
|
||||
public const long WriteTimeout = 30000;
|
||||
/// <summary>
|
||||
/// The amount of time in milliseconds to wait after the last config update before writing the config file. This is to prevent multiple rapid updates from causing multiple file writes.
|
||||
/// Default is 30 seconds.
|
||||
/// </summary>
|
||||
public const long WriteTimeoutInMs = 30000;
|
||||
|
||||
public static CTimer WriteTimer;
|
||||
static CCriticalSection fileLock = new CCriticalSection();
|
||||
private static Timer WriteTimer;
|
||||
static readonly object _fileLock = new();
|
||||
|
||||
|
||||
static ConfigWriter()
|
||||
{
|
||||
WriteTimer = new Timer(WriteTimeoutInMs);
|
||||
WriteTimer.Elapsed += (s, e) => WriteConfigFile(null);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the config properties of a device
|
||||
|
|
@ -53,6 +67,9 @@ public class ConfigWriter
|
|||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the config properties of a device
|
||||
/// </summary>
|
||||
public static bool UpdateDeviceConfig(DeviceConfig config)
|
||||
{
|
||||
bool success = false;
|
||||
|
|
@ -73,17 +90,20 @@ public class ConfigWriter
|
|||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the config properties of a room
|
||||
/// </summary>
|
||||
public static bool UpdateRoomConfig(DeviceConfig config)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
var roomConfigIndex = ConfigReader.ConfigObject.Rooms.FindIndex(d => d.Key.Equals(config.Key));
|
||||
var roomConfigIndex = ConfigReader.ConfigObject.Rooms.FindIndex(d => d.Key.Equals(config.Key));
|
||||
|
||||
if (roomConfigIndex >= 0)
|
||||
if (roomConfigIndex >= 0)
|
||||
{
|
||||
ConfigReader.ConfigObject.Rooms[roomConfigIndex] = config;
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Updated room of device: '{0}'", config.Key);
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Updated config of room: '{0}'", config.Key);
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
|
@ -98,10 +118,9 @@ public class ConfigWriter
|
|||
/// </summary>
|
||||
static void ResetTimer()
|
||||
{
|
||||
if (WriteTimer == null)
|
||||
WriteTimer = new CTimer(WriteConfigFile, WriteTimeout);
|
||||
|
||||
WriteTimer.Reset(WriteTimeout);
|
||||
WriteTimer.Stop();
|
||||
WriteTimer.Interval = WriteTimeoutInMs;
|
||||
WriteTimer.Start();
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Config File write timer has been reset.");
|
||||
}
|
||||
|
|
@ -120,10 +139,10 @@ public class ConfigWriter
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes
|
||||
/// Writes the specified configuration data to a file.
|
||||
/// </summary>
|
||||
/// <param name="filepath"></param>
|
||||
/// <param name="o"></param>
|
||||
/// <param name="filePath">The path of the file to write to.</param>
|
||||
/// <param name="configData">The configuration data to write.</param>
|
||||
public static void WriteFile(string filePath, string configData)
|
||||
{
|
||||
if (WriteTimer != null)
|
||||
|
|
@ -133,9 +152,11 @@ public class ConfigWriter
|
|||
|
||||
Debug.LogMessage(LogEventLevel.Information, "Attempting to write config file: '{0}'", filePath);
|
||||
|
||||
var lockAcquired = false;
|
||||
try
|
||||
{
|
||||
if (fileLock.TryEnter())
|
||||
lockAcquired = Monitor.TryEnter(_fileLock);
|
||||
if (lockAcquired)
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter(filePath))
|
||||
{
|
||||
|
|
@ -154,11 +175,8 @@ public class ConfigWriter
|
|||
}
|
||||
finally
|
||||
{
|
||||
if (fileLock != null && !fileLock.Disposed)
|
||||
fileLock.Leave();
|
||||
|
||||
if (lockAcquired)
|
||||
Monitor.Exit(_fileLock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -3,11 +3,13 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using PepperDash.Core;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Essentials.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Core.DeviceInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Helper methods for network devices
|
||||
/// </summary>
|
||||
public static class NetworkDeviceHelpers
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -24,7 +26,7 @@ public static class NetworkDeviceHelpers
|
|||
private static readonly char NewLineSplitter = CrestronEnvironment.NewLine.ToCharArray().First();
|
||||
private static readonly string NewLine = CrestronEnvironment.NewLine;
|
||||
|
||||
private static readonly CCriticalSection Lock = new CCriticalSection();
|
||||
private static readonly object _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Last resolved ARP table - it is recommended to refresh the arp before using this.
|
||||
|
|
@ -37,46 +39,45 @@ public static class NetworkDeviceHelpers
|
|||
public static void RefreshArp()
|
||||
{
|
||||
var error = false;
|
||||
try
|
||||
lock (_lock)
|
||||
{
|
||||
Lock.Enter();
|
||||
var consoleResponse = string.Empty;
|
||||
if (!CrestronConsole.SendControlSystemCommand("showarptable", ref consoleResponse)) return;
|
||||
if (string.IsNullOrEmpty(consoleResponse))
|
||||
try
|
||||
{
|
||||
var consoleResponse = string.Empty;
|
||||
if (!CrestronConsole.SendControlSystemCommand("showarptable", ref consoleResponse)) return;
|
||||
if (string.IsNullOrEmpty(consoleResponse))
|
||||
{
|
||||
error = true;
|
||||
return;
|
||||
}
|
||||
ArpTable.Clear();
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "ConsoleResponse of 'showarptable' : {0}{1}", NewLine, consoleResponse);
|
||||
|
||||
var myLines =
|
||||
consoleResponse.Split(NewLineSplitter)
|
||||
.ToList()
|
||||
.Where(o => (o.Contains(':') && !o.Contains("Type", StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList();
|
||||
foreach (var line in myLines)
|
||||
{
|
||||
var item = line;
|
||||
var seperator = item.Contains('\t') ? '\t' : ' ';
|
||||
var dataPoints = item.Split(seperator);
|
||||
if (dataPoints == null || dataPoints.Length < 2) continue;
|
||||
var ipAddress = SanitizeIpAddress(dataPoints.First().TrimAll());
|
||||
var macAddress = dataPoints.Last();
|
||||
ArpTable.Add(new ArpEntry(ipAddress, macAddress));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Exception in \"RefreshArp\" : {0}", ex.Message);
|
||||
error = true;
|
||||
return;
|
||||
}
|
||||
ArpTable.Clear();
|
||||
} // end lock
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "ConsoleResponse of 'showarptable' : {0}{1}", NewLine, consoleResponse);
|
||||
|
||||
var myLines =
|
||||
consoleResponse.Split(NewLineSplitter)
|
||||
.ToList()
|
||||
.Where(o => (o.Contains(':') && !o.Contains("Type", StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList();
|
||||
foreach (var line in myLines)
|
||||
{
|
||||
var item = line;
|
||||
var seperator = item.Contains('\t') ? '\t' : ' ';
|
||||
var dataPoints = item.Split(seperator);
|
||||
if (dataPoints == null || dataPoints.Length < 2) continue;
|
||||
var ipAddress = SanitizeIpAddress(dataPoints.First().TrimAll());
|
||||
var macAddress = dataPoints.Last();
|
||||
ArpTable.Add(new ArpEntry(ipAddress, macAddress));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Exception in \"RefreshArp\" : {0}", ex.Message);
|
||||
error = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Lock.Leave();
|
||||
OnArpTableUpdated(new ArpTableEventArgs(ArpTable, error));
|
||||
}
|
||||
OnArpTableUpdated(new ArpTableEventArgs(ArpTable, error));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -158,7 +159,14 @@ public static class NetworkDeviceHelpers
|
|||
/// </summary>
|
||||
public class ArpEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// IP Address of the ARP entry
|
||||
/// </summary>
|
||||
public readonly IPAddress IpAddress;
|
||||
|
||||
/// <summary>
|
||||
/// MAC Address of the ARP entry
|
||||
/// </summary>
|
||||
public readonly string MacAddress;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharpPro;
|
||||
using PepperDash.Core;
|
||||
|
|
@ -10,13 +11,27 @@ using Serilog.Events;
|
|||
|
||||
namespace PepperDash.Essentials.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Manages devices in the system, including activation and console commands to interact with devices
|
||||
/// </summary>
|
||||
public static class DeviceManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Event raised when all devices have been activated
|
||||
/// </summary>
|
||||
public static event EventHandler<EventArgs> AllDevicesActivated;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when all devices have been registered
|
||||
/// </summary>
|
||||
public static event EventHandler<EventArgs> AllDevicesRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when all devices have been initialized
|
||||
/// </summary>
|
||||
public static event EventHandler<EventArgs> AllDevicesInitialized;
|
||||
|
||||
private static readonly CCriticalSection DeviceCriticalSection = new CCriticalSection();
|
||||
private static readonly object _deviceLock = new();
|
||||
|
||||
//public static List<Device> Devices { get { return _Devices; } }
|
||||
//static List<Device> _Devices = new List<Device>();
|
||||
|
|
@ -28,7 +43,10 @@ public static class DeviceManager
|
|||
/// </summary>
|
||||
public static List<IKeyed> AllDevices => [.. Devices.Values];
|
||||
|
||||
public static bool AddDeviceEnabled;
|
||||
/// <summary>
|
||||
/// Flag to indicate whether adding devices is currently allowed. This is set to false once ActivateAll is called to prevent changes to the device list after activation.
|
||||
/// </summary>
|
||||
public static bool AddDeviceEnabled { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the control system by enabling device management and registering console commands.
|
||||
|
|
@ -65,11 +83,10 @@ public static class DeviceManager
|
|||
/// </summary>
|
||||
public static void ActivateAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
OnAllDevicesRegistered();
|
||||
OnAllDevicesRegistered();
|
||||
|
||||
DeviceCriticalSection.Enter();
|
||||
lock (_deviceLock)
|
||||
{
|
||||
AddDeviceEnabled = false;
|
||||
// PreActivate all devices
|
||||
Debug.LogMessage(LogEventLevel.Information, "****PreActivation starting...****");
|
||||
|
|
@ -125,11 +142,7 @@ public static class DeviceManager
|
|||
Debug.LogMessage(LogEventLevel.Information, "****PostActivation complete****");
|
||||
|
||||
OnAllDevicesActivated();
|
||||
}
|
||||
finally
|
||||
{
|
||||
DeviceCriticalSection.Leave();
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
private static void DeviceManager_Initialized(object sender, EventArgs e)
|
||||
|
|
@ -176,18 +189,13 @@ public static class DeviceManager
|
|||
/// </summary>
|
||||
public static void DeactivateAll()
|
||||
{
|
||||
try
|
||||
lock (_deviceLock)
|
||||
{
|
||||
DeviceCriticalSection.Enter();
|
||||
foreach (var d in Devices.Values.OfType<Device>())
|
||||
{
|
||||
d.Deactivate();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
DeviceCriticalSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
//static void ListMethods(string devKey)
|
||||
|
|
@ -266,11 +274,16 @@ public static class DeviceManager
|
|||
// Debug.LogMessage(LogEventLevel.Information, "Not yet implemented. Stay tuned");
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a device to the manager
|
||||
/// </summary>
|
||||
public static void AddDevice(IKeyed newDev)
|
||||
{
|
||||
var lockAcquired = false;
|
||||
try
|
||||
{
|
||||
if (!DeviceCriticalSection.TryEnter())
|
||||
lockAcquired = Monitor.TryEnter(_deviceLock);
|
||||
if (!lockAcquired)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Currently unable to add devices to Device Manager. Please try again");
|
||||
return;
|
||||
|
|
@ -300,15 +313,22 @@ public static class DeviceManager
|
|||
}
|
||||
finally
|
||||
{
|
||||
DeviceCriticalSection.Leave();
|
||||
if (lockAcquired)
|
||||
Monitor.Exit(_deviceLock);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a list of devices to the manager
|
||||
/// </summary>
|
||||
/// <param name="devicesToAdd"></param>
|
||||
public static void AddDevice(IEnumerable<IKeyed> devicesToAdd)
|
||||
{
|
||||
var lockAcquired = false;
|
||||
try
|
||||
{
|
||||
if (!DeviceCriticalSection.TryEnter())
|
||||
lockAcquired = Monitor.TryEnter(_deviceLock);
|
||||
if (!lockAcquired)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information,
|
||||
"Currently unable to add devices to Device Manager. Please try again");
|
||||
|
|
@ -336,15 +356,19 @@ public static class DeviceManager
|
|||
}
|
||||
finally
|
||||
{
|
||||
DeviceCriticalSection.Leave();
|
||||
if (lockAcquired)
|
||||
Monitor.Exit(_deviceLock);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a device from the manager
|
||||
/// </summary>
|
||||
/// <param name="newDev">The device to remove</param>
|
||||
public static void RemoveDevice(IKeyed newDev)
|
||||
{
|
||||
try
|
||||
lock (_deviceLock)
|
||||
{
|
||||
DeviceCriticalSection.Enter();
|
||||
if (newDev == null)
|
||||
return;
|
||||
if (Devices.ContainsKey(newDev.Key))
|
||||
|
|
@ -354,18 +378,22 @@ public static class DeviceManager
|
|||
else
|
||||
Debug.LogMessage(LogEventLevel.Information, "Device manager: Device '{0}' does not exist in manager. Cannot remove", newDev.Key);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DeviceCriticalSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all device keys currently in the manager
|
||||
/// </summary>
|
||||
/// <returns>A list of device keys</returns>
|
||||
/// <remarks>This method provides a way to retrieve a list of all device keys currently registered in the Device Manager. It returns an enumerable collection of strings representing the keys of the devices, allowing for easy access and manipulation of the device list as needed.</remarks>
|
||||
public static IEnumerable<string> GetDeviceKeys()
|
||||
{
|
||||
//return _Devices.Select(d => d.Key).ToList();
|
||||
return Devices.Keys;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all devices currently in the manager
|
||||
/// </summary> <returns>A list of devices</returns>
|
||||
public static IEnumerable<IKeyed> GetDevices()
|
||||
{
|
||||
//return _Devices.Select(d => d.Key).ToList();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
|
||||
|
|
@ -77,11 +78,11 @@ namespace PepperDash.Essentials.Core
|
|||
public abstract void FireUpdate();
|
||||
|
||||
/// <summary>
|
||||
/// Fires the update asynchronously within a CrestronInvoke
|
||||
/// Fires the update asynchronously within a Task
|
||||
/// </summary>
|
||||
public void InvokeFireUpdate()
|
||||
{
|
||||
CrestronInvoke.BeginInvoke(o => FireUpdate());
|
||||
Task.Run(() => FireUpdate());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using PepperDash.Core;
|
||||
using Crestron.SimplSharpPro.CrestronThread;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
|
|
@ -16,7 +15,7 @@ namespace PepperDash.Essentials.Core
|
|||
public static class FileIO
|
||||
{
|
||||
|
||||
static CCriticalSection fileLock = new CCriticalSection();
|
||||
static readonly object _fileLock = new();
|
||||
/// <summary>
|
||||
/// Delegate for GotFileEventHandler
|
||||
/// </summary>
|
||||
|
|
@ -103,9 +102,11 @@ namespace PepperDash.Essentials.Core
|
|||
/// </summary>
|
||||
public static string ReadDataFromFile(FileInfo file)
|
||||
{
|
||||
var lockAcquired = false;
|
||||
try
|
||||
{
|
||||
if (fileLock.TryEnter())
|
||||
lockAcquired = Monitor.TryEnter(_fileLock);
|
||||
if (lockAcquired)
|
||||
{
|
||||
DirectoryInfo dirInfo = new DirectoryInfo(file.DirectoryName);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "FileIO Getting Data {0}", file.FullName);
|
||||
|
|
@ -128,7 +129,6 @@ namespace PepperDash.Essentials.Core
|
|||
Debug.LogMessage(LogEventLevel.Information, "FileIO Unable to enter FileLock");
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -137,9 +137,8 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
finally
|
||||
{
|
||||
if (fileLock != null && !fileLock.Disposed)
|
||||
fileLock.Leave();
|
||||
|
||||
if (lockAcquired)
|
||||
Monitor.Exit(_fileLock);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +165,7 @@ namespace PepperDash.Essentials.Core
|
|||
{
|
||||
try
|
||||
{
|
||||
CrestronInvoke.BeginInvoke(o => _ReadDataFromFileASync(file));
|
||||
Task.Run(() => _ReadDataFromFileASync(file));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -177,9 +176,11 @@ namespace PepperDash.Essentials.Core
|
|||
private static void _ReadDataFromFileASync(FileInfo file)
|
||||
{
|
||||
string data;
|
||||
var lockAcquired = false;
|
||||
try
|
||||
{
|
||||
if (fileLock.TryEnter())
|
||||
lockAcquired = Monitor.TryEnter(_fileLock);
|
||||
if (lockAcquired)
|
||||
{
|
||||
DirectoryInfo dirInfo = new DirectoryInfo(file.Name);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "FileIO Getting Data {0}", file.FullName);
|
||||
|
|
@ -212,13 +213,9 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
finally
|
||||
{
|
||||
if (fileLock != null && !fileLock.Disposed)
|
||||
fileLock.Leave();
|
||||
|
||||
if (lockAcquired)
|
||||
Monitor.Exit(_fileLock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -228,35 +225,35 @@ namespace PepperDash.Essentials.Core
|
|||
/// <param name="filePath"></param>
|
||||
public static void WriteDataToFile(string data, string filePath)
|
||||
{
|
||||
Thread _WriteFileThread;
|
||||
_WriteFileThread = new Thread((O) => _WriteFileMethod(data, Global.FilePathPrefix + "/" + filePath), null, Thread.eThreadStartOptions.CreateSuspended);
|
||||
_WriteFileThread.Priority = Thread.eThreadPriority.LowestPriority;
|
||||
var _WriteFileThread = new System.Threading.Thread(() => _WriteFileMethod(data, Global.FilePathPrefix + "/" + filePath))
|
||||
{
|
||||
IsBackground = true,
|
||||
Priority = ThreadPriority.Lowest
|
||||
};
|
||||
_WriteFileThread.Start();
|
||||
Debug.LogMessage(LogEventLevel.Information, "New WriteFile Thread");
|
||||
|
||||
}
|
||||
|
||||
static object _WriteFileMethod(string data, string filePath)
|
||||
static void _WriteFileMethod(string data, string filePath)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Attempting to write file: '{0}'", filePath);
|
||||
|
||||
var lockAcquired = false;
|
||||
try
|
||||
{
|
||||
if (fileLock.TryEnter())
|
||||
lockAcquired = Monitor.TryEnter(_fileLock);
|
||||
if (lockAcquired)
|
||||
{
|
||||
|
||||
using (StreamWriter sw = new StreamWriter(filePath))
|
||||
{
|
||||
sw.Write(data);
|
||||
sw.Flush();
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "FileIO Unable to enter FileLock");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -264,12 +261,9 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
finally
|
||||
{
|
||||
if (fileLock != null && !fileLock.Disposed)
|
||||
fileLock.Leave();
|
||||
|
||||
if (lockAcquired)
|
||||
Monitor.Exit(_fileLock);
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using Timer = System.Timers.Timer;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Crestron.SimplSharp.CrestronXml;
|
||||
|
|
@ -78,6 +78,8 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||
|
||||
private bool _helpRequestSent;
|
||||
|
||||
private readonly object _guidFileLock = new();
|
||||
|
||||
private eFusionHelpResponse _helpRequestStatus;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -390,44 +392,31 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||
return;
|
||||
}
|
||||
|
||||
var fileLock = new CCriticalSection();
|
||||
|
||||
try
|
||||
lock (_guidFileLock)
|
||||
{
|
||||
if (fileLock.Disposed)
|
||||
try
|
||||
{
|
||||
return;
|
||||
Debug.LogMessage(LogEventLevel.Debug, this, "Writing GUIDs to file");
|
||||
|
||||
_guids = FusionOccSensor == null
|
||||
? new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets)
|
||||
: new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets, FusionOccSensor);
|
||||
|
||||
var json = JsonConvert.SerializeObject(_guids, Newtonsoft.Json.Formatting.Indented);
|
||||
|
||||
using (var sw = new StreamWriter(filePath))
|
||||
{
|
||||
sw.Write(json);
|
||||
sw.Flush();
|
||||
}
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, this, "Guids successfully written to file '{0}'", filePath);
|
||||
}
|
||||
|
||||
fileLock.Enter();
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, this, "Writing GUIDs to file");
|
||||
|
||||
_guids = FusionOccSensor == null
|
||||
? new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets)
|
||||
: new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets, FusionOccSensor);
|
||||
|
||||
var json = JsonConvert.SerializeObject(_guids, Newtonsoft.Json.Formatting.Indented);
|
||||
|
||||
using (var sw = new StreamWriter(filePath))
|
||||
catch (Exception e)
|
||||
{
|
||||
sw.Write(json);
|
||||
sw.Flush();
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "Error writing guid file: {0}", e);
|
||||
}
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, this, "Guids successfully written to file '{0}'", filePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "Error writing guid file: {0}", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!fileLock.Disposed)
|
||||
{
|
||||
fileLock.Leave();
|
||||
}
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -442,50 +431,37 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||
return;
|
||||
}
|
||||
|
||||
var fileLock = new CCriticalSection();
|
||||
|
||||
try
|
||||
lock (_guidFileLock)
|
||||
{
|
||||
if (fileLock.Disposed)
|
||||
try
|
||||
{
|
||||
return;
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
var json = File.ReadToEnd(filePath, Encoding.ASCII);
|
||||
|
||||
_guids = JsonConvert.DeserializeObject<FusionRoomGuids>(json);
|
||||
|
||||
// _config.IpId = _guids.IpId;
|
||||
|
||||
FusionStaticAssets = _guids.StaticAssets;
|
||||
}
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "Fusion Guids successfully read from file: {0}",
|
||||
filePath);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, this, "\r\n********************\r\n\tRoom Name: {0}\r\n\tIPID: {1:X}\r\n\tRoomGuid: {2}\r\n*******************", Room.Name, _config.IpIdInt, RoomGuid);
|
||||
|
||||
foreach (var item in FusionStaticAssets)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name,
|
||||
item.Value.SlotNumber, item.Value.InstanceId);
|
||||
}
|
||||
}
|
||||
|
||||
fileLock.Enter();
|
||||
|
||||
if (File.Exists(filePath))
|
||||
catch (Exception e)
|
||||
{
|
||||
var json = File.ReadToEnd(filePath, Encoding.ASCII);
|
||||
|
||||
_guids = JsonConvert.DeserializeObject<FusionRoomGuids>(json);
|
||||
|
||||
// _config.IpId = _guids.IpId;
|
||||
|
||||
FusionStaticAssets = _guids.StaticAssets;
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "Error reading guid file: {0}", e);
|
||||
}
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "Fusion Guids successfully read from file: {0}",
|
||||
filePath);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, this, "\r\n********************\r\n\tRoom Name: {0}\r\n\tIPID: {1:X}\r\n\tRoomGuid: {2}\r\n*******************", Room.Name, _config.IpIdInt, RoomGuid);
|
||||
|
||||
foreach (var item in FusionStaticAssets)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name,
|
||||
item.Value.SlotNumber, item.Value.InstanceId);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "Error reading guid file: {0}", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!fileLock.Disposed)
|
||||
{
|
||||
fileLock.Leave();
|
||||
}
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1950,12 +1926,13 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||
HelpRequestStatusFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
private void OnTimedEvent(object source, ElapsedEventArgs e)
|
||||
private void OnTimedEvent(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
this.LogInformation("Help request timeout reached for room '{0}'. Cancelling help request.", Room.Name);
|
||||
CancelHelpRequest();
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CancelHelpRequest()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Newtonsoft.Json;
|
|||
using Newtonsoft.Json.Converters;
|
||||
using PepperDash.Essentials.Core.Bridges;
|
||||
using Serilog.Events;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Monitoring;
|
||||
|
||||
|
|
@ -272,7 +273,7 @@ public class SystemMonitorController : EssentialsBridgeableDevice
|
|||
private void RefreshSystemMonitorData()
|
||||
{
|
||||
// this takes a while, launch a new thread
|
||||
CrestronInvoke.BeginInvoke(UpdateFeedback);
|
||||
Task.Run(() => UpdateFeedback(null));
|
||||
}
|
||||
|
||||
private void UpdateFeedback(object o)
|
||||
|
|
@ -744,7 +745,7 @@ public class ProgramStatusFeedbacks
|
|||
/// </summary>
|
||||
public void GetProgramInfo()
|
||||
{
|
||||
CrestronInvoke.BeginInvoke(GetProgramInfo);
|
||||
Task.Run(() => GetProgramInfo(null));
|
||||
}
|
||||
|
||||
private void GetProgramInfo(object o)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
|
||||
//using SSMono.IO;
|
||||
using PepperDash.Core.WebApi.Presets;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Presets;
|
||||
|
|
@ -19,11 +18,19 @@ namespace PepperDash.Essentials.Core.Presets;
|
|||
/// </summary>
|
||||
public class DevicePresetsModel : Device
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for PresetRecalled event, which is fired when a preset is recalled. Provides the device and channel that was recalled.
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
/// <param name="channel"></param>
|
||||
public delegate void PresetRecalledCallback(ISetTopBoxNumericKeypad device, string channel);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for PresetsSaved event, which is fired when presets are saved. Provides the list of presets that were saved.
|
||||
/// </summary> <param name="presets"></param>
|
||||
public delegate void PresetsSavedCallback(List<PresetChannel> presets);
|
||||
|
||||
private readonly CCriticalSection _fileOps = new CCriticalSection();
|
||||
private readonly object _fileOps = new();
|
||||
private readonly bool _initSuccess;
|
||||
|
||||
private readonly ISetTopBoxNumericKeypad _setTopBox;
|
||||
|
|
@ -37,6 +44,12 @@ public class DevicePresetsModel : Device
|
|||
private Action<bool> _enterFunction;
|
||||
private string _filePath;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for DevicePresetsModel when a set top box device is included. If the set top box does not implement the required INumericKeypad interface, the model will still be created but dialing functionality will be disabled and a message will be logged.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="setTopBox"></param>
|
||||
/// <param name="fileName"></param>
|
||||
public DevicePresetsModel(string key, ISetTopBoxNumericKeypad setTopBox, string fileName)
|
||||
: this(key, fileName)
|
||||
{
|
||||
|
|
@ -71,6 +84,11 @@ public class DevicePresetsModel : Device
|
|||
_enterFunction = setTopBox.KeypadEnter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for DevicePresetsModel when only a file name is provided. Dialing functionality will be disabled.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="fileName"></param>
|
||||
public DevicePresetsModel(string key, string fileName) : base(key)
|
||||
{
|
||||
PulseTime = 150;
|
||||
|
|
@ -88,27 +106,73 @@ public class DevicePresetsModel : Device
|
|||
_initSuccess = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when a preset is recalled, providing the device and channel that was recalled
|
||||
/// </summary>
|
||||
public event PresetRecalledCallback PresetRecalled;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when presets are saved, providing the list of presets that were saved
|
||||
/// </summary>
|
||||
public event PresetsSavedCallback PresetsSaved;
|
||||
|
||||
public int PulseTime { get; set; }
|
||||
public int DigitSpacingMs { get; set; }
|
||||
/// <summary>
|
||||
/// Time in milliseconds to pulse the digit for when dialing a channel
|
||||
/// </summary>
|
||||
public int PulseTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time in milliseconds to wait between pulsing digits when dialing a channel
|
||||
/// </summary>
|
||||
public int DigitSpacingMs { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the presets have finished loading from the file or not
|
||||
/// </summary>
|
||||
public bool PresetsAreLoaded { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of presets to display
|
||||
/// </summary>
|
||||
public List<PresetChannel> PresetsList { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of presets in the list
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return PresetsList != null ? PresetsList.Count : 0; }
|
||||
}
|
||||
|
||||
public bool UseLocalImageStorage { get; set; }
|
||||
public string ImagesLocalHostPrefix { get; set; }
|
||||
public string ImagesPathPrefix { get; set; }
|
||||
public string ListPathPrefix { get; set; }
|
||||
/// <summary>
|
||||
/// Indicates whether to use local image storage for preset images, which allows for more and larger images than the SIMPL+ zip file method
|
||||
/// </summary>
|
||||
public bool UseLocalImageStorage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The prefix for the local host URL for preset images
|
||||
/// </summary>
|
||||
public string ImagesLocalHostPrefix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path prefix for preset images
|
||||
/// </summary>
|
||||
public string ImagesPathPrefix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path prefix for preset lists
|
||||
/// </summary>
|
||||
public string ListPathPrefix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when presets are loaded
|
||||
/// </summary>
|
||||
public event EventHandler PresetsLoaded;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the file name for the presets list and loads the presets from that file. The file should be a JSON file in the format of the PresetsList class. If the file cannot be read, an empty list will be created and a message will be logged. This method is thread safe.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the presets file.</param>
|
||||
public void SetFileName(string path)
|
||||
{
|
||||
_filePath = ListPathPrefix + path;
|
||||
|
|
@ -117,12 +181,13 @@ public class DevicePresetsModel : Device
|
|||
LoadChannels();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the presets from the file specified by _filePath.
|
||||
/// </summary>
|
||||
public void LoadChannels()
|
||||
{
|
||||
try
|
||||
lock (_fileOps)
|
||||
{
|
||||
_fileOps.Enter();
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Loading presets from {0}", _filePath);
|
||||
PresetsAreLoaded = false;
|
||||
try
|
||||
|
|
@ -149,12 +214,12 @@ public class DevicePresetsModel : Device
|
|||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_fileOps.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dials a preset by its number in the list (starting at 1). If the preset number is out of range, nothing will happen.
|
||||
/// </summary>
|
||||
/// <param name="presetNum">The number of the preset to dial, starting at 1</param>
|
||||
public void Dial(int presetNum)
|
||||
{
|
||||
if (presetNum <= PresetsList.Count)
|
||||
|
|
@ -163,6 +228,10 @@ public class DevicePresetsModel : Device
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dials a preset by its channel number. If the channel number contains characters that are not 0-9 or '-', those characters will be ignored.
|
||||
/// If the model was not initialized with a valid set top box device, dialing will be disabled and a message will be logged.
|
||||
/// </summary> <param name="chanNum">The channel number to dial</param>
|
||||
public void Dial(string chanNum)
|
||||
{
|
||||
if (_dialIsRunning || !_initSuccess)
|
||||
|
|
@ -176,7 +245,7 @@ public class DevicePresetsModel : Device
|
|||
}
|
||||
|
||||
_dialIsRunning = true;
|
||||
CrestronInvoke.BeginInvoke(o =>
|
||||
Task.Run(() =>
|
||||
{
|
||||
foreach (var c in chanNum.ToCharArray())
|
||||
{
|
||||
|
|
@ -199,6 +268,11 @@ public class DevicePresetsModel : Device
|
|||
OnPresetRecalled(_setTopBox, chanNum);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dials a preset by its number in the list (starting at 1) using the provided set top box device. If the preset number is out of range, nothing will happen.
|
||||
/// </summary>
|
||||
/// <param name="presetNum"></param>
|
||||
/// <param name="setTopBox"></param>
|
||||
public void Dial(int presetNum, ISetTopBoxNumericKeypad setTopBox)
|
||||
{
|
||||
if (presetNum <= PresetsList.Count)
|
||||
|
|
@ -207,6 +281,13 @@ public class DevicePresetsModel : Device
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dials a preset by its channel number using the provided set top box device. If the channel number contains characters that are not 0-9 or '-', those characters will be ignored.
|
||||
/// If the provided set top box device does not implement the required INumericKeypad interface, dialing will be disabled and a message will be logged.
|
||||
/// If the model was not initialized with a valid set top box device, dialing will be disabled and a message will be logged.
|
||||
/// </summary>
|
||||
/// <param name="chanNum"></param>
|
||||
/// <param name="setTopBox"></param>
|
||||
public void Dial(string chanNum, ISetTopBoxNumericKeypad setTopBox)
|
||||
{
|
||||
_dialFunctions = new Dictionary<char, Action<bool>>(10)
|
||||
|
|
@ -243,6 +324,11 @@ public class DevicePresetsModel : Device
|
|||
handler(setTopBox, channel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the preset at the given index with the provided preset information, then saves the updated presets list to the file. If the index is out of range, nothing will happen.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the preset to update, starting at 0</param>
|
||||
/// <param name="preset">The preset information to update</param>
|
||||
public void UpdatePreset(int index, PresetChannel preset)
|
||||
{
|
||||
if (index >= PresetsList.Count)
|
||||
|
|
@ -257,6 +343,10 @@ public class DevicePresetsModel : Device
|
|||
OnPresetsSaved();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the entire presets list with the provided list, then saves the updated presets list to the file. If the provided list is null, nothing will happen.
|
||||
/// </summary>
|
||||
/// <param name="presets"></param>
|
||||
public void UpdatePresets(List<PresetChannel> presets)
|
||||
{
|
||||
PresetsList = presets;
|
||||
|
|
@ -268,10 +358,9 @@ public class DevicePresetsModel : Device
|
|||
|
||||
private void SavePresets()
|
||||
{
|
||||
try
|
||||
lock (_fileOps)
|
||||
{
|
||||
_fileOps.Enter();
|
||||
var pl = new PresetsList {Channels = PresetsList, Name = Name};
|
||||
var pl = new PresetsList { Channels = PresetsList, Name = Name };
|
||||
var json = JsonConvert.SerializeObject(pl, Formatting.Indented);
|
||||
|
||||
using (var file = File.Open(_filePath, FileMode.Truncate))
|
||||
|
|
@ -279,11 +368,6 @@ public class DevicePresetsModel : Device
|
|||
file.Write(json, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_fileOps.Leave();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void OnPresetsSaved()
|
||||
|
|
|
|||
|
|
@ -4,24 +4,35 @@ using System.Threading;
|
|||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
using Serilog.Events;
|
||||
using Thread = Crestron.SimplSharpPro.CrestronThread.Thread;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Queues;
|
||||
|
||||
|
||||
// TODO: The capacity argument in the constructors should be removed. Now that this class uses System.Threading rather than the Crestron library, there is no longer a thread capacity limit.
|
||||
// If a capacity limit is needed, it should be implemented by the caller by checking the QueueCount property before enqueuing items and deciding how to handle the situation when the queue is too full (e.g. drop messages, log warnings, etc.)
|
||||
|
||||
/// <summary>
|
||||
/// Threadsafe processing of queued items with pacing if required
|
||||
/// </summary>
|
||||
public class GenericQueue : IQueue<IQueueMessage>
|
||||
{
|
||||
private readonly string _key;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of items currently in the queue. This is not threadsafe, so it should only be used for informational purposes and not for processing logic.
|
||||
/// </summary>
|
||||
protected readonly ConcurrentQueue<IQueueMessage> _queue;
|
||||
|
||||
/// <summary>
|
||||
/// The thread that processes the queue items
|
||||
/// </summary>
|
||||
protected readonly Thread _worker;
|
||||
protected readonly CEvent _waitHandle = new CEvent();
|
||||
private readonly object _lock = new();
|
||||
|
||||
private bool _delayEnabled;
|
||||
private int _delayTime;
|
||||
|
||||
private const Thread.eThreadPriority _defaultPriority = Thread.eThreadPriority.MediumPriority;
|
||||
private const ThreadPriority _defaultPriority = ThreadPriority.Normal;
|
||||
|
||||
/// <summary>
|
||||
/// If the instance has been disposed.
|
||||
|
|
@ -96,7 +107,7 @@ public class GenericQueue : IQueue<IQueueMessage>
|
|||
/// <param name="key"></param>
|
||||
/// <param name="pacing"></param>
|
||||
/// <param name="priority"></param>
|
||||
public GenericQueue(string key, int pacing, Thread.eThreadPriority priority)
|
||||
public GenericQueue(string key, int pacing, ThreadPriority priority)
|
||||
: this(key, priority, 0, pacing)
|
||||
{
|
||||
}
|
||||
|
|
@ -107,7 +118,7 @@ public class GenericQueue : IQueue<IQueueMessage>
|
|||
/// <param name="key"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="capacity"></param>
|
||||
public GenericQueue(string key, Thread.eThreadPriority priority, int capacity)
|
||||
public GenericQueue(string key, ThreadPriority priority, int capacity)
|
||||
: this(key, priority, capacity, 0)
|
||||
{
|
||||
}
|
||||
|
|
@ -119,7 +130,7 @@ public class GenericQueue : IQueue<IQueueMessage>
|
|||
/// <param name="pacing"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="capacity"></param>
|
||||
public GenericQueue(string key, int pacing, Thread.eThreadPriority priority, int capacity)
|
||||
public GenericQueue(string key, int pacing, ThreadPriority priority, int capacity)
|
||||
: this(key, priority, capacity, pacing)
|
||||
{
|
||||
}
|
||||
|
|
@ -131,21 +142,18 @@ public class GenericQueue : IQueue<IQueueMessage>
|
|||
/// <param name="priority"></param>
|
||||
/// <param name="capacity"></param>
|
||||
/// <param name="pacing"></param>
|
||||
protected GenericQueue(string key, Thread.eThreadPriority priority, int capacity, int pacing)
|
||||
protected GenericQueue(string key, ThreadPriority priority, int capacity, int pacing)
|
||||
{
|
||||
_key = key;
|
||||
int cap = 25; // sets default
|
||||
if (capacity > 0)
|
||||
{
|
||||
cap = capacity; // overrides default
|
||||
}
|
||||
|
||||
_queue = new ConcurrentQueue<IQueueMessage>();
|
||||
_worker = new Thread(ProcessQueue, null, Thread.eThreadStartOptions.Running)
|
||||
_worker = new Thread(ProcessQueue)
|
||||
{
|
||||
Priority = priority,
|
||||
Name = _key
|
||||
Name = _key,
|
||||
IsBackground = true
|
||||
};
|
||||
_worker.Start();
|
||||
|
||||
SetDelayValues(pacing);
|
||||
}
|
||||
|
|
@ -167,9 +175,8 @@ public class GenericQueue : IQueue<IQueueMessage>
|
|||
/// <summary>
|
||||
/// Thread callback
|
||||
/// </summary>
|
||||
/// <param name="obj">The action used to process dequeued items</param>
|
||||
/// <returns>Null when the thread is exited</returns>
|
||||
private object ProcessQueue(object obj)
|
||||
private void ProcessQueue()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
|
|
@ -186,7 +193,7 @@ public class GenericQueue : IQueue<IQueueMessage>
|
|||
if (_delayEnabled)
|
||||
Thread.Sleep(_delayTime);
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
//swallowing this exception, as it should only happen on shut down
|
||||
}
|
||||
|
|
@ -202,12 +209,21 @@ public class GenericQueue : IQueue<IQueueMessage>
|
|||
}
|
||||
}
|
||||
}
|
||||
else _waitHandle.Wait();
|
||||
else
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_queue.IsEmpty)
|
||||
Monitor.Wait(_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues an item to be processed by the queue thread. If the queue has been disposed, the item will not be enqueued and a message will be logged.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void Enqueue(IQueueMessage item)
|
||||
{
|
||||
if (Disposed)
|
||||
|
|
@ -217,7 +233,8 @@ public class GenericQueue : IQueue<IQueueMessage>
|
|||
}
|
||||
|
||||
_queue.Enqueue(item);
|
||||
_waitHandle.Set();
|
||||
lock (_lock)
|
||||
Monitor.Pulse(_lock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -242,18 +259,17 @@ public class GenericQueue : IQueue<IQueueMessage>
|
|||
|
||||
if (disposing)
|
||||
{
|
||||
using (_waitHandle)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Disposing...");
|
||||
_queue.Enqueue(null);
|
||||
_waitHandle.Set();
|
||||
_worker.Join();
|
||||
}
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Disposing...");
|
||||
_queue.Enqueue(null);
|
||||
lock (_lock)
|
||||
Monitor.Pulse(_lock);
|
||||
_worker.Join();
|
||||
}
|
||||
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
/// Finalizer in case Dispose is not called. This will clean up the thread, but any items still in the queue will not be processed and could potentially be lost.
|
||||
~GenericQueue()
|
||||
{
|
||||
Dispose(true);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue