using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using Newtonsoft.Json; namespace HH.WCS.Mobox3.DSZSH.device { public class TcpClientHelper { private static Socket _clientSocket; private static byte[] _buffer = new byte[1024]; public static Dictionary _receivedDataQueue = new Dictionary(); public static string _ip { get; set; } public static int _port { get; set; } private static bool _isConnecting = false; // 标记是否正在连接中 private static readonly object _connectLock = new object(); // 连接操作的同步锁 /// /// 重连的话调用方再实例化一个就行了 /// /// /// public static bool Init(string ip, int port) { lock (_connectLock) { try { // 若正在连接中,直接返回 if (_isConnecting) { LogHelper.Info("已有连接正在尝试中,禁止重复操作"); return false; } _isConnecting = true; // 标记为连接中 // 释放旧 Socket(仅在未连接时) if (_clientSocket != null && !_clientSocket.Connected) { SafeCloseSocket(); } // 创建新 Socket 并连接 _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ipAdd = IPAddress.Parse(ip); IPEndPoint endPoint = new IPEndPoint(ipAdd, port); _clientSocket.BeginConnect(endPoint, ConnectCallback, null); // 更新 IP 和端口 _ip = ip; _port = port; return true; } catch (SocketException ex) { _isConnecting = false; LogHelper.Error($"初始化连接失败: {ex.Message}", ex); return false; } } } private static readonly object _linkLock = new object(); public static bool Link(string ip, int port) { lock (_linkLock) { try { // 若Socket存在但实际已断开,强制清理 if (_clientSocket != null && (_clientSocket.Poll(0, SelectMode.SelectRead) && _clientSocket.Available == 0)) { SafeCloseSocket(); } // 原有逻辑 if (_clientSocket != null && _clientSocket.Connected) { LogHelper.Info($"电梯已连接,无需重连,IP:{ip},端口:{port}"); return false; } return Init(ip, port); } catch (Exception ex) { LogHelper.Error($"电梯重连失败,IP:{ip},端口:{port},异常:{ex.Message}", ex); return false; } } } public static bool TcpClose() { try { _clientSocket?.Close(); return true; } catch { return false; } } public static void SendMsg(string ip, int port, string message) { try { if (_clientSocket?.Connected == true) { byte[] data = Encoding.UTF8.GetBytes(message); _clientSocket.BeginSend(data, 0, data.Length, SocketFlags.None, SendCallback, null); } else { Link(ip, port); } } catch { /* 异常处理 */ } } private static void SendCallback(IAsyncResult ar) { try { _clientSocket.EndSend(ar); } catch { /* 发送异常处理 */ } } private static void ConnectCallback(IAsyncResult ar) { try { lock (_connectLock) { // 检查 Socket 是否有效 if (_clientSocket == null || !ar.IsCompleted) { LogHelper.Info("连接已取消或Socket无效"); return; } // 完成连接 _clientSocket.EndConnect(ar); // 仅在连接成功时启动接收 if (_clientSocket.Connected) { LogHelper.Info($"成功连接到服务端:{_ip}:{_port}"); _clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, null); } else { LogHelper.Info("连接未成功,关闭Socket"); SafeCloseSocket(); } } } catch (ObjectDisposedException) { LogHelper.Info("连接过程中Socket被释放"); } catch (Exception ex) { LogHelper.Error($"连接失败:{ex.Message}", ex); SafeCloseSocket(); } finally { _isConnecting = false; // 重置连接状态 } } // 安全的Socket关闭方法 private static void SafeCloseSocket() { try { if (_clientSocket != null) { // 避免重复关闭 if (_clientSocket.Connected) { _clientSocket.Shutdown(SocketShutdown.Both); } _clientSocket.Close(); _clientSocket.Dispose(); // 断开后:清除对应IP:Port的接收数据 string key = $"{_ip}:{_port}"; if (_receivedDataQueue.ContainsKey(key)) { _receivedDataQueue.Remove(key); LogHelper.Info($"已清理队列数据,Key:{key}"); } } } catch (Exception ex) { LogHelper.Error($"释放Socket资源异常:{ex.Message}", ex); } finally { _clientSocket = null; _isConnecting = false; // 确保重置连接标记 } } private static void ReceiveCallback(IAsyncResult ar) { try { if (_clientSocket == null) { return; } int bytesRead = _clientSocket.EndReceive(ar); if (bytesRead > 0) { // 复制有效数据到新数组 byte[] receivedBytes = new byte[bytesRead]; Array.Copy(_buffer, 0, receivedBytes, 0, bytesRead); // 存入队列 string key = $"{_ip}:{_port}"; string receivedMessage = Encoding.UTF8.GetString(receivedBytes); _receivedDataQueue[key] = receivedBytes; // 继续接收下一批数据 _clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, null); } else { // 服务端主动关闭连接,触发清理 LogHelper.Info("连接已被服务端关闭"); SafeCloseSocket(); } } catch (Exception ex) { LogHelper.Error($"接收数据异常:{ex.Message}", ex); SafeCloseSocket(); // 异常时主动关闭 } } public static bool WriteElevatorDownOk(byte[] sends) { try { if (_clientSocket?.Connected == true) { _clientSocket.BeginSend(sends, 0, sends.Length, SocketFlags.None, SendCallback, null); return true; } else { Link(_ip, _port); LogHelper.Info($"写电梯入货数据失败(未连接):{Encoding.UTF8.GetString(sends)}"); return false; } } catch (Exception ex) { LogHelper.Error($"写电梯入货数据失败(发送了异常):{ex.Message}", ex); return false; } } public static byte[] ReadElevatorOutOk() { try { if (_clientSocket != null && _clientSocket?.Connected == true) { _receivedDataQueue.TryGetValue($"{_ip}:{_port}", out byte[] result); LogHelper.Info($"读电梯出货数据成功:{BitConverter.ToString(result)}"); return result; } else { Link(_ip, _port); LogHelper.Info($"读电梯出货数据失败(未连接),准备重连"); return null; } } catch (Exception ex) { LogHelper.Error($"读电梯出货数据失败(发生了异常:{JsonConvert.SerializeObject(ex)}):{ex.Message}", ex); return null; /* 异常处理 */ } } public static string ChekElevator() { try { var res = "读取电梯数据的model,索引从1开始,满足以下条件才能发任务 \r\n " + "字段,isNormal ,是否正常模式,1:正常模式,第7个Byte右侧第一位Bit \r\n" + "字段,isValid,当前位置是否有效,1:有效,0:不用管,第9个Byte右侧第一位Bit \r\n" + "字段,runMode,电梯运行模式,9=空闲泊停,7=自动运行,第10个Byte\r\n" + "字段,isLock_1_Out,一层出口是否占用,1 = 占用,第14个Byte右侧第一位Bit\r\n" + "字段,isLock_2_Out,二层出口是否占用,1 = 占用,第14个Byte右侧第二位Bit\r\n" + "字段,taskNO,任务号\r\n" + "判断电梯是否符合2楼到1楼搬送条件:isNormal 且 (runMode == 9 或 runMode == 7) 且 !isLock_1_Out \r\n" + "判断电梯是否符合1楼到成品库区条件:isNormal 且 (runMode == 9 或 runMode == 7) 且 isLock_1_Out\r\n"; var isRead = ReadElevatorOutOk(); var log = BitConverter.ToString(isRead); res += "读取到的电梯byte数组:" + log + "\r\n"; //if (isRead != null && isRead.Length >= 14) { // var elevatorReadInfo = new ElevatorReadInfo() { // isNormal = (isRead[6] & 1) == 1, // isValid = (isRead[8] & 1) == 1, // runMode = isRead[9], // isLock_1_Out = (isRead[13] & 1) == 1, // isLock_2_Out = (isRead[13] & (1 << 1)) == 1, // }; // log = JsonConvert.SerializeObject(elevatorReadInfo); // res += "解析后的电梯信息" + log + "\r\n"; // var res1 = elevatorReadInfo.is2To1Ok(); // res += "判断电梯是否符合2楼到1楼搬送条件,如果符合则返回true,结果" + res1 + "\r\n"; // var res2 = elevatorReadInfo.is1ToOk(); // res += "判断电梯是否符合1楼到成品库区条件,如果符合则返回true,结果" + res2 + "\r\n"; //} //else { // return "读取电梯状态失败,byte数组要求大于等于14个且不为空"; //} return res; } catch (Exception ex) { return ex.Message; } } public static bool IsDuanDian() { try { var isRead = ReadElevatorOutOk(); //if (isRead != null && isRead.Length >= 14) { // var elevatorReadInfo = new ElevatorReadInfo() { // isNormal = (isRead[6] & 1) == 1, // isValid = (isRead[8] & 1) == 1, // runMode = isRead[9], // isLock_1_Out = (isRead[13] & 1) == 1, // isLock_2_Out = (isRead[13] & (1 << 1)) == 1, // }; // if (elevatorReadInfo.runMode == 5)//5=断电重连 // { // SafeCloseSocket(); // return true; // } //} return false; } catch (Exception ex) { LogHelper.Error($"判断电梯是否断电(发生了异常:{JsonConvert.SerializeObject(ex)}):{ex.Message}", ex); return false; } } } }