1299 lines
50 KiB
C#
1299 lines
50 KiB
C#
using System;
|
||
using System.IO;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Data;
|
||
using System.Drawing;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Windows.Forms;
|
||
using System.Threading;
|
||
|
||
namespace JoyD.Windows.CS.Toprie
|
||
{
|
||
public partial class Camera : UserControl
|
||
{
|
||
// 设备管理器实例
|
||
private DeviceManager _deviceManager;
|
||
|
||
// 是否正在接收图像
|
||
private bool _isReceivingImage = false;
|
||
|
||
// 显示错误的定时器
|
||
private System.Windows.Forms.Timer _errorDisplayTimer;
|
||
|
||
public Camera()
|
||
{
|
||
InitializeComponent();
|
||
|
||
// 为右键菜单添加Opening事件,用于在菜单显示前更新色彩模式的选中状态
|
||
this.contextMenuStrip1.Opening += ContextMenuStrip1_Opening;
|
||
|
||
// 将设计模式状态传递给DeviceManager
|
||
DeviceManager.IsDesignMode = DesignMode;
|
||
|
||
// 只有在非设计模式下才初始化设备管理器和错误定时器
|
||
if (!DesignMode)
|
||
{
|
||
// 清空现有日志
|
||
try
|
||
{
|
||
string logFile = Path.Combine(Application.StartupPath, "log.txt");
|
||
if (File.Exists(logFile))
|
||
{
|
||
File.WriteAllText(logFile, string.Empty);
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
InitializeDeviceManager();
|
||
InitializeErrorTimer();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Camera控件加载事件
|
||
/// 在控件加载时自动启动相机显示热图
|
||
/// </summary>
|
||
private void Camera_Load(object sender, EventArgs e)
|
||
{
|
||
// 只有在非设计模式下才启动相机
|
||
if (!DesignMode)
|
||
{
|
||
try
|
||
{
|
||
StartCamera();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError($"自动启动相机失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 注意:UserControl不支持FormClosing事件,资源清理已在Dispose方法中处理
|
||
|
||
/// <summary>
|
||
/// 初始化设备管理器
|
||
/// </summary>
|
||
private void InitializeDeviceManager()
|
||
{
|
||
_deviceManager = new DeviceManager
|
||
{
|
||
AutoReconnectEnabled = true,
|
||
ReconnectInterval = 2000 // 2秒
|
||
};
|
||
|
||
// 确保DeviceManager的设计模式状态与控件一致
|
||
DeviceManager.IsDesignMode = DesignMode;
|
||
|
||
// 设置静态属性
|
||
DeviceManager.MaxReconnectAttempts = 5;
|
||
|
||
// 注册图像接收事件
|
||
_deviceManager.ImageReceived += DeviceManager_ImageReceived;
|
||
|
||
// 注册连接状态变更事件
|
||
_deviceManager.ConnectionStatusChanged += DeviceManager_ConnectionStatusChanged;
|
||
|
||
// 注册连接异常事件
|
||
_deviceManager.ConnectionException += DeviceManager_ConnectionException;
|
||
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化错误显示定时器
|
||
/// </summary>
|
||
private void InitializeErrorTimer()
|
||
{
|
||
_errorDisplayTimer = new System.Windows.Forms.Timer { Interval = 3000 }; // 显示3秒后清除
|
||
_errorDisplayTimer.Tick += ErrorDisplayTimer_Tick;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启动相机
|
||
/// </summary>
|
||
public void StartCamera()
|
||
{
|
||
try
|
||
{
|
||
// 只有在没有接收图像时才启动
|
||
if (!_isReceivingImage)
|
||
{
|
||
// 清理错误显示
|
||
ShowError(string.Empty);
|
||
|
||
// 使用异步方式连接设备和设置模式,避免UI线程阻塞
|
||
ThreadPool.QueueUserWorkItem(delegate
|
||
{
|
||
try
|
||
{
|
||
// 设置为热图模式
|
||
bool modeSet = false;
|
||
|
||
// 启用自动重连
|
||
_deviceManager.AutoReconnectEnabled = true;
|
||
|
||
// 尝试连接设备
|
||
if (_deviceManager.ConnectDevice())
|
||
{
|
||
Console.WriteLine("设备连接成功");
|
||
|
||
// 如果热图模式未成功设置,在连接成功后再次尝试
|
||
if (!modeSet)
|
||
{
|
||
try
|
||
{
|
||
_deviceManager.SetImageMode(ImageMode.Infrared);
|
||
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
|
||
{
|
||
Console.WriteLine("相机已在运行状态");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError($"启动相机失败: {ex.Message}");
|
||
Console.WriteLine($"启动相机主流程错误: {ex.Message}");
|
||
Console.WriteLine(ex.StackTrace);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始接收图像(使用HTTP方式)
|
||
/// </summary>
|
||
private void StartReceiveImage()
|
||
{
|
||
try
|
||
{
|
||
if (!_isReceivingImage && _deviceManager.ConnectionStatus == ConnectionStatus.Connected)
|
||
{
|
||
Console.WriteLine("Camera开始使用HTTP方式接收图像");
|
||
// 直接调用HTTP方式的图像接收
|
||
_deviceManager.StartImageReceiving();
|
||
_isReceivingImage = true;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError($"开始接收图像失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止接收图像
|
||
/// </summary>
|
||
public void StopCamera()
|
||
{
|
||
try
|
||
{
|
||
if (_isReceivingImage)
|
||
{
|
||
_deviceManager.StopReceiveImage();
|
||
_isReceivingImage = false;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"停止相机失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设备管理器图像接收事件处理
|
||
/// </summary>
|
||
private void DeviceManager_ImageReceived(object sender, ImageReceivedEventArgs e)
|
||
{
|
||
Image image = null;
|
||
try
|
||
{
|
||
if (e.ImageData != null && e.ImageData.Length > 0)
|
||
{
|
||
// 创建内存流并从流中创建图像
|
||
using (MemoryStream ms = new MemoryStream(e.ImageData))
|
||
{
|
||
// 检查流是否可读且有效
|
||
if (ms.CanRead && ms.Length > 0)
|
||
{
|
||
// 从流中创建图像
|
||
using (Image tempImage = System.Drawing.Image.FromStream(ms))
|
||
{
|
||
// 创建一个全新的位图而不仅仅是克隆,确保数据完整性
|
||
image = new Bitmap(tempImage);
|
||
|
||
// 立即验证新创建的图像是否有效
|
||
try
|
||
{
|
||
// 访问Width和Height属性来验证图像是否有效
|
||
int width = image.Width;
|
||
int height = image.Height;
|
||
if (width <= 0 || height <= 0)
|
||
{
|
||
Console.WriteLine("创建的图像尺寸无效");
|
||
image.Dispose();
|
||
image = null;
|
||
}
|
||
}
|
||
catch (Exception)
|
||
{
|
||
Console.WriteLine("创建的图像无效");
|
||
image.Dispose();
|
||
image = null;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"创建图像失败: {ex.Message}");
|
||
// 确保在异常情况下释放资源
|
||
if (image != null)
|
||
{
|
||
image.Dispose();
|
||
image = null;
|
||
}
|
||
}
|
||
|
||
if (image == null)
|
||
{
|
||
Console.WriteLine("接收到空或无效的图像数据");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 尝试创建图像的克隆以传递给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
|
||
{
|
||
// 创建本地副本以避免闭包问题
|
||
Image imageForUI = clonedImage;
|
||
clonedImage = null; // 防止在异步操作期间被修改
|
||
|
||
// 使用BeginInvoke在UI线程上更新图像,避免可能的死锁问题
|
||
this.BeginInvoke(new Action(() =>
|
||
{
|
||
try
|
||
{
|
||
UpdateImageOnUI(imageForUI);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"更新UI图像失败: {ex.Message}");
|
||
// 如果UI更新失败,确保克隆的图像被释放
|
||
if (imageForUI != null)
|
||
{
|
||
try
|
||
{
|
||
imageForUI.Dispose();
|
||
}
|
||
catch {}
|
||
}
|
||
}
|
||
}));
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// 异常情况下确保释放图像资源
|
||
if (clonedImage != null)
|
||
{
|
||
try
|
||
{
|
||
clonedImage.Dispose();
|
||
}
|
||
catch {}
|
||
clonedImage = null;
|
||
}
|
||
throw;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 在UI线程上直接更新图像
|
||
UpdateImageOnUI(clonedImage);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果控件已释放,确保释放图像资源
|
||
clonedImage.Dispose();
|
||
}
|
||
|
||
// 释放原始图像资源
|
||
image.Dispose();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"处理图像接收事件失败: {ex.Message}");
|
||
// 确保在任何异常情况下都释放原始图像
|
||
if (image != null)
|
||
{
|
||
try
|
||
{
|
||
image.Dispose();
|
||
}
|
||
catch {}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在UI线程上更新图像
|
||
/// </summary>
|
||
private void UpdateImageOnUI(Image image)
|
||
{
|
||
// 线程安全检查 - 确保在UI线程上执行
|
||
if (this.InvokeRequired)
|
||
{
|
||
try
|
||
{
|
||
this.BeginInvoke(new Action<Image>(UpdateImageOnUI), image);
|
||
}
|
||
catch (ObjectDisposedException)
|
||
{
|
||
// 如果控件已被释放,确保释放图像资源
|
||
if (image != null)
|
||
{
|
||
try { image.Dispose(); } catch {}
|
||
}
|
||
Console.WriteLine("控件已释放,跳过UI更新");
|
||
}
|
||
return;
|
||
}
|
||
// 空值检查
|
||
if (image == null)
|
||
{
|
||
Console.WriteLine("传入UpdateImageOnUI的图像为空");
|
||
return;
|
||
}
|
||
// 连接状态检查 - 只在设备实际连接时处理图像
|
||
if (_deviceManager.ConnectionStatus != ConnectionStatus.Connected)
|
||
{
|
||
Console.WriteLine("设备未连接,跳过图像更新");
|
||
try { image.Dispose(); } catch {}
|
||
return;
|
||
}
|
||
|
||
// 增强图像有效性检查
|
||
bool isImageValid = false;
|
||
Image safeImage = null;
|
||
|
||
try
|
||
{
|
||
// 创建一个安全的图像副本,确保原图像不被破坏
|
||
safeImage = new Bitmap(image);
|
||
|
||
// 预先验证图像是否有效
|
||
if (safeImage.Width > 0 && safeImage.Height > 0)
|
||
{
|
||
// 尝试访问图像的像素数据,这是更严格的验证方式
|
||
using (Graphics g = Graphics.FromImage(new Bitmap(1, 1)))
|
||
{
|
||
// 简单绘制操作验证图像是否可绘制
|
||
g.DrawImage(safeImage, 0, 0, 1, 1);
|
||
}
|
||
isImageValid = true;
|
||
Console.WriteLine($"图像验证成功: {safeImage.Width}x{safeImage.Height}");
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"图像尺寸无效: {safeImage.Width}x{safeImage.Height}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"图像验证失败: {ex.Message}");
|
||
// 释放临时创建的安全图像
|
||
if (safeImage != null)
|
||
{
|
||
try { safeImage.Dispose(); } catch {}
|
||
}
|
||
// 释放原始图像
|
||
try { image.Dispose(); } catch {}
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 更新图像前先检查控件是否存在/已释放
|
||
if (this.IsDisposed || imageBox == null || imageBox.IsDisposed)
|
||
{
|
||
// 如果控件已释放,确保图像也被释放
|
||
try { safeImage.Dispose(); } catch {}
|
||
try { image.Dispose(); } catch {}
|
||
Console.WriteLine("控件已释放,无法更新图像");
|
||
return;
|
||
}
|
||
|
||
// 保存旧图像引用,以便在设置新图像后释放
|
||
Image oldImage = imageBox.Image;
|
||
|
||
try
|
||
{
|
||
// 确保控件仍处于有效状态
|
||
if (imageBox.IsDisposed || !isImageValid || safeImage == null)
|
||
{
|
||
try { safeImage.Dispose(); } catch {}
|
||
try { image.Dispose(); } catch {}
|
||
return;
|
||
}
|
||
|
||
// 尝试设置新图像(使用验证过的安全副本)
|
||
imageBox.Image = safeImage;
|
||
|
||
// 验证图像是否成功设置
|
||
if (imageBox.Image != safeImage)
|
||
{
|
||
Console.WriteLine("图像设置失败,可能参数无效");
|
||
try { safeImage.Dispose(); } catch {}
|
||
try { image.Dispose(); } catch {}
|
||
return;
|
||
}
|
||
|
||
// 释放原始图像,因为我们现在使用的是安全副本
|
||
try { image.Dispose(); } catch {}
|
||
|
||
// 仅在新图像成功设置后释放旧图像
|
||
if (oldImage != null && oldImage != safeImage) // 防止自引用释放
|
||
{
|
||
try
|
||
{
|
||
oldImage.Dispose();
|
||
}
|
||
catch {}
|
||
}
|
||
}
|
||
catch (ArgumentException ex) when (ex.Message.Contains("参数无效"))
|
||
{
|
||
// 特别处理"参数无效"异常
|
||
Console.WriteLine($"图像参数无效异常: {ex.Message}");
|
||
try { safeImage.Dispose(); } catch {}
|
||
try { image.Dispose(); } catch {}
|
||
|
||
// 尝试设置旧图像回来,如果可能的话
|
||
if (oldImage != null)
|
||
{
|
||
try
|
||
{
|
||
imageBox.Image = oldImage;
|
||
}
|
||
catch
|
||
{
|
||
// 如果设置旧图像也失败,释放它
|
||
try { oldImage.Dispose(); } catch {}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"更新图像UI异常: {ex.Message}");
|
||
// 确保在任何异常情况下都释放所有图像资源
|
||
try { safeImage.Dispose(); } catch {}
|
||
try { image.Dispose(); } catch {}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设备管理器连接状态变更事件处理
|
||
/// </summary>
|
||
private void DeviceManager_ConnectionStatusChanged(object sender, ConnectionStatusChangedEventArgs e)
|
||
{
|
||
// 参数验证
|
||
if (e == null)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 警告: 接收到空的连接状态变更事件参数");
|
||
return;
|
||
}
|
||
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 接收连接状态变更事件: {e.Status}");
|
||
|
||
// 捕获所有可能的异常,确保事件处理器不会崩溃
|
||
try
|
||
{
|
||
// 首先检查控件状态
|
||
if (this.IsDisposed || this.Disposing)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 控件已释放或正在释放,跳过UI更新");
|
||
return;
|
||
}
|
||
|
||
// 检查事件参数中的状态信息
|
||
string statusMessage = $"连接状态变更为: {e.Status}";
|
||
if (!string.IsNullOrEmpty(e.DeviceInfo))
|
||
{
|
||
statusMessage += $" (设备信息: {e.DeviceInfo})";
|
||
}
|
||
|
||
// 线程安全处理 - 确保在UI线程上更新
|
||
if (this.InvokeRequired)
|
||
{
|
||
try
|
||
{
|
||
// 使用BeginInvoke代替Invoke,避免可能的死锁问题
|
||
this.BeginInvoke(new Action<ConnectionStatusChangedEventArgs>(args =>
|
||
{
|
||
// 再次检查控件状态,防止在异步调用期间控件被释放
|
||
if (!this.IsDisposed && !this.Disposing)
|
||
{
|
||
HandleConnectionStatusChanged(args);
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 异步调用期间控件已释放,跳过处理");
|
||
}
|
||
}), e);
|
||
}
|
||
catch (ObjectDisposedException ode)
|
||
{
|
||
// 捕获控件已释放异常,避免程序崩溃
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 控件已释放,无法进行UI线程调用: {ode.Message}");
|
||
}
|
||
catch (InvalidOperationException ioe)
|
||
{
|
||
// 捕获无效操作异常,通常发生在控件状态异常时
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] UI线程调用无效: {ioe.Message}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 直接在UI线程上处理
|
||
HandleConnectionStatusChanged(e);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 捕获所有其他异常,记录并继续
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 处理连接状态变更事件异常: {ex.Message}\n{ex.StackTrace}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理连接状态变更
|
||
/// </summary>
|
||
private void HandleConnectionStatusChanged(ConnectionStatusChangedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 确保在UI线程上更新UI状态
|
||
if (this.InvokeRequired)
|
||
{
|
||
this.Invoke(new Action<ConnectionStatusChangedEventArgs>(HandleConnectionStatusChanged), e);
|
||
return;
|
||
}
|
||
|
||
// 更新UI状态
|
||
UpdateUIState(e.Status == ConnectionStatus.Connected);
|
||
|
||
// 检查_deviceManager是否为空
|
||
if (_deviceManager == null)
|
||
{
|
||
Console.WriteLine("设备管理器未初始化");
|
||
return;
|
||
}
|
||
|
||
switch (e.Status)
|
||
{
|
||
case ConnectionStatus.Connected:
|
||
Console.WriteLine("设备已连接");
|
||
|
||
// 清除错误信息
|
||
ShowError(string.Empty);
|
||
|
||
// 仅在首次连接时设置为热图模式,重连时保留之前的模式
|
||
if (!_isReceivingImage) // 首次连接时_isReceivingImage为false
|
||
{
|
||
try
|
||
{
|
||
_deviceManager.SetImageMode(ImageMode.Infrared);
|
||
Console.WriteLine("首次连接,设置热图模式");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"设置热图模式失败: {ex.Message}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine("重连成功,保留当前图像模式");
|
||
}
|
||
|
||
// 注意:色彩模式同步现在在DeviceManager内部的连接成功处理中自动完成
|
||
// 无需在此处重复调用
|
||
|
||
// 开始接收图像(包含在try-catch中)
|
||
if (!_isReceivingImage)
|
||
{
|
||
StartReceiveImage();
|
||
}
|
||
break;
|
||
case ConnectionStatus.Disconnected:
|
||
Console.WriteLine("设备已断开连接");
|
||
|
||
// 停止接收图像(添加空检查和异常处理)
|
||
if (_isReceivingImage)
|
||
{
|
||
try
|
||
{
|
||
_deviceManager.StopReceiveImage();
|
||
_isReceivingImage = false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"停止接收图像失败: {ex.Message}");
|
||
_isReceivingImage = false; // 确保状态更新
|
||
}
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(e.DeviceInfo))
|
||
{
|
||
ShowError(e.DeviceInfo);
|
||
}
|
||
else
|
||
{
|
||
ShowError("设备连接已断开");
|
||
}
|
||
break;
|
||
case ConnectionStatus.Connecting:
|
||
case ConnectionStatus.Reconnecting:
|
||
Console.WriteLine($"正在连接设备...{(!string.IsNullOrEmpty(e.DeviceInfo) ? " " + e.DeviceInfo : "")}");
|
||
ShowError(string.Empty); // 清除之前的错误信息
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"处理连接状态变更时发生错误: {ex.Message}");
|
||
// 避免在异常处理中再次引起异常
|
||
try
|
||
{
|
||
ShowError($"连接状态处理错误: {ex.Message}");
|
||
}
|
||
catch
|
||
{}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新UI状态
|
||
/// </summary>
|
||
/// <param name="isConnected">是否已连接</param>
|
||
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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设备管理器连接异常事件处理
|
||
/// </summary>
|
||
private void DeviceManager_ConnectionException(object sender, ConnectionExceptionEventArgs e)
|
||
{
|
||
// 参数验证
|
||
if (e == null)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 警告: 接收到空的连接异常事件参数");
|
||
return;
|
||
}
|
||
|
||
// 记录详细的异常信息,但避免在UI线程上执行耗时操作
|
||
string exceptionMessage = e.Exception != null ? e.Exception.Message : "无详细异常信息";
|
||
string stackTrace = e.Exception != null ? e.Exception.StackTrace : "无堆栈信息";
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 接收连接异常事件: {e.Message}\n{exceptionMessage}\n{stackTrace}");
|
||
|
||
// 捕获所有可能的异常,确保异常处理不会导致程序崩溃
|
||
try
|
||
{
|
||
// 首先检查控件状态
|
||
if (this.IsDisposed || this.Disposing)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 控件已释放或正在释放,跳过异常显示");
|
||
return;
|
||
}
|
||
|
||
// 创建用于UI显示的错误消息
|
||
string uiErrorMessage = $"连接异常: {e.Message}";
|
||
if (string.IsNullOrEmpty(e.Message) && e.Exception != null)
|
||
{
|
||
uiErrorMessage = $"连接异常: {exceptionMessage}";
|
||
}
|
||
|
||
// 线程安全处理 - 确保在UI线程上更新
|
||
if (this.InvokeRequired)
|
||
{
|
||
try
|
||
{
|
||
// 创建局部变量保存错误消息,避免闭包问题
|
||
string errorMsg = uiErrorMessage;
|
||
|
||
// 使用BeginInvoke代替Invoke,避免可能的死锁问题
|
||
this.BeginInvoke(new Action(() =>
|
||
{
|
||
// 再次检查控件状态,防止在异步调用期间控件被释放
|
||
if (!this.IsDisposed && !this.Disposing)
|
||
{
|
||
try
|
||
{
|
||
ShowError(errorMsg);
|
||
}
|
||
catch (Exception showEx)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 显示错误消息异常: {showEx.Message}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 异步调用期间控件已释放,跳过错误显示");
|
||
}
|
||
}));
|
||
}
|
||
catch (ObjectDisposedException ode)
|
||
{
|
||
// 捕获控件已释放异常,避免程序崩溃
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 控件已释放,无法进行UI线程调用: {ode.Message}");
|
||
}
|
||
catch (InvalidOperationException ioe)
|
||
{
|
||
// 捕获无效操作异常,通常发生在控件状态异常时
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] UI线程调用无效: {ioe.Message}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 直接在UI线程上处理
|
||
try
|
||
{
|
||
ShowError(uiErrorMessage);
|
||
}
|
||
catch (Exception showEx)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 显示错误消息异常: {showEx.Message}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 捕获所有其他异常,记录并继续
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 处理连接异常事件异常: {ex.Message}\n{ex.StackTrace}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示错误信息
|
||
/// </summary>
|
||
private void ShowError(string message)
|
||
{
|
||
Console.WriteLine(message);
|
||
|
||
// 可以根据需要添加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(width, height))
|
||
using (Graphics g = Graphics.FromImage(errorBitmap))
|
||
{
|
||
g.Clear(Color.Black);
|
||
using (Font font = new Font("Arial", 10))
|
||
using (Brush brush = new SolidBrush(Color.Red))
|
||
{
|
||
SizeF textSize = g.MeasureString(message, font);
|
||
PointF textLocation = new PointF(
|
||
(errorBitmap.Width - textSize.Width) / 2,
|
||
(errorBitmap.Height - textSize.Height) / 2);
|
||
g.DrawString(message, font, brush, textLocation);
|
||
}
|
||
|
||
UpdateImageOnUI(errorBitmap);
|
||
}
|
||
|
||
// 启动定时器,3秒后清除错误显示
|
||
_errorDisplayTimer.Stop();
|
||
_errorDisplayTimer.Start();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 错误显示定时器事件
|
||
/// </summary>
|
||
private void ErrorDisplayTimer_Tick(object sender, EventArgs e)
|
||
{
|
||
_errorDisplayTimer.Stop();
|
||
// 清除错误显示,恢复到等待图像状态
|
||
|
||
// 检查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);
|
||
using (Font font = new Font("Arial", 10))
|
||
using (Brush brush = new SolidBrush(Color.White))
|
||
{
|
||
string waitingText = "正在等待热图数据...";
|
||
SizeF textSize = g.MeasureString(waitingText, font);
|
||
PointF textLocation = new PointF(
|
||
(waitingBitmap.Width - textSize.Width) / 2,
|
||
(waitingBitmap.Height - textSize.Height) / 2);
|
||
g.DrawString(waitingText, font, brush, textLocation);
|
||
}
|
||
|
||
UpdateImageOnUI(waitingBitmap);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 右键菜单显示前的事件处理方法
|
||
/// 用于更新色彩模式菜单项的选中状态
|
||
/// </summary>
|
||
private void ContextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 清除视频模式菜单项的选中状态
|
||
thermalModeToolStripMenuItem.Checked = false;
|
||
visibleModeToolStripMenuItem.Checked = false;
|
||
fusionMode1ToolStripMenuItem.Checked = false;
|
||
fusionMode2ToolStripMenuItem.Checked = false;
|
||
fusionMode3ToolStripMenuItem.Checked = false;
|
||
fusionMode4ToolStripMenuItem.Checked = false;
|
||
fusionMode5ToolStripMenuItem.Checked = false;
|
||
|
||
// 清除色彩模式菜单项的选中状态
|
||
whiteHotToolStripMenuItem.Checked = false;
|
||
blackHotToolStripMenuItem.Checked = false;
|
||
ironRedToolStripMenuItem.Checked = false;
|
||
lavaToolStripMenuItem.Checked = false;
|
||
rainbowToolStripMenuItem.Checked = false;
|
||
ironGrayToolStripMenuItem.Checked = false;
|
||
redHotToolStripMenuItem.Checked = false;
|
||
rainbow2ToolStripMenuItem.Checked = false;
|
||
|
||
// 尝试获取当前色彩模式并更新对应菜单项的选中状态
|
||
if (_deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Connected)
|
||
{
|
||
try
|
||
{
|
||
// 获取当前色彩模式
|
||
PaletteType currentPalette = _deviceManager.CurrentPaletteType;
|
||
|
||
// 根据当前色彩模式设置对应菜单项的选中状态
|
||
switch (currentPalette)
|
||
{
|
||
case PaletteType.WhiteHot:
|
||
whiteHotToolStripMenuItem.Checked = true;
|
||
break;
|
||
case PaletteType.BlackHot:
|
||
blackHotToolStripMenuItem.Checked = true;
|
||
break;
|
||
case PaletteType.IronRed:
|
||
ironRedToolStripMenuItem.Checked = true;
|
||
break;
|
||
case PaletteType.Lava:
|
||
lavaToolStripMenuItem.Checked = true;
|
||
break;
|
||
case PaletteType.Rainbow:
|
||
rainbowToolStripMenuItem.Checked = true;
|
||
break;
|
||
case PaletteType.IronGray:
|
||
ironGrayToolStripMenuItem.Checked = true;
|
||
break;
|
||
case PaletteType.RedHot:
|
||
redHotToolStripMenuItem.Checked = true;
|
||
break;
|
||
case PaletteType.Rainbow2:
|
||
rainbow2ToolStripMenuItem.Checked = true;
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("获取当前色彩模式失败: " + ex.Message);
|
||
}
|
||
|
||
// 更新视频模式菜单项的选中状态
|
||
try
|
||
{
|
||
// 更改为使用ImageMode枚举
|
||
var currentImageMode = _deviceManager.CurrentImageMode;
|
||
thermalModeToolStripMenuItem.Checked = currentImageMode == ImageMode.Infrared;
|
||
visibleModeToolStripMenuItem.Checked = currentImageMode == ImageMode.Natural;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("获取当前图像模式失败: " + ex.Message);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新右键菜单选中状态失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
#region 色彩模式切换方法
|
||
|
||
/// <summary>
|
||
/// 白热色彩模式
|
||
/// </summary>
|
||
private void whiteHotToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_deviceManager != null)
|
||
{
|
||
Console.WriteLine("切换到白热色彩模式");
|
||
_deviceManager.SetPaletteType(PaletteType.WhiteHot);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"切换到白热色彩模式失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 黑热色彩模式
|
||
/// </summary>
|
||
private void blackHotToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_deviceManager != null)
|
||
{
|
||
Console.WriteLine("切换到黑热色彩模式");
|
||
_deviceManager.SetPaletteType(PaletteType.BlackHot);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"切换到黑热色彩模式失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 铁红色彩模式
|
||
/// </summary>
|
||
private void ironRedToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_deviceManager != null)
|
||
{
|
||
Console.WriteLine("切换到铁红色彩模式");
|
||
_deviceManager.SetPaletteType(PaletteType.IronRed);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"切换到铁红色彩模式失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 熔岩色彩模式
|
||
/// </summary>
|
||
private void lavaToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_deviceManager != null)
|
||
{
|
||
Console.WriteLine("切换到熔岩色彩模式");
|
||
_deviceManager.SetPaletteType(PaletteType.Lava);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"切换到熔岩色彩模式失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 彩虹色彩模式
|
||
/// </summary>
|
||
private void rainbowToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_deviceManager != null)
|
||
{
|
||
Console.WriteLine("切换到彩虹色彩模式");
|
||
_deviceManager.SetPaletteType(PaletteType.Rainbow);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"切换到彩虹色彩模式失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 铁灰色彩模式
|
||
/// </summary>
|
||
private void ironGrayToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_deviceManager != null)
|
||
{
|
||
Console.WriteLine("切换到铁灰色彩模式");
|
||
_deviceManager.SetPaletteType(PaletteType.IronGray);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"切换到铁灰色彩模式失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 红热色彩模式
|
||
/// </summary>
|
||
private void redHotToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_deviceManager != null)
|
||
{
|
||
Console.WriteLine("切换到红热色彩模式");
|
||
_deviceManager.SetPaletteType(PaletteType.RedHot);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"切换到红热色彩模式失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 彩虹2色彩模式
|
||
/// </summary>
|
||
private void rainbow2ToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_deviceManager != null)
|
||
{
|
||
Console.WriteLine("切换到彩虹2色彩模式");
|
||
_deviceManager.SetPaletteType(PaletteType.Rainbow2);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"切换到彩虹2色彩模式失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 视频模式切换方法
|
||
|
||
/// <summary>
|
||
/// 红外模式
|
||
/// </summary>
|
||
private void thermalModeToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
_deviceManager.SetImageMode(ImageMode.Infrared);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("切换到红外模式失败: " + ex.Message);
|
||
ShowError("切换到红外模式失败");
|
||
}
|
||
}
|
||
|
||
private void visibleModeToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
_deviceManager.SetImageMode(ImageMode.Natural);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("切换到自然模式失败: " + ex.Message);
|
||
ShowError("切换到自然模式失败");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 清理资源
|
||
/// </summary>
|
||
protected override void Dispose(bool disposing)
|
||
{
|
||
if (disposing)
|
||
{
|
||
// 检查是否处于设计模式
|
||
if (!DesignMode)
|
||
{
|
||
// 停止相机并释放相关资源
|
||
try
|
||
{
|
||
StopCamera();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("关闭相机时出错: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
// 取消注册事件并释放设备管理器
|
||
if (_deviceManager != null)
|
||
{
|
||
|
||
// 移除所有事件监听
|
||
_deviceManager.ImageReceived -= DeviceManager_ImageReceived;
|
||
_deviceManager.ConnectionStatusChanged -= DeviceManager_ConnectionStatusChanged;
|
||
_deviceManager.ConnectionException -= DeviceManager_ConnectionException;
|
||
// 释放设备管理器资源
|
||
_deviceManager.Dispose();
|
||
_deviceManager = null;
|
||
}
|
||
|
||
// 无论是否在设计模式下,都需要释放定时器
|
||
if (_errorDisplayTimer != null)
|
||
{
|
||
_errorDisplayTimer.Stop();
|
||
_errorDisplayTimer.Dispose();
|
||
_errorDisplayTimer = null;
|
||
}
|
||
|
||
// 无论是否在设计模式下,都需要释放图像资源
|
||
if (imageBox != null && !imageBox.IsDisposed && imageBox.Image != null)
|
||
{
|
||
imageBox.Image.Dispose();
|
||
imageBox.Image = null;
|
||
}
|
||
|
||
// 释放组件资源
|
||
if (components != null)
|
||
{
|
||
components.Dispose();
|
||
}
|
||
}
|
||
base.Dispose(disposing);
|
||
}
|
||
}
|
||
}
|