diff --git a/Windows/CS/Framework4.0/Toprie/Toprie.ico b/Windows/CS/Framework4.0/Toprie/Toprie.ico
new file mode 100644
index 0000000..2cf24ed
Binary files /dev/null and b/Windows/CS/Framework4.0/Toprie/Toprie.ico differ
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs
index de50a30..626decf 100644
--- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs
@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
-using System.Drawing;
using System.Data;
+using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
+using System.Threading;
namespace JoyD.Windows.CS.Toprie
{
@@ -59,7 +60,12 @@ namespace JoyD.Windows.CS.Toprie
///
private void InitializeDeviceManager()
{
- _deviceManager = new DeviceManager();
+ _deviceManager = new DeviceManager
+ {
+ AutoReconnectEnabled = true,
+ ReconnectInterval = 2000, // 2秒
+ MaxReconnectAttempts = 5
+ };
// 注册图像接收事件
_deviceManager.ImageReceived += DeviceManager_ImageReceived;
@@ -69,6 +75,8 @@ namespace JoyD.Windows.CS.Toprie
// 注册连接异常事件
_deviceManager.ConnectionException += DeviceManager_ConnectionException;
+
+
}
///
@@ -81,34 +89,98 @@ namespace JoyD.Windows.CS.Toprie
}
///
- /// 启动并连接设备
+ /// 启动相机
///
public void StartCamera()
{
try
{
- // 设置为热图模式
- _deviceManager.CurrentImageMode = ImageMode.Thermal;
-
- // 启用自动重连
- _deviceManager.AutoReconnectEnabled = true;
-
- // 连接设备
- bool connected = _deviceManager.ConnectDevice();
-
- if (connected)
+ // 只有在没有接收图像时才启动
+ if (!_isReceivingImage)
{
- // 开始接收图像
- StartReceiveImage();
+ // 清理错误显示
+ ShowError(string.Empty);
+
+ // 使用异步方式连接设备和设置模式,避免UI线程阻塞
+ ThreadPool.QueueUserWorkItem(delegate
+ {
+ try
+ {
+ // 设置为热图模式
+ bool modeSet = false;
+ try
+ {
+ _deviceManager.SetImageMode(ImageMode.Thermal);
+ modeSet = true;
+ Console.WriteLine("已设置热图模式");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"设置热图模式失败: {ex.Message}");
+ }
+
+ // 启用自动重连
+ _deviceManager.AutoReconnectEnabled = true;
+
+ // 尝试连接设备
+ if (_deviceManager.ConnectDevice())
+ {
+ Console.WriteLine("设备连接成功");
+
+ // 如果热图模式未成功设置,在连接成功后再次尝试
+ if (!modeSet)
+ {
+ try
+ {
+ _deviceManager.SetImageMode(ImageMode.Thermal);
+ Console.WriteLine("连接后已设置热图模式");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"连接后设置热图模式失败: {ex.Message}");
+ }
+ }
+
+ // 在连接成功后开始接收图像
+ this.Invoke(new Action(() =>
+ {
+ // 再次检查是否已在UI线程设置为接收状态
+ if (!_isReceivingImage)
+ {
+ StartReceiveImage();
+ }
+ }));
+ }
+ else
+ {
+ // 连接失败时显示错误
+ string errorMsg = "设备连接失败,请检查设备状态或网络连接";
+ this.Invoke(new Action(() => ShowError(errorMsg)));
+ Console.WriteLine(errorMsg);
+ }
+ }
+ catch (Exception ex)
+ {
+ // 捕获所有异常,防止后台线程崩溃
+ string errorMsg = $"启动相机异常: {ex.Message}";
+ this.Invoke(new Action(() => ShowError(errorMsg)));
+ Console.WriteLine(errorMsg);
+ Console.WriteLine(ex.StackTrace);
+ }
+ });
+
+ Console.WriteLine("相机启动流程已开始");
}
else
{
- ShowError("无法连接到设备,请检查设备是否在线");
+ Console.WriteLine("相机已在运行状态");
}
}
catch (Exception ex)
{
ShowError($"启动相机失败: {ex.Message}");
+ Console.WriteLine($"启动相机主流程错误: {ex.Message}");
+ Console.WriteLine(ex.StackTrace);
}
}
@@ -155,36 +227,176 @@ namespace JoyD.Windows.CS.Toprie
///
private void DeviceManager_ImageReceived(object sender, ImageReceivedEventArgs e)
{
- if (this.InvokeRequired)
+ // 不在这里使用using,因为我们需要将图像传递给UI线程
+ Image image = e.ImageData;
+
+ if (image == null)
{
- // 在UI线程上更新图像
- this.Invoke(new Action(() => UpdateImage(e.ImageData)));
+ Console.WriteLine("接收到空图像数据");
+ return;
}
- else
+
+ try
{
- UpdateImage(e.ImageData);
+ // 尝试创建图像的克隆以传递给UI线程
+ Image clonedImage = null;
+ try
+ {
+ clonedImage = (Image)image.Clone();
+
+ // 验证克隆的图像是否有效
+ if (clonedImage.Width <= 0 || clonedImage.Height <= 0)
+ {
+ Console.WriteLine("克隆的图像尺寸无效");
+ clonedImage.Dispose();
+ image.Dispose(); // 确保原始图像也被释放
+ return;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"克隆图像失败: {ex.Message}");
+ image.Dispose(); // 确保原始图像被释放
+ return;
+ }
+
+ // 确保在UI线程上更新,并且检查控件是否已被释放
+ if (!this.IsDisposed && !this.Disposing)
+ {
+ if (this.InvokeRequired)
+ {
+ try
+ {
+ // 使用BeginInvoke在UI线程上更新图像,避免可能的死锁问题
+ this.BeginInvoke(new Action(() =>
+ {
+ try
+ {
+ UpdateImageOnUI(clonedImage);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"更新UI图像失败: {ex.Message}");
+ // 如果UI更新失败,确保克隆的图像被释放
+ if (clonedImage != null)
+ {
+ clonedImage.Dispose();
+ }
+ }
+ }));
+ }
+ catch (Exception)
+ {
+ // 异常情况下确保释放图像资源
+ clonedImage?.Dispose();
+ throw;
+ }
+ }
+ else
+ {
+ // 在UI线程上直接更新图像
+ UpdateImageOnUI(clonedImage);
+ }
+ }
+ else
+ {
+ // 如果控件已释放,确保释放图像资源
+ clonedImage.Dispose();
+ }
+
+ // 释放原始图像资源
+ image.Dispose();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"处理图像接收事件失败: {ex.Message}");
+ // 确保在任何异常情况下都释放原始图像
+ if (image != null)
+ {
+ try
+ {
+ image.Dispose();
+ }
+ catch {}
+ }
}
}
///
- /// 更新图像显示
+ /// 在UI线程上更新图像
///
- private void UpdateImage(Image image)
+ private void UpdateImageOnUI(Image image)
{
+ // 空值检查
+ if (image == null)
+ {
+ Console.WriteLine("传入UpdateImageOnUI的图像为空");
+ return;
+ }
+
try
{
- // 释放之前的图像资源
- if (imageBox.Image != null)
+ // 更新图像前先检查控件是否存在/已释放
+ if (this.IsDisposed || imageBox == null || imageBox.IsDisposed)
{
- imageBox.Image.Dispose();
+ // 如果控件已释放,确保图像也被释放
+ image.Dispose();
+ Console.WriteLine("控件已释放,无法更新图像");
+ return;
}
- // 设置新图像
- imageBox.Image = (Image)image.Clone();
+ // 保存旧图像引用,以便在设置新图像后释放
+ Image oldImage = imageBox.Image;
+
+ try
+ {
+ // 尝试设置新图像
+ imageBox.Image = image;
+
+ // 验证图像是否成功设置
+ if (imageBox.Image != image)
+ {
+ Console.WriteLine("图像设置失败,可能参数无效");
+ image.Dispose(); // 释放未成功设置的图像
+ return;
+ }
+
+ // 仅在新图像成功设置后释放旧图像
+ if (oldImage != null && oldImage != image) // 防止自引用释放
+ {
+ oldImage.Dispose();
+ }
+ }
+ catch (ArgumentException ex) when (ex.Message.Contains("参数无效"))
+ {
+ // 特别处理"参数无效"异常
+ Console.WriteLine($"图像参数无效: {ex.Message}");
+ image.Dispose(); // 释放无效图像
+
+ // 尝试设置旧图像回来,如果可能的话
+ if (oldImage != null)
+ {
+ try
+ {
+ imageBox.Image = oldImage;
+ }
+ catch
+ {
+ // 如果设置旧图像也失败,释放它
+ oldImage.Dispose();
+ }
+ }
+ }
}
catch (Exception ex)
{
- Console.WriteLine($"更新图像失败: {ex.Message}");
+ Console.WriteLine($"更新图像UI失败: {ex.Message}");
+ // 确保在任何异常情况下都释放传入的图像资源
+ try
+ {
+ image.Dispose();
+ }
+ catch {}
}
}
@@ -193,13 +405,26 @@ namespace JoyD.Windows.CS.Toprie
///
private void DeviceManager_ConnectionStatusChanged(object sender, ConnectionStatusChangedEventArgs e)
{
- if (this.InvokeRequired)
+ // 确保在UI线程上更新,并且检查控件是否已被释放
+ if (!this.IsDisposed && !this.Disposing)
{
- this.Invoke(new Action(() => HandleConnectionStatusChanged(e)));
- }
- else
- {
- HandleConnectionStatusChanged(e);
+ if (this.InvokeRequired)
+ {
+ try
+ {
+ // 使用BeginInvoke代替Invoke,避免可能的死锁问题
+ this.BeginInvoke(new Action(() => HandleConnectionStatusChanged(e)));
+ }
+ catch (ObjectDisposedException)
+ {
+ // 捕获控件已释放异常,避免程序崩溃
+ Console.WriteLine("控件已释放,跳过UI更新");
+ }
+ }
+ else
+ {
+ HandleConnectionStatusChanged(e);
+ }
}
}
@@ -208,10 +433,28 @@ namespace JoyD.Windows.CS.Toprie
///
private void HandleConnectionStatusChanged(ConnectionStatusChangedEventArgs e)
{
+ // 更新UI状态
+ UpdateUIState(e.NewStatus == ConnectionStatus.Connected);
+
switch (e.NewStatus)
{
case ConnectionStatus.Connected:
Console.WriteLine("设备已连接");
+
+ // 清除错误信息
+ ShowError(string.Empty);
+
+ // 确保设置为热图模式
+ try
+ {
+ _deviceManager.SetImageMode(ImageMode.Thermal);
+ Console.WriteLine("连接成功后确认热图模式");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"连接成功后设置热图模式失败: {ex.Message}");
+ }
+
if (!_isReceivingImage)
{
StartReceiveImage();
@@ -219,31 +462,109 @@ namespace JoyD.Windows.CS.Toprie
break;
case ConnectionStatus.Disconnected:
Console.WriteLine("设备已断开连接");
- _isReceivingImage = false;
+
+ // 停止接收图像
+ if (_isReceivingImage)
+ {
+ _deviceManager.StopReceiveImage();
+ _isReceivingImage = false;
+ }
+
if (!string.IsNullOrEmpty(e.Message))
{
ShowError(e.Message);
+ }
+ else
+ {
+ ShowError("设备连接已断开");
}
break;
case ConnectionStatus.Connecting:
case ConnectionStatus.Reconnecting:
- Console.WriteLine("正在连接设备...");
+ Console.WriteLine($"正在连接设备...{(!string.IsNullOrEmpty(e.Message) ? " " + e.Message : "")}");
+ ShowError(string.Empty); // 清除之前的错误信息
break;
}
}
+
+ ///
+ /// 更新UI状态
+ ///
+ /// 是否已连接
+ private void UpdateUIState(bool isConnected)
+ {
+ try
+ {
+ // 根据连接状态更新图像框状态
+ if (imageBox != null && !imageBox.IsDisposed)
+ {
+ if (isConnected)
+ {
+ imageBox.BorderStyle = BorderStyle.FixedSingle;
+ }
+ else
+ {
+ imageBox.BorderStyle = BorderStyle.Fixed3D;
+ }
+ }
+
+ // 更新图像框的边框颜色,提供视觉反馈
+ if (imageBox != null)
+ {
+ imageBox.BorderStyle = isConnected ? BorderStyle.FixedSingle : BorderStyle.Fixed3D;
+ // 可选:设置不同的边框颜色以提供更好的视觉反馈
+ if (isConnected)
+ {
+ imageBox.BackColor = Color.LightGreen;
+ }
+ else
+ {
+ imageBox.BackColor = Color.LightGray;
+ }
+ }
+
+ Console.WriteLine($"UI状态已更新为: {(isConnected ? "已连接" : "未连接")}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"更新UI状态时出错: {ex.Message}");
+ }
+ }
///
/// 设备管理器连接异常事件处理
///
private void DeviceManager_ConnectionException(object sender, ConnectionExceptionEventArgs e)
{
- if (this.InvokeRequired)
+ // 确保在UI线程上更新,并且检查控件是否已被释放
+ if (!this.IsDisposed && !this.Disposing)
{
- this.Invoke(new Action(() => ShowError($"连接异常: {e.ContextMessage}")));
- }
- else
- {
- ShowError($"连接异常: {e.ContextMessage}");
+ if (this.InvokeRequired)
+ {
+ try
+ {
+ // 使用BeginInvoke在UI线程上显示错误
+ this.BeginInvoke(new Action(() =>
+ {
+ // 记录异常日志
+ Console.WriteLine($"连接异常发生于 {e.Timestamp}: {e.ContextMessage}\n{e.Exception.Message}\n{e.Exception.StackTrace}");
+
+ ShowError($"连接异常: {e.ContextMessage}");
+ }));
+ }
+ catch (ObjectDisposedException)
+ {
+ // 捕获控件已释放异常,避免程序崩溃
+ Console.WriteLine("控件已释放,跳过异常处理");
+ }
+ }
+ else
+ {
+ // 记录异常日志
+ Console.WriteLine($"连接异常发生于 {e.Timestamp}: {e.ContextMessage}\n{e.Exception.Message}\n{e.Exception.StackTrace}");
+
+ ShowError($"连接异常: {e.ContextMessage}");
+ }
}
}
@@ -257,8 +578,19 @@ namespace JoyD.Windows.CS.Toprie
// 可以根据需要添加UI上的错误显示
// 这里简化处理,只在控制台输出
+ // 检查imageBox是否存在且尺寸有效
+ if (imageBox == null || imageBox.Width <= 0 || imageBox.Height <= 0)
+ {
+ Console.WriteLine("imageBox尺寸无效,跳过错误显示图像创建");
+ return;
+ }
+
+ // 确保使用有效的尺寸创建Bitmap
+ int width = Math.Max(100, imageBox.Width);
+ int height = Math.Max(100, imageBox.Height);
+
// 如果需要在图像区域显示错误文字,可以使用以下代码
- using (Bitmap errorBitmap = new Bitmap(imageBox.Width, imageBox.Height))
+ using (Bitmap errorBitmap = new Bitmap(width, height))
using (Graphics g = Graphics.FromImage(errorBitmap))
{
g.Clear(Color.Black);
@@ -272,7 +604,7 @@ namespace JoyD.Windows.CS.Toprie
g.DrawString(message, font, brush, textLocation);
}
- UpdateImage(errorBitmap);
+ UpdateImageOnUI(errorBitmap);
}
// 启动定时器,3秒后清除错误显示
@@ -287,7 +619,19 @@ namespace JoyD.Windows.CS.Toprie
{
_errorDisplayTimer.Stop();
// 清除错误显示,恢复到等待图像状态
- using (Bitmap waitingBitmap = new Bitmap(imageBox.Width, imageBox.Height))
+
+ // 检查imageBox是否存在且尺寸有效
+ if (imageBox == null || imageBox.Width <= 0 || imageBox.Height <= 0)
+ {
+ Console.WriteLine("imageBox尺寸无效,跳过等待图像创建");
+ return;
+ }
+
+ // 确保使用有效的尺寸创建Bitmap
+ int width = Math.Max(100, imageBox.Width);
+ int height = Math.Max(100, imageBox.Height);
+
+ using (Bitmap waitingBitmap = new Bitmap(width, height))
using (Graphics g = Graphics.FromImage(waitingBitmap))
{
g.Clear(Color.Black);
@@ -302,7 +646,7 @@ namespace JoyD.Windows.CS.Toprie
g.DrawString(waitingText, font, brush, textLocation);
}
- UpdateImage(waitingBitmap);
+ UpdateImageOnUI(waitingBitmap);
}
}
@@ -330,9 +674,12 @@ namespace JoyD.Windows.CS.Toprie
// 取消注册事件并释放设备管理器
if (_deviceManager != null)
{
+
+ // 移除所有事件监听
_deviceManager.ImageReceived -= DeviceManager_ImageReceived;
_deviceManager.ConnectionStatusChanged -= DeviceManager_ConnectionStatusChanged;
_deviceManager.ConnectionException -= DeviceManager_ConnectionException;
+ // 释放设备管理器资源
_deviceManager.Dispose();
_deviceManager = null;
}
@@ -346,7 +693,7 @@ namespace JoyD.Windows.CS.Toprie
}
// 无论是否在设计模式下,都需要释放图像资源
- if (imageBox.Image != null)
+ if (imageBox != null && !imageBox.IsDisposed && imageBox.Image != null)
{
imageBox.Image.Dispose();
imageBox.Image = null;
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs
index 6e56918..5858900 100644
--- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs
@@ -1,20 +1,24 @@
using System;
-using System.Linq;
using System.Collections.Generic;
-using System.Threading;
+using System.Linq;
+using System.Text;
using System.Threading.Tasks;
-using System.Net.NetworkInformation;
+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;
-using System.Text;
+using Toprie; // 修改引用路径,使用本地的A8SDK.cs
+using System.Net.NetworkInformation;
-namespace JoyD.Windows.CS.Toprie
+namespace Toprie
{
///
- /// 连接状态枚举
+ /// 设备连接状态枚举
///
public enum ConnectionStatus
{
@@ -23,40 +27,44 @@ namespace JoyD.Windows.CS.Toprie
Connected,
Reconnecting
}
-
+
///
/// 图像模式枚举
///
public enum ImageMode
{
- ///
- /// 热图模式(红外热成像)
- ///
Thermal,
- ///
- /// 全彩模式(可见光图像)
- ///
- FullColor
+ Visible,
+ Fusion
}
///
- /// 连接状态变更事件参数
+ /// 连接状态改变事件参数
///
public class ConnectionStatusChangedEventArgs : EventArgs
{
- public ConnectionStatus NewStatus { get; set; }
- public ConnectionStatus OldStatus { get; set; }
- public string Message { get; set; }
- public Exception Exception { get; set; }
- public DateTime Timestamp { get; private set; }
+ public ConnectionStatus Status { get; set; }
+ public string DeviceInfo { get; set; }
- public ConnectionStatusChangedEventArgs(ConnectionStatus oldStatus, ConnectionStatus newStatus, string message = null, Exception exception = null)
+ public ConnectionStatusChangedEventArgs(ConnectionStatus status, string deviceInfo = null)
{
- OldStatus = oldStatus;
- NewStatus = newStatus;
- Message = message;
- Exception = exception;
- Timestamp = DateTime.Now;
+ Status = status;
+ DeviceInfo = deviceInfo;
+ }
+ }
+
+ ///
+ /// 图像接收事件参数
+ ///
+ public class ImageReceivedEventArgs : EventArgs
+ {
+ public byte[] ImageData { get; set; }
+ public ImageMode Mode { get; set; }
+
+ public ImageReceivedEventArgs(byte[] imageData, ImageMode mode)
+ {
+ ImageData = imageData;
+ Mode = mode;
}
}
@@ -65,69 +73,134 @@ namespace JoyD.Windows.CS.Toprie
///
public class ConnectionExceptionEventArgs : EventArgs
{
- public Exception Exception { get; private set; }
- public string ContextMessage { get; private set; }
- public DateTime Timestamp { get; private set; }
+ public Exception Exception { get; set; }
+ public string Message { get; set; }
- public ConnectionExceptionEventArgs(Exception exception, string contextMessage)
+ public ConnectionExceptionEventArgs(Exception ex, string message = null)
{
- Exception = exception;
- ContextMessage = contextMessage;
- Timestamp = DateTime.Now;
+ Exception = ex;
+ Message = message ?? ex?.Message;
}
}
-
+
///
- /// 图像数据接收事件参数
+ /// 设备管理器类
///
- public class ImageReceivedEventArgs : EventArgs
+ ///
+ /// 设备信息类,用于存储设备的ID、IP等信息
+ ///
+ public class DeviceInfo
{
- public Image ImageData { get; private set; }
- public DateTime Timestamp { get; private set; }
-
- public ImageReceivedEventArgs(Image imageData)
+ ///
+ /// 设备ID
+ ///
+ public int DeviceID { get; set; }
+
+ ///
+ /// 设备IP地址
+ ///
+ public string IPAddress { get; set; }
+
+ ///
+ /// 设备型号
+ ///
+ public string Model { get; set; }
+
+ ///
+ /// 设备序列号
+ ///
+ public string SerialNumber { get; set; }
+
+ public override string ToString()
{
- ImageData = imageData;
- Timestamp = DateTime.Now;
+ return $"设备 {DeviceID} ({IPAddress})";
}
}
- ///
- /// 设备管理类,负责设备搜索、连接和断开(纯C#实现)
- ///
public class DeviceManager : IDisposable
{
- #region 私有成员
-
- // 设备IP地址
- private string _deviceIp = "192.168.100.2"; // 默认IP
- private int _devicePort = 8080; // 默认端口
-
+ // A8SDK实例
+ private A8SDK _a8Sdk;
// 设备ID列表
- private List _deviceIds;
-
- // 是否已初始化
- private bool _isInitialized;
-
- // 连接状态
- private ConnectionStatus _connectionStatus = ConnectionStatus.Disconnected;
+ private List _deviceIds = new List();
+ // 设备信息列表
+ private List _deviceList = new List();
+
+ // 目标设备ID,用于指定ID连接
+ private 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 bool _isDisposed = false;
// 图像模式
private ImageMode _currentImageMode = ImageMode.Thermal;
-
- // 自动重连相关
- private bool _isAutoReconnectEnabled = false;
- private int _reconnectInterval = 5000; // 默认5秒
- private int _maxReconnectAttempts = 5;
- private int _reconnectAttempts = 0;
+ // 自动重连是否启用
+ private bool _autoReconnectEnabled = true;
+ // 自动重连定时器
private System.Threading.Timer _reconnectTimer;
- private ManualResetEventSlim _stopRequested = new ManualResetEventSlim(false);
-
- // 心跳检测相关
+ // 重连间隔(毫秒)
+ private int _reconnectInterval = 2000;
+ // 重连尝试次数
+ private int _reconnectAttempts = 0;
+ // 最大重连尝试次数
+ private const int MaxReconnectAttempts = 5;
+ // 连接检查定时器
+ private System.Threading.Timer _connectionCheckTimer;
+ // 心跳定时器
private System.Threading.Timer _heartbeatTimer;
- private int _heartbeatInterval = 30000; // 30秒心跳
-
- #endregion 私有成员
+ // 连接超时时间(毫秒)
+ private const int ConnectionTimeout = 5000;
+ // 连接检查间隔(毫秒)
+ private const int ConnectionCheckInterval = 5000;
+ // 心跳间隔(毫秒)
+ private int _heartbeatInterval = 5000;
+ // TCP客户端
+ private TcpClient _imageTcpClient;
+ // 网络流
+ private NetworkStream _imageNetworkStream;
+ // 图像接收任务
+ private Task _imageReceivingTask;
+ // 取消令牌源
+ private CancellationTokenSource _imageReceivingCts;
+ // 停止请求事件
+ private ManualResetEvent _stopRequested = new ManualResetEvent(false);
+ // 缓冲区
+ private byte[] _imageBuffer = new byte[4096];
+ // 图像数据累积缓冲区
+ private byte[] _imageDataAccumulator = new byte[0];
+ // 多部分请求边界
+ private string _multipartBoundary = string.Empty;
+ // 锁对象
+ private object _lockObject = new object();
+ // 是否启用自动重连
+ private bool _isAutoReconnectEnabled = true;
+ // 最大重连次数
+ private int _maxReconnectAttempts = 5;
+ // 是否已连接
+ private bool _isConnected = false;
+ // 连接超时设置
+ private 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 TcpClient _imageTcpClient;
+ private bool _isInfraredMode = true;
#region 私有方法
@@ -161,11 +234,59 @@ namespace JoyD.Windows.CS.Toprie
/// 相关异常(如果有)
private void UpdateConnectionStatus(ConnectionStatus newStatus, string message = null, Exception exception = null)
{
+ // 检查对象是否已被释放
+ if (_isDisposed)
+ {
+ Console.WriteLine("警告: 尝试在已释放对象上更新连接状态");
+ return;
+ }
+
if (_connectionStatus != newStatus)
{
ConnectionStatus oldStatus = _connectionStatus;
_connectionStatus = newStatus;
- OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(oldStatus, newStatus, message, exception));
+
+ // 触发连接状态变更事件前再次检查对象是否已被释放
+ if (!_isDisposed)
+ {
+ try
+ {
+ OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(newStatus, message));
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"触发连接状态变更事件异常: {ex.Message}");
+ }
+ }
+
+ Console.WriteLine($"连接状态变更: {oldStatus} -> {newStatus}");
+ if (!string.IsNullOrEmpty(message))
+ {
+ Console.WriteLine($"状态消息: {message}");
+ }
+ if (exception != null)
+ {
+ Console.WriteLine($"异常信息: {exception.Message}");
+ }
+
+ // 如果断开连接且启用了自动重连,启动重连机制
+ if (newStatus == ConnectionStatus.Disconnected && _autoReconnectEnabled && oldStatus != ConnectionStatus.Connecting)
+ {
+ StartAutoReconnect();
+ }
+ // 如果连接成功,重置重连计数并启动图像接收
+ else if (newStatus == ConnectionStatus.Connected)
+ {
+ _currentReconnectAttempt = 0;
+ StopAutoReconnect();
+ // 启动图像接收
+ StartImageReceiving();
+ }
+ // 如果断开连接,停止图像接收
+ else if (newStatus == ConnectionStatus.Disconnected)
+ {
+ StopImageReceiving();
+ }
}
}
@@ -175,16 +296,33 @@ namespace JoyD.Windows.CS.Toprie
/// 是否初始化成功
private bool Initialize()
{
+ if (_isInitialized)
+ return true;
+
try
{
- _deviceIds = new List();
+ // 初始化设备ID列表
+ _deviceIds = new List(); // 保持与类中定义一致
_reconnectTimer = null;
_heartbeatTimer = null;
+ _connectionCheckTimer = null;
+
+ // 首先调用SDK静态初始化方法 - 这是连接成功的关键步骤!
+ int initResult = A8SDK.SDK_initialize();
+ if (initResult != 0)
+ {
+ Console.WriteLine($"SDK静态初始化失败,返回值: {initResult}");
+ _isInitialized = false;
+ return false;
+ }
+
+ Console.WriteLine("SDK静态初始化成功");
_isInitialized = true;
return true;
}
catch (Exception ex)
{
+ Console.WriteLine($"SDK初始化失败: {ex.Message}");
_isInitialized = false;
OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败"));
return false;
@@ -192,22 +330,65 @@ namespace JoyD.Windows.CS.Toprie
}
///
- /// 检查IP是否可达
+ /// 尝试ping设备IP地址
///
- /// IP地址
- /// 是否可达
- private bool IsIpReachable(string ipAddress)
+ /// 设备IP地址
+ /// 是否ping通
+ // PingDevice方法已在下方定义,删除重复实现
+
+ ///
+ /// 检查系统网络连接是否可用
+ ///
+ /// 网络是否可用
+ private bool IsNetworkAvailable()
{
try
{
- using (Ping ping = new Ping())
+ // 检查系统网络连接状态
+ System.Net.NetworkInformation.NetworkInterface[] interfaces =
+ System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
+
+ // 检查是否有活动的以太网或WiFi连接
+ foreach (var ni in interfaces)
{
- PingReply reply = ping.Send(ipAddress, 1000); // 1秒超时
- return reply.Status == IPStatus.Success;
+ // 仅检查物理网络接口(以太网、WiFi等)
+ if (ni.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up &&
+ (ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Ethernet ||
+ ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Wireless80211) &&
+ ni.Name != "本地连接*" && !ni.Description.Contains("虚拟") && !ni.Description.Contains("Virtual"))
+ {
+ // 获取该网络接口的IP信息,检查是否有有效的IP地址
+ var properties = ni.GetIPProperties();
+ foreach (var addr in properties.UnicastAddresses)
+ {
+ // 检查是否有IPv4地址
+ if (addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
+ {
+ // 如果设备IP不为空,可以尝试检查设备IP是否在该接口的子网内
+ if (!string.IsNullOrEmpty(_deviceIp))
+ {
+ try
+ {
+ // 检查设备IP是否与当前网络接口在同一子网(粗略判断)
+ var deviceAddress = System.Net.IPAddress.Parse(_deviceIp);
+ if (deviceAddress.GetAddressBytes()[0] == addr.Address.GetAddressBytes()[0])
+ {
+ return true;
+ }
+ }
+ catch { }
+ }
+ return true;
+ }
+ }
+ }
}
+ return false;
}
- catch (Exception)
+ catch (Exception ex)
{
+ Console.WriteLine($"检查网络可用性异常: {ex.Message}");
+ // 发生异常时默认为网络不可用,避免误判
return false;
}
}
@@ -259,6 +440,246 @@ namespace JoyD.Windows.CS.Toprie
{
ConnectionStatusChanged?.Invoke(this, e);
}
+
+ ///
+ /// 启动连接状态检查
+ ///
+ private void StartConnectionCheck()
+ {
+ try
+ {
+ // 首先停止现有的连接检查,确保资源释放
+ StopConnectionCheck();
+
+ Console.WriteLine("启动连接状态检查,每5秒检查一次");
+
+ // 使用try-catch包装定时器创建,避免异常
+ try
+ {
+ // 每5秒检查一次连接状态
+ _connectionCheckTimer = new System.Threading.Timer(state =>
+ {
+ // 确保定时器状态检查在工作线程中安全执行
+ try
+ {
+ // 避免在定时器回调中可能的资源访问冲突
+ if (_connectionCheckTimer == null)
+ return;
+
+ // 关键修改:即使_isInitialized为false,也需要检查连接状态
+ // 当显示为已连接但初始化状态为false时,必须检查连接
+ if (_connectionStatus == ConnectionStatus.Connected)
+ {
+ // 如果初始化失败但显示已连接,尝试重新初始化
+ if (!_isInitialized)
+ {
+ Console.WriteLine("警告: 显示已连接但初始化状态为false,尝试重新初始化...");
+ if (!Initialize())
+ {
+ Console.WriteLine("重新初始化失败,确认连接已断开");
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备未初始化,连接已断开");
+ // 启动自动重连
+ if (_autoReconnectEnabled)
+ {
+ StartAutoReconnect();
+ }
+ }
+ }
+
+ // 无论初始化状态如何,只要显示为已连接就进行检查
+ CheckConnectionWrapper();
+ }
+ // 正常情况下的连接检查
+ else if (_isInitialized && _a8Sdk != null && _currentDeviceId != -1)
+ {
+ CheckConnectionWrapper();
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"定时器回调异常: {ex.Message}");
+ // 异常时如果是已连接状态,将其设为断开
+ if (_connectionStatus == ConnectionStatus.Connected)
+ {
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接检查异常", ex);
+ _isInitialized = false;
+ // 启动自动重连
+ if (_autoReconnectEnabled)
+ {
+ StartAutoReconnect();
+ }
+ }
+ // 发生异常时停止定时器,避免持续报错
+ StopConnectionCheck();
+ }
+ }, null, 5000, 5000);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"创建连接检查定时器失败: {ex.Message}");
+ _connectionCheckTimer = null;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"启动连接检查异常: {ex.Message}");
+ StopConnectionCheck();
+ }
+ }
+
+ // 连接检查的安全包装方法
+ private void CheckConnectionWrapper()
+ {
+ try
+ {
+ // 检查连接有效性
+ bool isStillConnected = CheckConnectionValidity();
+
+ // 连接状态变化时更新状态
+ if (!isStillConnected && _connectionStatus == ConnectionStatus.Connected)
+ {
+ Console.WriteLine("检测到连接已断开");
+ // 断开连接时重置初始化状态和SDK实例
+ _isInitialized = false;
+ _a8Sdk = null;
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接已断开:设备离线");
+ // 断开连接后自动启动重连
+ if (_autoReconnectEnabled)
+ {
+ Console.WriteLine("连接断开,启动自动重连");
+ StartAutoReconnect();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"检查连接状态异常: {ex.Message}");
+ // 检查异常时,将状态设为断开
+ if (_connectionStatus == ConnectionStatus.Connected)
+ {
+ // 异常时重置初始化状态和SDK实例
+ _isInitialized = false;
+ _a8Sdk = null;
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接状态检查异常", ex);
+ // 异常时启动自动重连
+ if (_autoReconnectEnabled)
+ {
+ Console.WriteLine("检查异常,启动自动重连");
+ StartAutoReconnect();
+ }
+ }
+ }
+ }
+
+ ///
+ /// 检查连接有效性的内部方法
+ ///
+ /// 连接是否有效
+ private bool CheckConnectionValidity()
+ {
+ try
+ {
+ // 重要修改:先检查设备ID有效性,避免后续无效检查
+ if (_currentDeviceId == -1)
+ {
+ Console.WriteLine("当前设备ID无效");
+ return false;
+ }
+
+ // 优化1: 优先尝试ping设备IP地址,这是物理连接断开的最直接检测
+ if (!PingDevice(_deviceIp))
+ {
+ Console.WriteLine($"设备IP {_deviceIp} 不可达,物理连接可能已断开");
+ return false;
+ }
+
+ // 优化2: 系统网络接口检测作为辅助检查,不再作为首要条件
+ if (!IsNetworkAvailable())
+ {
+ Console.WriteLine("警告: 系统网络连接不可用,但ping检测通过");
+ }
+
+ // 优化3: 加强SDK连接状态验证,参考热像仪示例的实现方式
+ try
+ {
+ // 确保SDK实例存在
+ if (_a8Sdk == null)
+ {
+ Console.WriteLine("SDK实例不存在,重新初始化...");
+ _a8Sdk = new A8SDK(_deviceIp);
+ }
+
+ // 简化心跳检测逻辑,避免在心跳失败时过度访问可能无效的资源
+ bool heartbeatResult = _a8Sdk.Heartbeat();
+ if (!heartbeatResult)
+ {
+ Console.WriteLine("SDK心跳检测失败");
+ // 心跳失败时,安全释放SDK实例
+ if (_a8Sdk != null)
+ {
+ _a8Sdk = null;
+ }
+ return false;
+ }
+ Console.WriteLine("SDK心跳检测成功");
+
+ // 设备名称获取使用try-catch,避免在心跳成功但设备响应异常时崩溃
+ try
+ {
+ // 如果SDK支持获取设备名称,可以尝试获取
+ // string deviceName = _a8Sdk.Get_device_name();
+ // if (string.IsNullOrEmpty(deviceName))
+ // {
+ // Console.WriteLine("设备名称获取失败,可能连接不稳定");
+ // }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"获取设备名称异常,但心跳检测成功: {ex.Message}");
+ // 设备名称获取失败不应导致整体连接失效
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"SDK心跳检测失败: {ex.Message}");
+ return false;
+ }
+
+ // 所有检查通过,连接有效
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"连接有效性检查异常: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 停止连接状态检查
+ ///
+ private void StopConnectionCheck()
+ {
+ try
+ {
+ // 使用局部变量暂存,避免多线程访问时的空引用问题
+ System.Threading.Timer timerToDispose = Interlocked.Exchange(ref _connectionCheckTimer, null);
+ if (timerToDispose != null)
+ {
+ // 立即停止定时器,避免回调执行
+ timerToDispose.Change(Timeout.Infinite, Timeout.Infinite);
+ // 安全释放定时器资源
+ timerToDispose.Dispose();
+ Console.WriteLine("连接检查已停止");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"停止连接检查异常: {ex.Message}");
+ // 确保即使异常,引用也被清空
+ _connectionCheckTimer = null;
+ }
+ }
///
/// 触发连接异常事件
@@ -427,6 +848,520 @@ namespace JoyD.Windows.CS.Toprie
#endregion 公共属性
+ #region 图像接收方法
+
+ ///
+ /// 开始接收图像数据
+ ///
+ public void StartImageReceiving()
+ {
+ try
+ {
+ // 确保之前的连接已关闭
+ StopImageReceiving();
+
+ // 重置停止事件
+ _stopImageEvent = new ManualResetEvent(false);
+ _isReceivingImages = true;
+
+ // 创建并启动图像接收线程
+ _imageReceiveThread = new Thread(ReceiveImageDataWithHttpWebRequest)
+ {
+ IsBackground = true,
+ Name = "ImageReceiveThread"
+ };
+ _imageReceiveThread.Start();
+ }
+ catch (Exception ex)
+ {
+ // 记录异常
+ Console.WriteLine("StartImageReceiving error: " + ex.Message);
+ _isReceivingImages = false;
+ }
+ }
+
+ ///
+ /// 停止接收图像数据
+ ///
+ public void StopImageReceiving()
+ {
+ try
+ {
+ _isReceivingImages = false;
+
+ // 通知线程停止
+ if (_stopImageEvent != null)
+ {
+ _stopImageEvent.Set();
+ }
+
+ // 等待线程结束
+ if (_imageReceiveThread != null && _imageReceiveThread.IsAlive)
+ {
+ _imageReceiveThread.Join(3000); // 最多等待3秒
+ _imageReceiveThread = null;
+ }
+
+ // 停止重连线程
+ if (_imageReconnectThread != null && _imageReconnectThread.IsAlive)
+ {
+ _imageReconnectThread.Join(1000);
+ _imageReconnectThread = null;
+ }
+
+ // 释放资源
+ if (_imageStream != null)
+ {
+ _imageStream.Close();
+ _imageStream.Dispose();
+ _imageStream = null;
+ }
+
+ if (_imageTcpClient != null)
+ {
+ _imageTcpClient.Close();
+ _imageTcpClient = null;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("StopImageReceiving error: " + ex.Message);
+ }
+ }
+
+ ///
+ /// 使用HttpWebRequest接收图像数据
+ ///
+ private void ReceiveImageDataWithHttpWebRequest()
+ {
+ try
+ {
+ string url = string.Format("http://{0}:8080{1}", DeviceIp, _isInfraredMode ? "/img.jpg" : "/visible.jpg");
+ while (_isReceivingImages)
+ {
+ // 添加时间戳避免缓存
+ 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"; // 模拟浏览器
+
+ 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);
+ StartImageReconnect();
+ }
+ }
+ }
+
+ ///
+ /// 处理接收到的图像数据
+ ///
+ /// 原始数据
+ private void ProcessReceivedImageData(byte[] data)
+ {
+ if (data == null || data.Length == 0)
+ return;
+
+ // 检查是否为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);
+ }
+ }
+
+ ///
+ /// 验证并处理图像数据
+ ///
+ /// 图像数据
+ 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 receivedImage = Image.FromStream(ms);
+ OnImageReceived(new ImageReceivedEventArgs(imageData, _currentImageMode));
+ return;
+ }
+ }
+ }
+
+ // 对于其他格式或无法确认完整度的情况,直接尝试创建
+ Image receivedImage = Image.FromStream(ms);
+ OnImageReceived(new ImageReceivedEventArgs(imageData, _currentImageMode));
+ }
+ }
+ catch (Exception ex)
+ {
+ 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 receivedImage = Image.FromStream(ms);
+ OnImageReceived(new ImageReceivedEventArgs(validImageData, _currentImageMode));
+ }
+ }
+ catch
+ {
+ // 二次尝试也失败,放弃处理
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// 启动图像重连
+ ///
+ private void StartImageReconnect()
+ {
+ // 避免重复创建重连线程
+ 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 图像处理辅助方法
+
+ ///
+ /// 查找图像数据的结束位置(特别是JPEG的EOF标记)
+ ///
+ /// 数据缓冲区
+ /// 开始查找的位置
+ /// 图像结束位置索引,-1表示未找到
+ 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;
+ }
+
+ ///
+ /// 检查数据是否为HTTP响应
+ ///
+ /// 待检查的数据
+ /// 是否为HTTP响应
+ 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/");
+ }
+
+ ///
+ /// 查找图像数据的开始位置,跳过HTTP头部
+ ///
+ /// 数据缓冲区
+ /// 图像数据开始位置索引,-1表示未找到
+ 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;
+ }
+
+ ///
+ /// 从HTTP响应中提取Content-Type
+ ///
+ /// HTTP响应数据
+ /// Content-Type字符串
+ 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;
+ }
+
+ ///
+ /// 从Content-Type中提取multipart boundary
+ ///
+ /// Content-Type字符串
+ /// boundary字符串
+ 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;
+ }
+
+ ///
+ /// 处理multipart格式的图像数据
+ ///
+ /// 数据缓冲区
+ /// multipart boundary
+ /// 原始图像数据缓冲区
+ /// 处理到的位置索引,-1表示未找到完整的图像块
+ 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 receivedImage = Image.FromStream(ms);
+ OnImageReceived(new ImageReceivedEventArgs(receivedImage, _currentImageMode));
+ }
+ return nextBoundaryPos;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"处理multipart图像失败: {ex.Message}");
+ }
+ }
+ }
+ return nextBoundaryPos;
+ }
+ nextBoundaryPos++;
+ }
+ break;
+ }
+ startPos++;
+ }
+ return -1;
+ }
+
+ #endregion 图像处理辅助方法
+
#region 视频流处理方法
///
@@ -439,26 +1374,26 @@ namespace JoyD.Windows.CS.Toprie
}
///
- /// 发送模式变更命令
+ /// 发送模式切换命令
///
/// 图像模式
- private void SendModeChangeCommand(ImageMode mode)
+ private bool SendModeChangeCommand(ImageMode mode)
{
try
{
- // 使用HTTP请求来切换图像模式
- string modePath = mode == ImageMode.Thermal ? "thermal.jpg" : "color.jpg";
- string url = $"http://{_deviceIp}:{_devicePort}/{modePath}?mode={mode}";
-
- using (WebClient client = new WebClient())
+ if (_a8Sdk != null)
{
- client.DownloadData(url); // 发送请求但不处理响应
+ // 根据当前图像模式设置SDK的图像模式
+ bool success = _a8Sdk.SetImageMode(mode);
+ return success;
}
+ return false;
}
catch (Exception ex)
{
Console.WriteLine($"切换图像模式失败: {ex.Message}");
- OnConnectionException(new ConnectionExceptionEventArgs(ex, "发送模式变更命令失败"));
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "切换图像模式失败"));
+ return false;
}
}
@@ -496,60 +1431,38 @@ namespace JoyD.Windows.CS.Toprie
{
try
{
+ // 持续获取图像数据
while (!_stopRequested.IsSet)
{
- try
+ if (_a8Sdk != null)
{
- // 根据当前模式选择图像路径
- string imagePath = _currentImageMode == ImageMode.Thermal ? "thermal.jpg" : "color.jpg";
- string url = $"http://{_deviceIp}:{_devicePort}/{imagePath}?{DateTime.Now.Ticks}";
-
- HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
- request.Timeout = 5000; // 5秒超时
- request.Method = "GET";
- request.KeepAlive = true;
-
- using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
+ try
{
- if (response.StatusCode == HttpStatusCode.OK)
+ // 使用A8SDK获取图像数据
+ byte[] imageData = _a8Sdk.GetImageData();
+
+ if (imageData != null && imageData.Length > 0 && IsValidImageData(imageData))
{
- using (Stream stream = response.GetResponseStream())
+ // 创建内存流
+ using (MemoryStream ms = new MemoryStream(imageData))
{
- using (MemoryStream memoryStream = new MemoryStream())
+ // 创建图像
+ using (Image image = Image.FromStream(ms))
{
- byte[] buffer = new byte[4096];
- int bytesRead;
- while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
- {
- memoryStream.Write(buffer, 0, bytesRead);
- }
-
- byte[] imageData = memoryStream.ToArray();
- if (IsValidImageData(imageData))
- {
- using (MemoryStream imageStream = new MemoryStream(imageData))
- {
- Image image = Image.FromStream(imageStream);
- OnImageReceived(new ImageReceivedEventArgs(image));
- }
- }
+ // 触发图像接收事件
+ OnImageReceived(new ImageReceivedEventArgs(image.Clone() as Image));
}
}
}
}
- }
- catch (WebException webEx)
- {
- // 处理超时等网络错误
- if (webEx.Status == WebExceptionStatus.Timeout)
+ catch (Exception ex)
{
- Console.WriteLine("图像接收超时");
- }
- else if (webEx.Status == WebExceptionStatus.ConnectFailure)
- {
- Console.WriteLine("图像连接失败,可能设备已断开");
- // 触发连接断开事件
- UpdateConnectionStatus(ConnectionStatus.Disconnected, "图像连接失败,设备可能已断开", webEx);
+ Console.WriteLine($"SDK获取图像失败: {ex.Message}");
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "SDK获取图像数据异常"));
+
+ // 检查连接状态
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK获取图像失败,设备可能已断开", ex);
+
// 如果启用了自动重连,开始重连
if (_isAutoReconnectEnabled)
{
@@ -557,18 +1470,8 @@ namespace JoyD.Windows.CS.Toprie
}
break; // 退出图像接收线程
}
- else
- {
- Console.WriteLine($"图像接收错误: {webEx.Message}");
- OnConnectionException(new ConnectionExceptionEventArgs(webEx, "图像接收过程中发生异常"));
- }
}
- catch (Exception ex)
- {
- Console.WriteLine($"图像处理错误: {ex.Message}");
- OnConnectionException(new ConnectionExceptionEventArgs(ex, "图像处理过程中发生异常"));
- }
-
+
// 短暂休眠,控制帧率
Thread.Sleep(100);
}
@@ -639,30 +1542,65 @@ namespace JoyD.Windows.CS.Toprie
{
if (!_isInitialized && !Initialize())
{
+ Console.WriteLine("设备管理器未初始化,无法搜索设备");
return new List();
}
List foundDevices = new List();
_deviceIds.Clear();
+ _deviceList.Clear();
- // 1. 尝试UDP广播搜索
- List discoveredIps = BroadcastSearch();
+ // 使用A8SDK的静态方法进行设备搜索
+ List deviceIds = A8SDK.SearchDevices();
- // 2. 如果UDP广播没有找到设备,尝试在本地网络扫描
- if (discoveredIps.Count == 0)
- {
- discoveredIps = ScanLocalNetwork();
- }
-
- // 为每个发现的IP分配一个设备ID
+ Console.WriteLine($"搜索到 {deviceIds.Count} 个设备");
+
+ // 将SDK返回的设备ID和IP转换为我们的格式
int deviceId = 1;
- foreach (string ip in discoveredIps)
+ foreach (string id in deviceIds)
{
- _deviceIds.Add(deviceId);
- foundDevices.Add(deviceId);
- deviceId++;
+ 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)
@@ -678,128 +1616,86 @@ namespace JoyD.Windows.CS.Toprie
///
public Task> SearchDevicesAsync()
{
- return Task.Factory.StartNew(() => SearchDevices());
- }
-
- ///
- /// UDP广播搜索设备
- ///
- /// 发现的设备IP列表
- private List BroadcastSearch()
- {
- List discoveredIps = new List();
-
- try
+ return Task.Factory.StartNew(() =>
{
- // 使用UDP广播查找设备
- using (UdpClient udpClient = new UdpClient())
+ try
{
- udpClient.EnableBroadcast = true;
- udpClient.Client.ReceiveTimeout = 2000;
+ if (!_isInitialized && !Initialize())
+ {
+ Console.WriteLine("设备管理器未初始化,无法搜索设备");
+ return new List();
+ }
- // 广播地址和端口
- IPEndPoint broadcastEndpoint = new IPEndPoint(IPAddress.Broadcast, 8080);
+ List foundDevices = new List();
+ _deviceIds.Clear();
+ _deviceList.Clear();
+
+ // 使用A8SDK的静态方法进行设备搜索
+ List deviceIds = A8SDK.SearchDevices();
- // 发送搜索命令
- byte[] searchCommand = Encoding.ASCII.GetBytes("SEARCH_DEVICE");
- udpClient.Send(searchCommand, searchCommand.Length, broadcastEndpoint);
-
- // 接收响应
- IPEndPoint remoteEndpoint = new IPEndPoint(IPAddress.Any, 0);
+ Console.WriteLine($"搜索到 {deviceIds.Count} 个设备");
- try
+ // 将SDK返回的设备ID和IP转换为我们的格式
+ int deviceId = 1;
+ foreach (string id in deviceIds)
{
- byte[] response = udpClient.Receive(ref remoteEndpoint);
- string responseString = Encoding.ASCII.GetString(response);
-
- // 检查响应是否来自我们的设备
- if (responseString.Contains("DEVICE_FOUND"))
+ try
{
- discoveredIps.Add(remoteEndpoint.Address.ToString());
- }
- }
- catch (SocketException ex)
- {
- // 超时异常,正常处理
- if (ex.SocketErrorCode != SocketError.TimedOut)
- {
- Console.WriteLine($"UDP接收异常: {ex.Message}");
- }
- }
- }
-
- // 额外检查默认IP是否可达
- if (IsIpReachable(_deviceIp))
- {
- if (!discoveredIps.Contains(_deviceIp))
- {
- discoveredIps.Add(_deviceIp);
- }
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine($"UDP广播搜索失败: {ex.Message}");
- }
-
- return discoveredIps;
- }
-
- ///
- /// 扫描本地网络
- ///
- /// 发现的设备IP列表
- private List ScanLocalNetwork()
- {
- List discoveredIps = new List();
-
- try
- {
- // 获取本地IP地址和子网
- foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
- {
- if (ni.OperationalStatus == OperationalStatus.Up && ni.NetworkInterfaceType != NetworkInterfaceType.Loopback)
- {
- foreach (UnicastIPAddressInformation ipInfo in ni.GetIPProperties().UnicastAddresses)
- {
- if (ipInfo.Address.AddressFamily == AddressFamily.InterNetwork)
+ // 假设ID字符串中包含IP信息,格式可能是 "设备ID|IP地址"
+ string[] parts = id.Split('|');
+ string ipAddress = parts.Length > 1 ? parts[1] : "未知IP";
+
+ // 创建设备信息对象
+ DeviceInfo deviceInfo = new DeviceInfo
{
- IPAddress ip = ipInfo.Address;
- IPAddress subnet = GetSubnetMask(ipInfo.IPv4Mask);
-
- // 扫描常见的设备IP范围
- // 这里简化处理,只扫描几个常用的IP
- string[] commonDeviceIps = new string[]
- {
- "192.168.100.2", // 默认IP
- "192.168.1.10",
- "192.168.0.10",
- "10.0.0.10"
- };
-
- foreach (string deviceIp in commonDeviceIps)
- {
- if (IsIpReachable(deviceIp))
- {
- if (!discoveredIps.Contains(deviceIp))
- {
- discoveredIps.Add(deviceIp);
- }
- }
- }
- }
+ 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}");
+ // 继续处理下一个设备
}
}
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine($"本地网络扫描失败: {ex.Message}");
- }
- return discoveredIps;
+ // 如果通过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();
+ }
+ });
}
+ // BroadcastSearch方法已被A8SDK.SearchDevices替代
+
+ // ScanLocalNetwork方法已被A8SDK.SearchDevices替代
+
///
/// 获取子网掩码
///
@@ -815,116 +1711,278 @@ namespace JoyD.Windows.CS.Toprie
///
/// 是否连接成功
public bool ConnectDevice()
+ {
+ // 默认使用ID=1连接设备
+ ConnectDevice(1);
+ return _connectionStatus == ConnectionStatus.Connected;
+ }
+
+ public void ConnectDevice(int deviceId)
{
try
{
+ // 取消之前的连接操作
+ if (_connectCancellationTokenSource != null)
+ {
+ _connectCancellationTokenSource.Cancel();
+ _connectCancellationTokenSource.Dispose();
+ }
+
+ // 更新状态为连接中
UpdateConnectionStatus(ConnectionStatus.Connecting, "开始连接设备...");
-
- // 确保已初始化
- if (!_isInitialized && !Initialize())
- {
- UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备管理器初始化失败");
- return false;
- }
- // 停止任何现有的重连定时器
- StopAutoReconnect();
+ _connectCancellationTokenSource = new CancellationTokenSource();
+ CancellationToken token = _connectCancellationTokenSource.Token;
- // 停止任何正在进行的连接检查
- StopAllImageReceivers();
-
- // 检查IP是否可达
- if (!IsIpReachable(_deviceIp))
+ // 使用Timer实现超时取消
+ System.Threading.Timer timeoutTimer = null;
+ timeoutTimer = new System.Threading.Timer((state) =>
{
- UpdateConnectionStatus(ConnectionStatus.Disconnected, $"设备IP {_deviceIp} 不可达");
- if (_isAutoReconnectEnabled)
+ if (_connectCancellationTokenSource != null && !_connectCancellationTokenSource.IsCancellationRequested)
{
- StartAutoReconnect();
+ _connectCancellationTokenSource.Cancel();
+ Console.WriteLine($"连接超时,自动取消连接操作");
}
- return false;
- }
+ timeoutTimer.Dispose();
+ }, null, _connectTimeout, Timeout.Infinite);
- // 尝试TCP连接测试
- bool tcpConnected = TestTcpConnection(_deviceIp, _devicePort);
- if (!tcpConnected)
+ try
{
- UpdateConnectionStatus(ConnectionStatus.Disconnected, $"TCP连接到 {_deviceIp}:{_devicePort} 失败");
- if (_isAutoReconnectEnabled)
+ bool result = false;
+ if (!token.IsCancellationRequested)
{
- StartAutoReconnect();
- }
- return false;
- }
+ try
+ {
+ // 确保已初始化
+ if (!_isInitialized && !Initialize())
+ {
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备管理器初始化失败");
+ return;
+ }
- // 发送连接命令
- bool commandSent = SendConnectCommand();
- if (!commandSent)
+ // 停止任何现有的重连定时器
+ 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}");
+
+ // 先检测IP可达性,避免不必要的SDK初始化
+ if (!PingDevice(_deviceIp))
+ {
+ Console.WriteLine($"设备IP {_deviceIp} 不可达,连接失败");
+ throw new Exception($"设备IP {_deviceIp} 不可达");
+ }
+
+ // 创建SDK实例
+ _a8Sdk = new A8SDK(_deviceIp);
+ Console.WriteLine("SDK实例创建完成");
+
+ // 验证连接是否成功(通过心跳检测)
+ if (!_a8Sdk.Heartbeat())
+ {
+ Console.WriteLine("心跳检测失败");
+ // 安全释放SDK实例
+ if (_a8Sdk != null)
+ {
+ _a8Sdk = null;
+ }
+ throw new Exception("心跳检测失败");
+ }
+ Console.WriteLine("心跳检测成功,设备连接有效");
+
+ // 连接成功
+ result = true;
+
+ // 安全地设置设备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;
+ }
+ 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;
+ _reconnectAttempts = 0;
+
+ // 启动心跳检测和连接检查
+ StartHeartbeat();
+ StartConnectionCheck();
+
+ UpdateConnectionStatus(ConnectionStatus.Connected, "设备连接成功");
+ }
+ else if (!token.IsCancellationRequested)
+ {
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK连接失败,心跳检测未通过");
+ _a8Sdk = null;
+ }
+ }
+ finally
{
- UpdateConnectionStatus(ConnectionStatus.Disconnected, "发送连接命令失败");
- if (_isAutoReconnectEnabled)
+ // 确保即使发生异常,也停止定时器
+ if (timeoutTimer != null)
{
- StartAutoReconnect();
+ timeoutTimer.Dispose();
}
- return false;
}
-
- // 启动心跳检测
- StartHeartbeat();
-
- UpdateConnectionStatus(ConnectionStatus.Connected, "设备连接成功");
-
- // 重置重连尝试计数
- _reconnectAttempts = 0;
-
- return true;
}
catch (Exception ex)
{
Console.WriteLine($"连接设备失败: {ex.Message}");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接设备时发生异常", ex);
OnConnectionException(new ConnectionExceptionEventArgs(ex, "连接设备失败"));
-
- // 如果启用了自动重连,开始重连
- if (_isAutoReconnectEnabled)
- {
- StartAutoReconnect();
- }
-
- return false;
+ }
+
+ // 如果连接失败且启用了自动重连,开始重连
+ if (_connectionStatus != ConnectionStatus.Connected && _isAutoReconnectEnabled)
+ {
+ StartAutoReconnect();
}
}
-
+
///
- /// 测试TCP连接
+ /// 连接到指定设备(异步版本)
///
- /// IP地址
- /// 端口
- /// 是否连接成功
- private bool TestTcpConnection(string ip, int port)
+ public Task ConnectAsync()
+ {
+ var taskCompletionSource = new TaskCompletionSource();
+
+ try
+ {
+ ConnectDevice();
+ taskCompletionSource.SetResult(_connectionStatus == ConnectionStatus.Connected);
+ }
+ catch (Exception ex)
+ {
+ taskCompletionSource.SetException(ex);
+ }
+
+ return taskCompletionSource.Task;
+ }
+
+ ///
+ /// 根据设备ID连接设备
+ ///
+ /// 设备ID
+ public void ConnectDevice(int deviceId)
{
try
{
- using (TcpClient client = new TcpClient())
+ _targetDeviceId = deviceId;
+
+ // 检查是否已有设备列表
+ if (_deviceList != null && _deviceList.Count > 0)
{
- IAsyncResult result = client.BeginConnect(ip, port, null, null);
- bool success = result.AsyncWaitHandle.WaitOne(2000); // 2秒超时
- if (success)
+ // 查找指定ID的设备
+ var device = _deviceList.FirstOrDefault(d => d.DeviceID == deviceId);
+ if (device != null)
{
- client.EndConnect(result);
- return true;
- }
- else
- {
- client.Close();
- return false;
+ _deviceIp = device.IPAddress;
+ ConnectDevice();
+ return;
}
}
+
+ // 如果找不到设备,先搜索设备
+ SearchDevicesAsync();
+
+ // 延迟后尝试连接
+ Task.Delay(500).ContinueWith(t =>
+ {
+ if (_deviceList != null && _deviceList.Count > 0)
+ {
+ var device = _deviceList.FirstOrDefault(d => d.DeviceID == deviceId);
+ if (device != null)
+ {
+ _deviceIp = device.IPAddress;
+ ConnectDevice();
+ }
+ }
+ });
}
- catch (Exception)
+ catch (Exception ex)
{
- return false;
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, $"通过设备ID连接异常: {ex.Message}", ex);
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "通过设备ID连接失败"));
}
}
+
+ ///
+ /// 断开设备连接(兼容Form1)
+ ///
+ public void DisconnectDevice()
+ {
+ Disconnect();
+ }
+
+ // TestTcpConnection方法不再需要,由A8SDK内部处理连接测试
///
/// 发送连接命令
@@ -965,22 +2023,51 @@ namespace JoyD.Windows.CS.Toprie
{
try
{
- // 停止重连定时器
+ // 停止自动重连和连接检查
StopAutoReconnect();
+ StopConnectionCheck();
- // 停止心跳检测
- StopHeartbeat();
-
- // 停止图像接收
- StopAllImageReceivers();
-
- // 设置断开状态
- UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备已断开连接");
+ // 取消正在进行的连接操作
+ 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}");
- OnConnectionException(new ConnectionExceptionEventArgs(ex, "断开连接失败"));
+ Console.WriteLine($"断开连接异常: {ex.Message}");
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "断开连接时发生异常", ex);
+ OnConnectionException(new ConnectionExceptionEventArgs(
+ ex, "断开连接异常"));
}
}
@@ -996,15 +2083,69 @@ namespace JoyD.Windows.CS.Toprie
_reconnectTimer = new System.Threading.Timer(ReconnectCallback, null, 0, _reconnectInterval);
}
+ ///
+ /// 启动定期连接状态检查
+ ///
+ /// 检查间隔(毫秒)
+ public void StartPeriodicConnectionCheck(int checkInterval)
+ {
+ try
+ {
+ // 停止现有的检查
+ 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, "启动连接检查失败"));
+ }
+ }
+
///
/// 停止自动重连
///
private void StopAutoReconnect()
{
- if (_reconnectTimer != null)
+ try
{
- _reconnectTimer.Change(Timeout.Infinite, Timeout.Infinite);
- _reconnectTimer.Dispose();
+ // 使用局部变量暂存,避免多线程访问时的空引用问题
+ System.Threading.Timer timerToDispose = Interlocked.Exchange(ref _reconnectTimer, null);
+ if (timerToDispose != null)
+ {
+ // 立即停止定时器,避免回调执行
+ timerToDispose.Change(Timeout.Infinite, Timeout.Infinite);
+ // 安全释放定时器资源
+ timerToDispose.Dispose();
+ Console.WriteLine("自动重连已停止");
+ }
+
+ // 重置重连尝试次数
+ _currentReconnectAttempt = 0;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"停止自动重连异常: {ex.Message}");
+ // 确保即使异常,引用也被清空
_reconnectTimer = null;
}
}
@@ -1017,35 +2158,184 @@ namespace JoyD.Windows.CS.Toprie
{
try
{
- // 检查是否达到最大重连次数
- if (_maxReconnectAttempts > 0 && _reconnectAttempts >= _maxReconnectAttempts)
+ _currentReconnectAttempt++;
+ Console.WriteLine($"自动重连尝试 {_currentReconnectAttempt}");
+
+ UpdateConnectionStatus(ConnectionStatus.Reconnecting,
+ $"尝试自动重连...");
+
+ bool connectionSuccessful = false;
+
+ // 仅用IP地址重连设备,这样更直接更快
+ if (!string.IsNullOrEmpty(_deviceIp))
{
- UpdateConnectionStatus(ConnectionStatus.Disconnected, $"达到最大重连次数 {_maxReconnectAttempts}");
+ Console.WriteLine($"用IP地址 {_deviceIp} 重连设备");
+ try
+ {
+ // 检查IP是否可达
+ if (PingDevice(_deviceIp))
+ {
+ // 注意:这里需要先释放旧实例,避免资源泄漏
+ if (_a8Sdk != null)
+ {
+ try
+ {
+ // 调用静态SDK_destroy方法释放全局资源
+ // A8SDK.SDK_destroy();
+ Console.WriteLine("重连前SDK全局资源已释放");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"释放旧SDK实例资源时发生异常: {ex.Message}");
+ }
+ finally
+ {
+ _a8Sdk = null; // 确保引用被清空
+ }
+ }
+
+ // 创建新的SDK实例
+ Console.WriteLine($"创建新的SDK实例,目标IP: {_deviceIp}");
+ _a8Sdk = new A8SDK(_deviceIp);
+
+ // 添加心跳检测重试机制,适应网络不稳定情况
+ bool isConnected = false;
+ int maxHeartbeatRetries = 3; // 心跳检测最大重试次数
+
+ for (int retry = 0; retry < maxHeartbeatRetries; retry++)
+ {
+ try
+ {
+ Console.WriteLine($"尝试建立连接并发送心跳包...(尝试 {retry + 1}/{maxHeartbeatRetries})");
+
+ // 添加延时,给SDK实例初始化一些时间
+ if (retry > 0)
+ {
+ Console.WriteLine("等待500ms后重试...");
+ Thread.Sleep(500);
+ }
+
+ // 发送心跳包验证连接
+ bool heartbeatResult = _a8Sdk.Heartbeat();
+ if (heartbeatResult)
+ {
+ Console.WriteLine("心跳检测成功!");
+ isConnected = true;
+ break;
+ }
+ else
+ {
+ Console.WriteLine("心跳检测失败");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"心跳检测异常: {ex.Message}");
+ }
+ }
+
+ if (isConnected)
+ {
+ // 连接成功,进行额外验证
+ try
+ {
+ Console.WriteLine("进行额外连接验证...");
+ // 再次发送心跳包确保连接稳定
+ bool finalResult = _a8Sdk.Heartbeat();
+ if (finalResult)
+ {
+ Console.WriteLine($"使用IP地址 {_deviceIp} 重连成功");
+ _currentDeviceId = 1; // 临时ID,确保状态更新正确
+ UpdateConnectionStatus(ConnectionStatus.Connected, $"设备 {_deviceIp} 连接成功");
+ StartConnectionCheck();
+ connectionSuccessful = true;
+ }
+ else
+ {
+ Console.WriteLine("最终验证失败");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"连接验证异常: {ex.Message}");
+ }
+ }
+
+ // 如果连接不成功,释放资源
+ if (!connectionSuccessful)
+ {
+ Console.WriteLine($"使用IP地址 {_deviceIp} 重连失败,所有心跳尝试都未成功");
+ _a8Sdk = null;
+ }
+ }
+ else
+ {
+ Console.WriteLine($"IP地址 {_deviceIp} 不可达,等待设备上线");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"使用IP地址重连异常: {ex.Message}");
+ }
+ }
+
+ // 如果IP地址连接失败,再尝试使用设备ID连接(保持兼容性)
+ if (!connectionSuccessful && _currentDeviceId != -1)
+ {
+ Console.WriteLine($"尝试使用设备ID {_currentDeviceId} 连接");
+ ConnectDevice(_currentDeviceId);
+ if (_connectionStatus == ConnectionStatus.Connected)
+ {
+ connectionSuccessful = true;
+ }
+ }
+
+ // 如果没有保存的设备ID,但有搜索到的设备,尝试连接第一个
+ if (!connectionSuccessful && _deviceIds != null && _deviceIds.Count > 0)
+ {
+ Console.WriteLine($"尝试使用搜索到的设备列表中的第一个设备");
+ ConnectDevice(_deviceIds[0]);
+ if (_connectionStatus == ConnectionStatus.Connected)
+ {
+ connectionSuccessful = true;
+ }
+ }
+
+ // 如果连接成功,停止重连定时器
+ if (connectionSuccessful)
+ {
+ Console.WriteLine("设备连接成功,停止自动重连");
StopAutoReconnect();
return;
}
-
- _reconnectAttempts++;
- UpdateConnectionStatus(ConnectionStatus.Reconnecting, $"尝试重连... (第 {_reconnectAttempts} 次)");
- // 检查IP是否可达
- if (IsIpReachable(_deviceIp))
+ // 设置下一次重连的时间间隔
+ int retryInterval = _reconnectInterval;
+ if (_currentReconnectAttempt % 5 == 0) // 每5次尝试后增加间隔
{
- // 尝试连接
- bool connected = ConnectDevice();
- if (connected)
- {
- UpdateConnectionStatus(ConnectionStatus.Connected, "自动重连成功");
- }
+ retryInterval = Math.Min(retryInterval * 2, 10000); // 最大10秒
+ Console.WriteLine($"调整重连间隔为 {retryInterval}ms");
+ }
+ Console.WriteLine($"重连失败,{retryInterval}ms后再次尝试");
+
+ // 更新定时器,确保下一次重连
+ if (_reconnectTimer != null)
+ {
+ _reconnectTimer.Change(retryInterval, Timeout.Infinite);
}
}
catch (Exception ex)
{
- Console.WriteLine($"自动重连时发生异常: {ex.Message}");
- OnConnectionException(new ConnectionExceptionEventArgs(ex, "自动重连失败"));
+ Console.WriteLine($"重连回调函数发生异常: {ex.Message}");
+ // 确保定时器继续工作,防止重连机制中断
+ if (_reconnectTimer != null)
+ {
+ _reconnectTimer.Change(_reconnectInterval, Timeout.Infinite);
+ }
}
}
+
///
/// 开始心跳检测
///
@@ -1080,7 +2370,7 @@ namespace JoyD.Windows.CS.Toprie
try
{
// 检查设备是否可达
- if (!IsIpReachable(_deviceIp))
+ if (!PingDevice(_deviceIp))
{
Console.WriteLine("心跳检测失败,设备不可达");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "心跳检测失败,设备不可达");
@@ -1093,25 +2383,24 @@ namespace JoyD.Windows.CS.Toprie
return;
}
- // 尝试发送心跳请求
- string url = $"http://{_deviceIp}:{_devicePort}/heartbeat";
- try
+ // 使用SDK的Heartbeat方法进行心跳检测
+ if (_a8Sdk != null)
{
- WebRequest request = WebRequest.Create(url);
- request.Timeout = 2000; // 2秒超时
- using (WebResponse response = request.GetResponse())
+ if (!_a8Sdk.Heartbeat())
{
- using (Stream dataStream = response.GetResponseStream())
+ Console.WriteLine("SDK心跳检测失败");
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK心跳检测失败");
+
+ // 如果启用了自动重连,开始重连
+ if (_isAutoReconnectEnabled)
{
- // 只需确保请求成功,不需要读取内容
- dataStream.ReadByte();
+ StartAutoReconnect();
}
}
}
- catch (Exception)
+ else
{
- // 心跳请求失败,但继续使用PING作为备用检测
- Console.WriteLine("心跳请求失败,但PING检测通过");
+ Console.WriteLine("SDK实例不存在");
}
}
catch (Exception ex)
@@ -1127,6 +2416,383 @@ namespace JoyD.Windows.CS.Toprie
private bool _disposed = false;
+ ///
+ /// 释放资源
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ Console.WriteLine($"温度数据已成功保存到: {filePath}");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"写入CSV文件失败: {ex.Message}");
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"保存温度数据到CSV时发生异常: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 温度数据点结构体
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// 启动定期连接状态检查(兼容Form1)
+ ///
+ public void StartPeriodicConnectionCheck(int intervalMs)
+ {
+ StartConnectionCheck();
+ }
+
+ ///
+ /// 停止定期连接状态检查(兼容Form1)
+ ///
+ public void StopPeriodicConnectionCheck()
+ {
+ StopConnectionCheck();
+ }
+
+ ///
+ /// 检查系统网络连接是否可用
+ ///
+ /// 网络是否可用
+ private bool IsNetworkAvailable()
+ {
+ try
+ {
+ // 检查系统网络连接状态
+ System.Net.NetworkInformation.NetworkInterface[] interfaces =
+ System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
+
+ // 检查是否有活动的以太网或WiFi连接
+ foreach (var ni in interfaces)
+ {
+ // 仅检查物理网络接口(以太网、WiFi等)
+ if (ni.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up &&
+ (ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Ethernet ||
+ ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Wireless80211) &&
+ ni.Name != "本地连接*" && !ni.Description.Contains("虚拟") && !ni.Description.Contains("Virtual"))
+ {
+ // 获取该网络接口的IP信息,检查是否有有效的IP地址
+ var properties = ni.GetIPProperties();
+ foreach (var addr in properties.UnicastAddresses)
+ {
+ // 检查是否有IPv4地址
+ if (addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
+ {
+ // 如果设备IP不为空,可以尝试检查设备IP是否在该接口的子网内
+ if (!string.IsNullOrEmpty(_deviceIp))
+ {
+ try
+ {
+ // 检查设备IP是否与当前网络接口在同一子网(粗略判断)
+ var deviceAddress = System.Net.IPAddress.Parse(_deviceIp);
+ if (deviceAddress.GetAddressBytes()[0] == addr.Address.GetAddressBytes()[0])
+ {
+ return true;
+ }
+ }
+ catch { }
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"检查网络可用性异常: {ex.Message}");
+ // 发生异常时默认为网络不可用,避免误判
+ return false;
+ }
+ }
+
+ ///
+ /// 尝试ping设备IP地址
+ ///
+ /// 设备IP地址
+ /// 是否ping通
+ // PingDevice方法已在文件上方定义,删除重复实现
+
+ // 图像相关变量
+ private Thread _imageReceiveThread;
+ private Thread _imageReconnectThread;
+ private bool _isReceivingImages = false;
+ private ManualResetEvent _stopImageThreadEvent = new ManualResetEvent(false);
+ private const int IMAGE_PORT = 8080; // HTTP端口,用于获取图像
+ private TcpClient _imageTcpClient;
+ private NetworkStream _imageStream;
+
+ ///
+ /// 温度数据点结构体,用于存储单个位置的温度数据
+ ///
+ public struct TemperatureDataPoint
+ {
+ ///
+ /// X坐标
+ ///
+ public int X { get; set; }
+
+ ///
+ /// Y坐标
+ ///
+ public int Y { get; set; }
+
+ ///
+ /// 温度值
+ ///
+ public float Temperature { get; set; }
+
+ ///
+ /// 构造函数
+ ///
+ /// X坐标
+ /// Y坐标
+ /// 温度值
+ public TemperatureDataPoint(int x, int y, float temperature)
+ {
+ X = x;
+ Y = y;
+ Temperature = temperature;
+ }
+ }
+
+ ///
+ /// 保存温度数据到CSV文件
+ ///
+ /// CSV文件路径
+ /// 是否保存成功
+ 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 temperatureData = new List();
+
+ try
+ {
+ // 使用A8SDK的Get_all_temp方法获取所有温度数据
+ Console.WriteLine("正在获取设备温度数据...");
+
+ 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, 平均={imageTemp.globa.avg_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(0, 2, imageTemp.globa.avg_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].temp > -273.0f)
+ {
+ Console.WriteLine($"区域 {i+1} 温度: {imageTemp.area[i].temp}°C");
+ temperatureData.Add(new TemperatureDataPoint(i + 1, 0, imageTemp.area[i].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;
+ }
+ }
+
+ ///
+ /// 生成模拟温度数据
+ ///
+ /// 温度数据列表
+ private void GenerateMockTemperatureData(List 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));
+ }
+ }
+ }
+
+ #region IDisposable 实现
+
+ private bool _disposed = false;
+
///
/// 释放资源
///
@@ -1148,13 +2814,48 @@ namespace JoyD.Windows.CS.Toprie
{
// 释放托管资源
StopAutoReconnect();
+ StopConnectionCheck();
StopHeartbeat();
StopAllImageReceivers();
_stopRequested.Dispose();
}
+ // 安全释放SDK实例,避免内存访问冲突
+ try
+ {
+ if (_a8Sdk != null)
+ {
+ Console.WriteLine("释放SDK实例资源");
+ // 先设置为null,避免在Dispose过程中的访问
+ _a8Sdk = null;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"释放SDK实例异常: {ex.Message}");
+ // 即使异常也确保引用被清空
+ _a8Sdk = null;
+ }
+
+ // 释放全局SDK资源,使用try-catch避免异常传播
+ try
+ {
+ Console.WriteLine("释放SDK全局资源");
+ if (_isInitialized)
+ {
+ // 如果SDK支持,调用静态方法释放全局资源
+ // A8SDK.SDK_destroy();
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"释放SDK全局资源异常: {ex.Message}");
+ }
+
// 释放非托管资源
_isInitialized = false;
+ _connectionStatus = ConnectionStatus.Disconnected;
+ _currentDeviceId = -1;
_disposed = true;
}
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj b/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj
index cde2b8c..b208f93 100644
--- a/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj
@@ -25,6 +25,9 @@
true
true
+
+ Toprie.ico
+
@@ -74,5 +77,8 @@
Camera.cs
+
+
+
\ No newline at end of file
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.ico b/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.ico
new file mode 100644
index 0000000..2cf24ed
Binary files /dev/null and b/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.ico differ