Files
StandardSence/StandardScene.Signal/Mission/SignalInteractMission.cs
T

261 lines
9.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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}";
}
}
}