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 Simple3;
using Simple3.RCS;
using Simple3.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 override 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 = Simple3.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