using LessokajiWeaverUtilities.Utilities;
using Newtonsoft.Json;
using SimpleLite.RCS;
using SimpleLite.Props;
using SimpleCore;
using SimpleCore.Library;
using SimpleCore.PropType;
using StandardScene;
using StandardScene.Scheduler.FassEvent;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SimpleLite.RCS.CarTypes;
namespace StandardScene.Scheduler
{
///
/// FASS 车辆事件调度进程。
///
/// 移植自 fass/FASS.Scheduler/Services/Events/EventCarService.cs:
/// 按 JSON 配置定时轮询全场车辆,匹配触发条件后执行交管/PLC/电量等条件判断,
/// 最终对 MagCar 下发 Start(放行)或 Stop(停车)。
///
///
/// 使用方式:在场景中添加本 Mission →「启动进程」→ 默认加载 Configs\EventCar\FG2512073.json。
///
///
[MissionType(Name = "FASS车辆事件进程", editor = typeof(EventCarMission))]
[I18N.DocumentTranslation(Name = "FASS Event Car Mission", locale = "en")]
public class EventCarMission : Mission
{
/// EventCar JSON 配置文件路径(相对程序目录或绝对路径)。
[FieldMember] public string EventConfigPath = "Config\\EventCar\\traffic.json";
/// 主循环轮询间隔(毫秒),对应 FASS TimerTick 频率,默认 500ms。
[FieldMember] public int TickIntervalMs = 500;
/// 与下料站/安全信号交互用的西门子 PLC IP。
[FieldMember] public string PlcIpAddress = "127.0.0.1";
/// PLC 端口,S7 默认 102。
[FieldMember] public int PlcPort = 102;
/// PLC 型号字符串,如 S7_1500、S7_300,传给 IoTClient SiemensClient。
[FieldMember] public string PlcVersion = "S7_1500";
/// 是否启用 ME11 下料站 PLC 信号交互(false 时 requestStaion/InplaceStaion 等直接返回 true)。
[FieldMember] public bool IsEnableMEll = true;
/// 充电完成放行默认电量阈值(%),可被事件内 ChargeStart 参数覆盖。
[FieldMember] public double ChargeStartThreshold = 93;
[JsonIgnore] private bool _started;
[JsonIgnore] private Thread _workerThread;
[JsonIgnore] private FassEventCarHandler _handler;
/// 当前生效的车辆事件配置列表(TriggerSource=Car 且 TriggerEnable=true)。
[JsonIgnore] private List _eventCars = new List();
/// 记录每辆车上一周期的 CurrentNode,用于 PrevNode 触发与地标变化时清除任务防重复标记。
[JsonIgnore] private readonly Dictionary _prevNodeByCarId = new Dictionary();
/// 进程运行状态扩展字段。
public class EventCarMissionStatus : MissionStatus
{
/// 主循环已执行次数。
public int TickCount { get; set; }
/// 已加载的事件规则条数。
public int LoadedEventCount { get; set; }
/// 实际加载的配置文件绝对/解析路径。
public string ConfigPath { get; set; }
}
public override MissionStatus status { get; set; } = new EventCarMissionStatus();
public static Mission Create()
{
return new EventCarMission();
}
///
/// 启动后台轮询线程:加载配置 → 创建 Handler → 周期性 ProcessAllCars。
///
[MethodMember(Name = "启动进程", Description = "加载 FASS EventCar 配置并开始轮询车辆事件")]
public override void Execute()
{
if (_started)
{
status.status = "已在运行";
return;
}
try
{
ReloadConfig();
}
catch (Exception ex)
{
status.status = "配置加载失败";
Diagnosis.Post($"FASS车辆事件进程配置加载失败:{ExceptionFormatter.FormatEx(ex)}", "FassEventCar", true);
throw;
}
_handler = new FassEventCarHandler(this);
_started = true;
status.status = "运行中";
_workerThread = new Thread(WorkerLoop)
{
IsBackground = true,
Name = "EventCarMission"
};
_workerThread.Start();
Diagnosis.Post($"FASS车辆事件进程已启动,配置数={_eventCars.Count}", "FassEventCar", true);
}
/// 热加载 JSON 配置,运行中也可调用。
[MethodMember(Name = "重新加载配置", Description = "重新读取 EventCar JSON 配置")]
public void ReloadConfig()
{
var path = ResolveConfigPath();
_eventCars = FassEventConfigLoader.Load(path)
.Where(item => item.TriggerEnable && string.Equals(item.TriggerSource, "Car", StringComparison.OrdinalIgnoreCase))
.ToList();
var missionStatus = (EventCarMissionStatus)status;
missionStatus.LoadedEventCount = _eventCars.Count;
missionStatus.ConfigPath = path;
status.status = _started ? "运行中" : "配置已加载";
Diagnosis.Post($"FASS EventCar 配置已加载:{path},事件数={_eventCars.Count}", "FassEventCar", true);
}
/// 停止轮询并等待工作线程结束(最多 2 秒)。
[MethodMember(Name = "停止进程", Description = "停止 FASS 车辆事件轮询")]
public void Stop()
{
_started = false;
try
{
_workerThread?.Join(2000);
}
catch
{
}
status.status = "已停止";
Diagnosis.Post("FASS车辆事件进程已停止", "FassEventCar", true);
}
///
/// 后台主循环:每 TickIntervalMs 扫描一次全场车辆。
/// 单轮异常不会终止进程,仅写诊断日志。
///
private void WorkerLoop()
{
while (_started)
{
try
{
ProcessAllCars();
var missionStatus = (EventCarMissionStatus)status;
missionStatus.TickCount++;
status.status = $"运行中:{missionStatus.TickCount}";
}
catch (Exception ex)
{
Diagnosis.Post($"FASS车辆事件进程循环异常:{ExceptionFormatter.FormatEx(ex)}", "FassEventCar", true);
}
Thread.Sleep(Math.Max(100, TickIntervalMs));
}
}
///
/// 遍历所有 Car,构建快照并异步调用 Handler(与 FASS Task.Run 行为一致)。
///
/// 跳过 status 为空或 usage.refreshing=false 的车辆(未参与调度刷新的车不处理)。
/// 当地标变化时清除 ,允许在新地标重新触发事件。
///
///
private void ProcessAllCars()
{
foreach (var car in SimpleLib.GetAllCars().OfType())
{
if (car.status == null)
{
continue;
}
var usage = car.status.usage?.Get();
if (usage != null && usage.refreshing == false)
{
continue;
}
var currentNode = FassEventCarSnapshotFactory.GetCurrentNodeCode(car);
_prevNodeByCarId.TryGetValue(car.id, out var prevNode);
var snapshot = FassEventCarSnapshotFactory.Create(car, prevNode, FassEventCarSnapshotFactory.GetNextNodeCode(car));
Task.Run(() => _handler.HandleCarTimerTick(car, snapshot, _eventCars));
if (!string.Equals(prevNode, currentNode, StringComparison.OrdinalIgnoreCase))
{
_prevNodeByCarId[car.id] = currentNode;
ClearTaskTags(car);
}
}
}
/// 车辆换地标后删除防重复执行标签,使同一规则可在新站点再次触发。
private static void ClearTaskTags(Car car)
{
if (car.tags == null)
{
return;
}
Commons.DeleteTag(car.tags, FassEventCarHandler.ActiveTaskTag);
}
///
/// 解析配置文件路径:绝对路径直接用;相对路径依次尝试 BaseDirectory、CurrentDirectory、上级目录。
///
private string ResolveConfigPath()
{
if (Path.IsPathRooted(EventConfigPath))
{
return EventConfigPath;
}
var candidates = new[]
{
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, EventConfigPath),
Path.Combine(Directory.GetCurrentDirectory(), EventConfigPath),
Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", EventConfigPath))
};
return candidates.FirstOrDefault(File.Exists) ?? candidates[0];
}
}
}