using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using SimpleCore; using SimpleCore.PropType; using SimpleLite.RCS; using SimpleLite.RCS.CarTypes; using StandardScene.Signal.Site; namespace StandardScene.Signal.Plc { /// /// 不引用 Magnetic.dll:按方法名反射 Start/Stop/牵引棒。 /// MagCar / Mag2Car 有对应 MethodMember 时生效;没有则视为动作已完成。 /// public static class SignalCarAdapter { public static IEnumerable All() { try { return SimpleLib.GetAllCars().OfType(); } catch { return Array.Empty(); } } public static Car Find(int id) { if (id <= 0) return null; return SimpleLib.GetCar(id) as Car; } public static int PositionId(Car car) { if (car == null) return 0; try { if (car.siteID > 0) return car.siteID; var last = car.GetLastSite(); return last > 0 ? last : 0; } catch { return 0; } } public static string GetState(Car car) { if (car?.status?.enums != null && car.status.enums.TryGetValue("State", out var state) && !string.IsNullOrWhiteSpace(state)) return state; return ""; } public static bool IsMoving(Car car) { var state = GetState(car); return state == "运行中" || string.Equals(state, "Running", StringComparison.OrdinalIgnoreCase); } /// 对齐反编译 RunStatus==停止:非运行中即视为停稳,含充电中。 public static bool IsStopped(Car car) { if (car == null) return false; if (IsMoving(car)) return false; var state = GetState(car); if (state == "停止中" || state == "充电中" || string.Equals(state, "Stopping", StringComparison.OrdinalIgnoreCase) || string.Equals(state, "Charging", StringComparison.OrdinalIgnoreCase) || string.Equals(state, "WaitPass", StringComparison.OrdinalIgnoreCase)) return true; return !IsRunning(car); } public static bool IsCharging(Car car) { var state = GetState(car); return state == "充电中" || string.Equals(state, "Charging", StringComparison.OrdinalIgnoreCase); } public static bool IsRunning(Car car) { if (car == null) return false; if (IsMoving(car)) return true; if (car.status?.pendingLocks != null && car.status.pendingLocks.Length > 0) return true; if (car.tags != null && car.tags.Contains("occupied")) return true; return false; } public static double Soc(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; } public static string GroupId(Car car) { if (car?.fields == null) return ""; if (car.fields.TryGetValue("group", out var g) && !string.IsNullOrWhiteSpace(g)) return g; if (car.fields.TryGetValue("GroupID", out var g2) && !string.IsNullOrWhiteSpace(g2)) return g2; return ""; } public static IReadOnlyList CarsAt(IEnumerable siteIds) { var set = new HashSet(siteIds ?? Array.Empty()); if (set.Count == 0) return Array.Empty(); return All().Where(c => set.Contains(PositionId(c))).ToList(); } public static IReadOnlyList CarsAt(int siteId) { if (siteId <= 0) return Array.Empty(); return CarsAt(new[] { siteId }); } public static bool TryReadSiteField(int siteId, string key, out string value) { value = ""; if (siteId <= 0 || string.IsNullOrWhiteSpace(key)) return false; try { var site = SimpleLib.GetSite(siteId); if (site?.fields == null) return false; if (!site.fields.TryGetValue(key, out var raw) || string.IsNullOrWhiteSpace(raw)) return false; value = raw; return true; } catch { return false; } } public static bool TryReadSignalStop(int siteId, out string signalType, out string signalParams) { signalType = ""; signalParams = ""; if (!TryReadSiteField(siteId, SignalSiteFields.SignalType, out signalType)) return false; TryReadSiteField(siteId, SignalSiteFields.SignalParams, out signalParams); return true; } public static int ChargeSeconds(Car car, IDictionary startedAt) { if (car == null) return 0; var mi = car.GetType().GetMethod("GetChargeTime", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (mi != null) { try { var r = mi.Invoke(car, null); if (r is int i) return i; if (r is double d) return (int)d; if (r is TimeSpan ts) return (int)ts.TotalSeconds; } catch { } } if (IsCharging(car)) { if (!startedAt.ContainsKey(car.id)) startedAt[car.id] = DateTime.Now; return (int)(DateTime.Now - startedAt[car.id]).TotalSeconds; } startedAt.Remove(car.id); return 0; } public static void Start(Car car) { if (car == null) return; if (TryInvoke(car, "StartMag2Car") || TryInvoke(car, "StartMagCar")) return; TryInvoke(car, "SendStart", new object[] { (ushort)0 }); } public static void Stop(Car car) { if (car == null) return; if (TryInvoke(car, "StopMag2Car") || TryInvoke(car, "StopMagCar")) return; TryInvoke(car, "SendStop", new object[] { (ushort)0 }); } public static void StopCharge(Car car) { if (car == null) return; TryInvoke(car, "StopCharge"); } public static bool IsActionFinish(Car car, string action) { if (car == null) return false; if (string.IsNullOrWhiteSpace(action)) return true; var mi = car.GetType().GetMethod("IsActionFinish", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (mi == null) return true; try { var r = mi.Invoke(car, new object[] { action }); return r is true; } catch { return true; } } public static void Control(Car car, string action) { if (car == null || string.IsNullOrWhiteSpace(action)) return; if (TryInvoke(car, action)) return; TryInvoke(car, "ImmediateCommand", new object[] { action }); } private static bool TryInvoke(object target, string name, object[] args = null) { var type = target.GetType(); var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .Where(m => string.Equals(m.Name, name, StringComparison.Ordinal)); foreach (var mi in methods) { var ps = mi.GetParameters(); if (args == null) { if (ps.Length != 0) continue; try { mi.Invoke(target, null); return true; } catch { return false; } } if (ps.Length != args.Length) continue; try { mi.Invoke(target, args); return true; } catch { return false; } } return false; } } }