using System; using System.Configuration; using System.IO; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using CanSimulator; using log4net; namespace Aging.Infrastructure.Drivers { /// /// Local 传输后端:TCP 直连 CanSimulatorServer,握手 HELLO 挂载 (bus,nodeIndex), /// 命令串经 TransceiveAsync 一问一答,接收帧由服务端 EV:RECV 主动推送触发 FramePushed。 /// internal sealed class NodeChannelTransport : ICanTransport { private static readonly ILog Log = LogManager.GetLogger(typeof(NodeChannelTransport)); private readonly string _host; private readonly int _port; private readonly string _clientTag; private TcpClient _socket; private Stream _stream; private CancellationTokenSource _cts; private Task _readLoop; // 一问一答:同一时刻仅一个在途请求 private readonly SemaphoreSlim _requestLock = new(1, 1); private TaskCompletionSource _pending; private volatile bool _connected; private bool _disposed; private string _busName; private int _nodeIndex = -1; public bool IsConnected => _connected; public event Action FramePushed; public event Action TransportBroken; public NodeChannelTransport(string hostOverride, int? portOverride, string clientTag) { _host = hostOverride ?? ReadCfg("CanSimulator.Host", "127.0.0.1"); _port = portOverride ?? ReadIntCfg("CanSimulator.Port", 9000); _clientTag = clientTag ?? $"{Environment.MachineName}/{System.Diagnostics.Process.GetCurrentProcess().Id}"; } private static string ReadCfg(string key, string def) { try { var v = ConfigurationManager.AppSettings[key]; return string.IsNullOrWhiteSpace(v) ? def : v; } catch { return def; } } private static int ReadIntCfg(string key, int def) => int.TryParse(ReadCfg(key, null), out int v) ? v : def; public async Task ConnectAsync(string busName, int nodeIndex, CancellationToken ct) { if (_disposed) throw new ObjectDisposedException(nameof(NodeChannelTransport)); _busName = busName; _nodeIndex = nodeIndex; try { _socket = new TcpClient(); await _socket.ConnectAsync(_host, _port).ConfigureAwait(false); _socket.NoDelay = true; _stream = _socket.GetStream(); _cts = new CancellationTokenSource(); // HELLO 握手:读循环启动前同步直读一行响应(此时无并发推送) string ack = await HandshakeAsync( $"HELLO:{busName}:{nodeIndex}:{_clientTag}", ct).ConfigureAwait(false); if (ack == "ERR:NODE_BUSY") throw new InvalidOperationException( $"模拟器总线 {busName} 上节点编号 {nodeIndex} 已被占用,请修改设备端点的节点编号"); if (!ack.StartsWith("OK:HELLO", StringComparison.Ordinal)) throw new InvalidOperationException($"模拟器握手失败: {ack}"); _connected = true; _readLoop = Task.Run(() => ReadLoopAsync(_cts.Token)); Log.Info($"[CAN-TCP] 已挂载 {busName}:N{nodeIndex} @ {_host}:{_port}"); } catch (SocketException ex) { SafeCleanupSocket(); throw new InvalidOperationException( $"无法连接 CAN 模拟器服务 {_host}:{_port}({ex.Message})。请先启动 CanSimulatorServer.exe", ex); } } public async Task DisconnectAsync() { if (!_connected) return; try { await RequestLineAsync("DISCONNECT", CancellationToken.None).ConfigureAwait(false); } catch { /* 断开尽力而为 */ } Teardown(); } public async Task TransceiveAsync(string line, CancellationToken ct) { if (_disposed) throw new ObjectDisposedException(nameof(NodeChannelTransport)); if (!_connected) throw new InvalidOperationException("CAN 通道未连接"); try { return await RequestLineAsync(line, ct).ConfigureAwait(false); } catch (IOException ex) { OnBroken(); throw new InvalidOperationException("CAN 模拟器连接已中断: " + ex.Message, ex); } } /// 握手专用:写 HELLO 后直接同步读取一行响应(读循环尚未启动,无并发)。 private async Task HandshakeAsync(string helloLine, CancellationToken ct) { byte[] frame = SimulatorFraming.Encode(helloLine); await _stream.WriteAsync(frame, 0, frame.Length, ct).ConfigureAwait(false); await _stream.FlushAsync(ct).ConfigureAwait(false); string resp = await SimulatorFraming.ReadFrameAsync(_stream, ct).ConfigureAwait(false); return resp ?? ""; } /// 发送一行并等待其响应行;EV:RECV 推送在此过程中被分流出去,不计为响应。 private async Task RequestLineAsync(string line, CancellationToken ct) { await _requestLock.WaitAsync(ct).ConfigureAwait(false); try { var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _pending = tcs; byte[] frame = SimulatorFraming.Encode(line); await _stream.WriteAsync(frame, 0, frame.Length, ct).ConfigureAwait(false); await _stream.FlushAsync(ct).ConfigureAwait(false); // deadline:默认 5s,超时视为结果未知(上层禁止假定未执行) var timeoutTask = Task.Delay(TimeSpan.FromSeconds(5), ct); var done = await Task.WhenAny(tcs.Task, timeoutTask).ConfigureAwait(false); if (done != tcs.Task) { _pending = null; throw new TimeoutException($"CAN 模拟器命令超时: {line}"); } _pending = null; ct.ThrowIfCancellationRequested(); return await tcs.Task.ConfigureAwait(false); } finally { _requestLock.Release(); } } private async Task ReadLoopAsync(CancellationToken ct) { try { while (!ct.IsCancellationRequested) { string resp = await SimulatorFraming.ReadFrameAsync(_stream, ct).ConfigureAwait(false); if (resp == null) break; // EOF if (resp.StartsWith("EV:", StringComparison.Ordinal)) { FramePushed?.Invoke(resp); continue; } // 普通响应:交给在途请求 _pending?.TrySetResult(resp); } } catch (IOException) { } catch (InvalidDataException) { } catch (OperationCanceledException) { } catch (Exception ex) { Log.Warn($"[CAN-TCP] 读循环异常: {ex.Message}"); } finally { if (!_disposed) OnBroken(); } } private void OnBroken() { if (!_connected && _pending == null) return; _connected = false; _pending?.TrySetException(new IOException("CAN 模拟器通道已断开")); TransportBroken?.Invoke(); } private void Teardown() { _connected = false; try { _cts?.Cancel(); } catch { } SafeCleanupSocket(); } private void SafeCleanupSocket() { try { _stream?.Dispose(); } catch { } try { _socket?.Close(); } catch { } _stream = null; _socket = null; } public void Dispose() { if (_disposed) return; _disposed = true; Teardown(); try { _readLoop?.Wait(200); } catch { } _requestLock.Dispose(); _cts?.Dispose(); } } }