Files
StandardSence/StandardScene.Fass2Simulator/Fass2SimUdpHost.cs
T

187 lines
6.2 KiB
C#
Raw Normal View History

2026-07-17 13:36:01 +08:00
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace StandardScene.Fass2Simulator
{
public sealed class Fass2SimUdpHost : IDisposable
{
private readonly Fass2SimConfig _config;
private readonly Fass2SimVehicle _vehicle;
private readonly Fass2SimCommandHandler _commandHandler;
private readonly Fass2SimMotionEngine _motionEngine;
private readonly Fass2SimActionEngine _actionEngine;
private readonly IPEndPoint _schedulerEndpoint;
private UdpClient _listener;
private UdpClient _sender;
private Thread _receiveThread;
private Timer _reportTimer;
private volatile bool _running;
public Fass2SimUdpHost(
Fass2SimConfig config,
Fass2SimVehicle vehicle,
Fass2SimCommandHandler commandHandler,
Fass2SimMotionEngine motionEngine,
Fass2SimActionEngine actionEngine)
{
_config = config;
_vehicle = vehicle;
_commandHandler = commandHandler;
_motionEngine = motionEngine;
_actionEngine = actionEngine;
_schedulerEndpoint = new IPEndPoint(IPAddress.Parse(config.SchedulerHost), config.SchedulerListenPort);
}
public bool IsRunning => _running;
public event EventHandler StatusReported;
public void Start()
{
if (_running)
{
return;
}
var listenAddress = IPAddress.Parse(_config.VehicleListenAddress);
_listener = new UdpClient(new IPEndPoint(listenAddress, _config.VehicleListenPort));
_sender = new UdpClient();
_running = true;
_receiveThread = new Thread(ReceiveLoop)
{
IsBackground = true,
Name = $"Fass2SimRx:{_config.VehicleListenPort}"
};
_receiveThread.Start();
var interval = Math.Max(50, _config.ReportIntervalMs);
_reportTimer = new Timer(SendStateReport, null, 0, interval);
Fass2SimLog.WriteLine($"[{Now()}] 模拟车已启动");
Fass2SimLog.WriteLine($" 车体监听 : {_config.VehicleListenAddress}:{_config.VehicleListenPort}");
Fass2SimLog.WriteLine($" 上报目标 : {_config.SchedulerHost}:{_config.SchedulerListenPort}");
Fass2SimLog.WriteLine($" 车号 : {_config.VehicleCode}");
Fass2SimLog.WriteLine($" 上报周期 : {interval} ms");
Fass2SimLog.WriteLine($" 路段模拟 : dist={_config.DefaultSegmentDistance}mm 或 node.Distance, speed={_config.DefaultSpeed}mm/s");
Fass2SimLog.WriteLine($" 动作延时 : {_config.ActionDelayMs}ms0xA1 到位),AutoComplete={_config.AutoCompleteStationActions}");
Fass2SimLog.WriteLine($" 初始节点 : {_config.InitialNode}, 状态={Fass2SimProtocol.StateText(_config.InitialState)}");
Fass2SimLog.WriteLine("等待 MagFass2Car 联调(address=127.0.0.1, Port=5000, ListenPort=20103, VehicleCode=1");
}
public void Stop()
{
_running = false;
_reportTimer?.Dispose();
_reportTimer = null;
try
{
_listener?.Close();
}
catch
{
}
try
{
_receiveThread?.Join(1000);
}
catch
{
}
_receiveThread = null;
_listener = null;
try
{
_sender?.Close();
}
catch
{
}
_sender = null;
Fass2SimLog.WriteLine($"[{Now()}] 模拟车已停止");
}
public void Dispose()
{
Stop();
}
private void ReceiveLoop()
{
while (_running)
{
try
{
var remote = new IPEndPoint(IPAddress.Any, 0);
var packet = _listener.Receive(ref remote);
_commandHandler.Handle(packet, remote.ToString());
}
catch (SocketException) when (!_running)
{
break;
}
catch (ObjectDisposedException) when (!_running)
{
break;
}
catch (Exception ex)
{
Fass2SimLog.WriteLine($"[{Now()}] 接收异常: {ex.Message}");
}
}
}
private void SendStateReport(object _)
{
if (!_running)
{
return;
}
try
{
var interval = Math.Max(50, _config.ReportIntervalMs);
_actionEngine.Tick(interval);
_motionEngine.Tick(interval);
var snapshot = _vehicle.Snapshot();
var frame = Fass2SimProtocol.BuildState(snapshot);
_sender.Send(frame, frame.Length, _schedulerEndpoint);
_vehicle.RecordStateReportSent();
StatusReported?.Invoke(this, EventArgs.Empty);
if (snapshot.StateReportsSent == 0 || snapshot.StateReportsSent % 25 == 0)
{
var pending = _actionEngine.ExpectedStation == null
? "-"
: Fass2SimActionResolver.DescribePending(_actionEngine.ExpectedStation, snapshot);
if (string.IsNullOrEmpty(pending))
{
pending = "-";
}
Fass2SimLog.WriteLine(
$"[{Now()}] TX 状态 #{snapshot.StateReportsSent + 1}: node={snapshot.Node}, dist={snapshot.Distance}, state={Fass2SimProtocol.StateText(snapshot.State)}, lift={snapshot.Lift}, pending={pending}, task={snapshot.Task}");
}
}
catch (Exception ex)
{
Fass2SimLog.WriteLine($"[{Now()}] 上报异常: {ex.Message}");
}
}
private static string Now()
{
return DateTime.Now.ToString("HH:mm:ss.fff");
}
}
}