166 lines
6.3 KiB
C#
166 lines
6.3 KiB
C#
using SimpleLite.RCS;
|
||
using SimpleCore.PropType;
|
||
using StandardScene.CarTypes;
|
||
using System;
|
||
using System.Globalization;
|
||
using SimpleLite.RCS.CarTypes;
|
||
|
||
namespace StandardScene.Scheduler.FassEvent
|
||
{
|
||
/// <summary>
|
||
/// 将 StandardScene 车辆对象转换为 FASS 事件系统可识别的快照字段。
|
||
/// 负责编号、地标、状态、电量等与 <c>EventCarService.TriggerHandler</c> 对齐的映射。
|
||
/// </summary>
|
||
internal static class FassEventCarSnapshotFactory
|
||
{
|
||
/// <summary>
|
||
/// 获取车辆“下一地标”编码。
|
||
/// 优先级:status.enums["NextNode"] → tags["NextNode"] → pendingLocks 首站(TagValue 或站点 ID)。
|
||
/// </summary>
|
||
public static string GetNextNodeCode(Car car)
|
||
{
|
||
if (car.status?.enums != null && car.status.enums.TryGetValue("NextNode", out var node) && !string.IsNullOrWhiteSpace(node))
|
||
{
|
||
return node;
|
||
}
|
||
|
||
if (car.tags != null && car.tags.TryGetValue("NextNode", out var tagNode) && !string.IsNullOrWhiteSpace(tagNode))
|
||
{
|
||
return tagNode;
|
||
}
|
||
|
||
// 调度 pendingLocks 通常表示车辆即将前往的站点,可近似为 FASS NextNodeCode。
|
||
if (car.status?.pendingLocks != null && car.status.pendingLocks.Length > 0)
|
||
{
|
||
var pendingSiteId = car.status.pendingLocks[0];
|
||
var site = SimpleCore.SimpleLib.GetSite(pendingSiteId);
|
||
if (site?.fields != null && site.fields.TryGetValue("TagValue", out var tagValue) && !string.IsNullOrWhiteSpace(tagValue))
|
||
{
|
||
return tagValue;
|
||
}
|
||
|
||
return pendingSiteId.ToString(CultureInfo.InvariantCulture);
|
||
}
|
||
|
||
return string.Empty;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据车辆与上一周期地标构建完整快照。
|
||
/// </summary>
|
||
/// <param name="car">场景车辆。</param>
|
||
/// <param name="prevNodeCode">上一周期 CurrentNode,由 EventCarMission 维护。</param>
|
||
/// <param name="nextNodeCode">下一地标;为空时自动调用 <see cref="GetNextNodeCode"/>。</param>
|
||
public static FassEventCarSnapshot Create(Car car, string prevNodeCode, string nextNodeCode)
|
||
{
|
||
return new FassEventCarSnapshot
|
||
{
|
||
CarId = car.id,
|
||
Code = GetCarCode(car),
|
||
CarModel = GetCarModel(car),
|
||
PrevNodeCode = prevNodeCode,
|
||
CurrentNodeCode = GetCurrentNodeCode(car),
|
||
NextNodeCode = string.IsNullOrWhiteSpace(nextNodeCode) ? GetNextNodeCode(car) : nextNodeCode,
|
||
State = GetFassState(car),
|
||
Angle = (int)Math.Round(car.th),
|
||
Charge = GetCharge(car)
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取 FASS 车辆编号。MagCar 优先使用 VehicleCode,否则 fields["Code"],最后回退 name。
|
||
/// </summary>
|
||
public static string GetCarCode(Car car)
|
||
{
|
||
if (car is MagCar magCar && magCar.VehicleCode > 0)
|
||
{
|
||
return magCar.VehicleCode.ToString(CultureInfo.InvariantCulture);
|
||
}
|
||
|
||
if (car.fields != null && car.fields.TryGetValue("Code", out var code) && !string.IsNullOrWhiteSpace(code))
|
||
{
|
||
return code;
|
||
}
|
||
|
||
return car.name;
|
||
}
|
||
|
||
/// <summary>获取车型。下料站 InplaceStaion 根据 CarModel 在 ME11 / 放行车型间切换。</summary>
|
||
public static string GetCarModel(Car car)
|
||
{
|
||
if (car.fields != null && car.fields.TryGetValue("CarModel", out var model) && !string.IsNullOrWhiteSpace(model))
|
||
{
|
||
return model;
|
||
}
|
||
|
||
return car.GetType().Name;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前地标号。MagCar 上报后写入 status.enums["CurrentNode"];
|
||
/// 其他车型可回退到 siteID。
|
||
/// </summary>
|
||
public static string GetCurrentNodeCode(Car car)
|
||
{
|
||
if (car.status?.enums != null && car.status.enums.TryGetValue("CurrentNode", out var node) && !string.IsNullOrWhiteSpace(node))
|
||
{
|
||
return node;
|
||
}
|
||
|
||
return car.siteID > 0 ? car.siteID.ToString(CultureInfo.InvariantCulture) : string.Empty;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将 StandardScene 车辆状态映射为 FASS 英文字符串。
|
||
/// <para>
|
||
/// 可手动覆盖:tags["FassState"] 或 enums["FassState"]。
|
||
/// WaitPass 需在停止中且 tags 含 WaitPass(FASS 中“等待放行”态)。
|
||
/// </para>
|
||
/// </summary>
|
||
public static string GetFassState(Car car)
|
||
{
|
||
if (car.tags != null && car.tags.TryGetValue("FassState", out var tagState) && !string.IsNullOrWhiteSpace(tagState))
|
||
{
|
||
return tagState;
|
||
}
|
||
|
||
if (car.status?.enums != null && car.status.enums.TryGetValue("FassState", out var enumState) && !string.IsNullOrWhiteSpace(enumState))
|
||
{
|
||
return enumState;
|
||
}
|
||
|
||
if (car.status?.enums != null && car.status.enums.TryGetValue("State", out var state))
|
||
{
|
||
switch (state)
|
||
{
|
||
case "运行中":
|
||
return "Running";
|
||
case "停止中":
|
||
return car.tags != null && car.tags.ContainsKey("WaitPass") ? "WaitPass" : "Stopping";
|
||
case "未准备":
|
||
return "NotReady";
|
||
case "充电中":
|
||
return "Charging";
|
||
default:
|
||
return state;
|
||
}
|
||
}
|
||
|
||
return string.Empty;
|
||
}
|
||
|
||
/// <summary>从 status.enums["Soc"] 解析电量百分比。</summary>
|
||
private static double GetCharge(Car car)
|
||
{
|
||
if (car.status?.enums != null &&
|
||
car.status.enums.TryGetValue("Soc", out var soc) &&
|
||
double.TryParse(soc, NumberStyles.Float, CultureInfo.InvariantCulture, out var value))
|
||
{
|
||
return value;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
}
|
||
}
|