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<string, byte[]> _receivedDataQueue = new Dictionary<string, byte[]>();
|
public static string _ip { get; set; }
|
public static int _port { get; set; }
|
|
private static bool _isConnecting = false; // 标记是否正在连接中
|
private static readonly object _connectLock = new object(); // 连接操作的同步锁
|
|
/// <summary>
|
/// 重连的话调用方再实例化一个就行了
|
/// </summary>
|
/// <param name="ip"></param>
|
/// <param name="port"></param>
|
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;
|
}
|
}
|
}
|
}
|