Files
StandardSence/StandardScene.Core/ExtendDevice/ButtonBox/ButtonMission.cs
T
2026-06-14 11:19:15 +08:00

807 lines
29 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 System.Windows.Forms;
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;
/// <summary>
/// 当前所有按钮盒实例列表
/// </summary>
private List<BasicButtonBox> _buttonBoxes = new List<BasicButtonBox>();
/// <summary>
/// 用于管理异步循环的取消令牌源
/// </summary>
private CancellationTokenSource _cancellationTokenSource;
/// <summary>
/// 保存监控配置与状态的后台任务,便于关闭时等待
/// </summary>
private Task _configTask;
private Task _stateTask;
/// <summary>
/// 同步锁,用于保护按钮盒列表的并发访问
/// </summary>
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";
}
/// <summary>
/// 异步监控按钮盒配置文件
/// </summary>
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;
}
}
}
}
/// <summary>
/// 停止监控任务
/// </summary>
[MethodMember(Name = "停止进程")]
[I18N.DocumentTranslation(Name = "Stop Mission", locale = "en")]
public void Stop()
{
StopInternalAsync().GetAwaiter().GetResult();
status.status = "/";
}
/// <summary>
/// 取消并释放当前的取消令牌源
/// </summary>
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();
}
/// <summary>
/// 断开所有按钮盒连接
/// </summary>
private void DisconnectAllButtonBoxes()
{
List<BasicButtonBox> 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);
}
}
}
/// <summary>
/// 监控按钮状态,用于触发按钮动作
/// </summary>
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;
}
}
}
/// <summary>
/// 加载按钮盒配置文件
/// </summary>
private List<ButtonBoxModel> LoadButtonBoxConfig()
{
try
{
if (File.Exists(_dataFilePath))
{
var jsonContent = File.ReadAllText(_dataFilePath, Encoding.UTF8);
if (!string.IsNullOrWhiteSpace(jsonContent))
{
var buttonBoxes = jsonContent.JsonTo<List<ButtonBoxModel>>();
return buttonBoxes ?? new List<ButtonBoxModel>();
}
}
}
catch (Exception ex)
{
Diagnosis.Log($"加载按钮盒配置文件失败: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true);
}
return new List<ButtonBoxModel>();
}
/// <summary>
/// 同步按钮盒列表,根据配置文件进行增删改
/// </summary>
private void SyncButtonBoxes(List<ButtonBoxModel> configButtonBoxes)
{
var boxesToAdd = new List<ButtonBoxModel>();
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);
}
}
}
/// <summary>
/// 判断是否需要更新按钮盒
/// </summary>
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);
}
/// <summary>
/// 检查按钮配置信息是否变更
/// </summary>
private bool HasButtonConfigsChanged(BasicButtonBox existingBox, ButtonBoxModel configBox)
{
var configButtons = configBox.Buttons ?? new List<ButtonModel>();
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;
}
/// <summary>
/// 更新按钮盒属性
/// </summary>
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<int>();
buttonBox.InitializeButtons(buttonIndices);
// 如果IP或端口改变,需要重新连接
if (needReconnect)
{
buttonBox.Disconnect();
buttonBox.Connect();
}
}
}
/// <summary>
/// 通过类型字符串创建按钮盒实例
/// </summary>
private BasicButtonBox CreateButtonBoxInstance(ButtonBoxModel configBox)
{
if (string.IsNullOrWhiteSpace(configBox.Type))
{
return null;
}
try
{
// 获取当前命名空间下所有继承自BasicButtonBox的类
// 跨程序集发现:按钮盒具体类型可能位于卫星插件 dllStandardScene.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<int>();
instance.InitializeButtons(buttonIndices);
// 自动连接
instance.Connect();
return instance;
}
catch (Exception ex)
{
Diagnosis.Log($"创建按钮盒实例失败: Type={configBox.Type}, Error={ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true);
return null;
}
}
/// <summary>
/// 获取当前所有按钮盒实例(只读)
/// </summary>
public IReadOnlyList<BasicButtonBox> GetButtonBoxes()
{
lock (_syncLock)
{
return _buttonBoxes.ToList().AsReadOnly();
}
}
/// <summary>
/// 执行按钮动作(在独立线程中异步执行,避免阻塞按钮监控循环)
/// </summary>
private void ExecuteButtonAction(ButtonModel buttonConfig, BasicButtonBox buttonBox)
{
Task.Run(() => ExecuteButtonActionInternal(buttonConfig, buttonBox));
}
/// <summary>
/// 实际执行业务方法的内部逻辑,包含成功/失败反馈。
/// </summary>
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);
}
}
}
/// <summary>
/// 处理反射调用结果:支持 Task/Task&lt;bool&gt; 等异步返回类型。
/// 返回 true 表示执行成功。
/// </summary>
private bool HandleMethodResult(object result)
{
try
{
switch (result)
{
case null:
return true;
case Task<bool> 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;
}
}
/// <summary>
/// 解析方法参数
/// </summary>
private object[] ParseMethodParameters(string paramsStr, MethodInfo methodInfo)
{
var paramInfos = methodInfo.GetParameters();
if (paramInfos.Length == 0)
{
return Array.Empty<object>();
}
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<object>();
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();
}
}
/// <summary>
/// 转换参数
/// </summary>
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;
}
/// <summary>
/// 获取类型默认值
/// </summary>
private object GetDefaultValue(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
/// <summary>
/// 打开按钮盒管理界面
/// </summary>
[MethodMember(Name = "打开管理界面")]
[I18N.DocumentTranslation(Name = "Open Manager", locale = "en")]
public static void OpenViewer()
{
try
{
var manager = ButtonBoxManager.Instance;
// 确保窗体没有被销毁
if (manager.IsDisposed)
{
// 如果窗体被销毁,单例会自动重新创建
manager = ButtonBoxManager.Instance;
}
if (manager.Visible)
{
// 如果界面已经可见,将其激活并置于最前
if (manager.WindowState == FormWindowState.Minimized)
{
manager.WindowState = FormWindowState.Normal;
}
manager.Activate();
manager.BringToFront();
}
else
{
// 如果界面不可见,显示它
manager.Show();
manager.Activate();
}
}
catch (Exception ex)
{
MessageBox.Show($"打开按钮盒管理界面失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}