mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-08-31 19:08:29 +00:00
feat: add optional room combiner operation timeout setting
This commit is contained in:
parent
e623865c2a
commit
8e9dbb6f44
2 changed files with 139 additions and 36 deletions
|
|
@ -75,8 +75,14 @@ namespace PepperDash.Essentials.Core
|
||||||
|
|
||||||
private CTimer _scenarioChangeDebounceTimer;
|
private CTimer _scenarioChangeDebounceTimer;
|
||||||
|
|
||||||
|
private CTimer _combinationOperationTimeoutTimer;
|
||||||
|
|
||||||
private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s
|
private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s
|
||||||
|
|
||||||
|
private const int DefaultCombinationOperationTimeoutSeconds = 300;
|
||||||
|
|
||||||
|
private int _combinationOperationTimeoutSeconds = DefaultCombinationOperationTimeoutSeconds;
|
||||||
|
|
||||||
private Mutex _scenarioChange = new Mutex();
|
private Mutex _scenarioChange = new Mutex();
|
||||||
|
|
||||||
private readonly object _combinationOperationLock = new object();
|
private readonly object _combinationOperationLock = new object();
|
||||||
|
|
@ -112,6 +118,12 @@ namespace PepperDash.Essentials.Core
|
||||||
_scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds;
|
_scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_propertiesConfig.CombinationOperationTimeoutSeconds.HasValue
|
||||||
|
&& _propertiesConfig.CombinationOperationTimeoutSeconds.Value > 0)
|
||||||
|
{
|
||||||
|
_combinationOperationTimeoutSeconds = _propertiesConfig.CombinationOperationTimeoutSeconds.Value;
|
||||||
|
}
|
||||||
|
|
||||||
IsInAutoModeFeedback = new BoolFeedback(() => _isInAutoMode);
|
IsInAutoModeFeedback = new BoolFeedback(() => _isInAutoMode);
|
||||||
|
|
||||||
// default to auto mode
|
// default to auto mode
|
||||||
|
|
@ -268,7 +280,7 @@ namespace PepperDash.Essentials.Core
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetCombinationOperationStatus(
|
var operationId = SetCombinationOperationStatus(
|
||||||
CombinationOperationState.InProgress,
|
CombinationOperationState.InProgress,
|
||||||
newScenario != null ? newScenario.Key : null,
|
newScenario != null ? newScenario.Key : null,
|
||||||
null,
|
null,
|
||||||
|
|
@ -294,21 +306,21 @@ namespace PepperDash.Essentials.Core
|
||||||
|
|
||||||
RoomCombinationScenarioChanged?.Invoke(this, new EventArgs());
|
RoomCombinationScenarioChanged?.Invoke(this, new EventArgs());
|
||||||
|
|
||||||
SetCombinationOperationStatus(
|
TrySetCombinationOperationTerminalStatus(
|
||||||
|
operationId,
|
||||||
CombinationOperationState.Completed,
|
CombinationOperationState.Completed,
|
||||||
_currentScenario != null ? _currentScenario.Key : null,
|
_currentScenario != null ? _currentScenario.Key : null,
|
||||||
null,
|
null);
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
this.LogException(ex, "Error changing room combination scenario");
|
this.LogException(ex, "Error changing room combination scenario");
|
||||||
|
|
||||||
SetCombinationOperationStatus(
|
TrySetCombinationOperationTerminalStatus(
|
||||||
|
operationId,
|
||||||
CombinationOperationState.Failed,
|
CombinationOperationState.Failed,
|
||||||
newScenario != null ? newScenario.Key : null,
|
newScenario != null ? newScenario.Key : null,
|
||||||
"Combination operation failed",
|
"Combination operation failed");
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -503,12 +515,14 @@ namespace PepperDash.Essentials.Core
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private void SetCombinationOperationStatus(
|
private string SetCombinationOperationStatus(
|
||||||
CombinationOperationState state,
|
CombinationOperationState state,
|
||||||
string scenarioKey,
|
string scenarioKey,
|
||||||
string message,
|
string message,
|
||||||
bool resetStartedUtc)
|
bool resetStartedUtc)
|
||||||
{
|
{
|
||||||
|
string operationId;
|
||||||
|
|
||||||
lock (_combinationOperationLock)
|
lock (_combinationOperationLock)
|
||||||
{
|
{
|
||||||
if (resetStartedUtc)
|
if (resetStartedUtc)
|
||||||
|
|
@ -528,9 +542,91 @@ namespace PepperDash.Essentials.Core
|
||||||
_combinationOperation.State = state;
|
_combinationOperation.State = state;
|
||||||
_combinationOperation.Message = message;
|
_combinationOperation.Message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operationId = _combinationOperation.OperationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == CombinationOperationState.InProgress)
|
||||||
|
{
|
||||||
|
StartCombinationOperationTimeout(operationId);
|
||||||
|
}
|
||||||
|
else if (state == CombinationOperationState.Completed
|
||||||
|
|| state == CombinationOperationState.Failed
|
||||||
|
|| state == CombinationOperationState.TimedOut
|
||||||
|
|| state == CombinationOperationState.Idle)
|
||||||
|
{
|
||||||
|
StopCombinationOperationTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
CombinationOperationStatusChanged?.Invoke(this, EventArgs.Empty);
|
CombinationOperationStatusChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
|
return operationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TrySetCombinationOperationTerminalStatus(
|
||||||
|
string operationId,
|
||||||
|
CombinationOperationState state,
|
||||||
|
string scenarioKey,
|
||||||
|
string message)
|
||||||
|
{
|
||||||
|
var statusUpdated = false;
|
||||||
|
|
||||||
|
lock (_combinationOperationLock)
|
||||||
|
{
|
||||||
|
if (_combinationOperation == null
|
||||||
|
|| !string.Equals(_combinationOperation.OperationId, operationId, StringComparison.Ordinal)
|
||||||
|
|| _combinationOperation.State != CombinationOperationState.InProgress)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_combinationOperation.ScenarioKey = scenarioKey ?? _combinationOperation.ScenarioKey;
|
||||||
|
_combinationOperation.State = state;
|
||||||
|
_combinationOperation.Message = message;
|
||||||
|
statusUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statusUpdated)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StopCombinationOperationTimeout();
|
||||||
|
CombinationOperationStatusChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartCombinationOperationTimeout(string operationId)
|
||||||
|
{
|
||||||
|
StopCombinationOperationTimeout();
|
||||||
|
|
||||||
|
if (_combinationOperationTimeoutSeconds <= 0 || string.IsNullOrEmpty(operationId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_combinationOperationTimeoutTimer = new CTimer(
|
||||||
|
_ => HandleCombinationOperationTimeout(operationId),
|
||||||
|
_combinationOperationTimeoutSeconds * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopCombinationOperationTimeout()
|
||||||
|
{
|
||||||
|
if (_combinationOperationTimeoutTimer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_combinationOperationTimeoutTimer.Dispose();
|
||||||
|
_combinationOperationTimeoutTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleCombinationOperationTimeout(string operationId)
|
||||||
|
{
|
||||||
|
TrySetCombinationOperationTerminalStatus(
|
||||||
|
operationId,
|
||||||
|
CombinationOperationState.TimedOut,
|
||||||
|
null,
|
||||||
|
"Combination operation timed out");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CombinationOperationStatus CloneCombinationOperationStatus(CombinationOperationStatus status)
|
private static CombinationOperationStatus CloneCombinationOperationStatus(CombinationOperationStatus status)
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ namespace PepperDash.Essentials.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsRoomCombinerPropertiesConfig
|
public class EssentialsRoomCombinerPropertiesConfig
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the system operates in automatic mode.
|
/// Gets or sets a value indicating whether the system operates in automatic mode.
|
||||||
/// <remarks>Some systems don't have partitions sensors, and show shouldn't allow auto mode to be turned on. When this is true in the configuration,
|
/// <remarks>Some systems don't have partitions sensors, and show shouldn't allow auto mode to be turned on. When this is true in the configuration,
|
||||||
/// auto mode won't be allowed to be turned on.</remarks>
|
/// auto mode won't be allowed to be turned on.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("disableAutoMode")]
|
[JsonProperty("disableAutoMode")]
|
||||||
public bool DisableAutoMode { get; set; }
|
public bool DisableAutoMode { get; set; }
|
||||||
|
|
@ -49,11 +49,18 @@ namespace PepperDash.Essentials.Core
|
||||||
[JsonProperty("defaultScenarioKey")]
|
[JsonProperty("defaultScenarioKey")]
|
||||||
public string defaultScenarioKey { get; set; }
|
public string defaultScenarioKey { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the debounce time, in seconds, for scenario changes.
|
/// Gets or sets the debounce time, in seconds, for scenario changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("scenarioChangeDebounceTimeSeconds")]
|
[JsonProperty("scenarioChangeDebounceTimeSeconds")]
|
||||||
public int ScenarioChangeDebounceTimeSeconds { get; set; }
|
public int ScenarioChangeDebounceTimeSeconds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the timeout, in seconds, for room combination operations.
|
||||||
|
/// When null or less than or equal to zero, the default timeout is used.
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("combinationOperationTimeoutSeconds", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
|
public int? CombinationOperationTimeoutSeconds { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -61,14 +68,14 @@ namespace PepperDash.Essentials.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PartitionConfig : IKeyName
|
public class PartitionConfig : IKeyName
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the unique key associated with the object.
|
/// Gets or sets the unique key associated with the object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("key")]
|
[JsonProperty("key")]
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name associated with the object.
|
/// Gets or sets the name associated with the object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("name")]
|
[JsonProperty("name")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
@ -91,26 +98,26 @@ namespace PepperDash.Essentials.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RoomCombinationScenarioConfig : IKeyName
|
public class RoomCombinationScenarioConfig : IKeyName
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the key associated with the object.
|
/// Gets or sets the key associated with the object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("key")]
|
[JsonProperty("key")]
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name associated with the object.
|
/// Gets or sets the name associated with the object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("name")]
|
[JsonProperty("name")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether to hide this scenario in the UI.
|
/// Gets or sets a value indicating whether to hide this scenario in the UI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("hideInUi", NullValueHandling = NullValueHandling.Ignore)]
|
[JsonProperty("hideInUi", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public bool HideInUi { get; set; }
|
public bool HideInUi { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the collection of partition states.
|
/// Gets or sets the collection of partition states.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("partitionStates")]
|
[JsonProperty("partitionStates")]
|
||||||
public List<PartitionState> PartitionStates { get; set; }
|
public List<PartitionState> PartitionStates { get; set; }
|
||||||
|
|
@ -121,14 +128,14 @@ namespace PepperDash.Essentials.Core
|
||||||
[JsonProperty("uiMap")]
|
[JsonProperty("uiMap")]
|
||||||
public Dictionary<string, string> UiMap { get; set; }
|
public Dictionary<string, string> UiMap { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of actions to be performed during device activation.
|
/// Gets or sets the list of actions to be performed during device activation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("activationActions")]
|
[JsonProperty("activationActions")]
|
||||||
public List<DeviceActionWrapper> ActivationActions { get; set; }
|
public List<DeviceActionWrapper> ActivationActions { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of actions to be performed when a device is deactivated.
|
/// Gets or sets the list of actions to be performed when a device is deactivated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("deactivationActions")]
|
[JsonProperty("deactivationActions")]
|
||||||
public List<DeviceActionWrapper> DeactivationActions { get; set; }
|
public List<DeviceActionWrapper> DeactivationActions { get; set; }
|
||||||
|
|
@ -139,14 +146,14 @@ namespace PepperDash.Essentials.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PartitionState
|
public class PartitionState
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the partition key used to group and organize data within a storage system.
|
/// Gets or sets the partition key used to group and organize data within a storage system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("partitionKey")]
|
[JsonProperty("partitionKey")]
|
||||||
public string PartitionKey { get; set; }
|
public string PartitionKey { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether a partition is currently present.
|
/// Gets or sets a value indicating whether a partition is currently present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("partitionSensedState")]
|
[JsonProperty("partitionSensedState")]
|
||||||
public bool PartitionPresent { get; set; }
|
public bool PartitionPresent { get; set; }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue