261 lines
9.7 KiB
C#
261 lines
9.7 KiB
C#
using System;
|
||||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Threading;
|
|||
|
|
using LessokajiWeaverUtilities.Utilities;
|
|||
|
|
using Newtonsoft.Json;
|
|||
|
|
using SimpleCore;
|
|||
|
|
using SimpleCore.Library;
|
|||
|
|
using SimpleLite.RCS;
|
|||
|
|
using StandardScene.Signal.Logic;
|
|||
|
|
using StandardScene.Signal.Model;
|
|||
|
|
using StandardScene.Signal.Plc;
|
|||
|
|
using StandardScene.Signal.Ui;
|
|||
|
|
using StandardScene.Utils;
|
|||
|
|
|
|||
|
|
namespace StandardScene.Signal
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 第三方信号交互进程:PLC Worker(200ms)+ 扫车握手(1s)。
|
|||
|
|
/// </summary>
|
|||
|
|
[MissionType(Name = "信号交互进程", editor = typeof(SignalInteractMission))]
|
|||
|
|
[I18N.DocumentTranslation(Name = "Signal Interact Mission", locale = "en")]
|
|||
|
|
public class SignalInteractMission : Mission
|
|||
|
|
{
|
|||
|
|
[FieldMember] public int TickMs = 1000;
|
|||
|
|
|
|||
|
|
[FieldMember] public int PlcPollMs = 200;
|
|||
|
|
|
|||
|
|
[FieldMember] public string PlcVersion = "S7_1500";
|
|||
|
|
|
|||
|
|
[FieldMember] public int PlcPort = 102;
|
|||
|
|
|
|||
|
|
[FieldMember] public int PlcSlot = 1;
|
|||
|
|
|
|||
|
|
[FieldMember] public string StationsPath = @"Config\Signal\stations.json";
|
|||
|
|
|
|||
|
|
[FieldMember] public string ReleasePointsPath = @"Config\Signal\release-points.json";
|
|||
|
|
|
|||
|
|
[FieldMember] public string HandshakePointsPath = @"Config\Signal\handshake-points.json";
|
|||
|
|
|
|||
|
|
[JsonIgnore] private bool _started;
|
|||
|
|
[JsonIgnore] private readonly object _workerLock = new object();
|
|||
|
|
[JsonIgnore] private readonly List<PlcStationWorker> _workers = new List<PlcStationWorker>();
|
|||
|
|
[JsonIgnore] private CarScanWorker _scan;
|
|||
|
|
[JsonIgnore] private int _statusTick;
|
|||
|
|
|
|||
|
|
public class SignalInteractMissionStatus : MissionStatus
|
|||
|
|
{
|
|||
|
|
public int StationCount { get; set; }
|
|||
|
|
public int ConnectedCount { get; set; }
|
|||
|
|
public int ReleasePointCount { get; set; }
|
|||
|
|
public int HandshakePointCount { get; set; }
|
|||
|
|
public int ScanTickCount { get; set; }
|
|||
|
|
public string StationsFile { get; set; }
|
|||
|
|
public string ReleasePointsFile { get; set; }
|
|||
|
|
public string HandshakePointsFile { get; set; }
|
|||
|
|
public List<PlcStationSnapshot> Stations { get; set; } = new List<PlcStationSnapshot>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public override MissionStatus status { get; set; } = new SignalInteractMissionStatus();
|
|||
|
|
|
|||
|
|
public static Mission Create() => new SignalInteractMission();
|
|||
|
|
|
|||
|
|
[MethodMember(Name = "启动进程", Description = "加载配置,启动 PLC Worker 与扫车握手")]
|
|||
|
|
public override void Execute()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
ReloadConfig();
|
|||
|
|
_started = true;
|
|||
|
|
RestartWorkers();
|
|||
|
|
RestartScan();
|
|||
|
|
var st = (SignalInteractMissionStatus)status;
|
|||
|
|
Diagnosis.Post(
|
|||
|
|
$"信号交互进程已启动,机构={st.StationCount},握手点={st.HandshakePointCount},放行点={st.ReleasePointCount}",
|
|||
|
|
"Signal", true);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
status.status = "启动失败";
|
|||
|
|
Diagnosis.Post($"信号交互进程启动失败:{ExceptionFormatter.FormatEx(ex)}", "Signal", true);
|
|||
|
|
throw;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[MethodMember(Name = "重新加载配置", Description = "重新读取 JSON;若进程已启动则重启 Worker/扫车")]
|
|||
|
|
public void ReloadConfig()
|
|||
|
|
{
|
|||
|
|
var stationsFile = SignalConfigStore.Resolve(StationsPath);
|
|||
|
|
var releaseFile = SignalConfigStore.Resolve(ReleasePointsPath);
|
|||
|
|
var handshakeFile = SignalConfigStore.Resolve(HandshakePointsPath);
|
|||
|
|
var stations = SignalConfigStore.Load<PlcStationModel>(stationsFile);
|
|||
|
|
var points = SignalConfigStore.Load<ReleasePointModel>(releaseFile);
|
|||
|
|
var handshake = SignalConfigStore.Load<HandshakePointModel>(handshakeFile);
|
|||
|
|
|
|||
|
|
var st = (SignalInteractMissionStatus)status;
|
|||
|
|
st.StationCount = stations.Count;
|
|||
|
|
st.ReleasePointCount = points.Count;
|
|||
|
|
st.HandshakePointCount = handshake.Count;
|
|||
|
|
st.StationsFile = stationsFile;
|
|||
|
|
st.ReleasePointsFile = releaseFile;
|
|||
|
|
st.HandshakePointsFile = handshakeFile;
|
|||
|
|
st.Stations = stations.Select(s => new PlcStationSnapshot { JgName = s.JgName }).ToList();
|
|||
|
|
status.status = _started ? "运行中" : "配置已加载";
|
|||
|
|
Diagnosis.Post(
|
|||
|
|
$"信号配置已加载:机构 {stations.Count};握手点 {handshake.Count} @ {handshakeFile};放行点 {points.Count} @ {releaseFile}",
|
|||
|
|
"Signal", true);
|
|||
|
|
|
|||
|
|
if (_started)
|
|||
|
|
{
|
|||
|
|
RestartWorkers();
|
|||
|
|
_scan?.ReplaceConfig(handshake, points);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[MethodMember(Name = "停止进程")]
|
|||
|
|
public new void Stop()
|
|||
|
|
{
|
|||
|
|
_started = false;
|
|||
|
|
try { _scan?.Stop(); }
|
|||
|
|
catch { }
|
|||
|
|
_scan = null;
|
|||
|
|
StopWorkers();
|
|||
|
|
status.status = "已停止";
|
|||
|
|
Diagnosis.Post("信号交互进程已停止", "Signal", true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[MethodMember(Name = "打开机构列表", Description = "按 PlcStationModel 增删改机构配置")]
|
|||
|
|
public void OpenStations()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var path = SignalConfigStore.Resolve(StationsPath);
|
|||
|
|
ModelListPanel<PlcStationModel>.Open("PLC 机构配置", path, x => x?.JgName ?? "");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
CycleUiHelper.Alert("错误", $"打开机构列表失败:{ex.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[MethodMember(Name = "打开握手点列表", Description = "按站点配置 JGControl / PointControl / TimedCharging / RateFlowControl")]
|
|||
|
|
public void OpenHandshakePoints()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var path = SignalConfigStore.Resolve(HandshakePointsPath);
|
|||
|
|
ModelListPanel<HandshakePointModel>.Open(
|
|||
|
|
"握手点配置",
|
|||
|
|
path,
|
|||
|
|
x => x == null ? "" : $"{x.Sit} {x.SignalType}");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
CycleUiHelper.Alert("错误", $"打开握手点列表失败:{ex.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[MethodMember(Name = "打开放行点列表", Description = "按 ReleasePointModel 增删改放行点")]
|
|||
|
|
public void OpenReleasePoints()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var path = SignalConfigStore.Resolve(ReleasePointsPath);
|
|||
|
|
ModelListPanel<ReleasePointModel>.Open("放行点配置", path, x => x == null ? "" : x.Sit.ToString());
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
CycleUiHelper.Alert("错误", $"打开放行点列表失败:{ex.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
internal IReadOnlyList<PlcStationRuntime> Runtimes
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
lock (_workerLock)
|
|||
|
|
return _workers.Select(w => w.Runtime).ToList();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void RestartScan()
|
|||
|
|
{
|
|||
|
|
try { _scan?.Stop(); }
|
|||
|
|
catch { }
|
|||
|
|
|
|||
|
|
var handshake = SignalConfigStore.Load<HandshakePointModel>(SignalConfigStore.Resolve(HandshakePointsPath));
|
|||
|
|
var release = SignalConfigStore.Load<ReleasePointModel>(SignalConfigStore.Resolve(ReleasePointsPath));
|
|||
|
|
_scan = new CarScanWorker(() => Runtimes, TickMs);
|
|||
|
|
_scan.ReplaceConfig(handshake, release);
|
|||
|
|
_scan.Start();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void RestartWorkers()
|
|||
|
|
{
|
|||
|
|
StopWorkers();
|
|||
|
|
var stationsFile = SignalConfigStore.Resolve(StationsPath);
|
|||
|
|
var stations = SignalConfigStore.Load<PlcStationModel>(stationsFile)
|
|||
|
|
.Where(s => !string.IsNullOrWhiteSpace(s.JgName))
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
var created = new List<PlcStationWorker>();
|
|||
|
|
lock (_workerLock)
|
|||
|
|
{
|
|||
|
|
foreach (var station in stations)
|
|||
|
|
{
|
|||
|
|
var port = station.Port > 0 ? station.Port : PlcPort;
|
|||
|
|
var runtime = new PlcStationRuntime(station);
|
|||
|
|
var session = new SiemensPlcSession(PlcVersion, station.Ip, port, PlcSlot);
|
|||
|
|
var worker = new PlcStationWorker(runtime, session, PlcPollMs, RefreshStatus);
|
|||
|
|
_workers.Add(worker);
|
|||
|
|
created.Add(worker);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
foreach (var worker in created)
|
|||
|
|
worker.Start();
|
|||
|
|
|
|||
|
|
PushStatus();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void StopWorkers()
|
|||
|
|
{
|
|||
|
|
List<PlcStationWorker> copy;
|
|||
|
|
lock (_workerLock)
|
|||
|
|
{
|
|||
|
|
copy = _workers.ToList();
|
|||
|
|
_workers.Clear();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
foreach (var w in copy)
|
|||
|
|
{
|
|||
|
|
try { w.Stop(); }
|
|||
|
|
catch { }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void RefreshStatus()
|
|||
|
|
{
|
|||
|
|
if (Interlocked.Increment(ref _statusTick) % 5 != 0)
|
|||
|
|
return;
|
|||
|
|
PushStatus();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void PushStatus()
|
|||
|
|
{
|
|||
|
|
List<PlcStationSnapshot> snaps;
|
|||
|
|
lock (_workerLock)
|
|||
|
|
snaps = _workers.Select(w => w.Runtime.ToSnapshot()).ToList();
|
|||
|
|
|
|||
|
|
var st = (SignalInteractMissionStatus)status;
|
|||
|
|
st.Stations = snaps;
|
|||
|
|
st.ConnectedCount = snaps.Count(s => s.IsConnected);
|
|||
|
|
st.StationCount = snaps.Count;
|
|||
|
|
st.ScanTickCount = _scan?.TickCount ?? 0;
|
|||
|
|
if (_started)
|
|||
|
|
status.status = $"运行中 {st.ConnectedCount}/{st.StationCount} 已连接 扫车{_scan?.TickCount ?? 0}";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|