using SimpleLite.RCS; using SimpleLite.RCS.CarTypes; using SimpleCore; using SimpleCore.Library; using SimpleCore.PropType; using StandardScene.Charge; using StandardScene.TCP; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace StandardScene.ChargeStationType { public class FLChargeStation : AbstractChargeStation { private AsyncTcpClient Client; private IPEndPoint _endPoint; // 重连相关 private Timer _reconnectTimer; private bool _isManualDisconnect = false; // 标识是否为手动断开 private int _reconnectAttempts = 0; // 重连尝试次数 private const int MAX_RECONNECT_ATTEMPTS = 5; // 最大重连次数 private const int RECONNECT_INTERVAL_MS = 3000; // 重连间隔(毫秒) private readonly object _connectionLock = new object(); // 连接锁 private bool _isConnecting = false; // 是否正在连接中 // 连接状态 public bool IsConnected { get; private set; } = false; public DateTime? LastConnectedTime { get; private set; } public DateTime? LastDisconnectedTime { get; private set; } /// /// 关闭当前TCP连接 /// public override void CloseCommunication() { lock (_connectionLock) { _isManualDisconnect = true; // 标记为手动断开,不触发自动重连 // 停止重连定时器 if (_reconnectTimer != null) { _reconnectTimer.Dispose(); _reconnectTimer = null; } _isConnecting = false; if (Client != null) { try { // 取消订阅事件,避免内存泄漏 Client.PlaintextReceived -= OnPlaintextReceived; Client.ServerConnected -= OnServerConnected; Client.ServerDisconnected -= OnServerDisconnected; // 关闭连接 Client.Close(); Client = null; IsConnected = false; LastDisconnectedTime = DateTime.Now; Diagnosis.Log($"FLChargeStation[{SiteId}] TCP connection closed manually"); } catch (Exception ex) { Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] failed to close connection: {ex.Message}"); } } } } /// /// 创建TCP连接 /// public override void CreateCommunication(IPAddress ip, int port) { lock (_connectionLock) { // 先关闭旧连接 if (Client != null) { CloseCommunication(); } _endPoint = new IPEndPoint(ip, port); _isManualDisconnect = false; // 重置手动断开标记 _reconnectAttempts = 0; // 重置重连次数 ConnectInternal(); } } /// /// 内部连接方法 /// private void ConnectInternal() { if (_isConnecting) { Diagnosis.Log($"FLChargeStation[{SiteId}] is already connecting, skip"); return; } try { _isConnecting = true; // 创建新的TCP客户端 Client = new AsyncTcpClient(_endPoint.Address, _endPoint.Port); Client.PlaintextReceived += OnPlaintextReceived; Client.ServerConnected += OnServerConnected; Client.ServerDisconnected += OnServerDisconnected; // 连接 Client.Connect(); if (Client.Connected) { Diagnosis.Log($"FLChargeStation[{SiteId}] connecting to {_endPoint.Address}:{_endPoint.Port}..."); } else { _isConnecting = false; Diagnosis.Log($"FLChargeStation[{SiteId}] connecting to {_endPoint.Address}:{_endPoint.Port}... 未建立连接"); } } catch (Exception ex) { Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] failed to create connection: {ex.Message}"); _isConnecting = false; // 触发重连 if (!_isManualDisconnect) { ScheduleReconnect(); } } } /// /// 手动触发重连 /// public void Reconnect() { lock (_connectionLock) { Diagnosis.Log($"FLChargeStation[{SiteId}] manual reconnect triggered"); _isManualDisconnect = false; _reconnectAttempts = 0; // 关闭现有连接 if (Client != null) { try { Client.PlaintextReceived -= OnPlaintextReceived; Client.ServerConnected -= OnServerConnected; Client.ServerDisconnected -= OnServerDisconnected; Client.Close(); Client = null; } catch { } } // 重新连接 ConnectInternal(); } } /// /// 安排自动重连 /// private void ScheduleReconnect() { if (_isManualDisconnect) { Diagnosis.Log($"FLChargeStation[{SiteId}] manual disconnect, skip auto reconnect"); return; } if (_reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) { Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] max reconnect attempts ({MAX_RECONNECT_ATTEMPTS}) reached, stop reconnecting"); return; } _reconnectAttempts++; // 停止现有定时器 if (_reconnectTimer != null) { _reconnectTimer.Dispose(); } Diagnosis.Log($"FLChargeStation[{SiteId}] scheduling reconnect attempt {_reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS} in {RECONNECT_INTERVAL_MS}ms"); // 创建新的定时器 _reconnectTimer = new Timer( callback: _ => AttemptReconnect(), state: null, dueTime: RECONNECT_INTERVAL_MS, period: Timeout.Infinite ); } /// /// 尝试重连 /// private void AttemptReconnect() { lock (_connectionLock) { if (_isManualDisconnect || IsConnected) { return; } Diagnosis.Log($"FLChargeStation[{SiteId}] attempting to reconnect (attempt {_reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS})..."); try { // 清理旧客户端 if (Client != null) { try { Client.PlaintextReceived -= OnPlaintextReceived; Client.ServerConnected -= OnServerConnected; Client.ServerDisconnected -= OnServerDisconnected; Client.Close(); } catch { } Client = null; } // 重新连接 ConnectInternal(); } catch (Exception ex) { Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] reconnect failed: {ex.Message}"); // 继续安排下一次重连 if (!_isManualDisconnect && _reconnectAttempts < MAX_RECONNECT_ATTEMPTS) { ScheduleReconnect(); } } } } private void OnPlaintextReceived(object sender, TcpDatagramReceivedEventArgs e) { var messageService = CommunicationMessageService.Instance; var recBytes = e.Datagram.Take(35).ToArray(); string IP = ((AsyncTcpClient)sender).RemoteIPEndPoint.Address.ToString(); Diagnosis.Post($"{string.Join(" ", recBytes.Select(p => $"{p:X2}"))}", $"{IP}", true); IsSafe = recBytes[28] == 2; messageService.AddReceiveMessage(_endPoint.Address.ToString(), _endPoint.Port, BitConverter.ToString(recBytes).Replace("-", " "), "FRLDTall"); } /// /// 服务器连接成功事件 /// private void OnServerConnected(object sender, TcpServerConnectedEventArgs e) { lock (_connectionLock) { _isConnecting = false; IsConnected = true; LastConnectedTime = DateTime.Now; _reconnectAttempts = 0; // 重置重连次数 Diagnosis.Log($"FLChargeStation[{SiteId}] TCP connected successfully to {_endPoint.Address}:{_endPoint.Port}"); // 停止重连定时器 if (_reconnectTimer != null) { _reconnectTimer.Dispose(); _reconnectTimer = null; } } } /// /// 服务器断开连接事件 /// private void OnServerDisconnected(object sender, TcpServerDisconnectedEventArgs e) { lock (_connectionLock) { _isConnecting = false; IsConnected = false; LastDisconnectedTime = DateTime.Now; Diagnosis.Log($"WARN:FLChargeStation[{SiteId}] TCP disconnected from {_endPoint.Address}:{_endPoint.Port}"); } } /// /// 发送充电指令到充电站 /// public override void SendToChargeStation(int isCharge, Car car,Site site) { // 检查连接状态 if (!IsConnected || Client == null) { Diagnosis.Log($"WARN:FLChargeStation[{SiteId}] not connected, cannot send charge command"); return; } try { var messageService = CommunicationMessageService.Instance; float soc = 0f, voltage = 0, electricCurrent = 0; int carId = 0; float setVoltage = 55.0f; float setElectricCurrent = 50.0f; //Site site = null; if (car != null) { soc = float.Parse(Commons.GetCarStatus(car, "Soc")); voltage = float.Parse(Commons.GetCarStatus(car, "Voltage")); electricCurrent = float.Parse(Commons.GetCarStatus(car, "ElectricCurrent")); carId = car.id; //site = SimpleLib.GetSite(car.status.holdingLocks.FirstOrDefault()); } if (site != null && site.fields.ContainsKey("setVoltage") && site.fields.ContainsKey("setElectricCurrent")) { setVoltage = float.Parse(site.fields["setVoltage"]); setElectricCurrent = float.Parse(site.fields["setElectricCurrent"]); } var msg = GetSendBytes((byte)(isCharge), setVoltage, setElectricCurrent, 1000, Convert.ToSingle(carId), Convert.ToSingle(soc), Convert.ToSingle(electricCurrent), Convert.ToSingle(voltage)); messageService.AddSendMessage(_endPoint.Address.ToString(), _endPoint.Port, BitConverter.ToString(msg).Replace("-", " "), "FRLDTall", site?.name); // 发送数据,带异常处理 try { Client.Send(msg); Diagnosis.Post($"{string.Join(" ", msg.Select(d => $"{d:X2}"))}", $"sendChargeSite: {SiteId}", true); } catch (ObjectDisposedException) { Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] client disposed during send, triggering reconnect"); IsConnected = false; } catch (SocketException ex) { Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] socket error during send: {ex.SocketErrorCode}, triggering reconnect"); IsConnected = false; } } catch (Exception e) { Diagnosis.Post($"ERR:FLChargeStation[{SiteId}] SwitchCharge Fail: {ExceptionFormatter.FormatEx(e)}"); } } private byte[] GetSendBytes(byte startCharge, float chargeVoltage, float chargeElectricCurrent, float chargeTimeSpan, float carId, float carSoc, float carElectricCurrent, float carVoltage) { var sendByte = new byte[32]; try { //BB 01 42 48 00 00 42 5C 00 00 00 00 00 02 00 5D 41 B4 00 00 41 28 00 00 00 00 00 00 00 00 00 EE sendByte = new byte[2] { 0xBB, startCharge } .Concat(BitConverter.GetBytes(chargeElectricCurrent).AsEnumerable().Reverse()) .Concat(BitConverter.GetBytes(chargeVoltage).AsEnumerable().Reverse()) .Concat(BitConverter.GetBytes((ushort)chargeTimeSpan).AsEnumerable().Reverse()) .Concat(BitConverter.GetBytes((ushort)carId).AsEnumerable().Reverse()) .Concat(BitConverter.GetBytes((ushort)carSoc).AsEnumerable().Reverse()) .Concat(BitConverter.GetBytes(carElectricCurrent).AsEnumerable().Reverse()) .Concat(BitConverter.GetBytes(carVoltage).AsEnumerable().Reverse()) .Concat(new byte[] { 00, 00, 00, 00, 00, 00, 00, 0xEE }).ToArray(); } catch (Exception ex) { Diagnosis.Post($"下发充电控制{(startCharge == 1 ? "启动" : "停止")}异常+ex:{ExceptionFormatter.FormatEx(ex)}", "error"); } return sendByte; } } }