using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; using Haoliang.Core.Services; using Haoliang.Models.Device; using Haoliang.Models.System; using Haoliang.Models.Common; namespace Haoliang.Core.Services { public interface IDeviceStateMachine { /// /// Get current state of device /// DeviceState GetCurrentState(int deviceId); /// /// Check if device can transition to target state /// Task CanTransitionAsync(int deviceId, DeviceState targetState); /// /// Transition device to new state /// Task TransitionToStateAsync(int deviceId, DeviceState targetState, object context = null); /// /// Trigger device event /// Task TriggerEventAsync(int deviceId, DeviceEvent deviceEvent, object context = null); /// /// Get device state history /// Task> GetStateHistoryAsync(int deviceId, DateTime? startDate = null, DateTime? endDate = null); /// /// Get device state statistics /// Task GetStateStatisticsAsync(int deviceId, DateTime? startDate = null, DateTime? endDate = null); /// /// Force device to specific state /// Task ForceStateAsync(int deviceId, DeviceState targetState, string reason); /// /// Validate device state /// Task ValidateStateAsync(int deviceId); /// /// Register state change handler /// void RegisterStateHandler(StateChangeHandler handler); /// /// Unregister state change handler /// void UnregisterStateHandler(StateChangeHandler handler); } public class DeviceStateMachine : IDeviceStateMachine, IHostedService { private readonly IDeviceRepository _deviceRepository; private readonly IDeviceCollectionService _collectionService; private readonly IAlarmService _alarmService; private readonly ICacheService _cacheService; private readonly Timer _stateCheckTimer; private readonly ConcurrentDictionary _deviceStates = new ConcurrentDictionary(); private readonly List _stateHandlers = new List(); private readonly object _lock = new object(); public DeviceStateMachine( IDeviceRepository deviceRepository, IDeviceCollectionService collectionService, IAlarmService alarmService, ICacheService cacheService) { _deviceRepository = deviceRepository; _collectionService = collectionService; _alarmService = alarmService; _cacheService = cacheService; // Start timer for periodic state checks _stateCheckTimer = new Timer(CheckDeviceStates, null, TimeSpan.Zero, TimeSpan.FromSeconds(30)); } public DeviceState GetCurrentState(int deviceId) { if (_deviceStates.TryGetValue(deviceId, out var context)) { return context.CurrentState; } return DeviceState.Unknown; } public async Task CanTransitionAsync(int deviceId, DeviceState targetState) { var currentState = GetCurrentState(deviceId); return await CanTransitionFromAsync(currentState, targetState, deviceId); } public async Task TransitionToStateAsync(int deviceId, DeviceState targetState, object context = null) { var result = new DeviceStateTransitionResult { DeviceId = deviceId, FromState = GetCurrentState(deviceId), ToState = targetState, Success = false, Message = "", Timestamp = DateTime.UtcNow }; try { // Validate transition if (!await CanTransitionAsync(deviceId, targetState)) { result.Message = $"Cannot transition from {result.FromState} to {targetState}"; return result; } // Get device var device = await _deviceRepository.GetByIdAsync(deviceId); if (device == null) { result.Message = "Device not found"; return result; } // Create transition context var transitionContext = new DeviceStateTransitionContext { DeviceId = deviceId, Device = device, FromState = result.FromState, ToState = targetState, Context = context, Timestamp = DateTime.UtcNow }; // Execute exit actions for current state var exitResult = await ExecuteStateActionsAsync(deviceId, result.FromState, transitionContext, true); if (!exitResult.Success) { result.Message = $"Failed to exit {result.FromState}: {exitResult.Message}"; return result; } // Execute enter actions for target state var enterResult = await ExecuteStateActionsAsync(deviceId, targetState, transitionContext, false); if (!enterResult.Success) { result.Message = $"Failed to enter {targetState}: {enterResult.Message}"; // Attempt to revert to original state await ExecuteStateActionsAsync(deviceId, result.FromState, transitionContext, true); return result; } // Update device state lock (_lock) { var deviceContext = _deviceStates.GetOrAdd(deviceId, new DeviceStateContext { CurrentState = targetState, PreviousState = result.FromState, StateChangedAt = DateTime.UtcNow, Context = context }); deviceContext.CurrentState = targetState; deviceContext.PreviousState = result.FromState; deviceContext.StateChangedAt = DateTime.UtcNow; deviceContext.Context = context; } // Record state change in history await RecordStateChangeAsync(deviceId, result.FromState, targetState, context); // Notify handlers await NotifyStateHandlersAsync(deviceId, result.FromState, targetState, transitionContext); // Update device status await UpdateDeviceStatusAsync(device, targetState); result.Success = true; result.Message = $"Successfully transitioned from {result.FromState} to {targetState}"; } catch (Exception ex) { result.Message = $"Error during state transition: {ex.Message}"; } return result; } public async Task TriggerEventAsync(int deviceId, DeviceEvent deviceEvent, object context = null) { var currentState = GetCurrentState(deviceId); var eventConfig = GetEventConfiguration(deviceEvent, currentState); if (eventConfig == null) { return new DeviceStateTransitionResult { DeviceId = deviceId, FromState = currentState, ToState = currentState, Success = false, Message = $"No event handler configured for {deviceEvent} in state {currentState}", Timestamp = DateTime.UtcNow }; } // Check event conditions if (!await EvaluateEventConditionsAsync(deviceId, deviceEvent, eventConfig.Conditions)) { return new DeviceStateTransitionResult { DeviceId = deviceId, FromState = currentState, ToState = currentState, Success = false, Message = $"Event conditions not met for {deviceEvent}", Timestamp = DateTime.UtcNow }; } // Transition to target state return await TransitionToStateAsync(deviceId, eventConfig.TargetState, context); } public async Task> GetStateHistoryAsync(int deviceId, DateTime? startDate = null, DateTime? endDate = null) { var history = await _deviceRepository.GetDeviceStateHistoryAsync(deviceId, startDate, endDate); // Add current state if not in history if (!history.Any(h => h.State == GetCurrentState(deviceId))) { history.Add(new DeviceStateHistory { DeviceId = deviceId, State = GetCurrentState(deviceId), ChangedAt = DateTime.UtcNow, ChangedBy = "System", Notes = "Current state" }); } return history.OrderBy(h => h.ChangedAt).ToList(); } public async Task GetStateStatisticsAsync(int deviceId, DateTime? startDate = null, DateTime? endDate = null) { var history = await GetStateHistoryAsync(deviceId, startDate, endDate); var statistics = new DeviceStateStatistics { DeviceId = deviceId, PeriodStart = startDate ?? DateTime.UtcNow.AddDays(-7), PeriodEnd = endDate ?? DateTime.UtcNow, StateTransitions = history.Count - 1, // Excluding initial state StateDurations = new Dictionary(), StateCounts = new Dictionary(), LastStateChange = history.LastOrDefault()?.ChangedAt }; // Calculate state durations for (int i = 0; i < history.Count - 1; i++) { var fromState = history[i].State; var toState = history[i + 1].State; var duration = history[i + 1].ChangedAt - history[i].ChangedAt; if (!statistics.StateDurations.ContainsKey(fromState)) { statistics.StateDurations[fromState] = TimeSpan.Zero; } statistics.StateDurations[fromState] += duration; if (!statistics.StateCounts.ContainsKey(fromState)) { statistics.StateCounts[fromState] = 0; } statistics.StateCounts[fromState]++; } // Add current state duration if (history.Any()) { var currentState = GetCurrentState(deviceId); var lastState = history.Last(); var currentDuration = DateTime.UtcNow - lastState.ChangedAt; if (!statistics.StateDurations.ContainsKey(currentState)) { statistics.StateDurations[currentState] = TimeSpan.Zero; } statistics.StateDurations[currentState] += currentDuration; if (!statistics.StateCounts.ContainsKey(currentState)) { statistics.StateCounts[currentState] = 0; } statistics.StateCounts[currentState]++; } return statistics; } public async Task ForceStateAsync(int deviceId, DeviceState targetState, string reason) { // Create force transition context var forceContext = new { Forced = true, Reason = reason }; var result = await TransitionToStateAsync(deviceId, targetState, forceContext); if (!result.Success) { // If normal transition fails, create a force entry in history await RecordStateChangeAsync(deviceId, GetCurrentState(deviceId), targetState, forceContext, true); // Update in-memory state lock (_lock) { var deviceContext = _deviceStates.GetOrAdd(deviceId, new DeviceStateContext()); deviceContext.CurrentState = targetState; deviceContext.PreviousState = GetCurrentState(deviceId); deviceContext.StateChangedAt = DateTime.UtcNow; deviceContext.Context = forceContext; } result.Success = true; result.Message = $"Forced state transition to {targetState}: {reason}"; } return result; } public async Task ValidateStateAsync(int deviceId) { var result = new DeviceValidationResult { DeviceId = deviceId, IsValid = true, Issues = new List(), CurrentState = GetCurrentState(deviceId), Timestamp = DateTime.UtcNow }; try { var device = await _deviceRepository.GetByIdAsync(deviceId); if (device == null) { result.IsValid = false; result.Issues.Add("Device not found"); return result; } // Check if device state matches actual status var actualStatus = await _collectionService.GetDeviceCurrentStatusAsync(deviceId); var expectedState = TranslateStatusToDeviceState(actualStatus); if (expectedState != result.CurrentState) { result.IsValid = false; result.Issues.Add($"State mismatch: expected {expectedState}, actual {result.CurrentState}"); } // Validate state-specific rules var stateValidation = await ValidateStateRulesAsync(deviceId, result.CurrentState); if (!stateValidation.IsValid) { result.IsValid = false; result.Issues.AddRange(stateValidation.Issues); } // Check for state timeouts var stateTimeout = await CheckStateTimeoutAsync(deviceId, result.CurrentState); if (stateTimeout.IsTimeout) { result.IsValid = false; result.Issues.Add($"State timeout: {result.CurrentState} exceeded maximum duration"); } } catch (Exception ex) { result.IsValid = false; result.Issues.Add($"Validation error: {ex.Message}"); } return result; } public void RegisterStateHandler(StateChangeHandler handler) { lock (_lock) { _stateHandlers.Add(handler); } } public void UnregisterStateHandler(StateChangeHandler handler) { lock (_lock) { _stateHandlers.Remove(handler); } } public Task StartAsync(CancellationToken cancellationToken) { // Initialize device states return InitializeDeviceStatesAsync(); } public Task StopAsync(CancellationToken cancellationToken) { _stateCheckTimer?.Dispose(); return Task.CompletedTask; } #region Private Methods private async Task InitializeDeviceStatesAsync() { var devices = await _deviceRepository.GetAllDevicesAsync(); foreach (var device in devices) { var stateContext = new DeviceStateContext { CurrentState = DeviceState.Unknown, PreviousState = DeviceState.Unknown, StateChangedAt = DateTime.UtcNow, Context = null }; // Get current device status to determine state var status = await _collectionService.GetDeviceCurrentStatusAsync(device.Id); stateContext.CurrentState = TranslateStatusToDeviceState(status); _deviceStates.AddOrUpdate(device.Id, stateContext, (key, existing) => stateContext); } } private async Task CheckDeviceStates(object state) { try { var devices = await _deviceRepository.GetAllActiveDevicesAsync(); foreach (var device in devices) { var validationResult = await ValidateStateAsync(device.Id); if (!validationResult.IsValid) { // Handle invalid state await HandleInvalidStateAsync(device.Id, validationResult); } // Check for state timeouts var stateTimeout = await CheckStateTimeoutAsync(device.Id, GetCurrentState(device.Id)); if (stateTimeout.IsTimeout) { await HandleStateTimeoutAsync(device.Id, stateTimeout); } } } catch (Exception ex) { // Log error Console.WriteLine($"Error checking device states: {ex.Message}"); } } private async Task ExecuteStateActionsAsync( int deviceId, DeviceState state, DeviceStateTransitionContext context, bool isExit) { var actions = isExit ? GetExitActions(state) : GetEnterActions(state); foreach (var action in actions) { try { var result = await action.ExecuteAsync(deviceId, context); if (!result.Success) { return new DeviceStateTransitionResult { DeviceId = deviceId, FromState = context.FromState, ToState = context.ToState, Success = false, Message = result.Message, Timestamp = DateTime.UtcNow }; } } catch (Exception ex) { return new DeviceStateTransitionResult { DeviceId = deviceId, FromState = context.FromState, ToState = context.ToState, Success = false, Message = $"Error executing action: {ex.Message}", Timestamp = DateTime.UtcNow }; } } return new DeviceStateTransitionResult { DeviceId = deviceId, FromState = context.FromState, ToState = context.ToState, Success = true, Message = "Actions executed successfully", Timestamp = DateTime.UtcNow }; } private List GetExitActions(DeviceState state) { return GetStateActions(state, "exit"); } private List GetEnterActions(DeviceState state) { return GetStateActions(state, "enter"); } private List GetStateActions(DeviceState state, string actionType) { // This would typically come from configuration or database // For now, return basic actions var actions = new List(); switch (state) { case DeviceState.Running: if (actionType == "exit") { actions.Add(new StopProductionAction()); } else { actions.Add(new StartProductionAction()); } break; case DeviceState.Idle: if (actionType == "enter") { actions.Add(new LogIdleAction()); } break; case DeviceState.Error: if (actionType == "enter") { actions.Add(new NotifyErrorAction()); } break; default: actions.Add(new LogStateAction(actionType, state)); break; } return actions; } private async Task CanTransitionFromAsync(DeviceState fromState, DeviceState targetState, int deviceId) { var allowedTransitions = GetAllowedTransitions(fromState); return allowedTransitions.Contains(targetState); } private List GetAllowedTransitions(DeviceState fromState) { // Define state transition rules var transitions = new Dictionary> { [DeviceState.Unknown] = new List { DeviceState.Offline, DeviceState.Idle }, [DeviceState.Offline] = new List { DeviceState.Online, DeviceState.Unknown }, [DeviceState.Online] = new List { DeviceState.Idle, DeviceState.Running, DeviceState.Error, DeviceState.Maintenance }, [DeviceState.Idle] = new List { DeviceState.Running, DeviceState.Offline, DeviceState.Maintenance }, [DeviceState.Running] = new List { DeviceState.Idle, DeviceState.Error, DeviceState.Stopped, DeviceState.Maintenance }, [DeviceState.Error] = new List { DeviceState.Idle, DeviceState.Maintenance, DeviceState.Unknown }, [DeviceState.Maintenance] = new List { DeviceState.Idle, DeviceState.Offline, DeviceState.Unknown }, [DeviceState.Stopped] = new List { DeviceState.Idle, DeviceState.Offline } }; return transitions.GetValueOrDefault(fromState, new List()); } private EventConfig GetEventConfiguration(DeviceEvent deviceEvent, DeviceState currentState) { // This would typically come from configuration var eventConfigs = new Dictionary<(DeviceEvent, DeviceState), EventConfig> { [(DeviceEvent.Start, DeviceState.Idle)] = new EventConfig { TargetState = DeviceState.Running, Conditions = new List>> { async deviceId => await IsDeviceReadyForProduction(deviceId) } }, [(DeviceEvent.Stop, DeviceState.Running)] = new EventConfig { TargetState = DeviceState.Idle, Conditions = new List>> { async deviceId => await IsProductionComplete(deviceId) } }, [(DeviceEvent.Error, DeviceState.Running)] = new EventConfig { TargetState = DeviceState.Error, Conditions = new List>> { async deviceId => await HasDeviceError(deviceId) } }, [(DeviceEvent.Resume, DeviceState.Maintenance)] = new EventConfig { TargetState = DeviceState.Idle, Conditions = new List>> { async deviceId => await IsMaintenanceComplete(deviceId) } } }; return eventConfigs.GetValueOrDefault((deviceEvent, currentState)); } private async Task EvaluateEventConditionsAsync(int deviceId, DeviceEvent deviceEvent, List>> conditions) { if (conditions == null || !conditions.Any()) return true; foreach (var condition in conditions) { var conditionResult = await condition(deviceId); if (!conditionResult) return false; } return true; } private async Task RecordStateChangeAsync(int deviceId, DeviceState fromState, DeviceState toState, object context, bool isForced = false) { var history = new DeviceStateHistory { DeviceId = deviceId, FromState = fromState, ToState = toState, ChangedAt = DateTime.UtcNow, ChangedBy = isForced ? "System (Forced)" : "System", Notes = isForced ? $"Forced transition: {JsonSerializer(context)}" : JsonSerializer(context) }; await _deviceRepository.AddDeviceStateHistoryAsync(history); } private async Task NotifyStateHandlersAsync(int deviceId, DeviceState fromState, DeviceState toState, DeviceStateTransitionContext context) { var handlersCopy = _stateHandlers.ToList(); // Copy to avoid modification during iteration foreach (var handler in handlersCopy) { try { await handler(deviceId, fromState, toState, context); } catch (Exception ex) { // Log error but continue with other handlers Console.WriteLine($"Error in state handler: {ex.Message}"); } } } private async Task UpdateDeviceStatusAsync(CNCDevice device, DeviceState state) { // Update device status in database device.Status = TranslateStateToDeviceStatus(state); await _deviceRepository.UpdateDeviceAsync(device); // Update cache _cacheService.InvalidateDeviceCache(device.Id); } private DeviceState TranslateStatusToDeviceState(DeviceCurrentStatus status) { if (status == null || status.Status == DeviceStatus.Unknown) return DeviceState.Unknown; return status.Status switch { DeviceStatus.Offline => DeviceState.Offline, DeviceStatus.Online => DeviceState.Online, DeviceStatus.Idle => DeviceState.Idle, DeviceStatus.Running => DeviceState.Running, DeviceStatus.Error => DeviceState.Error, DeviceStatus.Maintenance => DeviceState.Maintenance, DeviceStatus.Stopped => DeviceState.Stopped, _ => DeviceState.Unknown }; } private DeviceStatus TranslateStateToDeviceStatus(DeviceState state) { return state switch { DeviceState.Offline => DeviceStatus.Offline, DeviceState.Online => DeviceStatus.Online, DeviceState.Idle => DeviceStatus.Idle, DeviceState.Running => DeviceStatus.Running, DeviceState.Error => DeviceStatus.Error, DeviceState.Maintenance => DeviceStatus.Maintenance, DeviceState.Stopped => DeviceStatus.Stopped, _ => DeviceStatus.Unknown }; } private async Task ValidateStateRulesAsync(int deviceId, DeviceState state) { var result = new DeviceValidationResult { IsValid = true, Issues = new List() }; switch (state) { case DeviceState.Running: // Check if device should actually be running var device = await _deviceRepository.GetByIdAsync(deviceId); if (device != null && device.EnableProduction) { var status = await _collectionService.GetDeviceCurrentStatusAsync(deviceId); if (status.Status != DeviceStatus.Running) { result.IsValid = false; result.Issues.Add("Device is in Running state but actual status is not Running"); } } break; case DeviceState.Error: // Check if device has active errors var activeAlarms = await _alarmService.GetActiveAlertsByDeviceAsync(deviceId); if (!activeAlarms.Any(a => a.AlertType == "DeviceError")) { result.IsValid = false; result.Issues.Add("Device is in Error state but has no active error alerts"); } break; } return result; } private async Task CheckStateTimeoutAsync(int deviceId, DeviceState state) { var stateContext = _deviceStates.GetValueOrDefault(deviceId); if (stateContext == null) return new StateTimeoutInfo { IsTimeout = false }; var stateDurations = GetStateTimeoutDurations(); var maxDuration = stateDurations.GetValueOrDefault(state, TimeSpan.FromMinutes(30)); var currentDuration = DateTime.UtcNow - stateContext.StateChangedAt; return new StateTimeoutInfo { IsTimeout = currentDuration > maxDuration, State = state, CurrentDuration = currentDuration, MaxDuration = maxDuration, ExceededBy = currentDuration - maxDuration }; } private Dictionary GetStateTimeoutDurations() { return new Dictionary { [DeviceState.Unknown] = TimeSpan.FromMinutes(5), [DeviceState.Offline] = TimeSpan.FromHours(1), [DeviceState.Online] = TimeSpan.FromMinutes(15), [DeviceState.Idle] = TimeSpan.FromMinutes(10), [DeviceState.Running] = TimeSpan.FromHours(8), [DeviceState.Error] = TimeSpan.FromMinutes(60), [DeviceState.Maintenance] = TimeSpan.FromHours(4), [DeviceState.Stopped] = TimeSpan.FromMinutes(30) }; } private async Task HandleInvalidStateAsync(int deviceId, DeviceValidationResult validationResult) { // Log warning Console.WriteLine($"Device {deviceId} state validation failed: {string.Join(", ", validationResult.Issues)}"); // Attempt to transition to a safe state var safeState = DetermineSafeState(deviceId, validationResult.CurrentState); await ForceStateAsync(deviceId, safeState, $"State validation failed: {string.Join(", ", validationResult.Issues)}"); } private async Task HandleStateTimeoutAsync(int deviceId, StateTimeoutInfo timeoutInfo) { // Log warning Console.WriteLine($"Device {deviceId} state {timeoutInfo.State} exceeded maximum duration by {timeoutInfo.ExceededBy}"); // Trigger timeout event await TriggerEventAsync(deviceId, DeviceEvent.Timeout, timeoutInfo); } private DeviceState DetermineSafeState(int deviceId, DeviceState currentState) { return DeviceState.Idle; // Default safe state } private async Task IsDeviceReadyForProduction(int deviceId) { // Check if device is ready for production var device = await _deviceRepository.GetByIdAsync(deviceId); var status = await _collectionService.GetDeviceCurrentStatusAsync(deviceId); return device != null && device.EnableProduction && status.Status == DeviceStatus.Online && !device.IsUnderMaintenance; } private async Task IsProductionComplete(int deviceId) { // Check if production is complete var records = await _deviceRepository.GetProductionRecordsByDeviceAsync(deviceId); return records.Any() && records.Last().IsComplete; } private async Task HasDeviceError(int deviceId) { // Check if device has errors var alerts = await _alarmService.GetActiveAlertsByDeviceAsync(deviceId); return alerts.Any(a => a.AlertType == "DeviceError"); } private async Task IsMaintenanceComplete(int deviceId) { // Check if maintenance is complete var device = await _deviceRepository.GetByIdAsync(deviceId); return device != null && !device.IsUnderMaintenance; } private string JsonSerializer(object obj) { // Simplified JSON serialization return obj?.ToString() ?? "{}"; } #endregion } #region Supporting Classes and Interfaces public delegate Task StateChangeHandler(int deviceId, DeviceState fromState, DeviceState toState, DeviceStateTransitionContext context); public interface IStateAction { Task ExecuteAsync(int deviceId, DeviceStateTransitionContext context); } public class StateActionResult { public bool Success { get; set; } public string Message { get; set; } } public class DeviceStateContext { public DeviceState CurrentState { get; set; } public DeviceState PreviousState { get; set; } public DateTime StateChangedAt { get; set; } public object Context { get; set; } } public class DeviceStateTransitionContext { public int DeviceId { get; set; } public CNCDevice Device { get; set; } public DeviceState FromState { get; set; } public DeviceState ToState { get; set; } public object Context { get; set; } public DateTime Timestamp { get; set; } } public class EventConfig { public DeviceState TargetState { get; set; } public List>> Conditions { get; set; } } public class StateTimeoutInfo { public bool IsTimeout { get; set; } public DeviceState State { get; set; } public TimeSpan CurrentDuration { get; set; } public TimeSpan MaxDuration { get; set; } public TimeSpan ExceededBy { get; set; } } public class DeviceValidationResult { public int DeviceId { get; set; } public bool IsValid { get; set; } public List Issues { get; set; } public DeviceState CurrentState { get; set; } public DateTime Timestamp { get; set; } } #endregion #region State Action Implementations public class StopProductionAction : IStateAction { public async Task ExecuteAsync(int deviceId, DeviceStateTransitionContext context) { // Implementation to stop production return new StateActionResult { Success = true, Message = "Production stopped" }; } } public class StartProductionAction : IStateAction { public async Task ExecuteAsync(int deviceId, DeviceStateTransitionContext context) { // Implementation to start production return new StateActionResult { Success = true, Message = "Production started" }; } } public class LogIdleAction : IStateAction { public async Task ExecuteAsync(int deviceId, DeviceStateTransitionContext context) { // Implementation to log idle state return new StateActionResult { Success = true, Message = "Idle state logged" }; } } public class NotifyErrorAction : IStateAction { public async Task ExecuteAsync(int deviceId, DeviceStateTransitionContext context) { // Implementation to notify about error return new StateActionResult { Success = true, Message = "Error notification sent" }; } } public class LogStateAction : IStateAction { private readonly string _actionType; private readonly DeviceState _state; public LogStateAction(string actionType, DeviceState state) { _actionType = actionType; _state = state; } public async Task ExecuteAsync(int deviceId, DeviceStateTransitionContext context) { // Implementation to log state changes return new StateActionResult { Success = true, Message = $"{_actionType} action for {_state} logged" }; } } #endregion }