实现心跳检测
This commit is contained in:
@@ -148,6 +148,7 @@ namespace JoyD.Windows.CS.Toprie
|
||||
private System.Threading.Timer _reconnectTimer;
|
||||
// 重连间隔(毫秒)
|
||||
private int _reconnectInterval = 2000;
|
||||
private int _connectionCheckInterval = 5000;
|
||||
// 重连尝试次数
|
||||
private int _reconnectAttempts = 0;
|
||||
// 最大重连尝试次数
|
||||
@@ -198,6 +199,25 @@ namespace JoyD.Windows.CS.Toprie
|
||||
private Thread _imageReceiveThread;
|
||||
private Thread _imageReconnectThread;
|
||||
private Stream _imageStream;
|
||||
|
||||
/// <summary>
|
||||
/// 检查网络是否可用
|
||||
/// </summary>
|
||||
/// <returns>网络是否可用</returns>
|
||||
private bool IsNetworkAvailable()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isAvailable = System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable();
|
||||
Console.WriteLine($"网络可用性检查: {(isAvailable ? "可用" : "不可用")}");
|
||||
return isAvailable;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"网络检查异常: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private TcpClient _imageTcpClient;
|
||||
private bool _isInfraredMode = true;
|
||||
|
||||
@@ -295,39 +315,130 @@ namespace JoyD.Windows.CS.Toprie
|
||||
/// <returns>是否初始化成功</returns>
|
||||
private bool Initialize()
|
||||
{
|
||||
// 双重检查锁定模式,确保线程安全的初始化
|
||||
if (_isInitialized)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
// 初始化设备ID列表
|
||||
_deviceIds = new List<string>(); // 保持与类中定义一致
|
||||
_reconnectTimer = null;
|
||||
_heartbeatTimer = null;
|
||||
_connectionCheckTimer = null;
|
||||
// 清理旧资源(如果有)
|
||||
CleanupResources();
|
||||
|
||||
// 初始化设备ID列表和相关变量
|
||||
_deviceIds = new List<string>();
|
||||
_currentDeviceId = -1;
|
||||
// 保留默认设备IP,不要重置为空字符串
|
||||
// _deviceIp = string.Empty; // 注释掉这行,避免IP地址被清空
|
||||
_connectionStatus = ConnectionStatus.Disconnected;
|
||||
_currentReconnectAttempt = 0;
|
||||
|
||||
Console.WriteLine("开始SDK初始化...");
|
||||
|
||||
// 首先检查网络可用性
|
||||
if (!IsNetworkAvailable())
|
||||
{
|
||||
Console.WriteLine("网络不可用,初始化暂缓");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 首先调用SDK静态初始化方法 - 这是连接成功的关键步骤!
|
||||
int initResult = A8SDK.SDK_initialize();
|
||||
// 添加重试机制,增强初始化可靠性
|
||||
int initResult = -1;
|
||||
int maxInitRetries = 2;
|
||||
|
||||
for (int retry = 0; retry <= maxInitRetries; retry++)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (retry > 0)
|
||||
{
|
||||
Console.WriteLine($"SDK初始化重试 {retry}/{maxInitRetries}");
|
||||
Thread.Sleep(300); // 重试前等待一小段时间
|
||||
}
|
||||
|
||||
initResult = A8SDK.SDK_initialize();
|
||||
if (initResult == 0)
|
||||
{
|
||||
break; // 成功,跳出循环
|
||||
}
|
||||
|
||||
Console.WriteLine($"SDK静态初始化失败,返回值: {initResult}");
|
||||
}
|
||||
catch (Exception initEx)
|
||||
{
|
||||
Console.WriteLine($"SDK初始化异常: {initEx.Message},堆栈: {initEx.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
if (initResult != 0)
|
||||
{
|
||||
Console.WriteLine($"SDK静态初始化失败,返回值: {initResult}");
|
||||
Console.WriteLine($"SDK静态初始化失败,所有重试均未成功,返回值: {initResult}");
|
||||
_isInitialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Console.WriteLine("SDK静态初始化成功");
|
||||
|
||||
// 设置默认配置参数
|
||||
_maxReconnectAttempts = 15; // 增加最大重连次数
|
||||
_isAutoReconnectEnabled = true;
|
||||
|
||||
_isInitialized = true;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"SDK初始化失败: {ex.Message}");
|
||||
Console.WriteLine($"SDK初始化失败: {ex.Message},堆栈: {ex.StackTrace}");
|
||||
_isInitialized = false;
|
||||
OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理所有资源,确保安全释放
|
||||
/// </summary>
|
||||
private void CleanupResources()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 停止所有定时器
|
||||
StopAutoReconnect();
|
||||
StopHeartbeat();
|
||||
StopConnectionCheck();
|
||||
|
||||
// 安全释放SDK实例
|
||||
A8SDK oldSdk = Interlocked.Exchange(ref _a8Sdk, null);
|
||||
if (oldSdk != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine("释放旧SDK实例...");
|
||||
// 注意:如果SDK提供了destroy方法,应该在这里调用
|
||||
// A8SDK.SDK_destroy();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"释放SDK实例异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 重置状态变量
|
||||
_deviceIds = null;
|
||||
_connectionStatus = ConnectionStatus.Disconnected;
|
||||
_currentDeviceId = -1;
|
||||
// _deviceIp = string.Empty; // 注释掉这行,避免IP地址在资源清理时被清空
|
||||
_currentReconnectAttempt = 0;
|
||||
_isInitialized = false;
|
||||
|
||||
Console.WriteLine("资源清理完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"清理资源异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试ping设备IP地址
|
||||
/// </summary>
|
||||
@@ -337,18 +448,37 @@ namespace JoyD.Windows.CS.Toprie
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查系统网络连接状态
|
||||
// 尝试真正的ICMP ping,提供更准确的设备可达性检查
|
||||
using (var ping = new System.Net.NetworkInformation.Ping())
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"正在ping设备IP: {ipAddress},检查网络可达性...");
|
||||
var reply = ping.Send(ipAddress, 2000); // 增加超时时间到2秒
|
||||
bool isReachable = reply != null && reply.Status == System.Net.NetworkInformation.IPStatus.Success;
|
||||
Console.WriteLine($"Ping结果: {(isReachable ? "成功" : "失败")}");
|
||||
return isReachable;
|
||||
}
|
||||
catch (Exception pingEx)
|
||||
{
|
||||
Console.WriteLine($"Ping异常: {pingEx.Message}");
|
||||
// 如果ICMP ping失败,退回到原有的网络接口检查逻辑
|
||||
Console.WriteLine("尝试网络接口检查作为备选方案...");
|
||||
}
|
||||
}
|
||||
|
||||
// 改进的网络接口检查逻辑
|
||||
System.Net.NetworkInformation.NetworkInterface[] interfaces =
|
||||
System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
// 检查是否有活动的以太网或WiFi连接
|
||||
foreach (var ni in interfaces)
|
||||
{
|
||||
// 仅检查物理网络接口(以太网、WiFi等)
|
||||
// 仅检查活动的网络接口,移除过于严格的过滤条件
|
||||
if (ni.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up &&
|
||||
(ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Ethernet ||
|
||||
ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Wireless80211) &&
|
||||
ni.Name != "本地连接*" && !ni.Description.Contains("虚拟") && !ni.Description.Contains("Virtual"))
|
||||
ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Wireless80211 ||
|
||||
ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.GigabitEthernet))
|
||||
{
|
||||
// 获取该网络接口的IP信息,检查是否有有效的IP地址
|
||||
var properties = ni.GetIPProperties();
|
||||
@@ -362,20 +492,44 @@ namespace JoyD.Windows.CS.Toprie
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查设备IP是否与当前网络接口在同一子网(粗略判断)
|
||||
// 使用更准确的子网判断方法
|
||||
var deviceAddress = System.Net.IPAddress.Parse(_deviceIp);
|
||||
if (deviceAddress.GetAddressBytes()[0] == addr.Address.GetAddressBytes()[0])
|
||||
var localAddress = addr.Address;
|
||||
|
||||
// 获取子网掩码
|
||||
byte[] subnetMaskBytes = addr.IPv4Mask.GetAddressBytes();
|
||||
byte[] deviceAddressBytes = deviceAddress.GetAddressBytes();
|
||||
byte[] localAddressBytes = localAddress.GetAddressBytes();
|
||||
|
||||
// 检查是否在同一子网(使用子网掩码进行计算)
|
||||
bool isInSameSubnet = true;
|
||||
for (int i = 0; i < subnetMaskBytes.Length; i++)
|
||||
{
|
||||
if ((deviceAddressBytes[i] & subnetMaskBytes[i]) !=
|
||||
(localAddressBytes[i] & subnetMaskBytes[i]))
|
||||
{
|
||||
isInSameSubnet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInSameSubnet)
|
||||
{
|
||||
Console.WriteLine($"设备IP与本地网络接口在同一网段: {localAddress}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"子网判断异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine("未找到有效的网络连接或设备IP不在同一网段");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -595,18 +749,68 @@ namespace JoyD.Windows.CS.Toprie
|
||||
// 优化3: 加强SDK连接状态验证,参考热像仪示例的实现方式
|
||||
try
|
||||
{
|
||||
// 确保SDK实例存在
|
||||
// 使用线程安全的SDK实例创建
|
||||
if (_a8Sdk == null)
|
||||
{
|
||||
Console.WriteLine("SDK实例不存在,重新初始化...");
|
||||
_a8Sdk = new A8SDK(_deviceIp);
|
||||
}
|
||||
|
||||
// 简化心跳检测逻辑,避免在心跳失败时过度访问可能无效的资源
|
||||
int heartbeatResult = _a8Sdk.Heartbeat();
|
||||
if (heartbeatResult <= 0)
|
||||
// 增强心跳检测重试机制,提高连接稳定性
|
||||
int maxHeartbeatRetries = 3; // 增加到3次重试
|
||||
int heartbeatResult = -1;
|
||||
|
||||
for (int retry = 0; retry <= maxHeartbeatRetries; retry++)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"SDK心跳检测...(尝试 {retry + 1}/{maxHeartbeatRetries + 1})");
|
||||
heartbeatResult = _a8Sdk.Heartbeat();
|
||||
|
||||
// 严格检查返回值,确保连接有效
|
||||
if (heartbeatResult == 0) // 参考Toprie项目,假设0表示成功
|
||||
{
|
||||
Console.WriteLine("SDK心跳检测成功");
|
||||
break;
|
||||
}
|
||||
else if (retry < maxHeartbeatRetries)
|
||||
{
|
||||
Console.WriteLine($"SDK心跳检测失败,返回代码: {heartbeatResult},等待500ms后重试...");
|
||||
Thread.Sleep(500); // 增加重试间隔到500ms
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("SDK心跳检测多次失败,尝试重建SDK连接...");
|
||||
// 安全释放旧的SDK实例
|
||||
A8SDK oldSdk = Interlocked.Exchange(ref _a8Sdk, null);
|
||||
// 尝试重建SDK连接
|
||||
Thread.Sleep(500);
|
||||
_a8Sdk = new A8SDK(_deviceIp);
|
||||
Thread.Sleep(500);
|
||||
heartbeatResult = _a8Sdk.Heartbeat();
|
||||
|
||||
if (heartbeatResult == 0)
|
||||
{
|
||||
Console.WriteLine("SDK重新连接成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"SDK重新连接失败,返回代码: {heartbeatResult}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"心跳检测异常: {ex.Message}");
|
||||
if (retry < maxHeartbeatRetries)
|
||||
{
|
||||
Thread.Sleep(500); // 增加重试间隔到500ms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (heartbeatResult != 0)
|
||||
{
|
||||
Console.WriteLine("SDK心跳检测失败");
|
||||
// 心跳失败时,安全释放SDK实例
|
||||
if (_a8Sdk != null)
|
||||
{
|
||||
@@ -614,7 +818,26 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Console.WriteLine("SDK心跳检测成功");
|
||||
|
||||
// 添加额外的连接验证步骤
|
||||
try
|
||||
{
|
||||
Console.WriteLine("进行额外连接验证...");
|
||||
// 再次发送心跳包确保连接稳定
|
||||
int finalResult = _a8Sdk.Heartbeat();
|
||||
if (finalResult != 0)
|
||||
{
|
||||
Console.WriteLine($"最终验证失败,返回代码: {finalResult}");
|
||||
_a8Sdk = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"连接验证异常: {ex.Message}");
|
||||
_a8Sdk = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设备名称获取使用try-catch,避免在心跳成功但设备响应异常时崩溃
|
||||
try
|
||||
@@ -635,6 +858,8 @@ namespace JoyD.Windows.CS.Toprie
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"SDK心跳检测失败: {ex.Message}");
|
||||
// 确保异常时SDK实例被释放
|
||||
_a8Sdk = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -644,6 +869,8 @@ namespace JoyD.Windows.CS.Toprie
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"连接有效性检查异常: {ex.Message}");
|
||||
// 确保异常时SDK实例被释放
|
||||
_a8Sdk = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -694,6 +921,22 @@ namespace JoyD.Windows.CS.Toprie
|
||||
|
||||
#region 公共属性
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置设备IP地址
|
||||
/// </summary>
|
||||
public string IPAddress
|
||||
{
|
||||
get { return _deviceIp; }
|
||||
set
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
_deviceIp = value;
|
||||
Console.WriteLine($"设备IP地址已设置为: {_deviceIp}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前图像模式
|
||||
/// </summary>
|
||||
@@ -1749,6 +1992,17 @@ namespace JoyD.Windows.CS.Toprie
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否正在重连中,如果是则不执行连接
|
||||
if (_isReconnecting != 0)
|
||||
{
|
||||
Console.WriteLine("检测到正在重连过程中,暂停设备连接");
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "正在进行自动重连,请稍后再试");
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置连接中标志
|
||||
_isConnecting = true;
|
||||
|
||||
// 停止任何现有的重连定时器
|
||||
StopAutoReconnect();
|
||||
|
||||
@@ -1774,13 +2028,18 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
|
||||
// 使用真实SDK连接设备
|
||||
Console.WriteLine($"正在使用SDK连接设备 {deviceId},IP地址: {_deviceIp}");
|
||||
Console.WriteLine($"正在使用SDK连接设备 {deviceId},IP地址: {_deviceIp},端口: {_devicePort}");
|
||||
|
||||
// 先检测IP可达性,避免不必要的SDK初始化
|
||||
if (!PingDevice(_deviceIp))
|
||||
if (string.IsNullOrEmpty(_deviceIp))
|
||||
{
|
||||
Console.WriteLine("设备IP地址为空,请先设置有效的IP地址");
|
||||
throw new Exception("设备IP地址为空,请先设置有效的IP地址");
|
||||
}
|
||||
else if (!PingDevice(_deviceIp))
|
||||
{
|
||||
Console.WriteLine($"设备IP {_deviceIp} 不可达,连接失败");
|
||||
throw new Exception($"设备IP {_deviceIp} 不可达");
|
||||
throw new Exception($"设备IP {_deviceIp} 不可达,请检查网络连接");
|
||||
}
|
||||
|
||||
// 创建SDK实例
|
||||
@@ -1788,21 +2047,37 @@ namespace JoyD.Windows.CS.Toprie
|
||||
Console.WriteLine("SDK实例创建完成");
|
||||
|
||||
// 验证连接是否成功(通过心跳检测)
|
||||
if (_a8Sdk.Heartbeat() <= 0)
|
||||
Console.WriteLine("尝试发送心跳包进行连接验证...(尝试 1/3)");
|
||||
int heartbeatResult = _a8Sdk.Heartbeat();
|
||||
|
||||
// 尝试多次心跳检测,增加连接成功率
|
||||
int retryCount = 1;
|
||||
while (heartbeatResult <= 0 && retryCount < 3)
|
||||
{
|
||||
Console.WriteLine("心跳检测失败");
|
||||
retryCount++;
|
||||
Console.WriteLine($"心跳检测失败,等待500ms后重试...(尝试 {retryCount}/3)");
|
||||
Thread.Sleep(500);
|
||||
heartbeatResult = _a8Sdk.Heartbeat();
|
||||
}
|
||||
|
||||
if (heartbeatResult <= 0)
|
||||
{
|
||||
Console.WriteLine("多次心跳检测均失败");
|
||||
// 安全释放SDK实例
|
||||
if (_a8Sdk != null)
|
||||
{
|
||||
_a8Sdk = null;
|
||||
}
|
||||
throw new Exception("心跳检测失败");
|
||||
throw new Exception("心跳检测失败,设备可能未响应或端口配置错误");
|
||||
}
|
||||
Console.WriteLine("心跳检测成功,设备连接有效");
|
||||
|
||||
// 连接成功
|
||||
result = true;
|
||||
|
||||
// 重置连接中标志
|
||||
_isConnecting = false;
|
||||
|
||||
// 安全地设置设备ID
|
||||
try
|
||||
{
|
||||
@@ -1824,6 +2099,8 @@ namespace JoyD.Windows.CS.Toprie
|
||||
{
|
||||
_a8Sdk = null;
|
||||
}
|
||||
// 重置连接中标志
|
||||
_isConnecting = false;
|
||||
result = false;
|
||||
}
|
||||
finally
|
||||
@@ -2022,13 +2299,18 @@ namespace JoyD.Windows.CS.Toprie
|
||||
/// <summary>
|
||||
/// 开始自动重连
|
||||
/// </summary>
|
||||
// 重入保护标志
|
||||
private volatile int _isReconnecting = 0;
|
||||
// 连接进行中标志(用于防止重连期间再次触发连接)
|
||||
private volatile bool _isConnecting = false;
|
||||
|
||||
private void StartAutoReconnect()
|
||||
{
|
||||
// 停止现有的重连定时器
|
||||
StopAutoReconnect();
|
||||
|
||||
// 创建新的重连定时器
|
||||
_reconnectTimer = new System.Threading.Timer(ReconnectCallback, null, 0, _reconnectInterval);
|
||||
// 创建新的重连定时器 - 使用一次性定时器而非重复定时器
|
||||
_reconnectTimer = new System.Threading.Timer(ReconnectCallback, null, 0, Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2104,10 +2386,44 @@ namespace JoyD.Windows.CS.Toprie
|
||||
/// <param name="state">定时器状态</param>
|
||||
private void ReconnectCallback(object state)
|
||||
{
|
||||
// 使用Interlocked.Exchange实现原子操作检查,防止重入
|
||||
if (Interlocked.Exchange(ref _isReconnecting, 1) != 0)
|
||||
{
|
||||
Console.WriteLine("检测到重连回调正在执行,避免重入");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 检查是否已达到最大重连次数
|
||||
if (_currentReconnectAttempt >= _maxReconnectAttempts)
|
||||
{
|
||||
Console.WriteLine($"已达到最大重连次数 ({_maxReconnectAttempts}),停止自动重连");
|
||||
StopAutoReconnect();
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "已达到最大重连次数,请手动检查设备状态");
|
||||
return;
|
||||
}
|
||||
|
||||
// 改进的重连间隔递增策略,避免指数增长过快
|
||||
int currentInterval = Math.Min(_reconnectInterval * (int)(1 + _currentReconnectAttempt * 0.5), 30000); // 最多30秒
|
||||
|
||||
_currentReconnectAttempt++;
|
||||
Console.WriteLine($"自动重连尝试 {_currentReconnectAttempt}");
|
||||
Console.WriteLine($"自动重连尝试 {_currentReconnectAttempt}/{_maxReconnectAttempts},当前间隔: {currentInterval}ms");
|
||||
|
||||
// 在重试前先检查网络状态
|
||||
if (!IsNetworkAvailable())
|
||||
{
|
||||
Console.WriteLine("网络不可用,推迟重连尝试");
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "网络连接不可用,请检查网络设置");
|
||||
|
||||
// 网络不可用时,调整为重试间隔较长
|
||||
int networkDownInterval = Math.Min(_reconnectInterval * 3, 15000);
|
||||
if (_reconnectTimer != null)
|
||||
{
|
||||
_reconnectTimer.Change(networkDownInterval, Timeout.Infinite);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateConnectionStatus(ConnectionStatus.Reconnecting,
|
||||
$"尝试自动重连...");
|
||||
@@ -2120,33 +2436,29 @@ namespace JoyD.Windows.CS.Toprie
|
||||
Console.WriteLine($"用IP地址 {_deviceIp} 重连设备");
|
||||
try
|
||||
{
|
||||
// 检查IP是否可达
|
||||
// 检查IP是否可达,使用改进的PingDevice方法
|
||||
if (PingDevice(_deviceIp))
|
||||
{
|
||||
// 注意:这里需要先释放旧实例,避免资源泄漏
|
||||
if (_a8Sdk != null)
|
||||
// 使用Interlocked.Exchange确保线程安全地释放旧实例
|
||||
A8SDK oldSdk = Interlocked.Exchange(ref _a8Sdk, null);
|
||||
if (oldSdk != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 调用静态SDK_destroy方法释放全局资源
|
||||
// A8SDK.SDK_destroy();
|
||||
Console.WriteLine("重连前SDK全局资源已释放");
|
||||
Console.WriteLine("安全释放旧SDK实例资源");
|
||||
// 注意:根据SDK文档,如果有destroy方法应调用
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"释放旧SDK实例资源时发生异常: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_a8Sdk = null; // 确保引用被清空
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新的SDK实例
|
||||
Console.WriteLine($"创建新的SDK实例,目标IP: {_deviceIp}");
|
||||
_a8Sdk = new A8SDK(_deviceIp);
|
||||
A8SDK newSdk = new A8SDK(_deviceIp);
|
||||
|
||||
// 添加心跳检测重试机制,适应网络不稳定情况
|
||||
// 保存到字段前进行验证,避免无效实例
|
||||
bool isConnected = false;
|
||||
int maxHeartbeatRetries = 3; // 心跳检测最大重试次数
|
||||
|
||||
@@ -2159,12 +2471,13 @@ namespace JoyD.Windows.CS.Toprie
|
||||
// 添加延时,给SDK实例初始化一些时间
|
||||
if (retry > 0)
|
||||
{
|
||||
Console.WriteLine("等待500ms后重试...");
|
||||
Thread.Sleep(500);
|
||||
int retryDelay = 500 + (retry - 1) * 300; // 递增延时
|
||||
Console.WriteLine($"等待{retryDelay}ms后重试...");
|
||||
Thread.Sleep(retryDelay);
|
||||
}
|
||||
|
||||
// 发送心跳包验证连接
|
||||
int heartbeatResult = _a8Sdk.Heartbeat();
|
||||
int heartbeatResult = newSdk.Heartbeat();
|
||||
if (heartbeatResult > 0)
|
||||
{
|
||||
Console.WriteLine("心跳检测成功!");
|
||||
@@ -2173,12 +2486,12 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("心跳检测失败");
|
||||
Console.WriteLine($"心跳检测失败,返回值: {heartbeatResult}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"心跳检测异常: {ex.Message}");
|
||||
Console.WriteLine($"心跳检测异常: {ex.Message},堆栈: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2189,9 +2502,11 @@ namespace JoyD.Windows.CS.Toprie
|
||||
{
|
||||
Console.WriteLine("进行额外连接验证...");
|
||||
// 再次发送心跳包确保连接稳定
|
||||
int finalResult = _a8Sdk.Heartbeat();
|
||||
int finalResult = newSdk.Heartbeat();
|
||||
if (finalResult > 0)
|
||||
{
|
||||
// 验证成功后,安全地更新SDK实例
|
||||
Interlocked.Exchange(ref _a8Sdk, newSdk);
|
||||
Console.WriteLine($"使用IP地址 {_deviceIp} 重连成功");
|
||||
_currentDeviceId = 1; // 临时ID,确保状态更新正确
|
||||
UpdateConnectionStatus(ConnectionStatus.Connected, $"设备 {_deviceIp} 连接成功");
|
||||
@@ -2200,7 +2515,7 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("最终验证失败");
|
||||
Console.WriteLine($"最终验证失败,返回值: {finalResult}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -2213,7 +2528,8 @@ namespace JoyD.Windows.CS.Toprie
|
||||
if (!connectionSuccessful)
|
||||
{
|
||||
Console.WriteLine($"使用IP地址 {_deviceIp} 重连失败,所有心跳尝试都未成功");
|
||||
_a8Sdk = null;
|
||||
// 确保资源被释放
|
||||
try { newSdk = null; } catch { }
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2223,12 +2539,13 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"使用IP地址重连异常: {ex.Message}");
|
||||
Console.WriteLine($"使用IP地址重连异常: {ex.Message},堆栈: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
// 如果IP地址连接失败,再尝试使用设备ID连接(保持兼容性)
|
||||
if (!connectionSuccessful && _currentDeviceId != -1)
|
||||
// 但如果已经在连接中,则跳过
|
||||
if (!connectionSuccessful && _currentDeviceId != -1 && !_isConnecting)
|
||||
{
|
||||
Console.WriteLine($"尝试使用设备ID {_currentDeviceId} 连接");
|
||||
ConnectDevice(_currentDeviceId);
|
||||
@@ -2239,7 +2556,8 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
|
||||
// 如果没有保存的设备ID,但有搜索到的设备,尝试连接第一个
|
||||
if (!connectionSuccessful && _deviceIds != null && _deviceIds.Count > 0)
|
||||
// 但如果已经在连接中,则跳过
|
||||
if (!connectionSuccessful && _deviceIds != null && _deviceIds.Count > 0 && !_isConnecting)
|
||||
{
|
||||
Console.WriteLine($"尝试使用搜索到的设备列表中的第一个设备");
|
||||
if (int.TryParse(_deviceIds[0], out int deviceId))
|
||||
@@ -2263,29 +2581,47 @@ namespace JoyD.Windows.CS.Toprie
|
||||
// 当所有连接尝试都失败时,明确更新状态为Disconnected
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "所有连接尝试失败");
|
||||
|
||||
// 设置下一次重连的时间间隔
|
||||
int retryInterval = _reconnectInterval;
|
||||
if (_currentReconnectAttempt % 5 == 0) // 每5次尝试后增加间隔
|
||||
{
|
||||
retryInterval = Math.Min(retryInterval * 2, 10000); // 最大10秒
|
||||
// 改进的间隔递增策略,避免频繁重连导致的网络压力
|
||||
int retryInterval = Math.Min(_reconnectInterval * (1 + _currentReconnectAttempt / 5), 20000); // 最大20秒
|
||||
Console.WriteLine($"调整重连间隔为 {retryInterval}ms");
|
||||
}
|
||||
Console.WriteLine($"重连失败,{retryInterval}ms后再次尝试");
|
||||
|
||||
// 更新定时器,确保下一次重连
|
||||
if (_reconnectTimer != null)
|
||||
// 安全地更新定时器
|
||||
System.Threading.Timer currentTimer = _reconnectTimer;
|
||||
if (currentTimer != null)
|
||||
{
|
||||
_reconnectTimer.Change(retryInterval, Timeout.Infinite);
|
||||
try
|
||||
{
|
||||
// 使用一次性调度而非重复间隔
|
||||
currentTimer.Change(retryInterval, Timeout.Infinite);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
Console.WriteLine("定时器已被释放,无法更新");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"重连回调函数发生异常: {ex.Message}");
|
||||
Console.WriteLine($"重连回调函数发生异常: {ex.Message},堆栈: {ex.StackTrace}");
|
||||
// 确保定时器继续工作,防止重连机制中断
|
||||
if (_reconnectTimer != null)
|
||||
System.Threading.Timer currentTimer = _reconnectTimer;
|
||||
if (currentTimer != null)
|
||||
{
|
||||
_reconnectTimer.Change(_reconnectInterval, Timeout.Infinite);
|
||||
try
|
||||
{
|
||||
// 使用一次性调度而非重复间隔
|
||||
currentTimer.Change(_reconnectInterval, Timeout.Infinite);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
Console.WriteLine("定时器已被释放,无法恢复");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 无论如何都要重置重入标志,确保后续重连可以正常触发
|
||||
_isReconnecting = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2307,10 +2643,23 @@ namespace JoyD.Windows.CS.Toprie
|
||||
/// </summary>
|
||||
private void StopHeartbeat()
|
||||
{
|
||||
if (_heartbeatTimer != null)
|
||||
try
|
||||
{
|
||||
_heartbeatTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
_heartbeatTimer.Dispose();
|
||||
// 使用Interlocked.Exchange确保线程安全
|
||||
System.Threading.Timer timerToDispose = Interlocked.Exchange(ref _heartbeatTimer, null);
|
||||
if (timerToDispose != null)
|
||||
{
|
||||
// 立即停止定时器,避免回调执行
|
||||
timerToDispose.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
// 安全释放定时器资源
|
||||
timerToDispose.Dispose();
|
||||
Console.WriteLine("心跳检测已停止");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"停止心跳检测异常: {ex.Message}");
|
||||
// 确保即使异常,引用也被清空
|
||||
_heartbeatTimer = null;
|
||||
}
|
||||
}
|
||||
@@ -2323,8 +2672,27 @@ namespace JoyD.Windows.CS.Toprie
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查设备是否可达
|
||||
if (!PingDevice(_deviceIp))
|
||||
// 检查设备是否可达,使用改进的PingDevice方法
|
||||
bool deviceReachable = false;
|
||||
int pingRetries = 2;
|
||||
|
||||
// 添加ping重试机制
|
||||
for (int i = 0; i <= pingRetries; i++)
|
||||
{
|
||||
if (PingDevice(_deviceIp))
|
||||
{
|
||||
deviceReachable = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < pingRetries)
|
||||
{
|
||||
Console.WriteLine($"Ping设备失败,{300 * (i + 1)}ms后重试...");
|
||||
Thread.Sleep(300 * (i + 1)); // 递增延时
|
||||
}
|
||||
}
|
||||
|
||||
if (!deviceReachable)
|
||||
{
|
||||
Console.WriteLine("心跳检测失败,设备不可达");
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "心跳检测失败,设备不可达");
|
||||
@@ -2337,13 +2705,56 @@ namespace JoyD.Windows.CS.Toprie
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用SDK的Heartbeat方法进行心跳检测
|
||||
if (_a8Sdk != null)
|
||||
// 使用SDK的Heartbeat方法进行心跳检测,添加重试机制
|
||||
A8SDK currentSdk = _a8Sdk;
|
||||
if (currentSdk != null)
|
||||
{
|
||||
if (_a8Sdk.Heartbeat() <= 0)
|
||||
bool heartbeatSuccessful = false;
|
||||
int heartbeatRetries = 1;
|
||||
|
||||
for (int i = 0; i <= heartbeatRetries; i++)
|
||||
{
|
||||
Console.WriteLine("SDK心跳检测失败");
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK心跳检测失败");
|
||||
try
|
||||
{
|
||||
int heartbeatResult = currentSdk.Heartbeat();
|
||||
if (heartbeatResult > 0)
|
||||
{
|
||||
heartbeatSuccessful = true;
|
||||
Console.WriteLine("心跳检测成功");
|
||||
// 定期更新连接状态,表明连接正常
|
||||
if (_connectionStatus != ConnectionStatus.Connected)
|
||||
{
|
||||
UpdateConnectionStatus(ConnectionStatus.Connected, "设备连接已恢复");
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"SDK心跳检测失败,返回值: {heartbeatResult}");
|
||||
|
||||
if (i < heartbeatRetries)
|
||||
{
|
||||
Console.WriteLine("300ms后重试心跳检测...");
|
||||
Thread.Sleep(300);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"心跳检测异常: {ex.Message}");
|
||||
|
||||
if (i < heartbeatRetries)
|
||||
{
|
||||
Console.WriteLine("300ms后重试心跳检测...");
|
||||
Thread.Sleep(300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!heartbeatSuccessful)
|
||||
{
|
||||
Console.WriteLine("SDK心跳检测失败,连接可能已断开");
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK心跳检测失败,连接已断开");
|
||||
|
||||
// 如果启用了自动重连,开始重连
|
||||
if (_isAutoReconnectEnabled)
|
||||
@@ -2354,13 +2765,25 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("SDK实例不存在");
|
||||
Console.WriteLine("SDK实例不存在,尝试重连");
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK实例不存在");
|
||||
|
||||
if (_isAutoReconnectEnabled)
|
||||
{
|
||||
StartAutoReconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"心跳检测时发生异常: {ex.Message}");
|
||||
Console.WriteLine($"心跳检测时发生异常: {ex.Message},堆栈: {ex.StackTrace}");
|
||||
OnConnectionException(new ConnectionExceptionEventArgs(ex, "心跳检测失败"));
|
||||
|
||||
// 异常情况下也尝试重连
|
||||
if (_isAutoReconnectEnabled)
|
||||
{
|
||||
StartAutoReconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2393,62 +2816,7 @@ namespace JoyD.Windows.CS.Toprie
|
||||
StopConnectionCheck();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查系统网络连接是否可用
|
||||
/// </summary>
|
||||
/// <returns>网络是否可用</returns>
|
||||
private bool IsNetworkAvailable()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查系统网络连接状态
|
||||
System.Net.NetworkInformation.NetworkInterface[] interfaces =
|
||||
System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
// 检查是否有活动的以太网或WiFi连接
|
||||
foreach (var ni in interfaces)
|
||||
{
|
||||
// 仅检查物理网络接口(以太网、WiFi等)
|
||||
if (ni.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up &&
|
||||
(ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Ethernet ||
|
||||
ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Wireless80211) &&
|
||||
ni.Name != "本地连接*" && !ni.Description.Contains("虚拟") && !ni.Description.Contains("Virtual"))
|
||||
{
|
||||
// 获取该网络接口的IP信息,检查是否有有效的IP地址
|
||||
var properties = ni.GetIPProperties();
|
||||
foreach (var addr in properties.UnicastAddresses)
|
||||
{
|
||||
// 检查是否有IPv4地址
|
||||
if (addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||||
{
|
||||
// 如果设备IP不为空,可以尝试检查设备IP是否在该接口的子网内
|
||||
if (!string.IsNullOrEmpty(_deviceIp))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查设备IP是否与当前网络接口在同一子网(粗略判断)
|
||||
var deviceAddress = System.Net.IPAddress.Parse(_deviceIp);
|
||||
if (deviceAddress.GetAddressBytes()[0] == addr.Address.GetAddressBytes()[0])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"检查网络可用性异常: {ex.Message}");
|
||||
// 发生异常时默认为网络不可用,避免误判
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试ping设备IP地址
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace JoyD.Windows.CS.Toprie
|
||||
// 结构体引用已移至SharedStructures类
|
||||
|
||||
// 常量定义
|
||||
private const int SDK_PORT = 5000;
|
||||
private const int SDK_PORT = 8080;
|
||||
private const int BUFFER_SIZE = 4096;
|
||||
private const int TIMEOUT = 3000;
|
||||
private const string CMD_HEAD = "+CMD";
|
||||
@@ -1655,27 +1655,81 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
}
|
||||
|
||||
// 使用UDP协议发送心跳命令,与SDK保持一致
|
||||
public int Heartbeat()
|
||||
{
|
||||
// 关键修改:与SDK保持一致,使用UDP协议发送心跳命令
|
||||
// SDK中使用的是UDP协议(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))
|
||||
// 之前的实现使用了TCP协议,这是心跳失败的根本原因
|
||||
// 端口修改为18890,与SDK中的CMD_SERVER_UDP_PORT保持一致
|
||||
try
|
||||
{
|
||||
// 根据SDK实际实现,心跳命令格式是 +CMD:24$
|
||||
string command = $"{CMD_HEAD}:{(int)CMD_TYPE.HEARTBEAT}$";
|
||||
byte[] commandBytes = Encoding.ASCII.GetBytes(command);
|
||||
|
||||
// SDK实现中会尝试3次心跳,这里也采用相同的策略
|
||||
for (int retry = 0; retry < 3; retry++)
|
||||
{
|
||||
Console.WriteLine($"心跳检测...(尝试 {retry + 1}/3)");
|
||||
Console.WriteLine($"心跳命令: {command}");
|
||||
Console.WriteLine($"目标IP: {deviceIp}:18890");
|
||||
|
||||
using (UdpClient udpClient = new UdpClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
// 根据SDK格式构建心跳命令 - SDK格式
|
||||
string command = $"{deviceIp}:{(int)CMD_TYPE.HEARTBEAT}$";
|
||||
// 设置接收超时,与SDK中的50毫秒保持一致
|
||||
udpClient.Client.ReceiveTimeout = 50;
|
||||
|
||||
if (SendCommand(command, out string response))
|
||||
// 发送UDP数据报,端口使用18890与SDK保持一致
|
||||
udpClient.Send(commandBytes, commandBytes.Length, deviceIp, 18890);
|
||||
Console.WriteLine("UDP心跳命令已发送");
|
||||
|
||||
// 尝试接收响应
|
||||
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
byte[] responseBytes = udpClient.Receive(ref remoteEndPoint);
|
||||
string response = Encoding.ASCII.GetString(responseBytes);
|
||||
|
||||
Console.WriteLine($"收到UDP心跳响应: {response}");
|
||||
|
||||
// SDK要求响应中必须包含 ":ok" 字符串才算成功
|
||||
if (!string.IsNullOrEmpty(response) && response.Contains(":ok"))
|
||||
{
|
||||
// 验证响应格式
|
||||
if (response != null)
|
||||
{
|
||||
Console.WriteLine("心跳成功");
|
||||
Console.WriteLine("心跳成功: 响应包含':ok'");
|
||||
return 1; // 返回1表示成功,与DeviceManager中的判断一致
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("心跳响应不包含':ok',验证失败");
|
||||
}
|
||||
}
|
||||
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut)
|
||||
{
|
||||
// 超时异常,继续重试
|
||||
Console.WriteLine("UDP心跳接收超时");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"UDP心跳异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 如果不是最后一次尝试,短暂延迟后重试
|
||||
if (retry < 2)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
// 3次尝试都失败
|
||||
Console.WriteLine("三次心跳检测均失败");
|
||||
return -1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"心跳检测异常: {ex.Message}");
|
||||
Console.WriteLine($"异常堆栈: {ex.StackTrace}");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user