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.InfoEx(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) { //if (ip == _ip && port == _port) { LogHelper.Info($"产线已连接,无需重连,IP:{ip},端口:{port}"); return false; //} //LogHelper.Info($"oldIP={_ip};newIP={ip};oldPort={_port};newPort={port}"); //SafeCloseSocket(); } return Init(ip, port); } catch (Exception ex) { LogHelper.InfoEx(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.InfoEx(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.InfoEx(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.InfoEx(ex); SafeCloseSocket(); // 异常时主动关闭 } } /// /// {\"item_code\":\"CG1001\",\"batch_no\":\"BN1001\",\"cntr_code\":\"CN2505111\"}
/// {"item_code":"CG1001","batch_no":"BN1001","cntr_code":"CN2505111"} ///
/// /// public static bool TryReadProductionLine(out byte[] read) { read = null; try { if (_clientSocket != null && _clientSocket?.Connected == true) { if (!_receivedDataQueue.TryGetValue($"{_ip}:{_port}", out byte[] result)) { LogHelper.Info($"读产线托盘下线数据失败"); //read = result; return false; } LogHelper.Info($"读产线托盘下线数据成功:{BitConverter.ToString(result)}"); read = result; return true; } else { //LogHelper.Info($"_clientSocket={_clientSocket} connected={_clientSocket?.Connected}"); Link(_ip, _port); LogHelper.Info($"读产线托盘下线数据失败 (未连接) ,准备重连"); return false; } } catch (Exception ex) { LogHelper.InfoEx(ex); return false; /* 异常处理 */ } //return false; } /// /// 将Modbus寄存器数组转换为字节数组 /// /// Modbus寄存器数组 /// 字节数组 public static byte[] ConvertRegistersToBytes(ushort[] registers) { // 每个寄存器是16位(2字节),所以总字节数是寄存器数量的2倍 byte[] bytes = new byte[registers.Length * 2]; for (int i = 0; i < registers.Length; i++) { // Modbus使用大端序,高位字节在前 bytes[i * 2] = (byte)(registers[i] >> 8); // 高位字节 bytes[i * 2 + 1] = (byte)(registers[i] & 0xFF); // 低位字节 } return bytes; } /// /// 将字节数组转换为字符串 /// /// 字节数组 /// 转换后的字符串 public static string ConvertBytesToString(byte[] bytes) { // 查找第一个0x00字节(字符串结束符)的位置 int length = Array.IndexOf(bytes, (byte)0); if (length < 0) length = bytes.Length; // 如果没有结束符,使用全部字节 // 根据设备使用的编码转换(常见的有ASCII或UTF-8) // 这里使用ASCII编码作为示例,实际应根据设备文档确定编码方式 return Encoding.ASCII.GetString(bytes, 0, length); // 如果是UTF-8编码,使用下面这行代替上面那行 // return Encoding.UTF8.GetString(bytes, 0, length); } 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.InfoEx(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.InfoEx(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.InfoEx(ex); return false; } } } }