using System; using System.Collections.Generic; using System.Linq; using SimpleCore.Library; using SimpleLite.RCS.CarTypes; using StandardScene.Signal.Plc; namespace StandardScene.Signal.Logic { /// /// 区域车组限流。对齐反编译 DefaultPlugin.RateFlowControl。 /// /// /// 独立握手类型 RateFlowControl 用分号:区域;车组;限额;点1,点2。 /// 挂在 JGControl 第三段时用短横:区域-车组-限额-点1,点2(因为前两段机构名/动作已经占用分号)。 /// 统计「站点列表上、同车组、且不是本车」的数量,小于限额才放行。 /// 解析异常时放行(return true),避免配错把产线卡死。 /// public static class RateFlowHandler { /// 独立限流点:通过则停充并启动。 public static void OnStopped(Car car, SignalStopSpec spec, IDictionary logByArea) { if (AllowPass(car, spec.SignalParams, logByArea, sep: ';')) { SignalCarAdapter.StopCharge(car); SignalCarAdapter.Start(car); } } /// JGControl 第三段限流,短横分隔。不通过时调用方应直接 return,不启动。 public static bool AllowPass(Car car, string rateFlowString, IDictionary logByArea) => AllowPass(car, rateFlowString, logByArea, sep: '-'); /// 独立类型用 ';',JGControl 挂载用 '-'。 private static bool AllowPass(Car car, string raw, IDictionary logByArea, char sep) { try { var parts = (raw ?? "").Split(sep); var area = parts.Length > 0 ? parts[0] : ""; var group = parts.Length > 1 ? parts[1] : ""; var limit = 0; if (parts.Length > 2) int.TryParse(parts[2], out limit); var sites = new List(); if (parts.Length > 3) { foreach (var s in parts[3].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { if (int.TryParse(s.Trim(), out var id)) sites.Add(id); } } var others = SignalCarAdapter.CarsAt(sites) .Where(x => x.id != car.id && string.Equals(SignalCarAdapter.GroupId(x), group, StringComparison.Ordinal)) .ToList(); var count = others.Count; if (count < limit) { Diagnosis.Log( $"限流区域{area}限制AGV组{group}流量{limit}辆AGV,当前停放{count},放行AGV{car.id}", "RateFlow", true); Note(logByArea, area, car, raw); return true; } Diagnosis.Log( $"限流区域{area}限制AGV组{group}流量{limit}辆AGV,当前停放{count},无法放行", "RateFlow", true); Note(logByArea, area, car, raw); return false; } catch (Exception ex) { Diagnosis.Log($"处理区域限流业务异常:{ex.Message}", "RateFlow", true); return true; } } /// 同一限流区域仅在「决策车号变化」时再打一条带原始参数的日志。 private static void Note(IDictionary logByArea, string area, Car car, string raw) { if (logByArea == null) return; var changed = !logByArea.TryGetValue(area, out var last) || last != car.id; logByArea[area] = car.id; if (changed) Diagnosis.Log($"AGV{car.id}停在地标{SignalCarAdapter.PositionId(car)},配置参数:【{raw}】", "RateFlow", true); } } }