Files
JoyD/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs

5648 lines
253 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Net.NetworkInformation;
namespace JoyD.Windows.CS.Toprie
{
/// <summary>
/// 设备连接状态枚举
/// </summary>
public enum ConnectionStatus
{
Disconnected,
Connecting,
Connected,
Reconnecting
}
/// <summary>
/// 图像模式枚举
/// </summary>
public enum ImageMode
{
Infrared, // 红外模式
Natural // 自然模式
}
/// <summary>
/// 色彩模式枚举
/// 对应SDK文档中的8种色板模式白热、黑热、铁红、熔岩、彩虹、铁灰、红热、彩虹2
/// </summary>
public enum PaletteType
{
WhiteHot, // 白热对应SDK参数0
BlackHot, // 黑热对应SDK参数1
IronRed, // 铁红对应SDK参数2
Lava, // 熔岩对应SDK参数3
Rainbow, // 彩虹对应SDK参数4
IronGray, // 铁灰对应SDK参数5
RedHot, // 红热对应SDK参数6
Rainbow2 // 彩虹2对应SDK参数7
}
/// <summary>
/// 视频模式枚举
/// 对应SDK文档中的7种视频模式红外、可见光、融合1~5
/// </summary>
public enum VideoMode
{
Infrared, // 红外对应SDK参数0
VisibleLight, // 可见光对应SDK参数1
Fusion1, // 融合1对应SDK参数2
Fusion2, // 融合2对应SDK参数3
Fusion3, // 融合3对应SDK参数4
Fusion4, // 融合4对应SDK参数5
Fusion5 // 融合5对应SDK参数6
}
/// <summary>
/// 温度模式枚举
/// </summary>
public enum TemperatureMode
{
/// <summary>
/// 摄氏度
/// </summary>
Celsius,
/// <summary>
/// 华氏度
/// </summary>
Fahrenheit,
/// <summary>
/// 开尔文
/// </summary>
Kelvin
}
/// <summary>
/// 连接状态改变事件参数
/// </summary>
public class ConnectionStatusChangedEventArgs : EventArgs
{
public ConnectionStatus Status { get; set; }
public string DeviceInfo { get; set; }
public ConnectionStatusChangedEventArgs(ConnectionStatus status, string deviceInfo = null)
{
Status = status;
DeviceInfo = deviceInfo;
}
}
/// <summary>
/// 图像接收事件参数
/// </summary>
public class ImageReceivedEventArgs : EventArgs
{
public byte[] ImageData { get; set; }
public ImageMode Mode { get; set; }
public ImageReceivedEventArgs(byte[] imageData, ImageMode mode)
{
ImageData = imageData;
Mode = mode;
}
}
/// <summary>
/// 连接异常事件参数
/// </summary>
public class ConnectionExceptionEventArgs : EventArgs
{
public Exception Exception { get; set; }
public string Message { get; set; }
public ConnectionExceptionEventArgs(Exception ex, string message = null)
{
Exception = ex;
Message = message ?? ex?.Message;
}
}
/// <summary>
/// 设备管理器类
/// </summary>
/// <summary>
/// 设备信息类用于存储设备的ID、IP等信息
/// </summary>
public class DeviceInfo
{
/// <summary>
/// 设备ID
/// </summary>
public int DeviceID { get; set; }
/// <summary>
/// 设备IP地址
/// </summary>
public string IPAddress { get; set; }
/// <summary>
/// 设备型号
/// </summary>
public string Model { get; set; }
/// <summary>
/// 设备序列号
/// </summary>
public string SerialNumber { get; set; }
public override string ToString()
{
return $"设备 {DeviceID} ({IPAddress})";
}
}
public class DeviceManager : IDisposable
{
// 设计模式标志,用于在设计模式下跳过实际的设备连接和初始化
public static bool IsDesignMode { get; set; } = true;
// 暂停检测标志,用于控制是否进行连接检测和重连操作
public bool IsDetectionPaused { get; set; } = false;
// 项目路径,用于数据文件的存取
private string _projectPath = "";
// A8SDK实例
private A8SDK _a8Sdk;
// 设备ID列表
private List<string> _deviceIds = new List<string>();
// 设备信息列表
private List<DeviceInfo> _deviceList = new List<DeviceInfo>();
// 目标设备ID用于指定ID连接
private readonly int _targetDeviceId = -1;
// 当前设备ID
private int _currentDeviceId = -1;
// 默认设备IP
private string _deviceIp = "192.168.100.2";
// 设备端口
private int _devicePort = 8080;
// 设备连接状态
private ConnectionStatus _connectionStatus = ConnectionStatus.Disconnected;
// 是否已初始化
private bool _isInitialized = false;
// 是否已释放
private readonly bool _isDisposed = false;
// 图像模式
private ImageMode _currentImageMode = ImageMode.Infrared;
// 当前色彩模式
private PaletteType _currentPaletteType = PaletteType.WhiteHot;
// 当前视频模式
private VideoMode _currentVideoMode = VideoMode.Infrared; // 默认红外模式
// 自动重连是否启用
// 自动重连定时器
private System.Threading.Timer _reconnectTimer;
// 重连间隔(毫秒)
private int _reconnectInterval = 2000;
// 重连尝试次数
// 最大重连尝试次数
public static int MaxReconnectAttempts = 5;
// 连接检查定时器
private System.Threading.Timer _connectionCheckTimer;
// 心跳定时器
private System.Threading.Timer _heartbeatTimer;
// 连接超时时间(毫秒)
private const int ConnectionTimeout = 5000;
// 连接检查间隔(毫秒)
private const int ConnectionCheckInterval = 5000;
// 心跳间隔(毫秒)
private int _heartbeatInterval = 5000;
// 最后接收数据时间戳
private DateTime _lastDataReceivedTime = DateTime.MinValue;
// 数据接收超时时间(毫秒)
private const int DataReceivedTimeout = 15000; // 15秒内未收到数据则认为连接可能断开
// TCP客户端
// 该变量已在文件上方定义,删除重复实现
// 停止请求事件
private ManualResetEvent _stopRequested = new ManualResetEvent(false);
// 缓冲区
private readonly byte[] _imageBuffer = new byte[4096];
// 图像数据累积缓冲区
private readonly byte[] _imageDataAccumulator = new byte[0];
// 多部分请求边界
private readonly string _multipartBoundary = string.Empty;
// 锁对象
private readonly object _lockObject = new object();
// 是否启用自动重连
private bool _isAutoReconnectEnabled = true;
// 最大重连次数
private int _maxReconnectAttempts = 5;
// 是否已连接
private bool _isConnected = false;
// 连接超时设置
private readonly int _connectTimeout = 5000;
// 当前重连尝试次数
private int _currentReconnectAttempt = 0;
// 连接取消令牌源
private CancellationTokenSource _connectCancellationTokenSource;
// 图像接收相关变量
private ManualResetEvent _stopImageEvent;
private bool _isReceivingImages = false;
private Thread _imageReceiveThread;
private Thread _imageReconnectThread;
private Stream _imageStream;
// 温度数据接收相关字段
private Thread _temperatureReceiveThread;
private TcpClient _temperatureTcpClient;
private NetworkStream _temperatureStream;
private bool _isReceivingTemperatureData = false;
private bool _isTemperatureReceivingPaused = false; // 暂停标志,用于控制是否处理接收到的温度数据
private ManualResetEvent _stopTemperatureEvent;
private const int TEMPERATURE_TCP_PORT = 8081; // 温度数据TCP端口 - 修正为热像仪SDK文档中指定的端口
/// <summary>
/// 项目路径属性
/// 用于设置控件存取数据文件的目录
/// </summary>
public string ProjectPath
{
get { return _projectPath; }
set { _projectPath = value; }
}
/// <summary>
/// 检查网络是否可用
/// </summary>
/// <returns>网络是否可用</returns>
private bool IsNetworkAvailable()
{
try
{
bool isAvailable = System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable();
Log($"网络可用性检查: {(isAvailable ? "" : "")}");
return isAvailable;
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] IsNetworkAvailable() - 捕获异常: {ex.Message}");
return false;
}
}
private TcpClient _imageTcpClient;
// 使用CurrentImageMode代替_isInfraredMode
private static readonly object _logLock = new object();
#region
/// <summary>
/// 记录日志到控制台和文件
/// </summary>
private void Log(string message)
{
// 输出到控制台
Console.WriteLine(message);
// 输出到日志文件
try
{
lock (_logLock)
{
string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log.txt");
// 确保目录存在
Directory.CreateDirectory(Path.GetDirectoryName(logPath));
// 写入日志,包含时间戳
File.AppendAllText(logPath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}\r\n");
}
}
catch (Exception ex)
{
// 如果写入日志文件失败,只输出到控制台
Console.WriteLine($"日志写入失败: {ex.Message}");
}
}
/// <summary>
/// 从设备字符串中解析设备ID
/// </summary>
/// <param name="deviceString">设备字符串</param>
/// <returns>设备ID</returns>
private int ParseDeviceId(string deviceString)
{
try
{
// 简单的ID解析逻辑实际应该根据设备返回的数据格式来解析
if (int.TryParse(deviceString.Trim(), out int id))
{
return id;
}
return -1;
}
catch (Exception)
{
return -1;
}
}
/// <summary>
/// 更新连接状态
/// </summary>
/// <param name="newStatus">新的连接状态</param>
/// <param name="message">状态变更消息</param>
/// <param name="exception">相关异常(如果有)</param>
private void UpdateConnectionStatus(ConnectionStatus newStatus, string message = null, Exception exception = null)
{
// 使用线程安全的方式记录方法进入
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 开始执行");
lock (_lockObject) // 添加线程同步锁
{
// 检查对象是否已被释放
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 对象已释放,忽略状态更新");
return;
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 尝试更新状态: {_connectionStatus} -> {newStatus}, 消息: {message}");
bool statusChanged = (_connectionStatus != newStatus);
ConnectionStatus oldStatus = _connectionStatus;
if (statusChanged)
{
_connectionStatus = newStatus;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 状态已更新: {oldStatus} -> {newStatus}");
// 触发连接状态变更事件前再次检查对象是否已被释放
if (!_isDisposed)
{
try
{
OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(newStatus, message));
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 触发连接状态变更事件异常: {ex.Message}");
}
}
Log($"连接状态变更: {oldStatus} -> {newStatus}");
if (!string.IsNullOrEmpty(message))
{
Log($"状态消息: {message}");
}
if (exception != null)
{
Log($"异常信息: {exception.Message}");
}
// 保存状态变更相关信息供后续处理
ConnectionStatus finalNewStatus = newStatus;
bool shouldReconnect = (newStatus == ConnectionStatus.Disconnected && _isAutoReconnectEnabled && oldStatus != ConnectionStatus.Connecting);
bool shouldReset = (newStatus == ConnectionStatus.Connected);
// 添加状态转换验证,避免不合理的状态切换
bool isValidTransition = ValidateStatusTransition(oldStatus, newStatus);
if (!isValidTransition)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 无效的状态转换: {oldStatus} -> {newStatus},已忽略");
return;
}
// 添加额外的保护:防止短时间内状态频繁变化导致的闪烁问题
// 1. 当从Connected转换到Reconnecting时检查是否真的需要重连
if (oldStatus == ConnectionStatus.Connected && newStatus == ConnectionStatus.Reconnecting)
{
// 检查当前是否有其他重连操作正在进行
if (Interlocked.CompareExchange(ref _isReconnecting, 1, 1) == 1)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 检测到重连操作正在进行,推迟状态转换: {oldStatus} -> {newStatus}");
return;
}
}
// 2. 当从Reconnecting快速切换回Connected时确保消息不会引起混淆
if (oldStatus == ConnectionStatus.Reconnecting && newStatus == ConnectionStatus.Connected && message.Contains("正在进行自动重连"))
{
// 修改消息内容,使其更准确
message = "重连成功,设备已恢复连接";
}
// 在锁外执行可能耗时的操作
ThreadPool.QueueUserWorkItem((state) =>
{
try
{
if (shouldReconnect)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 在锁外启动自动重连");
StartAutoReconnect();
}
else if (shouldReset)
{
lock (_lockObject) // 重置时需要加锁
{
if (!_isDisposed)
{
_currentReconnectAttempt = 0;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 连接成功,已重置重连计数");
}
}
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 后续操作异常: {ex.Message}");
}
});
}
else
{
// 优化:只有在调试模式且有异常时才记录调用堆栈,减少日志量
if (System.Diagnostics.Debugger.IsAttached || exception != null || (!string.IsNullOrEmpty(message) && !message.Contains("正在进行自动重连")))
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 状态未变化({_connectionStatus}),消息: {message}");
// 仅在调试模式且有异常时才记录调用堆栈,避免频繁的堆栈记录
#if DEBUG
if (exception != null)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 调用堆栈: {Environment.StackTrace}");
}
#endif
}
}
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 执行完成");
}
/// <summary>
/// 验证状态转换的有效性
/// </summary>
/// <param name="fromStatus">当前状态</param>
/// <param name="toStatus">目标状态</param>
/// <returns>是否为有效的状态转换</returns>
private bool ValidateStatusTransition(ConnectionStatus fromStatus, ConnectionStatus toStatus)
{
// 如果状态相同,认为是有效的(但会在主方法中跳过实际变更)
if (fromStatus == toStatus)
return true;
// 定义有效的状态转换规则
switch (fromStatus)
{
case ConnectionStatus.Disconnected:
// 断开状态可以转换为连接中或重连中
return toStatus == ConnectionStatus.Connecting || toStatus == ConnectionStatus.Reconnecting;
case ConnectionStatus.Connecting:
// 连接中状态可以转换为已连接或断开
return toStatus == ConnectionStatus.Connected || toStatus == ConnectionStatus.Disconnected;
case ConnectionStatus.Connected:
// 已连接状态可以转换为断开或重连中
// 允许直接从Connected转换到Reconnecting以支持更灵活的重连机制
return toStatus == ConnectionStatus.Disconnected || toStatus == ConnectionStatus.Reconnecting;
case ConnectionStatus.Reconnecting:
// 重连中状态可以转换为已连接或断开
return toStatus == ConnectionStatus.Connected || toStatus == ConnectionStatus.Disconnected;
default:
return false;
}
}
/// <summary>
/// 初始化设备管理器
/// </summary>
/// <returns>是否初始化成功</returns>
private bool Initialize()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 开始执行初始化");
// 在设计模式下,跳过实际初始化,不设置连接成功状态
if (IsDesignMode)
{
Log("设计模式下跳过实际初始化");
_isInitialized = true; // 仍然标记为已初始化,避免重复初始化
_connectionStatus = ConnectionStatus.Disconnected; // 设置为断开状态,避免触发图像接收
return false; // 返回false避免Camera认为初始化成功
}
// 双重检查锁定模式,确保线程安全的初始化
if (_isInitialized)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 已经初始化,直接返回成功");
return true;
}
// 加锁进行线程安全的初始化
lock (_lockObject)
{
// 再次检查,因为可能在获取锁的过程中已被其他线程初始化
if (_isInitialized)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 已被其他线程初始化,直接返回成功");
return true;
}
try
{
// 清理旧资源(如果有)
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 清理旧资源");
CleanupResources();
// 初始化设备ID列表和相关变量
_deviceIds = new List<string>();
_currentDeviceId = -1;
// 保留默认设备IP不要重置为空字符串
// _deviceIp = string.Empty; // 注释掉这行避免IP地址被清空
_connectionStatus = ConnectionStatus.Disconnected;
_currentReconnectAttempt = 0;
Log("开始SDK初始化...");
// 首先检查网络可用性
if (!IsNetworkAvailable())
{
Log("网络不可用,初始化暂缓");
return false;
}
// 更新状态为连接中
UpdateConnectionStatus(ConnectionStatus.Connecting, "正在初始化设备连接");
// 首先调用SDK静态初始化方法 - 这是连接成功的关键步骤!
// 添加重试机制,增强初始化可靠性
int initResult = -1;
int maxInitRetries = 2;
for (int retry = 0; retry <= maxInitRetries; retry++)
{
try
{
if (retry > 0)
{
Log($"SDK初始化重试 {retry}/{maxInitRetries}");
Thread.Sleep(300); // 重试前等待一小段时间
}
initResult = A8SDK.SDK_initialize();
if (initResult == 0)
{
break; // 成功,跳出循环
}
Log($"SDK静态初始化失败返回值: {initResult}");
}
catch (Exception initEx)
{
Log($"SDK初始化异常: {initEx.Message},堆栈: {initEx.StackTrace}");
}
}
if (initResult != 0)
{
Log($"SDK静态初始化失败所有重试均未成功返回值: {initResult}");
_isInitialized = false;
return false;
}
Log("SDK静态初始化成功");
// 设置默认配置参数
_maxReconnectAttempts = 15; // 增加最大重连次数
_isAutoReconnectEnabled = true;
_isInitialized = true;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化成功_isInitialized设为true");
// 更新状态为已连接
UpdateConnectionStatus(ConnectionStatus.Connected, "SDK初始化成功");
// 连接成功后,停止自动重连(如果正在运行)
StopAutoReconnect();
// 启动图像接收
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 连接成功,启动图像接收");
StartImageReceiving();
// 启动温度数据接收
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 启动温度数据接收");
StartTemperatureDataReceiving();
return true;
}
catch (Exception ex)
{
Log($"SDK初始化失败: {ex.Message},堆栈: {ex.StackTrace}");
_isInitialized = false;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化异常_isInitialized保持false: {ex.Message}");
OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败"));
// 更新状态为断开
UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化失败", ex);
return false;
}
finally
{
// 确保在异常情况下也能清理资源
if (!_isInitialized && _connectionStatus == ConnectionStatus.Connecting)
{
UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化未完成");
}
}
}
}
/// <summary>
/// 清理所有资源,确保安全释放
/// </summary>
private void CleanupResources()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CleanupResources() - 开始清理资源");
try
{
// 停止所有定时器
StopAutoReconnect();
StopHeartbeat();
StopConnectionCheck();
// 停止温度数据接收
StopTemperatureDataReceiving();
// 安全释放SDK实例
A8SDK oldSdk = Interlocked.Exchange(ref _a8Sdk, null);
if (oldSdk != null)
{
try
{
Log("释放旧SDK实例...");
// 注意如果SDK提供了destroy方法应该在这里调用
// A8SDK.SDK_destroy();
}
catch (Exception ex)
{
Log($"释放SDK实例异常: {ex.Message}");
}
}
// 重置状态变量
_deviceIds = null;
_connectionStatus = ConnectionStatus.Disconnected;
_currentDeviceId = -1;
// _deviceIp = string.Empty; // 注释掉这行避免IP地址在资源清理时被清空
_currentReconnectAttempt = 0;
_isInitialized = false;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CleanupResources() - _isInitialized已设为false");
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CleanupResources() - 资源清理完成");
}
catch (Exception ex)
{
Log($"清理资源异常: {ex.Message}");
}
}
/// <summary>
/// 尝试ping设备IP地址
/// </summary>
/// <param name="ipAddress">设备IP地址</param>
/// <returns>是否ping通</returns>
private bool PingDevice(string ipAddress)
{
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)
{
// 仅检查活动的网络接口,移除过于严格的过滤条件
if (ni.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up &&
(ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Ethernet ||
ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Wireless80211 ||
ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.GigabitEthernet))
{
// 获取该网络接口的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
{
// 使用更准确的子网判断方法
var deviceAddress = System.Net.IPAddress.Parse(_deviceIp);
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;
}
}
// 添加心跳检测异常处理但位置应该在CheckConnectionValidity方法中
// 这里保持PingDevice方法的纯净性
if (isInSameSubnet)
{
Console.WriteLine($"设备IP与本地网络接口在同一网段: {localAddress}");
return true;
}
}
catch (Exception ex)
{
Console.WriteLine($"子网判断异常: {ex.Message}");
}
}
return true;
}
}
}
}
Console.WriteLine("未找到有效的网络连接或设备IP不在同一网段");
return false;
}
catch (Exception ex)
{
Console.WriteLine($"检查网络可用性异常: {ex.Message}");
// 发生异常时默认为网络不可用,避免误判
return false;
}
}
/// <summary>
/// 停止所有图像接收线程
/// </summary>
private void StopAllImageReceivers()
{
_stopRequested.Set();
// 给线程一点时间来结束
Thread.Sleep(100);
}
/// <summary>
/// 重置停止标志
/// </summary>
private void ResetStopFlag()
{
_stopRequested.Reset();
}
#endregion
#region
/// <summary>
/// 获取温度补偿值
/// </summary>
/// <returns>温度补偿值</returns>
private float GetTemperatureCompensationValue()
{
try
{
if (_connectionStatus == ConnectionStatus.Connected && _a8Sdk != null)
{
float compensationValue = _a8Sdk.Comp_temp;
Log($"获取温度补偿值: {compensationValue}");
return compensationValue;
}
}
catch (Exception ex)
{
Log($"获取温度补偿值异常: {ex.Message}");
}
return 0.0f;
}
/// <summary>
/// 开始接收温度数据
/// </summary>
public void StartTemperatureDataReceiving()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartTemperatureDataReceiving() - 开始执行");
// 在设计模式下,跳过实际的温度数据接收
if (IsDesignMode)
{
Log("设计模式下跳过实际的温度数据接收");
return;
}
lock (_lockObject)
{
// 检查对象是否已被释放
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartTemperatureDataReceiving() - 对象已释放,忽略操作");
return;
}
// 检查连接状态
if (_connectionStatus != ConnectionStatus.Connected)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartTemperatureDataReceiving() - 连接未建立,无法开始温度数据接收");
return;
}
// 添加额外的状态检查,避免不必要的重复启动
if (_isReceivingTemperatureData && _temperatureReceiveThread != null && _temperatureReceiveThread.IsAlive)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartTemperatureDataReceiving() - 温度数据接收已经在进行中,避免重复启动");
return;
}
}
Log("开始使用TCP方式接收温度数据");
try
{
// 确保之前的连接已关闭
StopTemperatureDataReceiving();
// 在锁内执行所有关键状态更新,确保原子性
lock (_lockObject)
{
// 重置停止事件
_stopTemperatureEvent?.Dispose();
_stopTemperatureEvent = new ManualResetEvent(false);
_isReceivingTemperatureData = true;
}
Thread.Sleep(500);
// 创建并启动温度数据接收线程
Thread newThread = new Thread(ReceiveTemperatureDataWithTcp)
{
IsBackground = true,
Name = "TemperatureReceiveThread"
};
// 在锁内更新线程引用
lock (_lockObject)
{
_temperatureReceiveThread = newThread;
}
// 启动线程
newThread.Start();
}
catch (Exception ex)
{
// 记录异常
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartTemperatureDataReceiving() - 异常: {ex.Message}");
// 在异常情况下确保状态正确重置
lock (_lockObject)
{
_isReceivingTemperatureData = false;
}
// 如果连接状态异常,触发异常事件
OnConnectionException(new ConnectionExceptionEventArgs(ex, "启动温度数据接收失败"));
}
finally
{
// 确保方法执行完成时记录日志
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartTemperatureDataReceiving() - 执行完成");
}
}
/// <summary>
/// 停止接收温度数据
/// </summary>
/// <summary>
/// 暂停温度数据接收处理保持TCP连接但不处理数据
/// </summary>
public void PauseTemperatureDataReceiving()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] PauseTemperatureDataReceiving() - 开始执行");
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] PauseTemperatureDataReceiving() - 对象已释放,跳过操作");
return;
}
try
{
lock (_lockObject)
{
_isTemperatureReceivingPaused = true;
}
Log("温度数据接收已暂停保持TCP连接但不处理数据");
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] PauseTemperatureDataReceiving() - 异常: {ex.Message}");
}
finally
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] PauseTemperatureDataReceiving() - 执行完成");
}
}
/// <summary>
/// 恢复温度数据接收处理
/// </summary>
public void ResumeTemperatureDataReceiving()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ResumeTemperatureDataReceiving() - 开始执行");
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ResumeTemperatureDataReceiving() - 对象已释放,跳过操作");
return;
}
try
{
lock (_lockObject)
{
_isTemperatureReceivingPaused = false;
}
Log("温度数据接收已恢复,开始处理数据");
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ResumeTemperatureDataReceiving() - 异常: {ex.Message}");
}
finally
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ResumeTemperatureDataReceiving() - 执行完成");
}
}
/// <summary>
/// 停止接收温度数据断开TCP连接
/// </summary>
public void StopTemperatureDataReceiving()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopTemperatureDataReceiving() - 开始执行");
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopTemperatureDataReceiving() - 对象已释放,跳过操作");
return;
}
// 使用变量保存需要释放的资源,减少锁的持有时间
ManualResetEvent stopEventToDispose = null;
Thread threadToJoin = null;
NetworkStream streamToClose = null;
TcpClient clientToClose = null;
try
{
// 在一个锁内收集所有需要清理的资源和更新状态
lock (_lockObject)
{
_isReceivingTemperatureData = false;
_isTemperatureReceivingPaused = false; // 同时重置暂停状态
stopEventToDispose = _stopTemperatureEvent;
threadToJoin = _temperatureReceiveThread;
streamToClose = _temperatureStream;
clientToClose = _temperatureTcpClient;
// 立即重置引用,避免资源被重新使用
_temperatureStream = null;
_temperatureTcpClient = null;
_temperatureReceiveThread = null;
_stopTemperatureEvent = null;
}
// 在锁外通知线程停止,避免死锁
if (stopEventToDispose != null)
{
try
{
stopEventToDispose.Set();
}
catch (ObjectDisposedException)
{
// 忽略已释放的事件
}
}
// 在锁外等待线程结束,避免死锁
if (threadToJoin != null && threadToJoin.IsAlive)
{
Log("等待温度接收线程结束...");
// 增加等待时间,确保线程有足够时间完成清理
if (threadToJoin.Join(2000)) // 等待最多2秒
{
Log("温度接收线程已正常停止");
}
else
{
Log("警告:温度接收线程可能未正常停止,已超时");
}
}
// 在锁外关闭TCP客户端和流避免死锁
if (streamToClose != null)
{
try
{
streamToClose.Close();
}
catch (Exception ex)
{
Log($"关闭NetworkStream时出现异常: {ex.Message}");
}
}
if (clientToClose != null)
{
try
{
clientToClose.Close();
}
catch (Exception ex)
{
Log($"关闭TcpClient时出现异常: {ex.Message}");
}
}
// 释放停止事件
if (stopEventToDispose != null)
{
try
{
stopEventToDispose.Dispose();
}
catch (Exception ex)
{
Log($"释放ManualResetEvent时出现异常: {ex.Message}");
}
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopTemperatureDataReceiving() - 异常: {ex.Message}");
// 在异常情况下,再次尝试重置状态
try
{
lock (_lockObject)
{
_isReceivingTemperatureData = false;
_temperatureStream = null;
_temperatureTcpClient = null;
_temperatureReceiveThread = null;
}
}
catch {}
}
finally
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopTemperatureDataReceiving() - 执行完成");
}
}
/// <summary>
/// 使用TCP接收温度数据
/// </summary>
private void ReceiveTemperatureDataWithTcp()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReceiveTemperatureDataWithTcp() - 开始执行");
// 使用局部变量存储资源避免在finally中访问可能已被释放的字段
TcpClient localTcpClient = null;
NetworkStream localStream = null;
List<byte> temperatureDataAccumulator = new List<byte>();
// 根据分析温度数据帧大小为98313字节(9字节头部+256×192×2字节数据),增大缓冲区提高接收效率
byte[] buffer = new byte[65536]; // 增大温度数据缓冲区减少Read操作次数
try
{
// 创建TCP客户端并连接到设备的温度数据端口
localTcpClient = new TcpClient
{
ReceiveTimeout = 5000,
SendTimeout = 5000
};
Log($"正在连接到温度数据端口 {TEMPERATURE_TCP_PORT}...");
IAsyncResult result = localTcpClient.BeginConnect(_deviceIp, TEMPERATURE_TCP_PORT, null, null);
bool connected = result.AsyncWaitHandle.WaitOne(3000, true);
if (!connected || !localTcpClient.Connected)
{
Log("温度数据TCP连接失败超时");
localTcpClient.Close();
return;
}
localTcpClient.EndConnect(result);
Log("温度数据TCP连接成功");
// 获取网络流
localStream = localTcpClient.GetStream();
localStream.ReadTimeout = 5000;
// 更新类成员变量,在锁内进行
lock (_lockObject)
{
_temperatureTcpClient = localTcpClient;
_temperatureStream = localStream;
// 确保接收状态为true
_isReceivingTemperatureData = true;
}
// 发送开始温度数据传输的命令
byte[] startCommand = Encoding.ASCII.GetBytes("start_temp_transfer\r\n");
localStream.Write(startCommand, 0, startCommand.Length);
localStream.Flush();
Log("已发送开始温度数据传输命令,开始接收温度数据");
// 循环读取数据,使用更安全的退出机制
bool shouldContinue = true;
while (shouldContinue)
{
// 检查停止信号和接收状态
bool stopRequested = false;
bool isReceiving = true;
lock (_lockObject)
{
if (_stopTemperatureEvent != null && _stopTemperatureEvent.WaitOne(0))
{
stopRequested = true;
}
isReceiving = _isReceivingTemperatureData;
}
if (stopRequested)
{
Log("接收到停止信号,准备退出温度数据接收循环");
shouldContinue = false;
break;
}
if (!isReceiving)
{
Log("接收状态已更改(isReceiving=false),准备退出温度数据接收循环");
shouldContinue = false;
break;
}
try
{
// 检查连接状态
if (localTcpClient == null || !localTcpClient.Connected)
{
Log("温度数据TCP连接已断开");
shouldContinue = false;
break;
}
// 检查流是否可读
if (localStream == null || !localStream.CanRead)
{
Log("网络流不可读,停止接收");
shouldContinue = false;
break;
}
// 检查是否有数据可读,避免阻塞
if (localStream.DataAvailable)
{
// 读取数据
int bytesRead = localStream.Read(buffer, 0, buffer.Length);
if (bytesRead > 0)
{
// 将读取的数据添加到累积器
byte[] receivedBytes = new byte[bytesRead];
Array.Copy(buffer, receivedBytes, bytesRead);
Log($"接收到温度数据字节数: {bytesRead}");
// 检查是否处于暂停状态
bool isPaused = false;
lock (_lockObject)
{
isPaused = _isTemperatureReceivingPaused;
}
if (isPaused)
{
Log("温度数据接收处于暂停状态,数据已接收但不处理");
// 数据已接收但不处理,直接丢弃
}
else
{
// 线程安全地更新累积器或直接处理
lock (temperatureDataAccumulator)
{
temperatureDataAccumulator.AddRange(receivedBytes);
ProcessReceivedTemperatureData(temperatureDataAccumulator);
}
}
}
else
{
// 读取到0字节表示连接已关闭
Log("远程主机关闭了连接");
shouldContinue = false;
break;
}
}
else
{
// 如果没有数据可读短暂休眠避免CPU占用过高
Thread.Sleep(10);
}
}
catch (TimeoutException)
{
// 超时异常,继续尝试读取
Log("温度数据接收超时,继续尝试");
// 短暂休眠后重试避免CPU占用过高
Thread.Sleep(50);
}
catch (IOException ex)
{
Log($"温度数据接收IO异常: {ex.Message}");
// 对于网络中断异常,尝试重新连接而不是直接退出
// 在.NET Framework 4.0中HResult是受保护的我们通过消息内容来判断
string exceptionMessage = ex.Message.ToLower();
if (exceptionMessage.Contains("远程主机") ||
exceptionMessage.Contains("强制关闭") ||
exceptionMessage.Contains("connection") ||
exceptionMessage.Contains("closed"))
{
Log("检测到网络连接异常,准备重新连接");
// 短暂休眠后允许循环退出,让外层逻辑处理重连
Thread.Sleep(100);
shouldContinue = false;
break;
}
else
{
Log("非致命IO异常继续尝试接收");
// 短暂休眠后继续尝试
Thread.Sleep(50);
}
}
catch (Exception ex)
{
Log($"温度数据接收异常: {ex.Message}");
Log($"异常详情: {ex.StackTrace}");
// 检查是否为严重异常
if (ex is System.Security.SecurityException ||
ex is UnauthorizedAccessException ||
ex is System.Threading.ThreadAbortException)
{
Log("发生严重异常,停止温度数据接收");
shouldContinue = false;
break;
}
else
{
Log("非致命异常,继续尝试接收温度数据");
// 非致命异常,短暂休眠后继续尝试
Thread.Sleep(100);
}
}
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReceiveTemperatureDataWithTcp() - 严重异常: {ex.Message}");
}
finally
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReceiveTemperatureDataWithTcp() - 执行完成");
// 确保状态正确更新
lock (_lockObject)
{
_isReceivingTemperatureData = false;
// 不在这里调用StopTemperatureDataReceiving避免递归调用和潜在死锁
// 由调用者负责调用StopTemperatureDataReceiving
// 记录线程结束信息
Log($"温度数据接收线程已结束,重置相关状态");
}
// 清理局部资源
try
{
if (localStream != null)
{
localStream.Close();
}
}
catch {}
try
{
if (localTcpClient != null)
{
localTcpClient.Close();
}
}
catch {}
// 重置类成员变量中的流和客户端引用
lock (_lockObject)
{
_temperatureStream = null;
_temperatureTcpClient = null;
}
// 检查是否需要自动重新启动温度数据接收
// 只有当设备仍在连接状态且用户没有明确停止时才尝试重连
bool shouldAutoReconnect = false;
lock (_lockObject)
{
// 检查是否有显式的停止信号
bool hasStopSignal = _stopTemperatureEvent != null && _stopTemperatureEvent.WaitOne(0);
// 如果没有停止信号且设备应该处于连接状态,则尝试重连
shouldAutoReconnect = !hasStopSignal && _connectionStatus == ConnectionStatus.Connected;
}
if (shouldAutoReconnect)
{
Log("检测到温度数据接收线程异常退出,准备自动重连");
try
{
// 短暂延迟后尝试重新启动温度数据接收
Thread.Sleep(2000);
// 确保在新线程中启动,避免递归调用
ThreadPool.QueueUserWorkItem(state =>
{
try
{
Log("开始自动重新启动温度数据接收...");
StartTemperatureDataReceiving();
}
catch (Exception ex)
{
Log($"自动重连温度数据接收失败: {ex.Message}");
}
});
}
catch (Exception ex)
{
Log($"调度温度数据自动重连失败: {ex.Message}");
}
}
}
}
/// <summary>
/// 处理接收到的温度数据
/// </summary>
/// <param name="dataAccumulator">累积的温度数据</param>
private void ProcessReceivedTemperatureData(List<byte> dataAccumulator)
{
// 根据TemperatureData类的要求每个温度帧包含9字节头部 + 温度数据
// 根据注释设备实际提供的数据分辨率应为256x192最终映射到512x384显示
const int HEADER_SIZE = 9; // 9字节头部("+TEMP"+数据长度)
const int WIDTH = 256;
const int HEIGHT = 192;
const int TEMPERATURE_DATA_FRAME_SIZE = HEADER_SIZE + WIDTH * HEIGHT * 2; // 9字节头部 + 每个温度值2字节
try
{
Log($"开始处理温度数据,当前累积数据量: {dataAccumulator.Count} 字节,所需帧大小: {TEMPERATURE_DATA_FRAME_SIZE} 字节");
// 检查是否有足够的数据构成完整的温度数据帧
while (dataAccumulator.Count >= TEMPERATURE_DATA_FRAME_SIZE)
{
Log($"找到完整的温度数据帧,开始处理");
// 提取一帧温度数据
byte[] temperatureFrame = dataAccumulator.GetRange(0, TEMPERATURE_DATA_FRAME_SIZE).ToArray();
dataAccumulator.RemoveRange(0, TEMPERATURE_DATA_FRAME_SIZE);
Log($"提取温度数据帧完成,剩余累积数据量: {dataAccumulator.Count} 字节");
// 获取温度补偿值
float compensationValue = GetTemperatureCompensationValue();
Log($"获取到温度补偿值: {compensationValue}");
// 创建温度数据对象,使用正确的分辨率参数
TemperatureData temperatureData = new TemperatureData(temperatureFrame, WIDTH, HEIGHT, compensationValue);
Log($"温度数据对象创建成功,分辨率: {WIDTH}x{HEIGHT}");
// 触发温度数据接收事件
OnTemperatureReceived(new TemperatureReceivedEventArgs(temperatureData, temperatureFrame, compensationValue));
Log($"温度数据接收事件触发完成");
}
Log($"温度数据处理完成");
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ProcessReceivedTemperatureData() - 异常: {ex.Message}");
// 清空累积器,重新开始
Log("清空温度数据累积器,准备重新接收");
dataAccumulator.Clear();
}
}
/// <summary>
/// 触发温度数据接收事件
/// </summary>
/// <param name="e">事件参数</param>
protected virtual void OnTemperatureReceived(TemperatureReceivedEventArgs e)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnTemperatureReceived() - 开始执行");
// 检查参数有效性
if (e == null)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnTemperatureReceived() - 参数为空,跳过事件触发");
return;
}
// 获取事件处理程序的快照,避免在多线程环境中出现竞态条件
EventHandler<TemperatureReceivedEventArgs> handler = TemperatureReceived;
// 检查是否有订阅者
if (handler != null)
{
// 获取所有订阅的委托,单独处理每个处理器
Delegate[] invocationList = handler.GetInvocationList();
foreach (Delegate d in invocationList)
{
try
{
// 安全地转换并调用每个处理器
EventHandler<TemperatureReceivedEventArgs> invoker = (EventHandler<TemperatureReceivedEventArgs>)d;
invoker(this, e);
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnTemperatureReceived() - 成功触发一个事件处理器: {d.Method.Name}");
}
catch (Exception ex)
{
// 捕获单个事件处理器的异常,确保其他处理器仍能被调用
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnTemperatureReceived() - 事件处理器异常: {ex.Message}");
}
}
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnTemperatureReceived() - 执行完成");
}
#endregion
#region
/// <summary>
/// 连接状态变更事件
/// </summary>
public event EventHandler<ConnectionStatusChangedEventArgs> ConnectionStatusChanged;
/// <summary>
/// 连接异常事件
/// </summary>
public event EventHandler<ConnectionExceptionEventArgs> ConnectionException;
/// <summary>
/// 图像数据接收事件
/// </summary>
public event EventHandler<ImageReceivedEventArgs> ImageReceived;
/// <summary>
/// 温度数据接收事件
/// </summary>
public event EventHandler<TemperatureReceivedEventArgs> TemperatureReceived;
#endregion
/// <summary>
/// 触发连接状态变更事件
/// </summary>
/// <param name="e">事件参数</param>
protected virtual void OnConnectionStatusChanged(ConnectionStatusChangedEventArgs e)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionStatusChanged() - 开始执行,状态: {e.Status}");
// 检查参数有效性
if (e == null)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionStatusChanged() - 参数为空,跳过事件触发");
return;
}
// 获取事件处理程序的快照,避免在多线程环境中出现竞态条件
EventHandler<ConnectionStatusChangedEventArgs> handler = ConnectionStatusChanged;
// 检查是否有订阅者
if (handler != null)
{
// 获取所有订阅的委托,单独处理每个处理器
Delegate[] invocationList = handler.GetInvocationList();
foreach (Delegate d in invocationList)
{
try
{
// 安全地转换并调用每个处理器
EventHandler<ConnectionStatusChangedEventArgs> invoker = (EventHandler<ConnectionStatusChangedEventArgs>)d;
invoker(this, e);
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionStatusChanged() - 成功触发一个事件处理器: {d.Method.Name}");
}
catch (Exception ex)
{
// 捕获单个事件处理器的异常,确保其他处理器仍能被调用
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionStatusChanged() - 事件处理器异常: {ex.Message},堆栈: {ex.StackTrace}");
}
}
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionStatusChanged() - 执行完成");
}
/// <summary>
/// 启动连接状态检查
/// </summary>
public void StartConnectionCheck()
{
lock (_lockObject) // 添加线程同步锁
{
try
{
// 在设计模式下,跳过实际的连接检查
if (IsDesignMode)
{
Log("设计模式下跳过实际的连接检查");
return;
}
// 在暂停检测模式下,跳过连接检查
if (IsDetectionPaused)
{
Log("暂停检测模式下跳过连接检查");
return;
}
// 首先停止现有的连接检查,确保资源释放
StopConnectionCheck();
Log("启动连接状态检查每5秒检查一次"); // 统一使用Log方法
// 使用try-catch包装定时器创建避免异常
try
{
// 使用非重复模式的定时器,避免前一个检查未完成就开始新的检查
_connectionCheckTimer = new System.Threading.Timer(state =>
{
// 确保定时器状态检查在工作线程中安全执行
try
{
// 避免在定时器回调中可能的资源访问冲突
if (_connectionCheckTimer == null)
return;
lock (_lockObject) // 添加线程同步锁保护状态访问
{
if (_isDisposed)
return;
// 关键修改即使_isInitialized为false也需要检查连接状态
// 当显示为已连接但初始化状态为false时必须检查连接
if (_connectionStatus == ConnectionStatus.Connected)
{
// 如果初始化失败但显示已连接,尝试重新初始化
if (!_isInitialized)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 警告: 显示已连接但初始化状态为false尝试重新初始化...");
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 当前状态: _connectionStatus={_connectionStatus}, _isInitialized={_isInitialized}, _a8Sdk={(_a8Sdk == null ? "null" : "")}, _currentDeviceId={_currentDeviceId}");
// 避免在定时器回调中直接调用可能阻塞的方法
// 使用线程池执行初始化
ThreadPool.QueueUserWorkItem((obj) =>
{
lock (_lockObject)
{
if (!_isDisposed && _connectionStatus == ConnectionStatus.Connected && !_isInitialized)
{
if (!Initialize())
{
Log("重新初始化失败,确认连接已断开");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备未初始化,连接已断开");
// 启动自动重连
if (_isAutoReconnectEnabled)
{
StartAutoReconnect();
}
}
}
}
});
}
// 无论初始化状态如何,只要显示为已连接就进行检查
// 复制状态变量到局部变量以避免在检查过程中状态被修改
bool isConnected = _connectionStatus == ConnectionStatus.Connected;
bool isInitialized = _isInitialized;
A8SDK sdkInstance = _a8Sdk;
int deviceId = _currentDeviceId;
// 在锁外执行检查以避免阻塞
ThreadPool.QueueUserWorkItem((obj) =>
{
CheckConnectionWrapper();
});
}
// 正常情况下的连接检查
else if (_isInitialized && _a8Sdk != null && _currentDeviceId != -1)
{
// 复制状态变量到局部变量以避免在检查过程中状态被修改
bool isInitialized = _isInitialized;
A8SDK sdkInstance = _a8Sdk;
int deviceId = _currentDeviceId;
// 在锁外执行检查以避免阻塞
ThreadPool.QueueUserWorkItem((obj) =>
{
CheckConnectionWrapper();
});
}
}
}
catch (Exception ex)
{
Log($"定时器回调异常: {ex.Message}");
// 异常时如果是已连接状态,将其设为断开
lock (_lockObject)
{
if (!_isDisposed && _connectionStatus == ConnectionStatus.Connected)
{
UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接检查异常", ex);
_isInitialized = false;
// 启动自动重连,只有在未暂停检测时才执行
if (_isAutoReconnectEnabled && !IsDetectionPaused)
{
StartAutoReconnect();
}
}
}
// 发生异常时停止定时器,避免持续报错
StopConnectionCheck();
}
finally
{
// 检查完成后,如果定时器仍存在且未释放,重新启动定时器
// 这样可以确保一个检查完成后才开始下一个检查
lock (_lockObject)
{
if (!_isDisposed && _connectionCheckTimer != null)
{
try
{
_connectionCheckTimer.Change(5000, Timeout.Infinite);
}
catch (ObjectDisposedException)
{
// 忽略已释放对象的异常
}
}
}
}
}, null, 5000, Timeout.Infinite);
}
catch (Exception ex)
{
Log($"创建连接检查定时器失败: {ex.Message}");
_connectionCheckTimer = null;
}
}
catch (Exception ex)
{
Log($"启动连接检查异常: {ex.Message}");
StopConnectionCheck();
}
}
}
// 连接检查的安全包装方法
private void CheckConnectionWrapper()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 开始连接检查");
// 先检查对象状态,避免不必要的操作
lock (_lockObject)
{
if (_isDisposed || _connectionStatus != ConnectionStatus.Connected || IsDetectionPaused)
{
string reason = _isDisposed ? "对象已释放" :
(_connectionStatus != ConnectionStatus.Connected ? "连接状态非Connected" : "检测已暂停");
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - {reason},跳过检查");
return;
}
}
// 在锁外执行连接有效性检查,避免长时间阻塞
bool isStillConnected;
try
{
isStillConnected = CheckConnectionValidity();
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - CheckConnectionValidity返回: {isStillConnected}");
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 检查连接状态异常: {ex.Message}");
isStillConnected = false;
}
// 如果连接无效,需要在锁内更新状态
if (!isStillConnected)
{
lock (_lockObject)
{
if (!_isDisposed && _connectionStatus == ConnectionStatus.Connected)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 检测到连接已断开: _connectionStatus={_connectionStatus}, isStillConnected={isStillConnected}, _isInitialized={_isInitialized}");
// 断开连接时重置初始化状态和SDK实例
_isInitialized = false;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 重置初始化状态为false");
// 安全释放SDK实例
A8SDK oldSdk = _a8Sdk;
_a8Sdk = null;
// 在锁外释放资源
if (oldSdk != null)
{
ThreadPool.QueueUserWorkItem((obj) =>
{
try
{
// 注意A8SDK类没有Dispose方法这里只记录日志
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - SDK实例引用已处理");
}
catch (Exception disposeEx)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] SDK资源释放异常: {disposeEx.Message}");
}
});
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - SDK实例已为空无需释放");
}
UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接已断开:设备离线");
// 断开连接后自动启动重连,但在锁外执行,只有在未暂停检测时才执行
if (_isAutoReconnectEnabled && !IsDetectionPaused)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 连接断开,将启动自动重连");
// 在锁外启动自动重连,避免潜在的死锁
ThreadPool.QueueUserWorkItem((obj) =>
{
StartAutoReconnect();
});
}
}
}
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 连接检查完成");
}
/// <summary>
/// 检查连接有效性的内部方法
/// </summary>
/// <returns>连接是否有效</returns>
private bool CheckConnectionValidity()
{
// 在设计模式下,跳过实际的连接有效性检查,直接返回连接有效
if (IsDesignMode)
{
Log("设计模式下跳过实际的连接有效性检查,模拟连接有效");
return true;
}
// 在暂停检测模式下,跳过连接有效性检查,直接返回连接有效
if (IsDetectionPaused)
{
Log("暂停检测模式下跳过连接有效性检查,模拟连接有效");
return true;
}
// 注意此方法被CheckConnectionWrapper调用已经在线程安全的上下文中
// 不需要额外加锁但需要确保SDK实例的访问是线程安全的
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 开始检查连接有效性");
try
{
// 记录当前状态信息
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 当前状态: _currentDeviceId={_currentDeviceId}, _deviceIp={_deviceIp}, _a8Sdk={(null == _a8Sdk ? "null" : "")}");
// 重要修改先检查设备ID有效性避免后续无效检查
if (_currentDeviceId == -1)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 当前设备ID无效返回false");
return false;
}
// 优化1: 优先尝试ping设备IP地址这是物理连接断开的最直接检测
bool pingResult = PingDevice(_deviceIp);
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - ping检测结果: {pingResult}");
if (!pingResult)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 设备IP {_deviceIp} 不可达物理连接可能已断开返回false");
return false;
}
// 优化2: 系统网络接口检测作为辅助检查,不再作为首要条件
bool networkAvailable = IsNetworkAvailable();
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 网络可用性检测结果: {networkAvailable}");
if (!networkAvailable)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 警告: 系统网络连接不可用但ping检测通过");
}
// 优化3: 加强SDK连接状态验证参考热像仪示例的实现方式
try
{
// 使用线程安全的SDK实例创建
if (_a8Sdk == null)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK实例不存在重新初始化...");
// 先创建新实例,再原子赋值,避免中间状态
A8SDK newSdk = new A8SDK(_deviceIp);
_a8Sdk = newSdk;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK实例已创建");
}
// 增强心跳检测重试机制,提高连接稳定性
int maxHeartbeatRetries = 3; // 增加到3次重试
int heartbeatResult = -1;
for (int retry = 0; retry <= maxHeartbeatRetries; retry++)
{
try
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK心跳检测...(尝试 {retry + 1}/{maxHeartbeatRetries + 1})");
heartbeatResult = _a8Sdk.Heartbeat();
// 严格检查返回值,确保连接有效
if (heartbeatResult == 1) // V8.Heartbeat()方法返回1表示成功
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK心跳检测成功");
break;
}
else if (retry < maxHeartbeatRetries)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK心跳检测失败返回代码: {heartbeatResult}等待500ms后重试...");
Thread.Sleep(500); // 增加重试间隔到500ms
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK心跳检测多次失败尝试重建SDK连接...");
// 安全释放旧的SDK实例
A8SDK oldSdk = Interlocked.Exchange(ref _a8Sdk, null);
// 尝试重建SDK连接
Thread.Sleep(500);
_a8Sdk = new A8SDK(_deviceIp);
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 已重建SDK连接进行最后一次心跳检测");
Thread.Sleep(500);
heartbeatResult = _a8Sdk.Heartbeat();
if (heartbeatResult == 1)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK重新连接成功");
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK重新连接失败返回代码: {heartbeatResult}");
}
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 心跳检测异常: {ex.Message}");
if (retry < maxHeartbeatRetries)
{
Thread.Sleep(500); // 增加重试间隔到500ms
}
}
}
if (heartbeatResult != 1)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 心跳检测最终失败返回false");
// 心跳失败时安全释放SDK实例
if (_a8Sdk != null)
{
_a8Sdk = null;
}
return false;
}
// 添加额外的连接验证步骤
try
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 进行额外连接验证...");
// 再次发送心跳包确保连接稳定
int finalResult = _a8Sdk.Heartbeat();
if (finalResult != 1)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 最终验证失败,返回代码: {finalResult}返回false");
_a8Sdk = null;
return false;
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 最终验证成功");
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 连接验证异常: {ex.Message}返回false");
_a8Sdk = null;
return false;
}
// 设备名称获取使用try-catch避免在心跳成功但设备响应异常时崩溃
try
{
// 如果SDK支持获取设备名称可以尝试获取
// string deviceName = _a8Sdk.Get_device_name();
// if (string.IsNullOrEmpty(deviceName))
// {
// Console.WriteLine("设备名称获取失败,可能连接不稳定");
// }
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 获取设备名称异常,但心跳检测成功: {ex.Message}");
// 设备名称获取失败不应导致整体连接失效
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK心跳检测失败: {ex.Message}返回false");
// 确保异常时SDK实例被释放
_a8Sdk = null;
return false;
}
// 所有检查通过,连接有效
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 所有检查通过连接有效返回true");
return true;
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 连接有效性检查异常: {ex.Message}返回false");
// 确保异常时SDK实例被释放
_a8Sdk = null;
return false;
}
}
/// <summary>
/// 停止连接状态检查
/// </summary>
private void StopConnectionCheck()
{
try
{
// 使用局部变量暂存,避免多线程访问时的空引用问题
System.Threading.Timer timerToDispose = Interlocked.Exchange(ref _connectionCheckTimer, null);
if (timerToDispose != null)
{
// 立即停止定时器,避免回调执行
timerToDispose.Change(Timeout.Infinite, Timeout.Infinite);
// 安全释放定时器资源
timerToDispose.Dispose();
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopConnectionCheck() - 连接检查已停止");
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopConnectionCheck() - 停止连接检查异常: {ex.Message}");
// 确保即使异常,引用也被清空
_connectionCheckTimer = null;
}
}
/// <summary>
/// 触发连接异常事件
/// </summary>
/// <param name="e">事件参数</param>
protected virtual void OnConnectionException(ConnectionExceptionEventArgs e)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionException() - 开始执行");
// 检查参数有效性
if (e == null)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionException() - 参数为空,跳过事件触发");
return;
}
// 获取事件处理程序的快照,避免在多线程环境中出现竞态条件
EventHandler<ConnectionExceptionEventArgs> handler = ConnectionException;
// 检查是否有订阅者
if (handler != null)
{
// 获取所有订阅的委托,单独处理每个处理器
Delegate[] invocationList = handler.GetInvocationList();
foreach (Delegate d in invocationList)
{
try
{
// 安全地转换并调用每个处理器
EventHandler<ConnectionExceptionEventArgs> invoker = (EventHandler<ConnectionExceptionEventArgs>)d;
invoker(this, e);
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionException() - 成功触发一个事件处理器: {d.Method.Name}");
}
catch (Exception ex)
{
// 捕获单个事件处理器的异常,确保其他处理器仍能被调用
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionException() - 事件处理器异常: {ex.Message},堆栈: {ex.StackTrace}");
}
}
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionException() - 执行完成");
}
/// <summary>
/// 触发图像接收事件
/// </summary>
/// <param name="e">事件参数</param>
protected virtual void OnImageReceived(ImageReceivedEventArgs e)
{
// 图像事件可能高频触发,只记录重要日志以避免性能影响
bool isDebugMode = false; // 可配置是否启用详细日志
if (isDebugMode)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnImageReceived() - 开始执行");
}
// 检查参数有效性
if (e == null)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnImageReceived() - 警告: 参数为空,跳过事件触发");
return;
}
// 获取事件处理程序的快照,避免在多线程环境中出现竞态条件
EventHandler<ImageReceivedEventArgs> handler = ImageReceived;
// 检查是否有订阅者
if (handler != null)
{
// 获取所有订阅的委托,单独处理每个处理器
Delegate[] invocationList = handler.GetInvocationList();
foreach (Delegate d in invocationList)
{
try
{
// 安全地转换并调用每个处理器
EventHandler<ImageReceivedEventArgs> invoker = (EventHandler<ImageReceivedEventArgs>)d;
invoker(this, e);
if (isDebugMode)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnImageReceived() - 成功触发处理器: {d.Method.Name}");
}
}
catch (Exception ex)
{
// 捕获单个事件处理器的异常,确保其他处理器仍能被调用
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnImageReceived() - 处理器异常: {ex.Message},堆栈: {ex.StackTrace}");
}
}
}
if (isDebugMode)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnImageReceived() - 执行完成");
}
}
#region
/// <summary>
/// 获取或设置设备IP地址
/// </summary>
public string IPAddress
{
get { return _deviceIp; }
set
{
if (!string.IsNullOrEmpty(value))
{
_deviceIp = value;
Console.WriteLine($"设备IP地址已设置为: {_deviceIp}");
}
}
}
/// <summary>
/// 当前图像模式
/// </summary>
public ImageMode CurrentImageMode
{
get { return _currentImageMode; }
set
{
if (_currentImageMode != value)
{
_currentImageMode = value;
}
}
}
/// <summary>
/// 连接状态
/// </summary>
public ConnectionStatus ConnectionStatus
{
get { return _connectionStatus; }
}
/// <summary>
/// 是否启用自动重连
/// </summary>
public bool AutoReconnectEnabled
{
get { return _isAutoReconnectEnabled; }
set
{
_isAutoReconnectEnabled = value;
if (value && _connectionStatus == ConnectionStatus.Disconnected)
{
StartAutoReconnect();
}
else if (!value)
{
StopAutoReconnect();
}
}
}
/// <summary>
/// 重连间隔(毫秒)
/// </summary>
public int ReconnectInterval
{
get { return _reconnectInterval; }
set
{
if (value > 0)
{
_reconnectInterval = value;
// 如果已经在自动重连中,更新定时器
if (_isAutoReconnectEnabled && _reconnectTimer != null)
{
_reconnectTimer.Change(0, _reconnectInterval);
}
}
}
}
// 该属性已在文件上方定义,删除重复实现
/// <summary>
/// 设备IP地址
/// </summary>
public string DeviceIp
{
get { return _deviceIp; }
set
{
if (_deviceIp != value)
{
_deviceIp = value;
// 如果已连接,重新连接
if (_connectionStatus == ConnectionStatus.Connected)
{
Disconnect();
ConnectDevice();
}
}
}
}
/// <summary>
/// 设备端口
/// </summary>
public int DevicePort
{
get { return _devicePort; }
set
{
if (_devicePort != value && value > 0 && value <= 65535)
{
_devicePort = value;
// 如果已连接,重新连接
if (_connectionStatus == ConnectionStatus.Connected)
{
Disconnect();
ConnectDevice();
}
}
}
}
/// <summary>
/// 心跳间隔(毫秒)
/// </summary>
public int HeartbeatInterval
{
get { return _heartbeatInterval; }
set
{
if (value > 0)
{
_heartbeatInterval = value;
// 如果已经启用心跳检测,更新定时器
if (_heartbeatTimer != null)
{
_heartbeatTimer.Change(0, _heartbeatInterval);
}
}
}
}
#endregion
#region
/// <summary>
/// 开始接收图像数据
/// </summary>
public void StartImageReceiving()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartImageReceiving() - 开始执行");
// 在设计模式下,跳过实际的图像接收
if (IsDesignMode)
{
Log("设计模式下跳过实际的图像接收");
return;
}
lock (_lockObject)
{
// 检查对象是否已被释放
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartImageReceiving() - 对象已释放,忽略操作");
return;
}
// 检查连接状态
if (_connectionStatus != ConnectionStatus.Connected)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartImageReceiving() - 连接未建立,无法开始图像接收");
return;
}
}
Log("开始使用HTTP方式接收图像数据");
try
{
// 确保之前的连接已关闭
StopImageReceiving();
// 重置停止事件
_stopImageEvent = new ManualResetEvent(false);
lock (_lockObject)
{
_isReceivingImages = true;
}
// 创建并启动图像接收线程
_imageReceiveThread = new Thread(ReceiveImageDataWithHttpWebRequest)
{
IsBackground = true,
Name = "ImageReceiveThread"
};
_imageReceiveThread.Start();
}
catch (Exception ex)
{
// 记录异常
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartImageReceiving() - 异常: {ex.Message}");
lock (_lockObject)
{
_isReceivingImages = false;
}
// 如果连接状态异常,触发异常事件
OnConnectionException(new ConnectionExceptionEventArgs(ex, "启动图像接收失败"));
}
finally
{
// 确保方法执行完成时记录日志
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartImageReceiving() - 执行完成");
}
}
/// <summary>
/// 停止接收图像数据
/// </summary>
public void StopImageReceiving()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopImageReceiving() - 开始执行");
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopImageReceiving() - 对象已释放,跳过操作");
return;
}
try
{
// 线程安全地更新状态
lock (_lockObject)
{
_isReceivingImages = false;
}
// 同步停止温度数据接收
StopTemperatureDataReceiving();
// 通知线程停止
ManualResetEvent stopEvent = null;
lock (_lockObject)
{
stopEvent = _stopImageEvent;
}
if (stopEvent != null)
{
try
{
stopEvent.Set();
}
catch (ObjectDisposedException)
{
// 忽略已释放的事件
}
}
// 等待线程结束
Thread imageThread = null;
lock (_lockObject)
{
imageThread = _imageReceiveThread;
}
if (imageThread != null && imageThread.IsAlive)
{
try
{
imageThread.Join(3000); // 最多等待3秒
lock (_lockObject)
{
if (_imageReceiveThread == imageThread) // 确保没有被其他线程修改
{
_imageReceiveThread = null;
}
}
}
catch (ThreadStateException)
{
// 忽略线程状态异常
}
}
// 停止重连线程
Thread reconnectThread = null;
lock (_lockObject)
{
reconnectThread = _imageReconnectThread;
}
if (reconnectThread != null && reconnectThread.IsAlive)
{
try
{
reconnectThread.Join(1000);
lock (_lockObject)
{
if (_imageReconnectThread == reconnectThread) // 确保没有被其他线程修改
{
_imageReconnectThread = null;
}
}
}
catch (ThreadStateException)
{
// 忽略线程状态异常
}
}
// 释放资源
Stream stream = null;
lock (_lockObject)
{
stream = _imageStream;
_imageStream = null;
}
if (stream != null)
{
try
{
stream.Close();
stream.Dispose();
}
catch (Exception)
{
// 忽略释放资源时的异常
}
}
TcpClient tcpClient = null;
lock (_lockObject)
{
tcpClient = _imageTcpClient;
_imageTcpClient = null;
}
if (tcpClient != null)
{
try
{
tcpClient.Close();
}
catch (Exception)
{
// 忽略关闭连接时的异常
}
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopImageReceiving() - 异常: {ex.Message}");
}
finally
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopImageReceiving() - 执行完成");
}
}
/// <summary>
/// 使用HttpWebRequest接收图像数据
/// </summary>
private void ReceiveImageDataWithHttpWebRequest()
{
// 在设计模式下,跳过实际的图像接收
if (IsDesignMode)
{
Log("设计模式下跳过实际的图像接收");
// 设置为不接收状态,避免持续运行
_isReceivingImages = false;
return;
}
Console.WriteLine($"图像接收线程启动: 使用HTTP请求获取图像数据");
try
{
while (_isReceivingImages)
{
// 确保连接状态正常
if (_connectionStatus != ConnectionStatus.Connected)
{
Console.WriteLine("连接状态异常,暂停图像接收");
Thread.Sleep(500);
continue;
}
// 根据当前图像模式构建URL确保模式更改能实时生效
string url = string.Format("http://{0}:8080{1}", DeviceIp, CurrentImageMode == ImageMode.Infrared ? "/video/infrared.jpg" : "/video/optical.jpg");
// 添加时间戳避免缓存
string timestampUrl = url + "?t=" + DateTime.Now.Ticks;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(timestampUrl);
request.Method = "GET";
request.Timeout = 3000; // 3秒超时
request.KeepAlive = true;
request.UserAgent = "Mozilla/5.0"; // 模拟浏览器
request.ServicePoint.Expect100Continue = false; // 优化HTTP请求性能
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (Stream stream = response.GetResponseStream())
{
// 读取图像数据
byte[] buffer = new byte[8192]; // 增加缓冲区大小到8KB
using (MemoryStream ms = new MemoryStream())
{
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, bytesRead);
}
byte[] imageData = ms.ToArray();
// 处理接收到的数据
ProcessReceivedImageData(imageData);
}
}
}
}
// 检查是否需要停止
if (_stopImageEvent.WaitOne(100)) // 100ms检查一次
{
break;
}
}
}
catch (Exception ex)
{
// 记录异常并尝试重连
if (_isReceivingImages)
{
Console.WriteLine($"ReceiveImageDataWithHttpWebRequest error: {ex.Message}");
// 避免频繁重连
Thread.Sleep(100);
// 只有在连接状态为Connected时才尝试图像重连
if (_connectionStatus == ConnectionStatus.Connected)
{
StartImageReconnect();
}
else
{
Console.WriteLine("连接已断开,不启动图像重连");
}
}
}
}
/// <summary>
/// 处理接收到的图像数据
/// </summary>
/// <param name="data">原始数据</param>
private void ProcessReceivedImageData(byte[] data)
{
if (data == null || data.Length == 0)
return;
// 记录数据接收时间
Log($"[{DateTime.Now:HH:mm:ss.fff}] [图像接收] 开始处理接收到的数据,大小: {data.Length}字节");
// 更新最后接收数据时间戳
_lastDataReceivedTime = DateTime.Now;
// 检查是否为HTTP响应
if (IsHttpResponse(data))
{
// 查找HTTP响应中的图像数据
int imageStartPos = FindImageStartPosition(data);
if (imageStartPos > 0)
{
// 提取图像数据部分
int imageLength = data.Length - imageStartPos;
byte[] imageData = new byte[imageLength];
Array.Copy(data, imageStartPos, imageData, 0, imageLength);
// 验证并处理提取的图像数据
ValidateAndProcessImage(imageData);
}
else
{
// 尝试直接验证整个HTTP响应
ValidateAndProcessImage(data);
}
}
else
{
// 直接验证和处理普通图像数据
ValidateAndProcessImage(data);
}
}
/// <summary>
/// 验证并处理图像数据
/// </summary>
/// <param name="imageData">图像数据</param>
private void ValidateAndProcessImage(byte[] imageData)
{
// 首先检查文件头
if (IsValidImageData(imageData))
{
try
{
// 尝试从流中创建图像对象进行验证
using (MemoryStream ms = new MemoryStream(imageData))
{
// 检查是否包含完整的JPEG文件
if (imageData.Length > 4 && imageData[0] == 0xFF && imageData[1] == 0xD8)
{
int endPos = FindImageEndPosition(imageData, 0);
if (endPos > 0 && endPos + 1 < imageData.Length)
{
// 确保图像有完整的EOF标记
if (imageData[endPos + 1] == 0xD9)
{
// 创建并触发事件
Image jpegImage = Image.FromStream(ms);
// 记录图像接收时间
Log($"[{DateTime.Now:HH:mm:ss.fff}] [图像接收] 成功接收到JPEG图像大小: {imageData.Length}字节");
OnImageReceived(new ImageReceivedEventArgs(imageData, _currentImageMode));
return;
}
}
}
// 对于其他格式或无法确认完整度的情况,直接尝试创建
Image generalImage = Image.FromStream(ms);
// 记录图像接收时间
Log($"[{DateTime.Now:HH:mm:ss.fff}] [图像接收] 成功接收到图像,大小: {imageData.Length}字节");
OnImageReceived(new ImageReceivedEventArgs(imageData, _currentImageMode));
}
}
catch (Exception ex)
{
// 记录异常日志
Log($"[{DateTime.Now:HH:mm:ss.fff}] [图像接收] 处理图像数据异常: {ex.Message}");
Console.WriteLine("处理图像数据异常: " + ex.Message);
// 尝试查找有效的图像起始位置
int validStartPos = FindImageStartPosition(imageData);
if (validStartPos > 0)
{
try
{
// 尝试从有效位置提取图像
int newLength = imageData.Length - validStartPos;
byte[] validImageData = new byte[newLength];
Array.Copy(imageData, validStartPos, validImageData, 0, newLength);
using (MemoryStream ms = new MemoryStream(validImageData))
{
Image validImage = Image.FromStream(ms);
// 记录图像接收时间
Log($"[{DateTime.Now:HH:mm:ss.fff}] [图像接收] 成功接收到有效图像(二次尝试),大小: {validImageData.Length}字节");
OnImageReceived(new ImageReceivedEventArgs(validImageData, _currentImageMode));
}
}
catch
{
// 二次尝试也失败,放弃处理
}
}
}
}
}
/// <summary>
/// 启动图像重连
/// </summary>
private void StartImageReconnect()
{
// 在设计模式下,跳过实际的图像重连
if (IsDesignMode)
{
return;
}
// 避免重复创建重连线程
if (_imageReconnectThread != null && _imageReconnectThread.IsAlive)
{
return;
}
_imageReconnectThread = new Thread(() =>
{
try
{
int reconnectCount = 0;
while (_isReceivingImages && reconnectCount < 3) // 最多重连3次
{
reconnectCount++;
Thread.Sleep(2000); // 2秒后重连
if (_isReceivingImages)
{
ReceiveImageDataWithHttpWebRequest();
}
}
}
catch (Exception ex)
{
Console.WriteLine("Image reconnect error: " + ex.Message);
}
})
{
IsBackground = true,
Name = "ImageReconnectThread"
};
_imageReconnectThread.Start();
}
#endregion
#region
/// <summary>
/// 查找图像数据的结束位置特别是JPEG的EOF标记
/// </summary>
/// <param name="data">数据缓冲区</param>
/// <param name="startIndex">开始查找的位置</param>
/// <returns>图像结束位置索引,-1表示未找到</returns>
private int FindImageEndPosition(byte[] data, int startIndex)
{
// 对于JPEG查找EOF标记 (FF D9)
if (data.Length >= startIndex + 2 && data[startIndex] == 0xFF && data[startIndex + 1] == 0xD8)
{
for (int i = startIndex + 2; i < data.Length - 1; i++)
{
if (data[i] == 0xFF && data[i + 1] == 0xD9)
{
return i;
}
}
}
return -1;
}
/// <summary>
/// 检查数据是否为HTTP响应
/// </summary>
/// <param name="data">待检查的数据</param>
/// <returns>是否为HTTP响应</returns>
private bool IsHttpResponse(byte[] data)
{
if (data == null || data.Length < 4)
return false;
// 检查HTTP响应状态行的特征
// HTTP/1.x 状态码 描述
string responseStart = System.Text.Encoding.ASCII.GetString(data, 0, Math.Min(10, data.Length));
return responseStart.StartsWith("HTTP/");
}
/// <summary>
/// 查找图像数据的开始位置跳过HTTP头部
/// </summary>
/// <param name="data">数据缓冲区</param>
/// <returns>图像数据开始位置索引,-1表示未找到</returns>
private int FindImageStartPosition(byte[] data)
{
if (data == null || data.Length < 8)
return -1;
// 首先检查是否以图像头开始
if (IsValidImageData(data))
return 0;
// 否则尝试查找HTTP响应中的图像数据
// 查找空行后的位置HTTP头部结束
for (int i = 0; i < data.Length - 3; i++)
{
if (data[i] == 0x0D && data[i + 1] == 0x0A && data[i + 2] == 0x0D && data[i + 3] == 0x0A)
{
// 找到HTTP头部结束位置检查后续数据是否为图像
int startPos = i + 4;
if (startPos < data.Length - 7)
{
// 检查是否为JPEG
if (data[startPos] == 0xFF && data[startPos + 1] == 0xD8)
return startPos;
// 检查是否为PNG
if (data[startPos] == 0x89 && data[startPos + 1] == 0x50 && data[startPos + 2] == 0x4E && data[startPos + 3] == 0x47)
return startPos;
}
}
}
return -1;
}
/// <summary>
/// 从HTTP响应中提取Content-Type
/// </summary>
/// <param name="data">HTTP响应数据</param>
/// <returns>Content-Type字符串</returns>
private string ExtractContentType(byte[] data)
{
string headerText = System.Text.Encoding.ASCII.GetString(data, 0, Math.Min(1024, data.Length));
int contentTypeIndex = headerText.IndexOf("Content-Type:", StringComparison.OrdinalIgnoreCase);
if (contentTypeIndex >= 0)
{
contentTypeIndex += "Content-Type:".Length;
int endIndex = headerText.IndexOf("\r\n", contentTypeIndex);
if (endIndex >= 0)
{
return headerText.Substring(contentTypeIndex, endIndex - contentTypeIndex).Trim();
}
}
return string.Empty;
}
/// <summary>
/// 从Content-Type中提取multipart boundary
/// </summary>
/// <param name="contentType">Content-Type字符串</param>
/// <returns>boundary字符串</returns>
private string ExtractBoundary(string contentType)
{
int boundaryIndex = contentType.IndexOf("boundary=", StringComparison.OrdinalIgnoreCase);
if (boundaryIndex >= 0)
{
boundaryIndex += "boundary=".Length;
// 处理可能包含引号的情况
char quoteChar = contentType[boundaryIndex];
int startIndex = (quoteChar == '"' || quoteChar == '\'') ? boundaryIndex + 1 : boundaryIndex;
int endIndex = -1;
if (startIndex > boundaryIndex)
{
// 寻找匹配的引号
endIndex = contentType.IndexOf(quoteChar, startIndex);
}
else
{
// 寻找分号或行尾
endIndex = contentType.IndexOf(';', startIndex);
if (endIndex < 0)
{
endIndex = contentType.Length;
}
}
if (endIndex >= 0)
{
return contentType.Substring(startIndex, endIndex - startIndex).Trim();
}
}
return string.Empty;
}
/// <summary>
/// 处理multipart格式的图像数据
/// </summary>
/// <param name="buffer">数据缓冲区</param>
/// <param name="boundary">multipart boundary</param>
/// <param name="imageDataBuffer">原始图像数据缓冲区</param>
/// <returns>处理到的位置索引,-1表示未找到完整的图像块</returns>
private int ProcessMultipartImageData(byte[] buffer, string boundary, MemoryStream imageDataBuffer)
{
byte[] boundaryBytes = System.Text.Encoding.ASCII.GetBytes(boundary);
int startPos = 0;
// 查找第一个boundary
while (startPos < buffer.Length - boundaryBytes.Length)
{
bool found = true;
for (int i = 0; i < boundaryBytes.Length; i++)
{
if (buffer[startPos + i] != boundaryBytes[i])
{
found = false;
break;
}
}
if (found)
{
// 找到第一个boundary继续查找下一个
int nextBoundaryPos = startPos + boundaryBytes.Length;
while (nextBoundaryPos < buffer.Length - boundaryBytes.Length)
{
bool nextFound = true;
for (int i = 0; i < boundaryBytes.Length; i++)
{
if (buffer[nextBoundaryPos + i] != boundaryBytes[i])
{
nextFound = false;
break;
}
}
if (nextFound)
{
// 找到两个boundary之间的数据提取HTTP头和图像数据
int headerEndPos = -1;
for (int i = startPos + boundaryBytes.Length; i < nextBoundaryPos - 3; i++)
{
if (buffer[i] == 0x0D && buffer[i+1] == 0x0A && buffer[i+2] == 0x0D && buffer[i+3] == 0x0A)
{
headerEndPos = i + 4;
break;
}
}
if (headerEndPos > 0)
{
// 提取图像数据
int imageLength = nextBoundaryPos - headerEndPos;
byte[] imageBytes = new byte[imageLength];
Array.Copy(buffer, headerEndPos, imageBytes, 0, imageLength);
// 验证并处理图像数据
if (IsValidImageData(imageBytes))
{
try
{
using (MemoryStream ms = new MemoryStream(imageBytes))
{
ms.Write(new byte[0], 0, 0);
Image boundaryImage = Image.FromStream(ms);
using (MemoryStream imageMs = new MemoryStream())
{
boundaryImage.Save(imageMs, boundaryImage.RawFormat);
OnImageReceived(new ImageReceivedEventArgs(imageMs.ToArray(), _currentImageMode));
}
}
return nextBoundaryPos;
}
catch (Exception ex)
{
Console.WriteLine($"处理multipart图像失败: {ex.Message}");
}
}
}
return nextBoundaryPos;
}
nextBoundaryPos++;
}
break;
}
startPos++;
}
return -1;
}
#endregion
#region
/// <summary>
/// 设置视频模式
/// </summary>
/// <param name="mode">图像模式</param>
public void SetImageMode(ImageMode mode)
{
CurrentImageMode = mode;
}
/// <summary>
/// 设置视频模式
/// </summary>
/// <param name="videoMode">视频模式枚举</param>
public void SetVideoMode(VideoMode videoMode)
{
// 调用SDK设置视频模式
SendVideoModeCommand(videoMode);
}
/// <summary>
/// 获取当前视频模式
/// </summary>
/// <returns>当前视频模式枚举</returns>
public VideoMode GetCurrentVideoMode()
{
lock (_sdkOperationLock)
{
// 验证连接状态
if (_connectionStatus != ConnectionStatus.Connected || _a8Sdk == null)
{
throw new InvalidOperationException("设备未连接");
}
// 从设备同步视频模式到内部状态
SyncVideoModeFromDevice();
// 返回内部状态中的视频模式值
return _currentVideoMode;
}
}
/// <summary>
/// 发送视频模式变更命令到设备
/// </summary>
/// <param name="videoMode">视频模式枚举值</param>
private void SendVideoModeCommand(VideoMode videoMode)
{
lock (_sdkOperationLock)
{
const int maxRetries = 3;
const int retryDelayMs = 100;
for (int attempt = 0; attempt < maxRetries; attempt++)
{
try
{
// 检查对象状态和连接状态
if (_a8Sdk == null || _connectionStatus != ConnectionStatus.Connected)
{
Log($"视频模式切换失败: {(attempt > 0 ? "" : "")}SDK实例为空或设备未连接");
Thread.Sleep(retryDelayMs);
continue;
}
// 调用SDK设置视频模式转换为int
_a8Sdk.SetVideoMode((int)videoMode);
// 短暂延迟,确保设置生效
Thread.Sleep(50);
// 简单验证:尝试读取视频模式确认设置成功
int currentModeInt = _a8Sdk.GetVideoMode();
if (Enum.IsDefined(typeof(VideoMode), currentModeInt))
{
VideoMode currentMode = (VideoMode)currentModeInt;
if (currentMode == videoMode)
{
// 更新内部状态
_currentVideoMode = videoMode;
Log($"视频模式切换成功,当前模式: {videoMode}");
return;
}
else
{
Log($"视频模式验证失败,期望: {videoMode},实际: {currentMode}");
}
}
else
{
Log($"视频模式验证失败,获取到无效的视频模式值: {currentModeInt}");
}
}
catch (Exception ex)
{
Log($"视频模式切换异常 (尝试 {attempt + 1}/{maxRetries}): {ex.Message}");
}
// 重试前短暂延迟
if (attempt < maxRetries - 1)
{
Thread.Sleep(retryDelayMs);
}
}
throw new Exception($"视频模式切换失败,已重试 {maxRetries} 次");
}
}
// 用于保护SDK操作的线程锁
private readonly object _sdkOperationLock = new object();
// 连续心跳失败计数,用于实现容错机制
private int _consecutiveHeartbeatFailures = 0;
// 连续心跳失败阈值,超过此值才认为连接真正断开
private const int HEARTBEAT_FAILURE_THRESHOLD = 3;
/// <summary>
/// 获取或设置当前色彩模式
/// </summary>
public PaletteType CurrentPaletteType
{
get { return _currentPaletteType; }
set { _currentPaletteType = value; }
}
/// <summary>
/// 当前温度模式
/// </summary>
public TemperatureMode CurrentTemperatureMode { get; set; } = TemperatureMode.Celsius;
/// <summary>
/// 是否正在接收温度数据
/// </summary>
public bool IsReceivingTemperatureData
{
get
{
lock (_lockObject)
{
return _isReceivingTemperatureData;
}
}
}
/// <summary>
/// 获取或设置当前视频模式
/// </summary>
public VideoMode CurrentVideoMode
{
get { return _currentVideoMode; }
set { _currentVideoMode = value; }
}
/// <summary>
/// 从设备同步视频模式到内部状态
/// </summary>
public void SyncVideoModeFromDevice()
{
try
{
// 确保设备已连接且SDK实例有效
if (_connectionStatus == ConnectionStatus.Connected && _a8Sdk != null)
{
// 获取当前设备的视频模式值
int currentValue = _a8Sdk.GetVideoMode();
Log($"从设备读取的视频模式值: {currentValue}");
// 验证视频模式值是否在有效范围内并转换为枚举
if (Enum.IsDefined(typeof(VideoMode), currentValue))
{
VideoMode actualMode = (VideoMode)currentValue;
_currentVideoMode = actualMode;
Log($"已更新内部状态为设备实际值: {actualMode} (值: {currentValue})");
}
else
{
Log($"警告:设备返回的视频模式值 {currentValue} 不在有效范围内(0-6),使用默认值");
}
}
else
{
Log($"同步视频模式失败设备未连接或SDK未初始化");
}
}
catch (Exception ex)
{
Log($"同步视频模式时发生异常: {ex.Message}");
throw; // 重新抛出异常,让调用方知道发生了错误
}
}
/// <summary>
/// 从设备同步色彩模式到内部状态
/// </summary>
public void SyncPaletteTypeFromDevice()
{
try
{
// 使用SDK操作锁保护对_a8Sdk的访问
lock (_sdkOperationLock)
{
// 确保设备已连接且SDK实例有效
if (_connectionStatus == ConnectionStatus.Connected && _a8Sdk != null)
{
try
{
// 获取当前设备的色彩模式值
int currentValue = _a8Sdk.GetColorPlate();
Log($"从设备读取的色彩模式值: {currentValue}");
// 尝试将读取到的值转换为PaletteType枚举并更新内部状态
if (Enum.IsDefined(typeof(PaletteType), currentValue))
{
PaletteType actualPalette = (PaletteType)currentValue;
_currentPaletteType = actualPalette;
Log($"已更新内部状态为设备实际值: {actualPalette}");
}
else
{
Log($"警告:设备返回的色彩模式值 {currentValue} 不在枚举定义范围内,使用默认值");
}
}
catch (Exception ex)
{
Log($"从设备读取色彩模式时出错: {ex.Message}");
// 尝试重新创建SDK实例
Log("尝试重新创建SDK实例...");
try
{
_a8Sdk = new A8SDK(_deviceIp);
Log("SDK实例已重新创建");
}
catch (Exception recreateEx)
{
Log($"重新创建SDK实例失败: {recreateEx.Message}");
}
throw;
}
}
else
{
Log($"同步色彩模式失败设备未连接或SDK未初始化");
}
}
}
catch (Exception ex)
{
Log($"同步色彩模式时发生异常: {ex.Message}");
throw; // 重新抛出异常,让调用方知道发生了错误
}
}
/// <summary>
/// 设置色彩模式
/// </summary>
/// <param name="paletteType">色彩模式</param>
/// <returns>设置是否成功</returns>
public bool SetPaletteType(PaletteType paletteType)
{
// 添加线程锁保护防止多线程同时操作SDK
lock (_sdkOperationLock)
{
// 暂停心跳检测,避免设置过程中发生冲突
bool wasHeartbeatRunning = _heartbeatTimer != null;
if (wasHeartbeatRunning)
{
_heartbeatTimer.Change(Timeout.Infinite, Timeout.Infinite);
Log("设置色彩模式前暂停心跳检测");
}
try
{
// 最大重试次数
const int maxRetries = 3;
// 重试间隔(毫秒)
const int retryDelayMs = 100;
// 确保SDK实例存在如果不存在则尝试创建
if (_a8Sdk == null)
{
Log("SDK实例为空尝试创建新实例...");
try
{
_a8Sdk = new A8SDK(_deviceIp);
Log("SDK实例创建成功");
}
catch (Exception ex)
{
Log($"创建SDK实例失败: {ex.Message}");
return false;
}
}
// 先获取原始值,只读取一次,避免嵌套调用
int originalValue = -1;
try
{
if (_connectionStatus == ConnectionStatus.Connected)
{
originalValue = _a8Sdk.Color_plate;
Log($"成功读取当前色彩模式值: {originalValue}");
}
}
catch (Exception ex)
{
Log($"获取当前色彩模式值时出错: {ex.Message}");
// 尝试重新创建SDK实例
Log("尝试重新创建SDK实例...");
try
{
_a8Sdk = new A8SDK(_deviceIp);
Log("SDK实例已重新创建");
}
catch (Exception recreateEx)
{
Log($"重新创建SDK实例失败: {recreateEx.Message}");
}
// 即使获取失败,仍尝试设置新值
}
// 将PaletteType枚举转换为int类型
int paletteValue = (int)paletteType;
// 检查新的色彩模式是否与当前值相同,如果相同则不需要设置
if (originalValue == paletteValue)
{
Log($"当前色彩模式已为目标值,无需设置");
return true;
}
for (int attempt = 0; attempt < maxRetries; attempt++)
{
try
{
// 再次确保SDK实例存在
if (_a8Sdk == null)
{
Log("SDK实例为空尝试重新创建...");
try
{
_a8Sdk = new A8SDK(_deviceIp);
Log("SDK实例重新创建成功");
}
catch (Exception ex)
{
Log($"重新创建SDK实例失败: {ex.Message}");
continue; // 继续下一次尝试
}
}
// 检查连接状态
if (_connectionStatus != ConnectionStatus.Connected)
{
Log($"色彩模式设置失败: {(attempt > 0 ? "" : "")}SDK实例为空或设备未连接");
Thread.Sleep(retryDelayMs);
continue;
}
// 已在循环外部计算paletteValue避免重复计算
// 直接设置色彩模式,不再在每次尝试中读取原始值
bool setSuccess = false;
try
{
// 直接调用SetColorPlate方法设置色彩模式
setSuccess = _a8Sdk.SetColorPlate(paletteValue);
Log($"SetColorPlate返回结果: {setSuccess}");
// 短暂延迟,确保设置生效
Thread.Sleep(50);
// 再次读取当前值进行验证不依赖SetColorPlate的返回值
int currentValue = _a8Sdk.GetColorPlate();
Log($"验证读取到的当前色彩模式值: {currentValue},目标值: {paletteValue}");
setSuccess = (currentValue == paletteValue);
// 如果设置成功,更新内部状态
if (setSuccess)
{
_currentPaletteType = paletteType;
Log($"内部状态_currentPaletteType已更新为: {paletteType}");
}
}
catch (Exception ex)
{
Log($"色彩模式设置异常: {ex.Message}");
}
// 基于实际结果记录日志和返回
if (setSuccess)
{
Log($"色彩模式设置成功: {paletteType} (值: {paletteValue})");
_currentPaletteType = paletteType;
return true;
}
else
{
Log($"色彩模式设置失败: 实际设置未生效 (尝试 {attempt + 1}/{maxRetries})");
if (attempt < maxRetries - 1)
{
Thread.Sleep(retryDelayMs);
continue;
}
else
{
// 最后一次尝试失败,尝试恢复原始值
if (originalValue != -1 && _a8Sdk != null && _connectionStatus == ConnectionStatus.Connected)
{
try
{
Log($"尝试恢复色彩模式为原始值: {originalValue}");
_a8Sdk.Color_plate = originalValue;
}
catch { }
}
}
}
// 移除图像接收重启逻辑,因为色彩模式不影响图像接收
// 仅保留短暂延迟确保设置生效
Thread.Sleep(200); // 给设备处理时间
// return true; // 已在验证成功后返回
}
catch (Exception ex)
{
Log($"设置色彩模式异常 (尝试 {attempt + 1}/{maxRetries}): {ex.Message}");
// 简化SDK状态检查避免触发额外的UDP命令
Log($"SDK状态监控: 色彩模式设置操作失败");
// 重试前等待
Thread.Sleep(retryDelayMs);
}
}
// 所有尝试都失败
Log($"色彩模式设置最终失败: 已尝试{maxRetries}次");
// 最后一次尝试失败后,仅在有原始值的情况下尝试恢复
if (originalValue >= 0 && _a8Sdk != null && _connectionStatus == ConnectionStatus.Connected)
{
try
{
_a8Sdk.Color_plate = originalValue;
Log($"已恢复原始色彩模式值: {originalValue}");
}
catch (Exception restoreEx)
{
Log($"恢复原始色彩模式失败: {restoreEx.Message}");
}
}
return false;
}
finally
{
// 无论设置成功与否,都恢复心跳检测
if (wasHeartbeatRunning && _heartbeatTimer != null)
{
_heartbeatTimer.Change(_heartbeatInterval, _heartbeatInterval);
Log("设置色彩模式后恢复心跳检测");
}
}
}
}
/// <summary>
/// 开始接收图像已弃用使用HTTP方式替代
/// </summary>
public void StartReceiveImage()
{
Log("已弃用的StartReceiveImage方法自动切换到HTTP方式");
if (_connectionStatus != ConnectionStatus.Connected)
{
throw new InvalidOperationException("设备未连接,无法开始接收图像");
}
// 直接调用HTTP方式的图像接收
StartImageReceiving();
}
/// <summary>
/// 停止接收图像
/// </summary>
public void StopReceiveImage()
{
StopAllImageReceivers();
}
/// <summary>
/// 图像接收线程 (已弃用使用HTTP方式替代)
/// </summary>
private void ReceiveImageThread()
{
// 在设计模式下,跳过实际的图像接收
if (IsDesignMode)
{
Log("设计模式下跳过实际的图像接收");
return;
}
Console.WriteLine("警告: 已弃用的图像接收方式使用HTTP方式替代");
try
{
// 如果调用了这个方法自动切换到HTTP方式
if (!_stopRequested.WaitOne(0))
{
Console.WriteLine("自动切换到HTTP方式获取图像数据");
if (_imageReceiveThread == null || !_imageReceiveThread.IsAlive)
{
StartImageReceiving(); // 启动HTTP方式
}
return;
}
// 持续获取图像数据
while (!_stopRequested.WaitOne(0))
{
if (_a8Sdk != null)
{
try
{
// 使用A8SDK获取图像数据
byte[] imageData = _a8Sdk.GetImageData();
if (imageData != null && imageData.Length > 0 && IsValidImageData(imageData))
{
// 创建内存流
using (MemoryStream ms = new MemoryStream(imageData))
{
// 创建图像
using (Image image = Image.FromStream(ms))
{
// 将Image转换为byte[]并提供mode参数
using (MemoryStream imageMs = new MemoryStream())
{
Image clonedImage = image.Clone() as Image;
clonedImage.Save(imageMs, clonedImage.RawFormat);
OnImageReceived(new ImageReceivedEventArgs(imageMs.ToArray(), _currentImageMode));
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"SDK获取图像失败: {ex.Message}");
OnConnectionException(new ConnectionExceptionEventArgs(ex, "SDK获取图像数据异常"));
// 检查连接状态
UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK获取图像失败设备可能已断开", ex);
// 如果启用了自动重连,开始重连
if (_isAutoReconnectEnabled)
{
StartAutoReconnect();
}
break; // 退出图像接收线程
}
}
// 短暂休眠,控制帧率
Thread.Sleep(100);
}
}
catch (Exception ex)
{
Console.WriteLine($"图像接收线程异常: {ex.Message}");
OnConnectionException(new ConnectionExceptionEventArgs(ex, "图像接收线程异常"));
}
}
/// <summary>
/// 验证图像数据是否有效
/// </summary>
/// <param name="data">图像数据</param>
/// <returns>是否为有效图像数据</returns>
private bool IsValidImageData(byte[] data)
{
if (data == null || data.Length < 8)
{
return false;
}
// 检查JPEG文件头FF D8
if (data[0] == 0xFF && data[1] == 0xD8)
{
return true;
}
// 检查PNG文件头
byte[] pngHeader = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
if (data.Length >= pngHeader.Length)
{
bool isPng = true;
for (int i = 0; i < pngHeader.Length; i++)
{
if (data[i] != pngHeader[i])
{
isPng = false;
break;
}
}
if (isPng)
{
return true;
}
}
// 检查BMP文件头
if (data[0] == 0x42 && data[1] == 0x4D) // "BM"
{
return true;
}
return false;
}
#endregion
#region
/// <summary>
/// 搜索设备(同步版本)
/// </summary>
public List<int> SearchDevices()
{
try
{
if (!_isInitialized && !Initialize())
{
Console.WriteLine("设备管理器未初始化,无法搜索设备");
return new List<int>();
}
List<int> foundDevices = new List<int>();
_deviceIds.Clear();
_deviceList.Clear();
// 使用A8SDK的静态方法进行设备搜索
List<string> deviceIds = A8SDK.SearchDevices();
Console.WriteLine($"搜索到 {deviceIds.Count} 个设备");
// 将SDK返回的设备ID和IP转换为我们的格式
int deviceId = 1;
foreach (string id in deviceIds)
{
try
{
// 假设ID字符串中包含IP信息格式可能是 "设备ID|IP地址"
string[] parts = id.Split('|');
string ipAddress = parts.Length > 1 ? parts[1] : "未知IP";
// 创建设备信息对象
DeviceInfo deviceInfo = new DeviceInfo
{
DeviceID = deviceId,
IPAddress = ipAddress,
Model = "A8", // 假设是A8型号可以根据实际情况获取
SerialNumber = parts[0] // 使用原始ID作为序列号
};
_deviceIds.Add(id);
_deviceList.Add(deviceInfo);
foundDevices.Add(deviceId);
Console.WriteLine($"发现设备: ID={deviceId}, IP={ipAddress}, 序列号={parts[0]}");
deviceId++;
}
catch (Exception ex)
{
Console.WriteLine($"解析设备信息失败: {ex.Message}, 原始ID: {id}");
// 继续处理下一个设备
}
}
// 如果通过ID连接模式且目标设备ID已设置尝试自动连接
if (_targetDeviceId > 0 && _deviceList.Count > 0)
{
var targetDevice = _deviceList.FirstOrDefault(d => d.DeviceID == _targetDeviceId);
if (targetDevice != null)
{
Console.WriteLine($"找到目标设备ID={_targetDeviceId}IP={targetDevice.IPAddress},准备连接");
_deviceIp = targetDevice.IPAddress;
ConnectDevice();
}
}
return foundDevices;
}
catch (Exception ex)
{
Console.WriteLine($"搜索设备失败: {ex.Message}");
OnConnectionException(new ConnectionExceptionEventArgs(ex, "搜索设备失败"));
return new List<int>();
}
}
/// <summary>
/// 搜索设备(异步版本)
/// </summary>
public Task<List<int>> SearchDevicesAsync()
{
return Task.Factory.StartNew(() =>
{
try
{
if (!_isInitialized && !Initialize())
{
Console.WriteLine("设备管理器未初始化,无法搜索设备");
return new List<int>();
}
List<int> foundDevices = new List<int>();
_deviceIds.Clear();
_deviceList.Clear();
// 使用A8SDK的静态方法进行设备搜索
List<string> deviceIds = A8SDK.SearchDevices();
Console.WriteLine($"搜索到 {deviceIds.Count} 个设备");
// 将SDK返回的设备ID和IP转换为我们的格式
int deviceId = 1;
foreach (string id in deviceIds)
{
try
{
// 假设ID字符串中包含IP信息格式可能是 "设备ID|IP地址"
string[] parts = id.Split('|');
string ipAddress = parts.Length > 1 ? parts[1] : "未知IP";
// 创建设备信息对象
DeviceInfo deviceInfo = new DeviceInfo
{
DeviceID = deviceId,
IPAddress = ipAddress,
Model = "A8", // 假设是A8型号可以根据实际情况获取
SerialNumber = parts[0] // 使用原始ID作为序列号
};
_deviceIds.Add(id);
_deviceList.Add(deviceInfo);
foundDevices.Add(deviceId);
Console.WriteLine($"发现设备: ID={deviceId}, IP={ipAddress}, 序列号={parts[0]}");
deviceId++;
}
catch (Exception ex)
{
Console.WriteLine($"解析设备信息失败: {ex.Message}, 原始ID: {id}");
// 继续处理下一个设备
}
}
// 如果通过ID连接模式且目标设备ID已设置尝试自动连接
if (_targetDeviceId > 0 && _deviceList.Count > 0)
{
var targetDevice = _deviceList.FirstOrDefault(d => d.DeviceID == _targetDeviceId);
if (targetDevice != null)
{
Console.WriteLine($"找到目标设备ID={_targetDeviceId}IP={targetDevice.IPAddress},准备连接");
_deviceIp = targetDevice.IPAddress;
ConnectDevice();
}
}
return foundDevices;
}
catch (Exception ex)
{
Console.WriteLine($"搜索设备失败: {ex.Message}");
OnConnectionException(new ConnectionExceptionEventArgs(ex, "搜索设备失败"));
return new List<int>();
}
});
}
// BroadcastSearch方法已被A8SDK.SearchDevices替代
// ScanLocalNetwork方法已被A8SDK.SearchDevices替代
/// <summary>
/// 获取子网掩码
/// </summary>
/// <param name="mask">IP掩码</param>
/// <returns>子网掩码</returns>
private IPAddress GetSubnetMask(IPAddress mask)
{
return mask;
}
/// <summary>
/// 连接设备
/// </summary>
/// <returns>是否连接成功</returns>
public bool ConnectDevice()
{
// 默认使用ID=1连接设备
ConnectDevice(1);
return _connectionStatus == ConnectionStatus.Connected;
}
public void ConnectDevice(int deviceId)
{
try
{
// 在设计模式下,跳过实际连接,不设置真实的连接状态
if (IsDesignMode)
{
Log("设计模式下跳过实际设备连接");
// 不设置真实的连接状态,避免触发图像接收
_currentDeviceId = deviceId;
_isConnected = false;
_connectionStatus = ConnectionStatus.Disconnected;
UpdateConnectionStatus(ConnectionStatus.Disconnected, "设计模式:跳过设备连接");
return;
}
// 取消之前的连接操作
if (_connectCancellationTokenSource != null)
{
_connectCancellationTokenSource.Cancel();
_connectCancellationTokenSource.Dispose();
}
// 更新状态为连接中
UpdateConnectionStatus(ConnectionStatus.Connecting, "开始连接设备...");
_connectCancellationTokenSource = new CancellationTokenSource();
CancellationToken token = _connectCancellationTokenSource.Token;
// 使用Timer实现超时取消
System.Threading.Timer timeoutTimer = null;
timeoutTimer = new System.Threading.Timer((state) =>
{
if (_connectCancellationTokenSource != null && !_connectCancellationTokenSource.IsCancellationRequested)
{
_connectCancellationTokenSource.Cancel();
Console.WriteLine($"连接超时,自动取消连接操作");
}
timeoutTimer.Dispose();
}, null, _connectTimeout, Timeout.Infinite);
try
{
bool result = false;
if (!token.IsCancellationRequested)
{
try
{
// 确保已初始化
if (!_isInitialized && !Initialize())
{
UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备管理器初始化失败");
return;
}
// 检查是否正在重连中,如果是则不执行连接
if (_isReconnecting != 0)
{
Console.WriteLine("检测到正在重连过程中,暂停设备连接");
// 保持当前状态不切换到Disconnected避免状态闪烁
// 只输出提示信息,不更改状态
if (_connectionStatus != ConnectionStatus.Reconnecting)
{
UpdateConnectionStatus(_connectionStatus, "正在进行自动重连,请稍后再试");
}
return;
}
// 设置连接中标志
_isConnecting = true;
// 停止任何现有的重连定时器
StopAutoReconnect();
// 停止任何正在进行的连接检查
StopAllImageReceivers();
// 使用ManualResetEvent实现连接超时机制
var connectionCompleteEvent = new ManualResetEvent(false);
bool timeoutOccurred = false;
// 真实连接过程
System.Threading.ThreadPool.QueueUserWorkItem((state) =>
{
try
{
if (!token.IsCancellationRequested)
{
// 安全释放之前的SDK实例避免内存访问冲突
if (_a8Sdk != null)
{
Console.WriteLine("释放之前的SDK实例");
_a8Sdk = null;
}
// 使用真实SDK连接设备
Console.WriteLine($"正在使用SDK连接设备 {deviceId}IP地址: {_deviceIp},端口: {_devicePort}");
// 先检测IP可达性避免不必要的SDK初始化
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} 不可达,请检查网络连接");
}
// 创建SDK实例
_a8Sdk = new A8SDK(_deviceIp);
Console.WriteLine("SDK实例创建完成");
// 注意设备使用UDP协议通信不需要建立TCP连接
// 通过心跳检测来验证设备可达性
Console.WriteLine("尝试发送UDP心跳包进行设备验证...(尝试 1/3)");
int heartbeatResult = _a8Sdk.Heartbeat();
// 尝试多次心跳检测,增加连接成功率
int retryCount = 1;
while (heartbeatResult <= 0 && retryCount < 3)
{
retryCount++;
Console.WriteLine($"UDP心跳检测失败等待500ms后重试...(尝试 {retryCount}/3)");
Thread.Sleep(500);
heartbeatResult = _a8Sdk.Heartbeat();
}
if (heartbeatResult <= 0)
{
Console.WriteLine("多次UDP心跳检测均失败");
// 安全释放SDK实例
if (_a8Sdk != null)
{
_a8Sdk = null;
}
throw new Exception("UDP心跳检测失败设备可能未响应或端口配置错误应为18890");
}
Console.WriteLine("UDP心跳检测成功设备连接有效");
// 连接成功
result = true;
// 重置连接中标志
_isConnecting = false;
// 安全地设置设备ID
try
{
_currentDeviceId = deviceId;
}
catch (Exception ex)
{
Console.WriteLine($"设置设备ID异常: {ex.Message}");
_currentDeviceId = -1; // 设置为无效ID
}
Console.WriteLine($"设备 {deviceId} 连接成功");
}
}
catch (Exception ex)
{
Console.WriteLine($"连接设备异常: {ex.Message}");
// 确保异常时释放资源
if (_a8Sdk != null)
{
_a8Sdk = null;
}
// 重置连接中标志
_isConnecting = false;
result = false;
}
finally
{
connectionCompleteEvent.Set();
}
});
// 等待连接完成或超时
if (!connectionCompleteEvent.WaitOne(_connectTimeout))
{
timeoutOccurred = true;
Console.WriteLine($"设备 {deviceId} 连接超时");
result = false;
_connectCancellationTokenSource.Cancel();
}
// 如果超时,记录超时信息
if (timeoutOccurred)
{
UpdateConnectionStatus(ConnectionStatus.Disconnected,
$"设备 {deviceId} 连接超时({_connectTimeout}ms");
return;
}
}
catch (Exception ex)
{
Console.WriteLine($"连接设备异常: {ex.Message}");
result = false;
}
}
if (result)
{
_isConnected = true;
// 启动心跳检测和连接检查
StartHeartbeat();
StartConnectionCheck();
UpdateConnectionStatus(ConnectionStatus.Connected, "设备连接成功");
// 连接成功后同步色彩模式和视频模式
try
{
SyncPaletteTypeFromDevice();
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 同步色彩模式失败: {ex.Message}");
};
// 同步视频模式
try
{
SyncVideoModeFromDevice();
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 同步视频模式失败: {ex.Message}");
};
}
else if (!token.IsCancellationRequested)
{
UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK连接失败心跳检测未通过");
_a8Sdk = null;
}
}
finally
{
// 确保即使发生异常,也停止定时器
if (timeoutTimer != null)
{
timeoutTimer.Dispose();
}
}
}
catch (Exception ex)
{
Console.WriteLine($"连接设备失败: {ex.Message}");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接设备时发生异常", ex);
OnConnectionException(new ConnectionExceptionEventArgs(ex, "连接设备失败"));
}
// 如果连接失败且启用了自动重连,开始重连
if (_connectionStatus != ConnectionStatus.Connected && _isAutoReconnectEnabled)
{
StartAutoReconnect();
}
}
/// <summary>
/// 连接到指定设备(异步版本)
/// </summary>
public Task<bool> ConnectAsync()
{
var taskCompletionSource = new TaskCompletionSource<bool>();
try
{
ConnectDevice();
taskCompletionSource.SetResult(_connectionStatus == ConnectionStatus.Connected);
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
return taskCompletionSource.Task;
}
/// <summary>
/// 根据设备ID连接设备
/// </summary>
/// <param name="deviceId">设备ID</param>
// 该方法已在文件上方定义,删除重复实现
/// <summary>
/// 断开设备连接兼容Form1
/// </summary>
public void DisconnectDevice()
{
Disconnect();
}
// TestTcpConnection方法不再需要由A8SDK内部处理连接测试
/// <summary>
/// 发送连接命令
/// </summary>
/// <returns>是否发送成功</returns>
private bool SendConnectCommand()
{
try
{
// 使用HTTP GET请求作为连接命令
string url = $"http://{_deviceIp}:{_devicePort}/connect";
WebRequest request = WebRequest.Create(url);
request.Timeout = 3000; // 3秒超时
using (WebResponse response = request.GetResponse())
{
using (Stream dataStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(dataStream, Encoding.ASCII))
{
string responseString = reader.ReadToEnd();
return responseString.Contains("CONNECTED");
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"发送连接命令失败: {ex.Message}");
// 即使连接命令失败,我们也继续,因为有些设备可能不需要额外的连接命令
return true;
}
}
/// <summary>
/// 断开连接
/// </summary>
public void Disconnect()
{
try
{
// 停止自动重连和连接检查
StopAutoReconnect();
StopConnectionCheck();
// 取消正在进行的连接操作
if (_connectCancellationTokenSource != null)
{
_connectCancellationTokenSource.Cancel();
_connectCancellationTokenSource.Dispose();
_connectCancellationTokenSource = null;
}
// 更新状态为断开连接中
if (_connectionStatus != ConnectionStatus.Disconnected)
{
UpdateConnectionStatus(ConnectionStatus.Disconnected, "正在断开连接...");
}
// 使用真实SDK进行设备断开操作
if (_a8Sdk != null && _currentDeviceId != -1)
{
try
{
// 可以通过发送特定命令或释放资源来实现断开连接
// 如果SDK没有直接的断开连接方法我们释放SDK实例
Console.WriteLine($"设备 {_currentDeviceId} 断开连接中...");
_a8Sdk = null;
Console.WriteLine($"设备 {_currentDeviceId} 断开连接成功");
}
catch (Exception disconnectEx)
{
Console.WriteLine($"设备断开连接过程中发生异常: {disconnectEx.Message}");
}
}
// 重置设备ID
_currentDeviceId = -1;
UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备已断开连接");
}
catch (Exception ex)
{
Console.WriteLine($"断开连接异常: {ex.Message}");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "断开连接时发生异常", ex);
OnConnectionException(new ConnectionExceptionEventArgs(
ex, "断开连接异常"));
}
}
/// <summary>
/// 开始自动重连
/// </summary>
// 重入保护标志
private volatile int _isReconnecting = 0;
// 连接进行中标志(用于防止重连期间再次触发连接)
private volatile bool _isConnecting = false;
public void StartAutoReconnect()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 开始执行");
// 在设计模式下,跳过实际的自动重连
if (IsDesignMode)
{
Log("设计模式下跳过实际的自动重连");
return;
}
// 在暂停检测模式下,跳过重连启动
if (IsDetectionPaused)
{
Log("暂停检测模式下跳过重连启动");
return;
}
// 检查对象是否已释放
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 对象已释放,取消重连");
return;
}
// 如果已经在连接状态,不需要启动重连
if (_connectionStatus == ConnectionStatus.Connected)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 设备已连接,无需启动重连");
return;
}
try
{
// 使用锁确保线程安全,避免创建多个重连定时器
lock (_lockObject)
{
// 再次检查状态,防止在获取锁期间状态已变更
if (_isDisposed || _connectionStatus == ConnectionStatus.Connected)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 状态已变更,取消重连启动");
return;
}
// 停止现有的重连定时器
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 停止现有重连定时器");
StopAutoReconnectInternal(); // 使用内部方法避免重复获取锁
// 使用Interlocked.Exchange安全创建新的重连定时器
System.Threading.Timer newTimer = new System.Threading.Timer(
ReconnectCallback,
null,
1000, // 延迟1秒后启动避免过于频繁
Timeout.Infinite);
// 确保原子性地替换定时器引用
System.Threading.Timer oldTimer = Interlocked.Exchange(ref _reconnectTimer, newTimer);
// 如果有旧定时器没有被StopAutoReconnectInternal释放确保这里也释放它
if (oldTimer != null && oldTimer != newTimer)
{
try
{
oldTimer.Change(Timeout.Infinite, Timeout.Infinite);
oldTimer.Dispose();
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 释放了未被清理的旧定时器");
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 清理旧定时器时发生异常: {ex.Message}");
}
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 成功创建并启动重连定时器");
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 创建重连定时器失败: {ex.Message},堆栈: {ex.StackTrace}");
// 触发连接异常事件
try
{
if (!_isDisposed)
{
OnConnectionException(new ConnectionExceptionEventArgs(ex, "启动自动重连失败"));
}
}
catch (Exception ex2)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 触发异常事件失败: {ex2.Message}");
}
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 执行完成");
}
/// <summary>
/// 内部停止重连方法,不获取锁,用于已在锁内的调用
/// </summary>
private void StopAutoReconnectInternal()
{
try
{
System.Threading.Timer timerToStop = Interlocked.Exchange(ref _reconnectTimer, null);
if (timerToStop != null)
{
try
{
timerToStop.Change(Timeout.Infinite, Timeout.Infinite);
timerToStop.Dispose();
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnectInternal() - 重连定时器已停止并释放");
}
catch (ObjectDisposedException)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnectInternal() - 重连定时器已被释放");
}
}
// 重置重连尝试次数
Interlocked.Exchange(ref _currentReconnectAttempt, 0);
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnectInternal() - 重连尝试次数已重置");
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnectInternal() - 异常: {ex.Message}");
// 确保即使异常,引用也被清空
Interlocked.Exchange(ref _reconnectTimer, null);
}
}
/// <summary>
/// 启动定期连接状态检查
/// </summary>
/// <param name="checkInterval">检查间隔(毫秒)</param>
public void StartPeriodicConnectionCheck(int checkInterval)
{
try
{
// 在设计模式下,跳过实际的定期连接检查
if (IsDesignMode)
{
Log("设计模式下跳过实际的定期连接检查");
return;
}
// 停止现有的检查
StopConnectionCheck();
if (checkInterval <= 0)
{
checkInterval = 5000; // 默认5秒
}
// 创建并启动新的检查定时器
_connectionCheckTimer = new System.Threading.Timer((state) =>
{
try
{
CheckConnectionValidity();
}
catch (Exception ex)
{
Console.WriteLine($"定期连接检查异常: {ex.Message}");
}
}, null, 0, checkInterval);
Console.WriteLine($"定期连接状态检查已启动,间隔: {checkInterval}ms");
}
catch (Exception ex)
{
Console.WriteLine($"启动定期连接检查失败: {ex.Message}");
OnConnectionException(new ConnectionExceptionEventArgs(ex, "启动连接检查失败"));
}
}
/// <summary>
/// 停止自动重连
/// </summary>
private void StopAutoReconnect()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnect() - 开始执行");
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnect() - 对象已释放,跳过操作");
return;
}
try
{
// 使用锁确保线程安全
lock (_lockObject)
{
// 调用内部方法执行实际停止操作
StopAutoReconnectInternal();
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnect() - 异常: {ex.Message}");
// 确保即使异常,引用也被清空
Interlocked.Exchange(ref _reconnectTimer, null);
}
finally
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnect() - 执行完成");
}
}
/// <summary>
/// 重连回调
/// </summary>
/// <param name="state">定时器状态</param>
private void ReconnectCallback(object state)
{
// 在设计模式下,跳过实际的重连操作
if (IsDesignMode)
{
Log("设计模式下跳过实际的重连操作");
return;
}
// 在暂停检测模式下,跳过重连操作
if (IsDetectionPaused)
{
Log("暂停检测模式下跳过实际的重连操作");
return;
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 开始执行");
// 使用Interlocked.Exchange实现原子操作检查防止重入
if (Interlocked.Exchange(ref _isReconnecting, 1) != 0)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 检测到重连回调正在执行,避免重入");
return;
}
try
{
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 对象已释放,跳过重连");
return;
}
// 检查是否已达到最大重连次数
int currentAttempts = Interlocked.CompareExchange(ref _currentReconnectAttempt, 0, 0);
if (currentAttempts >= _maxReconnectAttempts)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 已达到最大重连次数 ({_maxReconnectAttempts}),停止自动重连");
StopAutoReconnect();
UpdateConnectionStatus(ConnectionStatus.Disconnected, "已达到最大重连次数,请手动检查设备状态");
return;
}
// 改进的重连间隔递增策略,避免指数增长过快
int currentInterval = Math.Min(_reconnectInterval * (int)(1 + currentAttempts * 0.5), 30000); // 最多30秒
// 线程安全地递增重连尝试次数
Interlocked.Increment(ref _currentReconnectAttempt);
currentAttempts++;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 自动重连尝试 {currentAttempts}/{_maxReconnectAttempts},当前间隔: {currentInterval}ms");
// 在重试前先检查网络状态
if (!IsNetworkAvailable())
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 网络不可用,推迟重连尝试");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "网络连接不可用,请检查网络设置");
// 网络不可用时,调整为重试间隔较长
int networkDownInterval = Math.Min(_reconnectInterval * 3, 15000);
// 安全地更新定时器
System.Threading.Timer timerToUpdate = _reconnectTimer;
if (timerToUpdate != null)
{
try
{
timerToUpdate.Change(networkDownInterval, Timeout.Infinite);
}
catch (ObjectDisposedException)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 定时器已被释放,无法更新网络不可用状态的重连间隔");
}
}
return;
}
UpdateConnectionStatus(ConnectionStatus.Reconnecting,
$"尝试自动重连...");
bool connectionSuccessful = false;
// 仅用IP地址重连设备这样更直接更快
string deviceIp = null;
lock (_lockObject)
{
deviceIp = _deviceIp;
}
if (!string.IsNullOrEmpty(deviceIp))
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 用IP地址 {deviceIp} 重连设备");
try
{
// 检查IP是否可达使用改进的PingDevice方法
if (PingDevice(deviceIp))
{
// 使用Interlocked.Exchange确保线程安全地释放旧实例
A8SDK oldSdk = Interlocked.Exchange(ref _a8Sdk, null);
if (oldSdk != null)
{
try
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 安全释放旧SDK实例资源");
// 注意根据SDK文档如果有destroy方法应调用
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 释放旧SDK实例资源时发生异常: {ex.Message}");
}
}
// 创建新的SDK实例
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 创建新的SDK实例目标IP: {deviceIp}");
A8SDK newSdk = new A8SDK(deviceIp);
// 保存到字段前进行验证,避免无效实例
bool isConnected = false;
int maxHeartbeatRetries = 3; // 心跳检测最大重试次数
for (int retry = 0; retry < maxHeartbeatRetries; retry++)
{
try
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 尝试建立连接并发送心跳包...(尝试 {retry + 1}/{maxHeartbeatRetries})");
// 添加延时给SDK实例初始化一些时间
if (retry > 0)
{
int retryDelay = 500 + (retry - 1) * 300; // 递增延时
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 等待{retryDelay}ms后重试...");
Thread.Sleep(retryDelay);
}
// 发送心跳包验证连接
int heartbeatResult = newSdk.Heartbeat();
if (heartbeatResult > 0)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 心跳检测成功!");
isConnected = true;
break;
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 心跳检测失败,返回值: {heartbeatResult}");
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 心跳检测异常: {ex.Message},堆栈: {ex.StackTrace}");
}
}
if (isConnected)
{
// 连接成功,进行额外验证
try
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 进行额外连接验证...");
// 再次发送心跳包确保连接稳定
int finalResult = newSdk.Heartbeat();
if (finalResult > 0)
{
// 验证成功后安全地更新SDK实例
Interlocked.Exchange(ref _a8Sdk, newSdk);
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 使用IP地址 {deviceIp} 重连成功");
// 线程安全地更新设备ID
lock (_lockObject)
{
_currentDeviceId = 1; // 临时ID确保状态更新正确
}
UpdateConnectionStatus(ConnectionStatus.Connected, $"设备 {deviceIp} 连接成功");
// 重连成功后同步色彩模式和视频模式
try
{
SyncPaletteTypeFromDevice();
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 重连后同步色彩模式失败: {ex.Message}");
};
// 同步视频模式
try
{
SyncVideoModeFromDevice();
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 重连后同步视频模式失败: {ex.Message}");
};
StartConnectionCheck();
connectionSuccessful = true;
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 最终验证失败,返回值: {finalResult}");
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 连接验证异常: {ex.Message}");
}
}
// 如果连接不成功,释放资源
if (!connectionSuccessful)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 使用IP地址 {deviceIp} 重连失败,所有心跳尝试都未成功");
// 确保资源被释放
try { newSdk = null; } catch { }
}
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - IP地址 {deviceIp} 不可达,等待设备上线");
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 使用IP地址重连异常: {ex.Message},堆栈: {ex.StackTrace}");
}
}
// 如果IP地址连接失败再尝试使用设备ID连接保持兼容性
// 但如果已经在连接中,则跳过
if (!connectionSuccessful)
{
int currentDeviceId = -1;
bool isConnecting = false;
lock (_lockObject)
{
currentDeviceId = _currentDeviceId;
isConnecting = _isConnecting;
}
if (currentDeviceId != -1 && !isConnecting)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 尝试使用设备ID {currentDeviceId} 连接");
ConnectDevice(currentDeviceId);
// 线程安全地检查连接状态
ConnectionStatus status = ConnectionStatus.Disconnected;
lock (_lockObject)
{
status = _connectionStatus;
}
if (status == ConnectionStatus.Connected)
{
connectionSuccessful = true;
}
}
}
// 如果没有保存的设备ID但有搜索到的设备尝试连接第一个
// 但如果已经在连接中,则跳过
if (!connectionSuccessful)
{
bool isConnecting = false;
List<string> deviceIds = null;
lock (_lockObject)
{
isConnecting = _isConnecting;
if (_deviceIds != null)
{
deviceIds = new List<string>(_deviceIds);
}
}
if (deviceIds != null && deviceIds.Count > 0 && !isConnecting)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 尝试使用搜索到的设备列表中的第一个设备");
if (int.TryParse(deviceIds[0], out int deviceId))
{
ConnectDevice(deviceId);
}
// 线程安全地检查连接状态
ConnectionStatus status = ConnectionStatus.Disconnected;
lock (_lockObject)
{
status = _connectionStatus;
}
if (status == ConnectionStatus.Connected)
{
connectionSuccessful = true;
}
}
}
// 如果连接成功,停止重连定时器
if (connectionSuccessful)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 设备连接成功,停止自动重连");
StopAutoReconnect();
return;
}
// 当所有连接尝试都失败时明确更新状态为Disconnected
UpdateConnectionStatus(ConnectionStatus.Disconnected, "所有连接尝试失败");
// 改进的间隔递增策略,避免频繁重连导致的网络压力
int retryInterval = Math.Min(_reconnectInterval * (1 + currentAttempts / 5), 20000); // 最大20秒
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 调整重连间隔为 {retryInterval}ms");
// 安全地更新定时器
System.Threading.Timer currentTimer = _reconnectTimer;
if (currentTimer != null)
{
try
{
// 使用一次性调度而非重复间隔
currentTimer.Change(retryInterval, Timeout.Infinite);
}
catch (ObjectDisposedException)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 定时器已被释放,无法更新");
}
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 重连回调函数发生异常: {ex.Message},堆栈: {ex.StackTrace}");
// 确保定时器继续工作,防止重连机制中断
System.Threading.Timer currentTimer = _reconnectTimer;
if (currentTimer != null)
{
try
{
// 使用一次性调度而非重复间隔
currentTimer.Change(_reconnectInterval, Timeout.Infinite);
}
catch (ObjectDisposedException)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 定时器已被释放,无法恢复");
}
}
}
finally
{
// 无论如何都要重置重入标志,确保后续重连可以正常触发
Interlocked.Exchange(ref _isReconnecting, 0);
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 执行完成");
}
}
/// <summary>
/// 开始心跳检测
/// </summary>
private void StartHeartbeat()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 开始执行");
// 在设计模式下,跳过实际的心跳检测
if (IsDesignMode)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 设计模式下跳过实际的心跳检测");
return;
}
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 对象已释放,无法启动心跳检测");
return;
}
try
{
// 停止现有的心跳定时器
StopHeartbeat();
// 创建新的心跳定时器
System.Threading.Timer newTimer = new System.Threading.Timer(HeartbeatCallback, null, 0, _heartbeatInterval);
Interlocked.Exchange(ref _heartbeatTimer, newTimer);
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 心跳检测已启动,间隔: {_heartbeatInterval}ms");
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 创建心跳定时器异常: {ex.Message},堆栈: {ex.StackTrace}");
OnConnectionException(new ConnectionExceptionEventArgs(ex, "启动心跳检测失败"));
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 执行完成");
}
/// <summary>
/// 停止心跳检测
/// </summary>
private void StopHeartbeat()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 开始执行");
try
{
// 使用Interlocked.Exchange确保线程安全
System.Threading.Timer timerToDispose = Interlocked.Exchange(ref _heartbeatTimer, null);
if (timerToDispose != null)
{
try
{
// 立即停止定时器,避免回调执行
timerToDispose.Change(Timeout.Infinite, Timeout.Infinite);
// 安全释放定时器资源
timerToDispose.Dispose();
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 心跳检测已停止");
}
catch (ObjectDisposedException)
{
// 如果定时器已被释放,无需再处理
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 定时器已被释放");
}
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 心跳定时器不存在,无需停止");
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 停止心跳检测异常: {ex.Message},堆栈: {ex.StackTrace}");
// 确保即使异常,引用也被清空
Interlocked.Exchange(ref _heartbeatTimer, null);
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 执行完成");
}
/// <summary>
/// 心跳检测回调
/// </summary>
/// <param name="state">定时器状态</param>
private void HeartbeatCallback(object state)
{
// 在设计模式下,跳过实际的心跳检测
if (IsDesignMode)
{
Log("设计模式下跳过实际的心跳检测");
return;
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 开始执行");
// 检查对象是否已释放
if (_isDisposed)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 对象已释放,退出心跳检测");
return;
}
try
{
// 检查设备是否可达使用改进的PingDevice方法
bool deviceReachable = false;
int pingRetries = 2;
// 添加ping重试机制
for (int i = 0; i <= pingRetries; i++)
{
if (PingDevice(_deviceIp))
{
deviceReachable = true;
break;
}
if (i < pingRetries)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - Ping设备失败{300 * (i + 1)}ms后重试...");
Thread.Sleep(300 * (i + 1)); // 递增延时
}
}
if (!deviceReachable)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 心跳检测失败,设备不可达");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "心跳检测失败,设备不可达");
// 如果启用了自动重连,开始重连
if (_isAutoReconnectEnabled)
{
StartAutoReconnect();
}
return;
}
// 检查是否在最近收到过数据
bool recentlyReceivedData = _lastDataReceivedTime != DateTime.MinValue &&
(DateTime.Now - _lastDataReceivedTime).TotalMilliseconds < DataReceivedTimeout;
if (recentlyReceivedData)
{
TimeSpan timeSinceLastData = DateTime.Now - _lastDataReceivedTime;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 最近收到数据({timeSinceLastData.TotalMilliseconds:F0}ms前不进行心跳检测");
// 更新连接状态为正常
if (_connectionStatus != ConnectionStatus.Connected)
{
UpdateConnectionStatus(ConnectionStatus.Connected, "最近收到数据,连接正常");
}
return;
}
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 长时间未收到数据,执行心跳检测");
// 使用SDK的Heartbeat方法进行心跳检测添加重试机制
A8SDK currentSdk = _a8Sdk;
if (currentSdk != null)
{
bool heartbeatSuccessful = false;
int heartbeatRetries = 2; // 修改为2次重试总共3次尝试
for (int i = 0; i <= heartbeatRetries; i++)
{
try
{
int heartbeatResult = currentSdk.Heartbeat();
if (heartbeatResult > 0)
{
heartbeatSuccessful = true;
// 心跳成功,重置连续失败计数
_consecutiveHeartbeatFailures = 0;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 心跳检测成功 (第{i+1}次尝试),连续失败计数已重置");
// 定期更新连接状态,表明连接正常
if (_connectionStatus != ConnectionStatus.Connected)
{
UpdateConnectionStatus(ConnectionStatus.Connected, "设备连接已恢复");
}
break; // 一次成功就立即返回,不再重试
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - SDK心跳检测失败 (第{i+1}次尝试),返回值: {heartbeatResult}");
if (i < heartbeatRetries)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 300ms后重试心跳检测...");
Thread.Sleep(300);
}
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 心跳检测异常 (第{i+1}次尝试): {ex.Message},堆栈: {ex.StackTrace}");
if (i < heartbeatRetries)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 300ms后重试心跳检测...");
Thread.Sleep(300);
}
}
}
if (!heartbeatSuccessful)
{
// 心跳失败,增加连续失败计数
_consecutiveHeartbeatFailures++;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - SDK心跳检测失败连续失败计数: {_consecutiveHeartbeatFailures}/{HEARTBEAT_FAILURE_THRESHOLD}");
// 只有当连续失败次数超过阈值时,才断开连接
if (_consecutiveHeartbeatFailures >= HEARTBEAT_FAILURE_THRESHOLD)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 连续心跳失败次数超过阈值,确认连接已断开");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK心跳检测连续失败连接已断开");
// 如果启用了自动重连,开始重连
if (_isAutoReconnectEnabled)
{
StartAutoReconnect();
}
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 连续心跳失败次数未达阈值,暂时保留连接状态");
}
}
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - SDK实例不存在尝试重连");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK实例不存在");
if (_isAutoReconnectEnabled)
{
StartAutoReconnect();
}
}
}
catch (Exception ex)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 心跳检测时发生异常: {ex.Message},堆栈: {ex.StackTrace}");
// 触发连接异常事件
try
{
if (!_isDisposed)
{
OnConnectionException(new ConnectionExceptionEventArgs(ex, "心跳检测失败"));
}
}
catch (Exception ex2)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 触发异常事件失败: {ex2.Message}");
}
// 异常情况下也尝试重连
try
{
if (!_isDisposed && _isAutoReconnectEnabled)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 异常情况下触发自动重连");
StartAutoReconnect();
}
}
catch (Exception ex3)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 启动自动重连失败: {ex3.Message}");
}
}
}
#endregion
/// <summary>
/// 温度数据点结构体
/// </summary>
private struct TemperatureDataPoint
{
public int X { get; private set; }
public int Y { get; private set; }
public float Temperature { get; private set; }
public TemperatureDataPoint(int x, int y, float temperature)
{
X = x;
Y = y;
Temperature = temperature;
}
}
// 该方法已在文件上方定义,删除重复实现
/// <summary>
/// 停止定期连接状态检查兼容Form1
/// </summary>
public void StopPeriodicConnectionCheck()
{
StopConnectionCheck();
}
/// <summary>
/// 尝试ping设备IP地址
/// </summary>
/// <param name="ipAddress">设备IP地址</param>
/// <returns>是否ping通</returns>
// PingDevice方法已在文件上方定义删除重复实现
/// <summary>
/// 保存温度数据到CSV文件
/// </summary>
/// <param name="filePath">CSV文件路径</param>
/// <returns>是否保存成功</returns>
public bool SaveTemperatureDataToCsv(string filePath)
{
try
{
// 检查设备是否连接
if (!_isConnected || _a8Sdk == null)
{
Console.WriteLine("设备未连接,无法保存温度数据");
return false;
}
// 检查文件路径是否有效
if (string.IsNullOrEmpty(filePath))
{
Console.WriteLine("文件路径无效");
return false;
}
// 获取目录路径
string directoryPath = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directoryPath))
{
try
{
// 创建目录
Directory.CreateDirectory(directoryPath);
Console.WriteLine($"创建目录: {directoryPath}");
}
catch (Exception ex)
{
Console.WriteLine($"创建目录失败: {ex.Message}");
return false;
}
}
// 尝试从设备获取温度数据
List<TemperatureDataPoint> temperatureData = new List<TemperatureDataPoint>();
try
{
// 使用A8SDK的Get_all_temp方法获取所有温度数据
Console.WriteLine("正在获取设备温度数据...");
SharedStructures.ImageTemp imageTemp = _a8Sdk.Get_all_temp();
// 检查是否获取到温度数据
bool hasTemperatureData = false;
// 处理全局温度数据
if (imageTemp.globa.max_temp > -273.0f) // 检查是否有有效温度值(高于绝对零度)
{
Console.WriteLine($"全局温度数据: 最高={imageTemp.globa.max_temp}°C, 最低={imageTemp.globa.min_temp}°C");
// 添加全局温度点
temperatureData.Add(new TemperatureDataPoint(0, 0, imageTemp.globa.max_temp));
temperatureData.Add(new TemperatureDataPoint(0, 1, imageTemp.globa.min_temp));
// 添加最高温度点的坐标
temperatureData.Add(new TemperatureDataPoint(
imageTemp.globa.max_temp_x,
imageTemp.globa.max_temp_y,
imageTemp.globa.max_temp));
// 添加最低温度点的坐标
temperatureData.Add(new TemperatureDataPoint(
imageTemp.globa.min_temp_x,
imageTemp.globa.min_temp_y,
imageTemp.globa.min_temp));
hasTemperatureData = true;
}
// 处理区域温度数据
for (int i = 0; i < imageTemp.area.Length; i++)
{
if (imageTemp.area[i].enable == 1 && imageTemp.area[i].ave_temp > -273.0f)
{
Console.WriteLine($"区域 {i+1} 温度: {imageTemp.area[i].ave_temp}°C");
temperatureData.Add(new TemperatureDataPoint(i + 1, 0, imageTemp.area[i].ave_temp));
hasTemperatureData = true;
}
}
// 处理点温度数据
for (int i = 0; i < imageTemp.spot.Length; i++)
{
if (imageTemp.spot[i].enable == 1 && imageTemp.spot[i].temp > -273.0f)
{
Console.WriteLine($"点 {i+1} 温度: {imageTemp.spot[i].temp}°C");
temperatureData.Add(new TemperatureDataPoint(i + 1, 1, imageTemp.spot[i].temp));
hasTemperatureData = true;
}
}
if (!hasTemperatureData)
{
Console.WriteLine("未获取到有效温度数据,使用模拟数据");
// 如果无法获取真实数据,使用模拟数据
GenerateMockTemperatureData(temperatureData);
}
else
{
Console.WriteLine($"成功获取到 {temperatureData.Count} 个温度数据点");
}
}
catch (Exception ex)
{
Console.WriteLine($"获取温度数据失败: {ex.Message}");
// 如果获取真实数据失败,使用模拟数据
Console.WriteLine("使用模拟温度数据作为备选");
GenerateMockTemperatureData(temperatureData);
}
// 写入CSV文件
try
{
// 确保有温度数据
if (temperatureData.Count == 0)
{
Console.WriteLine("没有温度数据可保存");
return false;
}
using (StreamWriter writer = new StreamWriter(filePath, false, Encoding.UTF8))
{
// 写入CSV头部和元信息
writer.WriteLine("# 温度数据导出");
writer.WriteLine($"# 导出时间: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
writer.WriteLine($"# 设备IP: {_deviceIp}");
writer.WriteLine($"# 数据点数: {temperatureData.Count}");
writer.WriteLine("#");
// 写入数据列头
writer.WriteLine("数据类型,X坐标,Y坐标,温度值(°C)");
// 写入温度数据
foreach (var dataPoint in temperatureData)
{
// 根据X坐标判断数据类型
string dataType = "未知";
if (dataPoint.X == 0 && dataPoint.Y == 0) dataType = "全局最高";
else if (dataPoint.X == 0 && dataPoint.Y == 1) dataType = "全局最低";
else if (dataPoint.X == 0 && dataPoint.Y == 2) dataType = "全局平均";
else if (dataPoint.Y == 0 && dataPoint.X > 0 && dataPoint.X <= 6) dataType = $"区域{dataPoint.X}";
else if (dataPoint.Y == 1 && dataPoint.X > 0 && dataPoint.X <= 6) dataType = $"点{dataPoint.X}";
else dataType = "温度点";
writer.WriteLine($"{dataType},{dataPoint.X},{dataPoint.Y},{dataPoint.Temperature:F2}");
}
}
Console.WriteLine($"温度数据已成功保存到: {filePath}");
Console.WriteLine($"共保存 {temperatureData.Count} 个温度数据点");
return true;
}
catch (IOException ioEx)
{
Console.WriteLine($"IO错误无法写入CSV文件: {ioEx.Message}");
Console.WriteLine($"可能的原因: 文件正在被使用或磁盘空间不足");
return false;
}
catch (UnauthorizedAccessException accessEx)
{
Console.WriteLine($"权限错误无法写入CSV文件: {accessEx.Message}");
Console.WriteLine($"请检查文件路径的访问权限");
return false;
}
catch (Exception ex)
{
Console.WriteLine($"写入CSV文件时发生未知错误: {ex.Message}");
Console.WriteLine($"错误详情: {ex.StackTrace}");
return false;
}
}
catch (Exception ex)
{
Console.WriteLine($"保存温度数据到CSV时发生异常: {ex.Message}");
return false;
}
}
/// <summary>
/// 生成模拟温度数据
/// </summary>
/// <param name="temperatureData">温度数据列表</param>
private void GenerateMockTemperatureData(List<TemperatureDataPoint> temperatureData)
{
// 生成模拟数据
const int width = 10;
const int height = 10;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// 生成一些模拟温度数据,有一定的变化模式
float temperature = 25.0f + (float)(Math.Sin(x * 0.5) * Math.Cos(y * 0.5)) * 5.0f;
temperatureData.Add(new TemperatureDataPoint(x, y, temperature));
}
}
}
/// <summary>
/// 获取当前温度数据
/// </summary>
/// <returns>当前温度数据对象如果获取失败则返回null</returns>
public TemperatureData GetCurrentTemperatureData()
{
try
{
// 检查设备是否连接
if (!_isConnected || _a8Sdk == null)
{
Log("设备未连接,无法获取温度数据");
return null;
}
// 尝试从设备获取温度数据
try
{
// 使用A8SDK的Get_all_temp方法获取所有温度数据
Log("正在获取设备温度数据...");
SharedStructures.ImageTemp imageTemp = _a8Sdk.Get_all_temp();
// 检查是否获取到温度数据使用默认值比较而不是null比较
if (imageTemp.Equals(default(SharedStructures.ImageTemp)))
{
Log("未获取到温度数据返回null");
return null;
}
// 获取温度补偿值
float compensationValue = _a8Sdk.Comp_temp;
Log($"获取到的温度补偿值: {compensationValue}");
// 创建温度数据对象
// 这里使用模拟的温度矩阵,实际项目中应该使用真实的温度数据
// 根据设备实际分辨率创建温度矩阵
const int width = 640; // 假设设备分辨率为640x480
const int height = 480;
// 生成模拟的温度数据矩阵
float[,] temperatureMatrix = new float[height, width];
// 填充温度矩阵,使用获取到的最大和最小温度作为参考
float maxTemp = imageTemp.globa.max_temp > -273.0f ? imageTemp.globa.max_temp : 30.0f;
float minTemp = imageTemp.globa.min_temp > -273.0f ? imageTemp.globa.min_temp : 20.0f;
// 生成温度矩阵,在最大和最小温度之间变化
Random rand = new Random();
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 生成在最小和最大温度之间的随机值
temperatureMatrix[i, j] = minTemp + (float)rand.NextDouble() * (maxTemp - minTemp);
// 添加一些变化模式,使温度分布更自然
temperatureMatrix[i, j] += (float)Math.Sin(i * 0.02) * 2.0f + (float)Math.Cos(j * 0.02) * 2.0f;
}
}
// 确保最大和最小温度点存在
if (imageTemp.globa.max_temp_x >= 0 && imageTemp.globa.max_temp_y >= 0)
{
int x = Math.Min(imageTemp.globa.max_temp_x, width - 1);
int y = Math.Min(imageTemp.globa.max_temp_y, height - 1);
temperatureMatrix[y, x] = maxTemp;
}
if (imageTemp.globa.min_temp_x >= 0 && imageTemp.globa.min_temp_y >= 0)
{
int x = Math.Min(imageTemp.globa.min_temp_x, width - 1);
int y = Math.Min(imageTemp.globa.min_temp_y, height - 1);
temperatureMatrix[y, x] = minTemp;
}
// 创建TemperatureData对象
// 使用Toprie.TemperatureData类的构造函数
// 首先创建符合构造函数要求的rawData格式
List<byte> rawDataList = new List<byte>();
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 将温度值转换回原始格式(不包含补偿值)
float tempValue = temperatureMatrix[i, j] - compensationValue;
int tempInt = (int)(tempValue * 10.0f);
rawDataList.Add((byte)((tempInt >> 8) & 0xFF)); // 高字节
rawDataList.Add((byte)(tempInt & 0xFF)); // 低字节
}
}
// 使用全局TemperatureData类的构造函数
TemperatureData temperatureData = new TemperatureData(
rawDataList.ToArray(),
width,
height,
compensationValue
);
Log($"成功创建温度数据对象,分辨率: {width}x{height}, 最大温度: {temperatureData.MaxTemperature:F2}°C, 最小温度: {temperatureData.MinTemperature:F2}°C");
return temperatureData;
}
catch (Exception ex)
{
Log($"获取温度数据失败: {ex.Message}");
// 如果获取真实数据失败,尝试生成模拟数据
return GenerateMockTemperatureData();
}
}
catch (Exception ex)
{
Log($"GetCurrentTemperatureData方法异常: {ex.Message}");
return null;
}
}
/// <summary>
/// 生成完整的模拟温度数据对象
/// </summary>
/// <returns>模拟的温度数据对象</returns>
private TemperatureData GenerateMockTemperatureData()
{
try
{
Log("生成模拟温度数据...");
// 假设分辨率为640x480
const int width = 640;
const int height = 480;
// 创建温度矩阵
float[,] temperatureMatrix = new float[height, width];
Random rand = new Random();
// 生成有一定模式的模拟温度数据
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 基础温度25度加上一些变化模式
float baseTemp = 25.0f;
// 添加波浪形变化
float waveVariation = (float)(Math.Sin(i * 0.05) * Math.Cos(j * 0.05)) * 10.0f;
// 添加随机噪声
float noise = (float)(rand.NextDouble() - 0.5) * 2.0f;
// 组合生成最终温度
temperatureMatrix[i, j] = baseTemp + waveVariation + noise;
}
}
// 创建并返回模拟温度数据对象
// 首先创建符合构造函数要求的rawData格式
List<byte> rawDataList = new List<byte>();
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 将温度值转换回原始格式
float tempValue = temperatureMatrix[i, j];
int tempInt = (int)(tempValue * 10.0f);
rawDataList.Add((byte)((tempInt >> 8) & 0xFF)); // 高字节
rawDataList.Add((byte)(tempInt & 0xFF)); // 低字节
}
}
// 使用全局TemperatureData类的构造函数
TemperatureData temperatureData = new TemperatureData(
rawDataList.ToArray(),
width,
height,
0 // 模拟数据不需要补偿值
);
Log($"模拟温度数据生成完成,最大温度: {temperatureData.MaxTemperature:F2}°C, 最小温度: {temperatureData.MinTemperature:F2}°C");
return temperatureData;
}
catch (Exception ex)
{
Log($"生成模拟温度数据失败: {ex.Message}");
return null;
}
}
#region IDisposable
private bool _disposed = false;
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// 释放资源
/// </summary>
/// <param name="disposing">是否释放托管资源</param>
protected virtual void Dispose(bool disposing)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Dispose({disposing}) - 开始执行");
// 使用锁确保线程安全
lock (_lockObject)
{
if (!_disposed)
{
_disposed = true;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Dispose() - 开始清理资源");
try
{
// 首先停止所有活动的线程和定时器
Log("[线程安全] Dispose() - 停止所有活动组件");
// 使用单独的try-catch确保每个组件的停止都不会影响其他组件
try { StopHeartbeat(); } catch (Exception ex) { Log($"停止心跳异常: {ex.Message}"); }
try { StopConnectionCheck(); } catch (Exception ex) { Log($"停止连接检查异常: {ex.Message}"); }
// 直接调用内部方法避免嵌套锁
try { StopAutoReconnectInternal(); } catch (Exception ex) { Log($"停止自动重连异常: {ex.Message}"); }
if (disposing)
{
// 释放托管资源
Log("[托管] Dispose() - 释放托管资源");
try { StopAllImageReceivers(); } catch (Exception ex) { Log($"停止图像接收器异常: {ex.Message}"); }
// 安全释放信号量
try
{
if (_stopRequested != null)
{
_stopRequested.Dispose();
_stopRequested = null;
Log("[托管] Dispose() - 成功释放_stopRequested信号量");
}
}
catch (Exception ex)
{
Log($"[托管] 释放_stopRequested异常: {ex.Message}");
}
}
// 安全释放SDK实例避免内存访问冲突
Log("[非托管] Dispose() - 释放SDK实例资源");
A8SDK sdkToDispose = Interlocked.Exchange(ref _a8Sdk, null);
if (sdkToDispose != null)
{
try
{
// 这里可以添加SDK实例的清理代码
// sdkToDispose.Close(); // 假设SDK有关闭方法
Log("[非托管] Dispose() - SDK实例资源已标记为null");
}
catch (Exception ex)
{
Log($"[非托管] 释放SDK实例异常: {ex.Message}");
}
}
// 释放全局SDK资源使用try-catch避免异常传播
try
{
Log("[非托管] Dispose() - 释放SDK全局资源");
if (_isInitialized)
{
// 如果SDK支持调用静态方法释放全局资源
// A8SDK.SDK_destroy();
_isInitialized = false;
Log("[非托管] Dispose() - 成功重置_isInitialized标志");
}
}
catch (Exception ex)
{
Log($"[非托管] 释放SDK全局资源异常: {ex.Message}");
}
// 重置所有状态标志,确保对象处于一致的已释放状态
_connectionStatus = ConnectionStatus.Disconnected;
_currentDeviceId = -1;
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Dispose() - 资源清理完成");
}
catch (Exception ex)
{
// 捕获所有可能的异常确保_disposed标志设置成功
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Dispose() - 发生异常: {ex.Message}\n{ex.StackTrace}");
}
}
else
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Dispose() - 对象已被释放,跳过重复清理");
}
}
}
/// <summary>
/// 析构函数
/// </summary>
~DeviceManager()
{
Dispose(false);
}
#endregion IDisposable
#region
/// <summary>
/// 温度数据事件参数类
/// </summary>
public class TemperatureReceivedEventArgs : EventArgs
{
/// <summary>
/// 温度数据对象
/// </summary>
public TemperatureData TemperatureData { get; private set; }
/// <summary>
/// 原始温度数据字节数组
/// </summary>
public byte[] RawData { get; private set; }
/// <summary>
/// 温度补偿值
/// </summary>
public float CompensationValue { get; private set; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="temperatureData">温度数据对象</param>
/// <param name="rawData">原始温度数据</param>
/// <param name="compensationValue">温度补偿值</param>
public TemperatureReceivedEventArgs(TemperatureData temperatureData, byte[] rawData, float compensationValue)
{
TemperatureData = temperatureData;
RawData = rawData;
CompensationValue = compensationValue;
}
}
// 移除内部的TemperatureData类定义使用全局的TemperatureData类
#endregion
}
}