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

1614 lines
66 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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

using System;
using System.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;
// 全局图像缓冲区bitmap用于在其上绘制图像和mask信息
private Bitmap _imageBuffer = null;
private const int BUFFER_WIDTH = 512;
private const int BUFFER_HEIGHT = 384;
// 最后接收的图像
private Image _lastImage = null;
// 信息图像,用于显示额外信息
private Image _infoImage = null;
// 用于保护_lastImage的线程锁
private readonly object _lastImageLock = new object();
// 用于保护_infoImage的线程锁
private readonly object _infoImageLock = new object();
// 是否显示信息图像
private bool _isDisplayingInfo = false;
// 项目路径,用于数据文件的存取
private string _projectPath = "";
/// <summary>
/// 获取或设置项目路径,控件所需的数据文件将在此目录中进行存取
/// </summary>
[Category("配置")]
[Description("设置项目路径,控件所需的数据文件将在此目录中进行存取")]
[DefaultValue("")]
public string ProjectPath
{
get { return _projectPath; }
set
{
// 只有当值发生变化时才进行同步
if (_projectPath != value)
{
_projectPath = value;
// 如果DeviceManager已经初始化则同步更新其ProjectPath属性
if (_deviceManager != null)
{
_deviceManager.ProjectPath = _projectPath;
}
}
}
}
// 显示错误的定时器
private System.Windows.Forms.Timer _errorDisplayTimer;
/// <summary>
/// 更新设计模式状态到DeviceManager
/// </summary>
private void UpdateDesignModeStatus()
{
DeviceManager.IsDesignMode = DesignMode;
Console.WriteLine($"相机控件设计模式状态已更新: {DesignMode}");
}
/// <summary>
/// 暂停/恢复图像更新菜单项点击事件处理
/// </summary>
private void PauseImageUpdateToolStripMenuItem_Click(object sender, EventArgs e)
{
if (DesignMode) return;
try
{
bool isPaused = pauseImageUpdateToolStripMenuItem.Text == "暂停图像更新";
if (isPaused)
{
pauseImageUpdateToolStripMenuItem.Text = "恢复图像更新";
Console.WriteLine("图像更新已暂停");
// 在InfoImage中绘制暂停字样
lock (_infoImageLock)
{
// 释放之前的InfoImage资源
if (_infoImage != null)
{
try
{
_infoImage.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"释放InfoImage资源异常: {ex.Message}");
}
}
// 创建新的InfoImage并绘制暂停字样
_infoImage = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT);
using (Graphics g = Graphics.FromImage(_infoImage))
{
// 设置半透明背景
using (SolidBrush brush = new SolidBrush(Color.FromArgb(128, Color.Black)))
{
g.FillRectangle(brush, 0, 0, BUFFER_WIDTH, BUFFER_HEIGHT);
}
// 绘制暂停字样
using (Font font = new Font("Arial", 48, FontStyle.Bold))
using (SolidBrush textBrush = new SolidBrush(Color.Red))
{
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
g.DrawString("暂停", font, textBrush, new RectangleF(0, 0, BUFFER_WIDTH, BUFFER_HEIGHT), format);
}
}
}
// 设置显示信息标志并更新UI
_isDisplayingInfo = true;
UpdateImageOnUI();
}
else
{
pauseImageUpdateToolStripMenuItem.Text = "暂停图像更新";
Console.WriteLine("图像更新已恢复");
// 清除InfoImage和显示标志
lock (_infoImageLock)
{
if (_infoImage != null)
{
try
{
_infoImage.Dispose();
_infoImage = null;
}
catch (Exception ex)
{
Console.WriteLine($"释放InfoImage资源异常: {ex.Message}");
}
}
}
_isDisplayingInfo = false;
// 立即更新UI
UpdateImageOnUI();
// 恢复时,立即停止并重新开始图像接收,确保获取最新图像
if (_isReceivingImage && _deviceManager != null)
{
_deviceManager.StopImageReceiving();
_deviceManager.StartImageReceiving();
}
}
}
catch (Exception ex)
{
Console.WriteLine($"处理暂停/恢复图像更新时出错: {ex.Message}");
}
}
public Camera()
{
InitializeComponent();
// 为右键菜单添加Opening事件用于在菜单显示前更新色彩模式的选中状态
this.contextMenuStrip1.Opening += ContextMenuStrip1_Opening;
// 将设计模式状态传递给DeviceManager
UpdateDesignModeStatus();
// 初始化图像缓冲区
InitializeImageBuffer();
// 只有在非设计模式下才初始化设备管理器和错误定时器
if (!DesignMode)
{
// 清空现有日志
try
{
string logFile = Path.Combine(Application.StartupPath, "log.txt");
// 确保日志文件目录存在
string logDir = Path.GetDirectoryName(logFile);
if (!Directory.Exists(logDir))
{
Directory.CreateDirectory(logDir);
}
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()
{
// 只有在非设计模式下才初始化设备管理器
if (!DesignMode)
{
_deviceManager = new DeviceManager
{
AutoReconnectEnabled = true,
ReconnectInterval = 2000, // 2秒
ProjectPath = !string.IsNullOrEmpty(ProjectPath) ? ProjectPath : Application.StartupPath
};
// 设置静态属性
DeviceManager.MaxReconnectAttempts = 5;
// 注册图像接收事件
_deviceManager.ImageReceived += DeviceManager_ImageReceived;
// 注册连接状态变更事件
_deviceManager.ConnectionStatusChanged += DeviceManager_ConnectionStatusChanged;
// 注册连接异常事件
_deviceManager.ConnectionException += DeviceManager_ConnectionException;
}
}
/// <summary>
/// 初始化图像缓冲区
/// </summary>
private void InitializeImageBuffer()
{
try
{
// 创建512*384大小的bitmap作为图像缓冲区
_imageBuffer = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT);
Console.WriteLine($"图像缓冲区已初始化: {BUFFER_WIDTH}x{BUFFER_HEIGHT}");
// 初始化缓冲区为黑色背景
using (Graphics g = Graphics.FromImage(_imageBuffer))
{
g.Clear(Color.Black);
}
}
catch (Exception ex)
{
Console.WriteLine($"初始化图像缓冲区失败: {ex.Message}");
// 如果初始化失败确保_imageBuffer为null
_imageBuffer = null;
}
}
/// <summary>
/// 初始化错误显示定时器
/// </summary>
private void InitializeErrorTimer()
{
_errorDisplayTimer = new System.Windows.Forms.Timer { Interval = 3000 }; // 显示3秒后清除
_errorDisplayTimer.Tick += ErrorDisplayTimer_Tick;
}
/// <summary>
/// 启动相机
/// </summary>
public void StartCamera()
{
if (DesignMode) return;
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()
{
if (DesignMode) return;
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()
{
if (DesignMode) return;
try
{
if (_isReceivingImage)
{
_deviceManager.StopReceiveImage();
_isReceivingImage = false;
}
}
catch (Exception ex)
{
Console.WriteLine($"停止相机失败: {ex.Message}");
}
}
/// <summary>
/// 设备管理器图像接收事件处理
/// </summary>
private void DeviceManager_ImageReceived(object sender, ImageReceivedEventArgs e)
{
// 如果图像更新已暂停则不更新UI
// 通过菜单项文本判断是否暂停
if (pauseImageUpdateToolStripMenuItem.Text == "恢复图像更新")
return;
if (DesignMode) return;
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 newImage = new Bitmap(tempImage);
// 立即验证新创建的图像是否有效
try
{
// 访问Width和Height属性来验证图像是否有效
int width = newImage.Width;
int height = newImage.Height;
if (width <= 0 || height <= 0)
{
Console.WriteLine("创建的图像尺寸无效");
newImage.Dispose();
return;
}
}
catch (Exception)
{
Console.WriteLine("创建的图像无效");
newImage.Dispose();
return;
}
// 按照用户要求收到图像数据后将图像保存到LastImage中调用更新
lock (_lastImageLock)
{
// 释放旧的LastImage资源
if (_lastImage != null)
{
try { _lastImage.Dispose(); } catch {}
}
// 设置新的LastImage
_lastImage = newImage;
}
// 调用更新UI
this.BeginInvoke(new Action(() =>
{
try
{
UpdateImageOnUI();
}
catch (Exception ex)
{
Console.WriteLine($"更新UI图像失败: {ex.Message}");
}
}));
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"处理接收到的图像时出错: {ex.Message}");
}
}
/// <summary>
/// 在UI线程上更新图像
/// </summary>
private void UpdateImageOnUI(Image image) // 保留原有方法签名,处理旧调用
{
UpdateImageOnUI();
}
/// <summary>
/// 在UI线程上更新图像 - 新实现,按照用户要求:
/// 1. 先将LastImage绘制到全局缓冲
/// 2. 再将InfoImage绘制到缓冲
/// 3. 最后一次性绘制到图像框的bitmap
/// </summary>
private void UpdateImageOnUI()
{
if (DesignMode) return;
// 线程安全检查 - 确保在UI线程上执行
if (this.InvokeRequired)
{
try
{
this.BeginInvoke(new Action(UpdateImageOnUI));
}
catch (ObjectDisposedException)
{
Console.WriteLine("控件已释放跳过UI更新");
}
return;
}
Image lastImage = null;
Image infoImage = null;
Image oldImage = null;
try
{
// 连接状态检查 - 只在设备实际连接时处理图像
if (_deviceManager.ConnectionStatus != ConnectionStatus.Connected)
{
Console.WriteLine("设备未连接,跳过图像更新");
return;
}
// 检查图像缓冲区是否有效
if (_imageBuffer == null)
{
Console.WriteLine("图像缓冲区未初始化,尝试重新初始化");
InitializeImageBuffer();
if (_imageBuffer == null)
{
Console.WriteLine("重新初始化图像缓冲区失败");
return;
}
}
// 更新图像前先检查控件是否存在/已释放
if (this.IsDisposed || imageBox == null || imageBox.IsDisposed)
{
Console.WriteLine("控件已释放,无法更新图像");
return;
}
// 保存旧图像引用,以便在设置新图像后释放
oldImage = imageBox.Image;
// 获取当前的LastImage引用
lock (_lastImageLock)
{
if (_lastImage != null)
{
lastImage = (Image)_lastImage.Clone();
}
}
// 获取当前的InfoImage引用
lock (_infoImageLock)
{
if (_infoImage != null)
{
infoImage = (Image)_infoImage.Clone();
}
}
// 在缓冲区上绘制图像 - 按照用户要求的步骤执行
lock (_imageBuffer)
{
using (Graphics g = Graphics.FromImage(_imageBuffer))
{
// 清除缓冲区背景为黑色
g.Clear(Color.Black);
// 步骤1先将LastImage绘制到全局缓冲
if (lastImage != null)
{
// 保持原始图像比例,居中显示
float scale = Math.Min((float)BUFFER_WIDTH / lastImage.Width, (float)BUFFER_HEIGHT / lastImage.Height);
int scaledWidth = (int)(lastImage.Width * scale);
int scaledHeight = (int)(lastImage.Height * scale);
int x = (BUFFER_WIDTH - scaledWidth) / 2;
int y = (BUFFER_HEIGHT - scaledHeight) / 2;
g.DrawImage(lastImage, x, y, scaledWidth, scaledHeight);
}
// 步骤2再将InfoImage绘制到缓冲
if (infoImage != null)
{
g.DrawImage(infoImage, 0, 0);
}
else if (_isDisplayingInfo)
{
// 如果没有InfoImage但需要显示信息则绘制默认信息
using (Font font = new Font("微软雅黑", 12, FontStyle.Bold))
using (Brush textBrush = new SolidBrush(Color.Red))
{
g.DrawString("测试信息", font, textBrush, 10, 10);
}
}
// 检查是否需要绘制暂停状态的mask信息
bool isPaused = pauseImageUpdateToolStripMenuItem.Text == "恢复图像更新";
if (isPaused)
{
// 绘制半透明黑色背景
using (Brush brush = new SolidBrush(Color.FromArgb(100, Color.Black)))
{
g.FillRectangle(brush, 0, 0, BUFFER_WIDTH, BUFFER_HEIGHT);
}
// 绘制"暂停"文字
using (Font font = new Font("微软雅黑", 48, FontStyle.Bold))
using (Brush textBrush = new SolidBrush(Color.White))
{
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
g.DrawString("暂停", font, textBrush,
new Rectangle(0, 0, BUFFER_WIDTH, BUFFER_HEIGHT), format);
}
}
}
}
// 步骤3创建缓冲区的副本用于显示避免在显示时锁定缓冲区
Bitmap displayImage = null;
lock (_imageBuffer)
{
displayImage = (Bitmap)_imageBuffer.Clone();
}
// 将全局缓冲一次性绘制到图像框的bitmap
imageBox.Image = displayImage;
if (lastImage != null)
{
Console.WriteLine($"图像更新成功: {lastImage.Width}x{lastImage.Height}");
}
}
catch (ArgumentException ex) when (ex.Message.Contains("参数无效"))
{
// 特别处理"参数无效"异常
Console.WriteLine($"图像参数无效异常: {ex.Message}");
// 尝试设置旧图像回来,如果可能的话
if (oldImage != null && !this.IsDisposed && imageBox != null && !imageBox.IsDisposed)
{
try
{
imageBox.Image = oldImage;
}
catch
{
// 如果设置旧图像也失败,释放它
try { oldImage.Dispose(); } catch {}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"更新图像UI异常: {ex.Message}");
}
finally
{
// 确保在任何情况下都释放资源
if (lastImage != null) { try { lastImage.Dispose(); } catch { } }
if (infoImage != null) { try { infoImage.Dispose(); } catch { } }
if (oldImage != null && oldImage != imageBox.Image) { try { oldImage.Dispose(); } catch { } }
}
}
/// <summary>
/// 设备管理器连接状态变更事件处理
/// </summary>
private void DeviceManager_ConnectionStatusChanged(object sender, ConnectionStatusChangedEventArgs e)
{
if (DesignMode) return;
// 参数验证
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)
{
if (DesignMode) return;
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);
// 清除显示标志并刷新UI
_isDisplayingInfo = false;
UpdateImageOnUI();
// 仅在首次连接时设置为热图模式,重连时保留之前的模式
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; // 确保状态更新
}
}
// 在InfoImage中显示连接断开信息
lock (_infoImageLock)
{
// 如果_infoImage为null则创建新对象
if (_infoImage == null)
{
_infoImage = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT);
}
// 直接在现有InfoImage上重绘不需要Dispose
using (Graphics g = Graphics.FromImage(_infoImage))
{
// 设置半透明背景
using (SolidBrush brush = new SolidBrush(Color.FromArgb(128, Color.Black)))
{
g.FillRectangle(brush, 0, 0, BUFFER_WIDTH, BUFFER_HEIGHT);
}
// 绘制断开连接字样
using (Font font = new Font("Arial", 36, FontStyle.Bold))
using (SolidBrush textBrush = new SolidBrush(Color.Red))
{
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
g.DrawString("连接断开", font, textBrush, new RectangleF(0, 0, BUFFER_WIDTH, BUFFER_HEIGHT), format);
}
}
}
// 设置显示信息标志并更新UI
_isDisplayingInfo = true;
UpdateImageOnUI();
if (!string.IsNullOrEmpty(e.DeviceInfo))
{
ShowError(e.DeviceInfo);
}
else
{
ShowError("设备连接已断开");
}
break;
case ConnectionStatus.Connecting:
Console.WriteLine($"正在连接设备...{(!string.IsNullOrEmpty(e.DeviceInfo) ? " " + e.DeviceInfo : "")}");
ShowError(string.Empty); // 清除之前的错误信息
break;
case ConnectionStatus.Reconnecting:
Console.WriteLine($"正在重新连接设备...{(!string.IsNullOrEmpty(e.DeviceInfo) ? " " + e.DeviceInfo : "")}");
ShowError(string.Empty); // 清除之前的错误信息
// 在InfoImage中显示正在重连信息
lock (_infoImageLock)
{
// 如果_infoImage为null则创建新对象
if (_infoImage == null)
{
_infoImage = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT);
}
// 直接在现有InfoImage上重绘不需要Dispose
using (Graphics g = Graphics.FromImage(_infoImage))
{
// 设置半透明背景
using (SolidBrush brush = new SolidBrush(Color.FromArgb(128, Color.Black)))
{
g.FillRectangle(brush, 0, 0, BUFFER_WIDTH, BUFFER_HEIGHT);
}
// 绘制正在重连字样
using (Font font = new Font("Arial", 36, FontStyle.Bold))
using (SolidBrush textBrush = new SolidBrush(Color.Yellow))
{
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
g.DrawString("正在重连...", font, textBrush, new RectangleF(0, 0, BUFFER_WIDTH, BUFFER_HEIGHT), format);
}
}
}
// 设置显示信息标志并更新UI
_isDisplayingInfo = true;
UpdateImageOnUI();
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)
{
if (DesignMode) return;
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 (DesignMode) return;
// 参数验证
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)
{
if (DesignMode) return;
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)
{
if (DesignMode) return;
_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>
/// <summary>
/// 右键菜单打开事件处理
/// </summary>
private void ContextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
// 暂停菜单项的文本已经在点击事件中更新,这里无需再次更新
if (DesignMode) return;
try
{
// 检查是否处于暂停状态
bool isPaused = pauseImageUpdateToolStripMenuItem.Text == "恢复图像更新";
// 在暂停状态下,隐藏图像模式根菜单和色彩模式菜单
// 注意:根菜单隐藏后,其所有子菜单会自动隐藏,不需要单独设置
if (isPaused)
{
// 隐藏图像模式根菜单
if (imageModeToolStripMenuItem != null)
imageModeToolStripMenuItem.Visible = false;
// 隐藏色彩模式菜单
colorModeToolStripMenuItem.Visible = false;
// 当只有一个菜单项可见时,隐藏分隔符
toolStripSeparator1.Visible = false;
}
else
{
// 在非暂停状态下,显示图像模式根菜单
// 注意:根菜单显示后,其所有子菜单会自动显示,不需要单独设置
if (imageModeToolStripMenuItem != null)
imageModeToolStripMenuItem.Visible = true;
// 在非暂停状态下,显示分隔符
toolStripSeparator1.Visible = true;
// 根据当前图像模式控制色彩模式菜单的可见性
if (_deviceManager != null)
{
colorModeToolStripMenuItem.Visible = _deviceManager.CurrentImageMode == ImageMode.Infrared;
}
// 清除视频模式菜单项的选中状态
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 (_imageBuffer != null)
{
try
{
_imageBuffer.Dispose();
_imageBuffer = null;
Console.WriteLine("图像缓冲区资源已释放");
}
catch (Exception ex)
{
Console.WriteLine($"清理ImageBuffer资源异常: {ex.Message}");
}
}
// 释放LastImage资源
lock (_lastImageLock)
{
if (_lastImage != null)
{
try
{
_lastImage.Dispose();
_lastImage = null;
Console.WriteLine("LastImage资源已释放");
}
catch (Exception ex)
{
Console.WriteLine($"清理LastImage资源异常: {ex.Message}");
}
}
}
// 释放InfoImage资源
lock (_infoImageLock)
{
if (_infoImage != null)
{
try
{
_infoImage.Dispose();
_infoImage = null;
Console.WriteLine("InfoImage资源已释放");
}
catch (Exception ex)
{
Console.WriteLine($"清理InfoImage资源异常: {ex.Message}");
}
}
}
// 释放组件资源
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
}
}