using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using LessokajiWeaverUtilities.MagicAttributes; using LessokajiWeaverUtilities.Utilities; using SimpleLite; using SimpleLite.RCS; using SimpleLite.RCS.CarTypes; using SimpleCore; using SimpleCore.Library; using StandardScene; using StandardScene.Utils; namespace StandardScene.ExtendDevice.ButtonBox { [MissionType(Name = "按钮进程")] [I18N.DocumentTranslation(Name = "ButtonMission",locale = "en")] public class ButtonMission:Mission { private const string DataFileName = "ButtonBoxConfig.json"; private string _dataFilePath; /// /// 当前所有按钮盒实例列表 /// private List _buttonBoxes = new List(); /// /// 用于管理异步循环的取消令牌源 /// private CancellationTokenSource _cancellationTokenSource; /// /// 保存监控配置与状态的后台任务,便于关闭时等待 /// private Task _configTask; private Task _stateTask; /// /// 同步锁,用于保护按钮盒列表的并发访问 /// private readonly object _syncLock = new object(); [MethodMember(Name = "启动进程")] [I18N.DocumentTranslation(Name = "Start Mission", locale = "en")] public override void Execute() { // 设置数据文件路径 _dataFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DataFileName); // 如果已经启动,先停止之前的循环 StopInternalAsync().GetAwaiter().GetResult(); // 创建新的取消令牌源 _cancellationTokenSource = new CancellationTokenSource(); // 启动异步循环 var token = _cancellationTokenSource.Token; _configTask = Task.Run(async () => await MonitorButtonBoxConfigAsync(token), token); _stateTask = Task.Run(async () => await MonitorButtonStatesAsync(token), token); status.status = "Running"; } /// /// 异步监控按钮盒配置文件 /// private async Task MonitorButtonBoxConfigAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { // 读取配置文件 var configButtonBoxes = LoadButtonBoxConfig(); // 同步按钮盒列表 SyncButtonBoxes(configButtonBoxes); // 等待10秒 await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken); } catch (OperationCanceledException) { // 正常取消,退出循环 break; } catch (Exception ex) { // 记录错误,但继续运行 Diagnosis.Log($"按钮盒配置监控错误: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); // 发生错误时等待5秒后重试 try { await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); } catch (OperationCanceledException) { break; } } } } /// /// 停止监控任务 /// [MethodMember(Name = "停止进程")] [I18N.DocumentTranslation(Name = "Stop Mission", locale = "en")] public void Stop() { StopInternalAsync().GetAwaiter().GetResult(); status.status = "/"; } /// /// 取消并释放当前的取消令牌源 /// private async Task StopInternalAsync() { var cts = Interlocked.Exchange(ref _cancellationTokenSource, null); var configTask = Interlocked.Exchange(ref _configTask, null); var stateTask = Interlocked.Exchange(ref _stateTask, null); if (cts == null && configTask == null && stateTask == null) { return; } try { cts?.Cancel(); } catch (ObjectDisposedException) { // 已释放,忽略 } var runningTasks = new[] { configTask, stateTask } .Where(t => t != null) .ToArray(); if (runningTasks.Length > 0) { var aggregateTask = Task.WhenAll(runningTasks); var timeoutTask = Task.Delay(TimeSpan.FromSeconds(5)); var completedTask = await Task.WhenAny(aggregateTask, timeoutTask).ConfigureAwait(false); if (completedTask == timeoutTask) { Diagnosis.Log("停止按钮监控任务超时", "ButtonMission", true); } else { try { await aggregateTask.ConfigureAwait(false); } catch (Exception ex) { Diagnosis.Log($"停止按钮监控任务时发生异常: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); } } } cts?.Dispose(); DisconnectAllButtonBoxes(); } /// /// 断开所有按钮盒连接 /// private void DisconnectAllButtonBoxes() { List snapshot; lock (_syncLock) { snapshot = _buttonBoxes.ToList(); } foreach (var box in snapshot) { try { box.Disconnect(); } catch (Exception ex) { Diagnosis.Log($"停止按钮盒失败: Index={box.Index}, Error={ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); } } } /// /// 监控按钮状态,用于触发按钮动作 /// private async Task MonitorButtonStatesAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { List<(BasicButtonBox Box, ButtonModel Config)> snapshot; lock (_syncLock) { snapshot = _buttonBoxes .SelectMany(box => box.ButtonConfigs.Values.Select(cfg => (Box: box, Config: cfg))) .ToList(); } foreach (var (box, config) in snapshot) { if (box == null || config == null) { continue; } if (string.IsNullOrWhiteSpace(config.TriggerMission) || string.IsNullOrWhiteSpace(config.TriggerMethod)) { continue; } var desiredState = ButtonState.Pressed; if (!string.IsNullOrWhiteSpace(config.TriggerState) && Enum.TryParse(config.TriggerState, out ButtonState parsedState)) { desiredState = parsedState; } var currentState = box.GetButtonState(config.Index); bool isActive = currentState == desiredState&&box.IsOnline; int delay = config.TriggerDelay; if (delay <= 0) { delay = 1; } int uniqueId = unchecked((box.Index << 16) ^ config.Index); LadderLogic.TriggerOnce(isActive, delay*1000, () => { ExecuteButtonAction(config, box); }, uniqueId); } } catch (OperationCanceledException) { break; } catch (Exception ex) { Diagnosis.Log($"按钮状态监控错误: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); } try { await Task.Delay(TimeSpan.FromMilliseconds(500), cancellationToken); } catch (OperationCanceledException) { break; } } } /// /// 加载按钮盒配置文件 /// private List LoadButtonBoxConfig() { try { if (File.Exists(_dataFilePath)) { var jsonContent = File.ReadAllText(_dataFilePath, Encoding.UTF8); if (!string.IsNullOrWhiteSpace(jsonContent)) { var buttonBoxes = jsonContent.JsonTo>(); return buttonBoxes ?? new List(); } } } catch (Exception ex) { Diagnosis.Log($"加载按钮盒配置文件失败: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); } return new List(); } /// /// 同步按钮盒列表,根据配置文件进行增删改 /// private void SyncButtonBoxes(List configButtonBoxes) { var boxesToAdd = new List(); lock (_syncLock) { // 创建配置中的按钮盒索引字典,用于快速查找 var configDict = configButtonBoxes.ToDictionary(b => b.Index); // 创建当前按钮盒索引字典 var currentDict = _buttonBoxes.ToDictionary(b => b.Index); // 1. 删除:在配置中不存在的按钮盒 var toRemove = _buttonBoxes.Where(b => !configDict.ContainsKey(b.Index)).ToList(); foreach (var buttonBox in toRemove) { try { // 断开连接 buttonBox.Disconnect(); _buttonBoxes.Remove(buttonBox); Diagnosis.Post($"删除按钮盒: Index={buttonBox.Index}, IP={buttonBox.Ip}", "ButtonMission", true); } catch (Exception ex) { Diagnosis.Log($"删除按钮盒失败: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); } } // 2. 添加和修改:遍历配置中的按钮盒 foreach (var configBox in configButtonBoxes) { if (currentDict.TryGetValue(configBox.Index, out var existingBox)) { // 修改:检查是否需要更新 if (ShouldUpdateButtonBox(existingBox, configBox)) { try { UpdateButtonBox(existingBox, configBox); Diagnosis.Post($"更新按钮盒: Index={configBox.Index}, IP={configBox.Ip}, Type={configBox.Type}", "ButtonMission", true); } catch (Exception ex) { Diagnosis.Log($"更新按钮盒失败: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); } } } else { boxesToAdd.Add(configBox); } } } foreach (var configBox in boxesToAdd) { try { var newBox = CreateButtonBoxInstance(configBox); if (newBox != null) { lock (_syncLock) { _buttonBoxes.Add(newBox); } Diagnosis.Post($"添加按钮盒: Index={configBox.Index}, IP={configBox.Ip}, Type={configBox.Type}", "ButtonMission", true); } else { Diagnosis.Log($"无法创建按钮盒实例: Index={configBox.Index}, Type={configBox.Type}", "ButtonMission", true); } } catch (Exception ex) { Diagnosis.Log($"添加按钮盒失败: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); } } } /// /// 判断是否需要更新按钮盒 /// private bool ShouldUpdateButtonBox(BasicButtonBox existingBox, ButtonBoxModel configBox) { // 检查基本属性是否变更 if (existingBox.Ip != configBox.Ip || existingBox.Port != configBox.Port || existingBox.GetType().Name != configBox.Type) { return true; } // 检查按钮信息是否变更 return HasButtonConfigsChanged(existingBox, configBox); } /// /// 检查按钮配置信息是否变更 /// private bool HasButtonConfigsChanged(BasicButtonBox existingBox, ButtonBoxModel configBox) { var configButtons = configBox.Buttons ?? new List(); var configDict = configButtons.ToDictionary(b => b.Index); var existingDict = existingBox.ButtonConfigs; // 检查按钮数量是否变化 if (existingDict.Count != configDict.Count) { return true; } // 检查每个按钮的配置是否变化 foreach (var configButton in configButtons) { if (!existingDict.TryGetValue(configButton.Index, out var existingButton)) { // 新增了按钮 return true; } // 检查按钮配置是否变化 if (existingButton.TriggerMission != configButton.TriggerMission || existingButton.TriggerMethod != configButton.TriggerMethod || existingButton.TriggerMethodParams != configButton.TriggerMethodParams || existingButton.TriggerState != configButton.TriggerState || existingButton.TriggerDelay != configButton.TriggerDelay) { return true; } } // 检查是否有按钮被删除 foreach (var existingKey in existingDict.Keys) { if (!configDict.ContainsKey(existingKey)) { return true; } } return false; } /// /// 更新按钮盒属性 /// private void UpdateButtonBox(BasicButtonBox buttonBox, ButtonBoxModel configBox) { // 如果类型改变,需要重新创建实例 if (buttonBox.GetType().Name != configBox.Type) { // 断开旧连接 buttonBox.Disconnect(); // 从列表中移除 _buttonBoxes.Remove(buttonBox); // 创建新实例 var newBox = CreateButtonBoxInstance(configBox); if (newBox != null) { _buttonBoxes.Add(newBox); } } else { // 只更新属性 bool needReconnect = buttonBox.Ip != configBox.Ip || buttonBox.Port != configBox.Port; buttonBox.Ip = configBox.Ip; buttonBox.Port = configBox.Port; // 更新按钮配置信息 buttonBox.UpdateButtonConfigs(configBox.Buttons); // 初始化按钮状态(基于配置中的按钮索引) var buttonIndices = configBox.Buttons?.Select(b => b.Index).ToList() ?? new List(); buttonBox.InitializeButtons(buttonIndices); // 如果IP或端口改变,需要重新连接 if (needReconnect) { buttonBox.Disconnect(); buttonBox.Connect(); } } } /// /// 通过类型字符串创建按钮盒实例 /// private BasicButtonBox CreateButtonBoxInstance(ButtonBoxModel configBox) { if (string.IsNullOrWhiteSpace(configBox.Type)) { return null; } try { // 获取当前命名空间下所有继承自BasicButtonBox的类 // 跨程序集发现:按钮盒具体类型可能位于卫星插件 dll(StandardScene.Devices.ButtonBox), // 用内核同款全域类型发现替代仅扫当前程序集的 GetExecutingAssembly。 var buttonBoxType = SimpleLite.Utils.UiTypeDiscovery.AllTypes() .FirstOrDefault(t => t.IsClass && !t.IsAbstract && t.Namespace == typeof(BasicButtonBox).Namespace && t.IsSubclassOf(typeof(BasicButtonBox)) && t.Name == configBox.Type); if (buttonBoxType == null) { Diagnosis.Log($"未找到按钮盒类型: {configBox.Type}", "ButtonMission", true); return null; } // 使用反射创建实例 var instance = (BasicButtonBox)Activator.CreateInstance(buttonBoxType); // 设置属性 instance.Index = configBox.Index; instance.Ip = configBox.Ip; instance.Port = configBox.Port; // 初始化按钮配置信息 instance.InitializeButtonConfigs(configBox.Buttons); // 初始化按钮状态(基于配置中的按钮索引) var buttonIndices = configBox.Buttons?.Select(b => b.Index).ToList() ?? new List(); instance.InitializeButtons(buttonIndices); // 自动连接 instance.Connect(); return instance; } catch (Exception ex) { Diagnosis.Log($"创建按钮盒实例失败: Type={configBox.Type}, Error={ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); return null; } } /// /// 获取当前所有按钮盒实例(只读) /// public IReadOnlyList GetButtonBoxes() { lock (_syncLock) { return _buttonBoxes.ToList().AsReadOnly(); } } /// /// 执行按钮动作(在独立线程中异步执行,避免阻塞按钮监控循环) /// private void ExecuteButtonAction(ButtonModel buttonConfig, BasicButtonBox buttonBox) { Task.Run(() => ExecuteButtonActionInternal(buttonConfig, buttonBox)); } /// /// 实际执行业务方法的内部逻辑,包含成功/失败反馈。 /// private void ExecuteButtonActionInternal(ButtonModel buttonConfig, BasicButtonBox buttonBox) { var success = false; try { // 按钮动作执行后清零对应按钮寄存器(具体盒型按需重写,默认空实现) buttonBox.ClearButtonRegister(buttonConfig.Index); if (string.IsNullOrWhiteSpace(buttonConfig.TriggerMission) || string.IsNullOrWhiteSpace(buttonConfig.TriggerMethod)) { // 配置不完整,直接反馈失败 Diagnosis.Log("按钮配置缺少 TriggerMission 或 TriggerMethod,无法执行动作", "ButtonMission", true); return; } var mission = SimpleProject.proj?.Missions? .FirstOrDefault(m => m.GetType().Name == buttonConfig.TriggerMission || m.name == buttonConfig.TriggerMission); if (mission == null) { Diagnosis.Log($"未找到触发任务: {buttonConfig.TriggerMission}", "ButtonMission", true); return; } var method = mission.GetType().GetMethod(buttonConfig.TriggerMethod, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); if (method == null) { Diagnosis.Log($"任务 {buttonConfig.TriggerMission} 中未找到方法 {buttonConfig.TriggerMethod}", "ButtonMission", true); return; } var parameters = ParseMethodParameters(buttonConfig.TriggerMethodParams, method); if (method.IsStatic) { var result = method.Invoke(null, parameters); success = HandleMethodResult(result); } else { var result = method.Invoke(mission, parameters); success = HandleMethodResult(result); } } catch (Exception ex) { Diagnosis.Log($"执行按钮动作失败: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); } finally { // 业务方法执行完成后,回调按钮盒进行反馈(如灯光、蜂鸣等) try { buttonBox.OnActionExecuted(buttonConfig, success); } catch (Exception feedbackEx) { Diagnosis.Log($"按钮盒执行反馈失败: {ExceptionFormatter.FormatEx(feedbackEx)}", "ButtonMission", true); } } } /// /// 处理反射调用结果:支持 Task/Task<bool> 等异步返回类型。 /// 返回 true 表示执行成功。 /// private bool HandleMethodResult(object result) { try { switch (result) { case null: return true; case Task tb: return tb.GetAwaiter().GetResult(); case Task t: t.GetAwaiter().GetResult(); return true; case bool b: return b; default: return true; } } catch (Exception ex) { Diagnosis.Log($"按钮动作方法异步执行失败: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); return false; } } /// /// 解析方法参数 /// private object[] ParseMethodParameters(string paramsStr, MethodInfo methodInfo) { var paramInfos = methodInfo.GetParameters(); if (paramInfos.Length == 0) { return Array.Empty(); } if (string.IsNullOrWhiteSpace(paramsStr)) { return paramInfos.Select(p => p.HasDefaultValue ? p.DefaultValue : GetDefaultValue(p.ParameterType)).ToArray(); } try { var paramStrings = paramsStr.Split(','); var parameters = new List(); for (int i = 0; i < paramInfos.Length; i++) { var paramInfo = paramInfos[i]; var paramType = paramInfo.ParameterType; if (i < paramStrings.Length) { var trimmed = paramStrings[i].Trim(); parameters.Add(ConvertParameter(trimmed, paramType)); } else { parameters.Add(paramInfo.HasDefaultValue ? paramInfo.DefaultValue : GetDefaultValue(paramType)); } } return parameters.ToArray(); } catch (Exception ex) { Diagnosis.Log($"解析按钮参数失败: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); return paramInfos.Select(p => p.HasDefaultValue ? p.DefaultValue : GetDefaultValue(p.ParameterType)).ToArray(); } } /// /// 转换参数 /// private object ConvertParameter(string value, Type targetType) { if (targetType == typeof(string)) { return value; } if (targetType == typeof(int) || targetType == typeof(int?)) { return int.TryParse(value, out int result) ? result : (targetType == typeof(int?) ? (int?)null : 0); } if (targetType == typeof(double) || targetType == typeof(double?)) { return double.TryParse(value, out double result) ? result : (targetType == typeof(double?) ? (double?)null : 0d); } if (targetType == typeof(float) || targetType == typeof(float?)) { return float.TryParse(value, out float result) ? result : (targetType == typeof(float?) ? (float?)null : 0f); } if (targetType == typeof(bool) || targetType == typeof(bool?)) { return bool.TryParse(value, out bool result) ? result : (targetType == typeof(bool?) ? (bool?)null : false); } if (targetType.IsEnum) { try { return Enum.Parse(targetType, value, true); } catch { return Enum.GetValues(targetType).GetValue(0); } } return value; } /// /// 获取类型默认值 /// private object GetDefaultValue(Type type) { if (type.IsValueType) { return Activator.CreateInstance(type); } return null; } /// /// 打开按钮盒管理界面 /// [MethodMember(Name = "打开管理界面")] [I18N.DocumentTranslation(Name = "Open Manager", locale = "en")] public static void OpenViewer() { try { ButtonBoxManager.Open(); } catch (Exception ex) { CycleUiHelper.Alert("错误", $"打开按钮盒管理界面失败: {ex.Message}"); } } } }