using IoTClient.Clients.PLC; using IoTClient.Common.Enums; using LessokajiWeaverUtilities.Utilities; using Newtonsoft.Json; using SimpleLite.RCS; using SimpleLite.RCS.CarTypes; using SimpleCore.Library; using StandardScene.Chained; using StandardScene.Model; using System.Collections.Concurrent; using System.Threading; using static StandardScene.Chained.AbstractLoopMission; namespace StandardScene { public class LoopMissionStatus : AbstractLoopMissionStatus { } [MissionType(Name = "环线进程", editor = typeof(LoopMission))] [I18N.DocumentTranslation(Name = "Loop Mission", locale = "en")] public class LoopMission : AbstractLoopMission { [JsonIgnore] public override MissionStatus status { get; set; } = new LoopMissionStatus(); [JsonIgnore] public Thread myThread; [JsonIgnore] public bool started = false; [JsonIgnore] private readonly ConcurrentDictionary _buttonTriggeredSiteIds = new ConcurrentDictionary(); public float ChargesocStandard = 30; public float NoChargesocStandard = 80; /// /// API 触发:检查站点物料状态 /// protected override ExternalTriggerResult OnApiTrigger(int currentSiteId, LoopTask task, Car car) { /* var site = SimpleLib.GetSite(currentSiteId); if (site != null && site.fields.TryGetValue("hasMaterial", out var val) && val == "true") { // 物料存在,使用配置目标 return ExternalTriggerResult.UseConfigTarget(); } return ExternalTriggerResult.Fail();*/ if (car.status.enums.TryGetValue("Soc", out var soc)) { if (float.Parse(soc) <= ChargesocStandard) { //使用配置目标 return ExternalTriggerResult.UseConfigTarget(); } } /* var plcTarget = 17; if (plcTarget > 0) { // PLC 指定了目标站点 return ExternalTriggerResult.UseTarget(plcTarget); }*/ return ExternalTriggerResult.Fail(); } /// /// PLC 触发:根据 PLC 信号决定目标 /// protected override ExternalTriggerResult OnPlcTrigger(int currentSiteId, LoopTask task, Car car) { //var plcTarget = ReadPlcTargetSite(currentSiteId); SiemensClient client = new SiemensClient(SiemensVersion.S7_200Smart, "127.0.0.1", 103); var M100 = client.ReadBoolean("M100").Value; if (M100) { //使用配置目标 return ExternalTriggerResult.UseConfigTarget(); } /* var plcTarget = 17; if (plcTarget > 0) { // PLC 指定了目标站点 return ExternalTriggerResult.UseTarget(plcTarget); }*/ return ExternalTriggerResult.Fail(); } protected override ExternalTriggerResult OnChargeTrigger(int currentSiteId, LoopTask task, Car car) { //使用配置目标 return ExternalTriggerResult.UseConfigTarget(); if (car.status.enums.TryGetValue("Soc", out var soc)) { if (float.Parse(soc) >= NoChargesocStandard) { //使用配置目标 return ExternalTriggerResult.UseConfigTarget(); } } } /// /// 供 ButtonMission 反射调用:登记一个需要按钮放行的站点。 /// 如果站点已经存在于字典中则忽略,避免重复放行信号堆积。 /// /// 需要放行的站点 ID,通常对应 LoopTask.CurrentStationId /// 始终返回 true,表示本次按钮触发已被接受 public bool EnqueueButtonTriggerSite(int siteId) { if (siteId <= 0) { Diagnosis.Log($"按钮放行登记失败:无效站点 {siteId}", "LoopMission", true); return false; } var car = FindCarArrivedAtSite(siteId); if (car == null) { Diagnosis.Post($"按钮放行登记忽略:站点 {siteId} 当前没有到站车辆", "LoopMission", false); return false; } if (_buttonTriggeredSiteIds.TryAdd(siteId, 0)) { Diagnosis.Post($"按钮放行登记成功:站点 {siteId},车辆 {car.name}", "LoopMission", false); } else { Diagnosis.Post($"按钮放行已存在,忽略重复登记:站点 {siteId}", "LoopMission", false); } return true; } /// /// 按钮盒触发:只有当前站点已被按钮登记过,才允许使用配置目标放行。 /// 命中后立即消费并删除,保证一次按钮只放行一次。 /// protected override ExternalTriggerResult OnButtonTrigger(int currentSiteId, LoopTask task, Car car) { if (_buttonTriggeredSiteIds.TryRemove(currentSiteId, out _)) { Diagnosis.Post($"按钮放行消费成功:站点 {currentSiteId},车辆 {car?.name}", "LoopMission", false); return ExternalTriggerResult.UseConfigTarget(); } return ExternalTriggerResult.Fail(); } /* private int ReadPlcTargetSite(int siteId) { // 从 PLC 读取目标站点的业务逻辑 // 返回 0 表示未获取到有效目标 return 0; } */ } }