init commit

This commit is contained in:
zhaowei.huang
2026-06-14 11:19:15 +08:00
parent e79a3815a5
commit c8e540d272
174 changed files with 60830 additions and 39 deletions
@@ -0,0 +1,806 @@
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);
}
}
}
}