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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|