You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

997 lines
38 KiB
C#

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.Models.Device;
using Haoliang.Models.Models.System;
using Haoliang.Models.Common;
namespace Haoliang.Core.Services
{
public interface IDeviceStateMachine
{
/// <summary>
/// Get current state of device
/// </summary>
DeviceState GetCurrentState(int deviceId);
/// <summary>
/// Check if device can transition to target state
/// </summary>
Task<bool> CanTransitionAsync(int deviceId, DeviceState targetState);
/// <summary>
/// Transition device to new state
/// </summary>
Task<DeviceStateTransitionResult> TransitionToStateAsync(int deviceId, DeviceState targetState, object context = null);
/// <summary>
/// Trigger device event
/// </summary>
Task<DeviceStateTransitionResult> TriggerEventAsync(int deviceId, DeviceEvent deviceEvent, object context = null);
/// <summary>
/// Get device state history
/// </summary>
Task<List<DeviceStateHistory>> GetStateHistoryAsync(int deviceId, DateTime? startDate = null, DateTime? endDate = null);
/// <summary>
/// Get device state statistics
/// </summary>
Task<DeviceStateStatistics> GetStateStatisticsAsync(int deviceId, DateTime? startDate = null, DateTime? endDate = null);
/// <summary>
/// Force device to specific state
/// </summary>
Task<DeviceStateTransitionResult> ForceStateAsync(int deviceId, DeviceState targetState, string reason);
/// <summary>
/// Validate device state
/// </summary>
Task<DeviceValidationResult> ValidateStateAsync(int deviceId);
/// <summary>
/// Register state change handler
/// </summary>
void RegisterStateHandler(StateChangeHandler handler);
/// <summary>
/// Unregister state change handler
/// </summary>
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<int, DeviceStateContext> _deviceStates = new ConcurrentDictionary<int, DeviceStateContext>();
private readonly List<StateChangeHandler> _stateHandlers = new List<StateChangeHandler>();
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<bool> CanTransitionAsync(int deviceId, DeviceState targetState)
{
var currentState = GetCurrentState(deviceId);
return await CanTransitionFromAsync(currentState, targetState, deviceId);
}
public async Task<DeviceStateTransitionResult> 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<DeviceStateTransitionResult> 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<List<DeviceStateHistory>> 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<DeviceStateStatistics> 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<DeviceState, TimeSpan>(),
StateCounts = new Dictionary<DeviceState, int>(),
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<DeviceStateTransitionResult> 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<DeviceValidationResult> ValidateStateAsync(int deviceId)
{
var result = new DeviceValidationResult
{
DeviceId = deviceId,
IsValid = true,
Issues = new List<string>(),
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<DeviceStateTransitionResult> 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<StateAction> GetExitActions(DeviceState state)
{
return GetStateActions(state, "exit");
}
private List<StateAction> GetEnterActions(DeviceState state)
{
return GetStateActions(state, "enter");
}
private List<StateAction> GetStateActions(DeviceState state, string actionType)
{
// This would typically come from configuration or database
// For now, return basic actions
var actions = new List<StateAction>();
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<bool> CanTransitionFromAsync(DeviceState fromState, DeviceState targetState, int deviceId)
{
var allowedTransitions = GetAllowedTransitions(fromState);
return allowedTransitions.Contains(targetState);
}
private List<DeviceState> GetAllowedTransitions(DeviceState fromState)
{
// Define state transition rules
var transitions = new Dictionary<DeviceState, List<DeviceState>>
{
[DeviceState.Unknown] = new List<DeviceState> { DeviceState.Offline, DeviceState.Idle },
[DeviceState.Offline] = new List<DeviceState> { DeviceState.Online, DeviceState.Unknown },
[DeviceState.Online] = new List<DeviceState> { DeviceState.Idle, DeviceState.Running, DeviceState.Error, DeviceState.Maintenance },
[DeviceState.Idle] = new List<DeviceState> { DeviceState.Running, DeviceState.Offline, DeviceState.Maintenance },
[DeviceState.Running] = new List<DeviceState> { DeviceState.Idle, DeviceState.Error, DeviceState.Stopped, DeviceState.Maintenance },
[DeviceState.Error] = new List<DeviceState> { DeviceState.Idle, DeviceState.Maintenance, DeviceState.Unknown },
[DeviceState.Maintenance] = new List<DeviceState> { DeviceState.Idle, DeviceState.Offline, DeviceState.Unknown },
[DeviceState.Stopped] = new List<DeviceState> { DeviceState.Idle, DeviceState.Offline }
};
return transitions.GetValueOrDefault(fromState, new List<DeviceState>());
}
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<Func<int, Task<bool>>>
{
async deviceId => await IsDeviceReadyForProduction(deviceId)
}
},
[(DeviceEvent.Stop, DeviceState.Running)] = new EventConfig
{
TargetState = DeviceState.Idle,
Conditions = new List<Func<int, Task<bool>>>
{
async deviceId => await IsProductionComplete(deviceId)
}
},
[(DeviceEvent.Error, DeviceState.Running)] = new EventConfig
{
TargetState = DeviceState.Error,
Conditions = new List<Func<int, Task<bool>>>
{
async deviceId => await HasDeviceError(deviceId)
}
},
[(DeviceEvent.Resume, DeviceState.Maintenance)] = new EventConfig
{
TargetState = DeviceState.Idle,
Conditions = new List<Func<int, Task<bool>>>
{
async deviceId => await IsMaintenanceComplete(deviceId)
}
}
};
return eventConfigs.GetValueOrDefault((deviceEvent, currentState));
}
private async Task<bool> EvaluateEventConditionsAsync(int deviceId, DeviceEvent deviceEvent, List<Func<int, Task<bool>>> 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<DeviceValidationResult> ValidateStateRulesAsync(int deviceId, DeviceState state)
{
var result = new DeviceValidationResult { IsValid = true, Issues = new List<string>() };
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<StateTimeoutInfo> 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<DeviceState, TimeSpan> GetStateTimeoutDurations()
{
return new Dictionary<DeviceState, TimeSpan>
{
[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<bool> 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<bool> IsProductionComplete(int deviceId)
{
// Check if production is complete
var records = await _deviceRepository.GetProductionRecordsByDeviceAsync(deviceId);
return records.Any() && records.Last().IsComplete;
}
private async Task<bool> HasDeviceError(int deviceId)
{
// Check if device has errors
var alerts = await _alarmService.GetActiveAlertsByDeviceAsync(deviceId);
return alerts.Any(a => a.AlertType == "DeviceError");
}
private async Task<bool> 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<StateActionResult> 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<Func<int, Task<bool>>> 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<string> 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<StateActionResult> ExecuteAsync(int deviceId, DeviceStateTransitionContext context)
{
// Implementation to stop production
return new StateActionResult { Success = true, Message = "Production stopped" };
}
}
public class StartProductionAction : IStateAction
{
public async Task<StateActionResult> ExecuteAsync(int deviceId, DeviceStateTransitionContext context)
{
// Implementation to start production
return new StateActionResult { Success = true, Message = "Production started" };
}
}
public class LogIdleAction : IStateAction
{
public async Task<StateActionResult> 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<StateActionResult> 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<StateActionResult> ExecuteAsync(int deviceId, DeviceStateTransitionContext context)
{
// Implementation to log state changes
return new StateActionResult { Success = true, Message = $"{_actionType} action for {_state} logged" };
}
}
#endregion
}