1641 lines
64 KiB
C#
1641 lines
64 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;
|
||
using System.Threading.Tasks;
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 显示错误的定时器
|
||
|
||
|
||
/// <summary>
|
||
/// 更新设计模式状态到DeviceManager
|
||
/// </summary>
|
||
private void UpdateDesignModeStatus()
|
||
{
|
||
DeviceManager.IsDesignMode = DesignMode;
|
||
Console.WriteLine($"相机控件设计模式状态已更新: {DesignMode}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新InfoImage显示
|
||
/// 1. 如果断开,显示重连信息
|
||
/// 2. 否则如果暂停,显示暂停信息
|
||
/// 3. 最后调用更新UI
|
||
/// </summary>
|
||
private void UpdateInfo()
|
||
{
|
||
// 更新Ping状态到Info文本
|
||
Console.WriteLine($"Ping状态更新: {(IsDevicePingable ? "可Ping通" : "不可Ping通")}");
|
||
if (DesignMode) return;
|
||
|
||
try
|
||
{
|
||
lock (_infoImageLock)
|
||
{
|
||
// 释放之前的InfoImage资源
|
||
if (_infoImage != null)
|
||
{
|
||
try
|
||
{
|
||
_infoImage.Dispose();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"释放InfoImage资源异常: {ex.Message}");
|
||
}
|
||
_infoImage = null;
|
||
}
|
||
|
||
// 检查连接状态
|
||
bool isDisconnected = _deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Disconnected;
|
||
bool isReconnecting = _deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Reconnecting;
|
||
bool isPaused = _isPaused; // 使用_isPaused标志判断暂停状态
|
||
bool isPingFailed = !IsDevicePingable;
|
||
|
||
// 如果需要显示信息,创建InfoImage
|
||
if (isDisconnected || isReconnecting || isPaused || isPingFailed)
|
||
{
|
||
_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);
|
||
}
|
||
|
||
// 确定显示的文本和颜色
|
||
string text = "";
|
||
Color textColor = Color.White;
|
||
|
||
// 根据要求的优先级显示信息:先检查Ping状态和连接状态,最后检查暂停状态
|
||
if (isPingFailed || isDisconnected || isReconnecting)
|
||
{
|
||
// Ping不通或断开连接状态
|
||
if (isReconnecting)
|
||
{
|
||
text = "正在重连...";
|
||
textColor = Color.Yellow;
|
||
}
|
||
else if (isDisconnected)
|
||
{
|
||
text = "连接断开";
|
||
textColor = Color.Red;
|
||
}
|
||
else if (isPingFailed)
|
||
{
|
||
text = "Ping不通";
|
||
textColor = Color.Red;
|
||
}
|
||
}
|
||
else if (isPaused)
|
||
{
|
||
// 暂停状态
|
||
text = "暂停";
|
||
textColor = Color.Red;
|
||
}
|
||
|
||
// 绘制文本
|
||
using (Font font = new Font("Arial", 48, FontStyle.Bold))
|
||
using (SolidBrush textBrush = new SolidBrush(textColor))
|
||
{
|
||
StringFormat format = new StringFormat();
|
||
format.Alignment = StringAlignment.Center;
|
||
|
||
// 将主文本居中显示
|
||
g.DrawString(text, font, textBrush,
|
||
new RectangleF(0, BUFFER_HEIGHT / 3, BUFFER_WIDTH, BUFFER_HEIGHT / 3),
|
||
format);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 设置显示标志
|
||
_isDisplayingInfo = (isDisconnected || isReconnecting || isPaused);
|
||
}
|
||
|
||
// 调用更新UI
|
||
UpdateImageOnUI();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"更新Info显示时出错: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 暂停/恢复图像更新菜单项点击事件处理
|
||
/// 1、暂停或恢复时,设置暂停状态,调用更新Info
|
||
/// </summary>
|
||
private void PauseImageUpdateToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
if (DesignMode) return;
|
||
|
||
try
|
||
{
|
||
// 切换暂停状态
|
||
_isPaused = !_isPaused;
|
||
|
||
if (_isPaused)
|
||
{
|
||
// 设置暂停状态
|
||
pauseImageUpdateToolStripMenuItem.Text = "恢复图像更新";
|
||
|
||
// 暂停时停止图像接收
|
||
if (_isReceivingImage && _deviceManager != null)
|
||
{
|
||
_deviceManager.StopImageReceiving();
|
||
_isReceivingImage = false;
|
||
}
|
||
|
||
Console.WriteLine("图像更新已暂停");
|
||
}
|
||
else
|
||
{
|
||
// 设置恢复状态
|
||
pauseImageUpdateToolStripMenuItem.Text = "暂停图像更新";
|
||
|
||
// 恢复时,立即停止并重新开始图像接收,确保获取最新图像
|
||
if (_deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Connected)
|
||
{
|
||
_deviceManager.StopImageReceiving();
|
||
_deviceManager.StartImageReceiving();
|
||
_isReceivingImage = true;
|
||
}
|
||
|
||
Console.WriteLine("图像更新已恢复");
|
||
}
|
||
|
||
// 按照用户要求:暂停或恢复时,设置暂停状态,调用更新Info
|
||
UpdateInfo();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"处理暂停/恢复图像更新时出错: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
public Camera()
|
||
{
|
||
InitializeComponent();
|
||
|
||
// 为右键菜单添加Opening事件,用于在菜单显示前更新色彩模式的选中状态
|
||
this.contextMenuStrip1.Opening += ContextMenuStrip1_Opening;
|
||
|
||
// 将设计模式状态传递给DeviceManager
|
||
UpdateDesignModeStatus();
|
||
|
||
// 初始化图像缓冲区
|
||
InitializeImageBuffer();
|
||
|
||
// 初始化Ping定时器
|
||
_pingTimer = new System.Threading.Timer(PingTimer_Tick, null, Timeout.Infinite, Timeout.Infinite);
|
||
|
||
// 只有在非设计模式下才初始化设备管理器和错误定时器
|
||
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();
|
||
}
|
||
}
|
||
|
||
/// <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>
|
||
public void StartCamera()
|
||
{
|
||
// 启动设备Ping
|
||
StartDevicePing();
|
||
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()
|
||
{
|
||
// 停止设备Ping
|
||
StopDevicePing();
|
||
if (DesignMode) return;
|
||
try
|
||
{
|
||
if (_isReceivingImage)
|
||
{
|
||
_deviceManager.StopReceiveImage();
|
||
_isReceivingImage = false;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"停止相机失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private bool _isPaused = false; // 暂停状态标志
|
||
|
||
// Ping相关字段
|
||
private System.Threading.Timer _pingTimer;
|
||
private bool _isDevicePingable = false;
|
||
private const int _pingInterval = 500; // 0.5秒Ping一次
|
||
|
||
/// <summary>
|
||
/// 获取设备是否可Ping通
|
||
/// </summary>
|
||
public bool IsDevicePingable
|
||
{
|
||
get { return _isDevicePingable; }
|
||
private set
|
||
{
|
||
if (_isDevicePingable != value)
|
||
{
|
||
_isDevicePingable = value;
|
||
Console.WriteLine($"设备Ping状态变更: {(_isDevicePingable ? "可Ping通" : "不可Ping通")}");
|
||
// 状态变化时调用更新Info
|
||
UpdateInfo();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设备管理器图像接收事件处理
|
||
/// </summary>
|
||
private void DeviceManager_ImageReceived(object sender, ImageReceivedEventArgs e)
|
||
{
|
||
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
|
||
// 只有当图像更新未暂停时才更新UI
|
||
if (!_isPaused)
|
||
{
|
||
this.BeginInvoke(new Action(() =>
|
||
{
|
||
try
|
||
{
|
||
UpdateImageOnUI();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"更新UI图像失败: {ex.Message}");
|
||
}
|
||
}));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"处理接收到的图像时出错: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/// <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
|
||
{
|
||
// 移除连接状态检查,确保在任何状态下都能更新UI
|
||
// 根据流程要求,需要在断开或Ping不通时也能显示相关信息
|
||
|
||
// 检查图像缓冲区是否有效
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 步骤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>
|
||
/// 处理连接状态变更
|
||
/// 2、断开或连接时,设置连接状态,调用更新Info
|
||
/// </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("设备已连接");
|
||
|
||
// 仅在首次连接时设置为热图模式,重连时保留之前的模式
|
||
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)
|
||
{
|
||
try
|
||
{
|
||
StartReceiveImage();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"开始接收图像失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
// 设置连接状态后,调用更新Info
|
||
UpdateInfo();
|
||
break;
|
||
case ConnectionStatus.Disconnected:
|
||
Console.WriteLine("设备已断开连接");
|
||
|
||
// 停止接收图像(添加空检查和异常处理)
|
||
if (_isReceivingImage)
|
||
{
|
||
try
|
||
{
|
||
_deviceManager.StopReceiveImage();
|
||
_isReceivingImage = false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"停止接收图像失败: {ex.Message}");
|
||
_isReceivingImage = false; // 确保状态更新
|
||
}
|
||
}
|
||
|
||
// 设置连接状态后,调用更新Info
|
||
UpdateInfo();
|
||
|
||
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); // 清除之前的错误信息
|
||
|
||
// 设置重连状态后,调用更新Info
|
||
UpdateInfo();
|
||
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上显示
|
||
}
|
||
|
||
|
||
/// <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
|
||
|
||
#region 设备Ping相关方法
|
||
|
||
/// <summary>
|
||
/// Ping定时器的回调方法
|
||
/// </summary>
|
||
/// <param name="state">状态对象</param>
|
||
private void PingTimer_Tick(object state)
|
||
{
|
||
if (_deviceManager != null && !string.IsNullOrEmpty(_deviceManager.IPAddress))
|
||
{
|
||
Task.Factory.StartNew(() =>
|
||
{
|
||
bool pingResult = PingDevice(_deviceManager.IPAddress);
|
||
try
|
||
{
|
||
// 在线程安全的方式下更新状态
|
||
if (this.InvokeRequired)
|
||
{
|
||
this.Invoke(new Action<bool>(UpdatePingState), pingResult);
|
||
}
|
||
else
|
||
{
|
||
UpdatePingState(pingResult);
|
||
}
|
||
}
|
||
catch (ObjectDisposedException)
|
||
{
|
||
// 控件可能已被释放,忽略此更新
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行Ping操作
|
||
/// </summary>
|
||
/// <param name="ipAddress">要Ping的IP地址</param>
|
||
/// <returns>是否Ping通</returns>
|
||
private bool PingDevice(string ipAddress)
|
||
{
|
||
try
|
||
{
|
||
using (var ping = new System.Net.NetworkInformation.Ping())
|
||
{
|
||
var reply = ping.Send(ipAddress, 2000); // 2秒超时
|
||
return reply != null && reply.Status == System.Net.NetworkInformation.IPStatus.Success;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"Ping设备失败: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新Ping状态
|
||
/// </summary>
|
||
/// <param name="isPingable">是否可Ping通</param>
|
||
private void UpdatePingState(bool isPingable)
|
||
{
|
||
IsDevicePingable = isPingable;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始设备Ping
|
||
/// </summary>
|
||
private void StartDevicePing()
|
||
{
|
||
try
|
||
{
|
||
if (_pingTimer != null)
|
||
{
|
||
_pingTimer.Change(0, _pingInterval); // 立即开始,然后按间隔执行
|
||
Console.WriteLine("设备Ping已启动");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"启动设备Ping失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止设备Ping
|
||
/// </summary>
|
||
private void StopDevicePing()
|
||
{
|
||
try
|
||
{
|
||
if (_pingTimer != null)
|
||
{
|
||
_pingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||
Console.WriteLine("设备Ping已停止");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"停止设备Ping失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#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;
|
||
}
|
||
|
||
|
||
|
||
// 无论是否在设计模式下,都需要释放图像资源
|
||
|
||
// 释放Ping定时器
|
||
if (_pingTimer != null)
|
||
{
|
||
_pingTimer.Dispose();
|
||
_pingTimer = 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);
|
||
}
|
||
}
|
||
}
|