using System; using System.Collections.Generic; using System.Linq; using SimpleCore.Library; using SimpleLite.RCS.CarTypes; using StandardScene.Signal.Model; using StandardScene.Signal.Plc; namespace StandardScene.Signal.Logic { /// /// 机构进出站握手。对齐反编译 DefaultPlugin.JGControl / JGJudge / RequestIn / Arrived。 /// /// /// /// SignalParams 分号三段:机构名;动作;限流。 /// 动作 RequestIn(请求进入,配在机构口外等待点)或 Arrived(到位,配在机构内地标)。 /// 第三段可选,格式与独立限流不同:区域-车组-限额-点1,点2(短横分隔,见 )。 /// /// /// RequestIn 放行条件(需同时满足):PLC 读字节=允许进入、尚无请求进入车、写字节=无状态。 /// 下线机构额外要求 JgPointId 上没有其它车。通过后写「进入中」、记下 RequestInAgvNo、停充并启动。 /// /// /// 车已在跑但本车不是请求进入车、且 PLC 不是允许进入时,立即停车(停稳拍与行驶拍都会判)。 /// Arrived:若 InAgvNo 还不是本车,写「到位」、登记 InAgvNo、清空 RequestInAgvNo。 /// 之后的「离开中 / 已离开」由 PLC Worker 的 推进。 /// /// public static class JgControlHandler { /// 停稳:可选限流 → RequestIn 或 Arrived。 public static void OnStopped(Car car, SignalStopSpec spec, Func find, IDictionary rateFlowLog) { var jgName = spec.Part(0).Trim(); var action = spec.Part(1).Trim(); var rateFlow = spec.Part(2).Trim(); if (string.IsNullOrEmpty(jgName)) { Diagnosis.Log($"AGV{car.id}停在地标{SignalCarAdapter.PositionId(car)},读取配置机构名称为空", "JGControl", true); return; } if (!string.IsNullOrEmpty(rateFlow) && !RateFlowHandler.AllowPass(car, rateFlow, rateFlowLog)) { Diagnosis.Log($"AGV{car.id}停在地标{SignalCarAdapter.PositionId(car)},限流禁止通行", "JGControl", true); return; } if (string.Equals(action, "RequestIn", StringComparison.OrdinalIgnoreCase)) RequestIn(jgName, car, find); else if (string.Equals(action, "Arrived", StringComparison.OrdinalIgnoreCase)) Arrived(jgName, car, find); } /// 行驶中只做 RequestIn 安全闸:未允许进入且不是本车占用请求槽则停车。 public static void OnMoving(Car car, SignalStopSpec spec, Func find) { var jgName = spec.Part(0).Trim(); var action = spec.Part(1).Trim(); if (string.IsNullOrEmpty(jgName)) return; if (string.Equals(action, "RequestIn", StringComparison.OrdinalIgnoreCase)) RequestInJudge(jgName, car, find); } /// /// 请求进入。锁机构运行态,避免与 PLC Worker 同时改写字节 / 车号。 /// 后半段与 相同,覆盖「刚放行后又被调度层启动」的竞态。 /// private static void RequestIn(string jgName, Car car, Func find) { var rt = find(jgName); if (rt == null) { Diagnosis.Log($"{jgName} 未能找到机构", "JGControl", true); return; } lock (rt.Sync) { if (rt.ReadSignal == SignalReadValue.允许进入 && rt.RequestInAgvNo == 0 && rt.ReadWriteSignal == SignalWriteValue.无状态) { if (rt.Config.LineType is SignalLineType.单牵引下线机构 or SignalLineType.双牵引下线机构) { var inside = SignalCarAdapter.CarsAt(rt.Config.JgPointId); if (inside.Count > 0) { Diagnosis.Log( $"{jgName} 机构内有AGV【{string.Join(",", inside.Select(x => x.id))}】暂不放行AGV{car.id}", "JGControl", true); return; } } Diagnosis.Log($"{jgName} 机构允许进入,通知AGV{car.id}启动,通知机构AGV进入中", "JGControl", true); rt.WriteSignal = SignalWriteValue.进入中; rt.RequestInAgvNo = car.id; SignalCarAdapter.StopCharge(car); SignalCarAdapter.Start(car); return; } if (rt.ReadSignal != SignalReadValue.允许进入 && rt.RequestInAgvNo != car.id && SignalCarAdapter.IsMoving(car)) { SignalCarAdapter.Stop(car); Diagnosis.Log($"{jgName} 机构不允许进入,监控到AGV启动,暂停AGV{car.id}", "JGControl", true); } } } /// 行驶中的 RequestIn 安全闸,条件与 RequestIn 后半段一致。 private static void RequestInJudge(string jgName, Car car, Func find) { var rt = find(jgName); if (rt == null) { Diagnosis.Log($"{jgName} 未能找到机构", "JGControl", true); return; } lock (rt.Sync) { if (rt.ReadSignal != SignalReadValue.允许进入 && rt.RequestInAgvNo != car.id && SignalCarAdapter.IsMoving(car)) { SignalCarAdapter.Stop(car); Diagnosis.Log($"{jgName} 机构不允许进入,监控到AGV启动,暂停AGV{car.id}", "JGControl", true); } } } /// /// 到位。InAgvNo 已是本车则认为已报到,避免每秒重复写「到位」把 WriteSuccess 清掉。 /// private static void Arrived(string jgName, Car car, Func find) { var rt = find(jgName); if (rt == null) { Diagnosis.Log($"{jgName} 未能找到机构", "JGControl", true); return; } lock (rt.Sync) { if (rt.InAgvNo == car.id) return; Diagnosis.Log($"AGV{car.id}已到达机构{jgName}", "JGControl", true); rt.WriteSignal = SignalWriteValue.到位; rt.InAgvNo = car.id; rt.RequestInAgvNo = 0; } } } }