2026-08-14 14:43:11 +08:00
|
|
|
using System;
|
|
|
|
|
using SimpleCore.Library;
|
|
|
|
|
using StandardScene.Signal.Model;
|
|
|
|
|
|
|
|
|
|
namespace StandardScene.Signal.Plc
|
|
|
|
|
{
|
2026-08-17 15:20:27 +08:00
|
|
|
/// <summary>
|
|
|
|
|
/// 单个机构的运行态(不进 JSON)。扫车线程改写目标写字节,PLC Worker 负责真正下发。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 赋值 <see cref="WriteSignal"/> 会把 <see cref="WriteSuccess"/> 置 false,下一拍 Worker 发现
|
|
|
|
|
/// 目标与回读不一致就会重写 PLC。回读与目标一致后再标成功,AutoLeave 才继续往下一状态走。
|
|
|
|
|
/// 所有读写应在 <see cref="Sync"/> 内进行。
|
|
|
|
|
/// </remarks>
|
2026-08-14 14:43:11 +08:00
|
|
|
public sealed class PlcStationRuntime
|
|
|
|
|
{
|
|
|
|
|
private SignalWriteValue _writeSignal;
|
|
|
|
|
private SignalReadValue _readSignal;
|
|
|
|
|
private SignalWriteValue _readWriteSignal;
|
|
|
|
|
|
|
|
|
|
public PlcStationRuntime(PlcStationModel config)
|
|
|
|
|
{
|
|
|
|
|
Config = config ?? throw new ArgumentNullException(nameof(config));
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-17 15:20:27 +08:00
|
|
|
/// <summary>本机构静态配置(IP、DB 地址、地标、机构类型)。</summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public PlcStationModel Config { get; }
|
|
|
|
|
|
|
|
|
|
/// <summary>扫车线程与 PLC Worker 共用运行态,写入时加此锁。</summary>
|
|
|
|
|
public object Sync { get; } = new object();
|
|
|
|
|
|
2026-08-17 15:20:27 +08:00
|
|
|
/// <summary>PLC→调度:允许进入 / 允许离开 / 无状态。变化时打诊断。</summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public SignalReadValue ReadSignal
|
|
|
|
|
{
|
|
|
|
|
get => _readSignal;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_readSignal != value)
|
|
|
|
|
Diagnosis.Log($"{Config.JgName} 读信号 {_readSignal} → {value}", "PLC", true);
|
|
|
|
|
_readSignal = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-17 15:20:27 +08:00
|
|
|
/// <summary>
|
|
|
|
|
/// 调度→PLC 的目标写字节。赋值即视为尚未写成功,驱动 Worker 重写。
|
|
|
|
|
/// </summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public SignalWriteValue WriteSignal
|
|
|
|
|
{
|
|
|
|
|
get => _writeSignal;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_writeSignal != value)
|
|
|
|
|
Diagnosis.Log($"{Config.JgName} 写信号 {_writeSignal} → {value}", "PLC", true);
|
|
|
|
|
_writeSignal = value;
|
|
|
|
|
WriteSuccess = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-17 15:20:27 +08:00
|
|
|
/// <summary>从写地址回读到的当前值,用来确认 PLC 是否已吃进目标字节。</summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public SignalWriteValue ReadWriteSignal
|
|
|
|
|
{
|
|
|
|
|
get => _readWriteSignal;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_readWriteSignal != value)
|
|
|
|
|
Diagnosis.Log($"{Config.JgName} 回读写值 {_readWriteSignal} → {value}", "PLC", true);
|
|
|
|
|
_readWriteSignal = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-17 15:20:27 +08:00
|
|
|
/// <summary>已报到在机构内的 AGV 号;0 表示没有。Arrived / 上线自动到位 时写入。</summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public int InAgvNo { get; set; }
|
2026-08-17 15:20:27 +08:00
|
|
|
|
|
|
|
|
/// <summary>已获准进入、正在往机构开的 AGV 号;到位或离站清空后为 0。</summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public int RequestInAgvNo { get; set; }
|
2026-08-17 15:20:27 +08:00
|
|
|
|
|
|
|
|
/// <summary>最近一拍 PLC 读写是否成功。心跳或读写失败会立刻变 false。</summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public bool IsConnected { get; set; }
|
2026-08-17 15:20:27 +08:00
|
|
|
|
|
|
|
|
/// <summary>目标写字节是否已与 PLC 回读一致。改 WriteSignal 时自动清掉。</summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public bool WriteSuccess { get; set; }
|
2026-08-17 15:20:27 +08:00
|
|
|
|
|
|
|
|
/// <summary>内存侧心跳翻转标志,供监视面板看到 Worker 在跑;真正写 PLC 的是 HeartDb 位翻转。</summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public bool Heart { get; set; }
|
2026-08-17 15:20:27 +08:00
|
|
|
|
|
|
|
|
/// <summary>最近一次断线或周期异常原因,连接正常时清空。</summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public string LastError { get; set; } = "";
|
|
|
|
|
|
2026-08-17 15:20:27 +08:00
|
|
|
/// <summary>拷贝一份给 MissionStatus / 监视列表,避免 UI 直接绑运行态。</summary>
|
2026-08-14 14:43:11 +08:00
|
|
|
public PlcStationSnapshot ToSnapshot() => new PlcStationSnapshot
|
|
|
|
|
{
|
|
|
|
|
JgName = Config.JgName,
|
|
|
|
|
IsConnected = IsConnected,
|
|
|
|
|
ReadSignal = ReadSignal.ToString(),
|
|
|
|
|
WriteSignal = WriteSignal.ToString(),
|
|
|
|
|
ReadWriteSignal = ReadWriteSignal.ToString(),
|
|
|
|
|
InAgvNo = InAgvNo,
|
|
|
|
|
RequestInAgvNo = RequestInAgvNo,
|
|
|
|
|
WriteSuccess = WriteSuccess,
|
|
|
|
|
Heart = Heart,
|
|
|
|
|
LastError = LastError ?? ""
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|