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 = ""; /// /// 获取或设置项目路径,控件所需的数据文件将在此目录中进行存取 /// [Category("配置")] [Description("设置项目路径,控件所需的数据文件将在此目录中进行存取")] [DefaultValue("")] public string ProjectPath { get { return _projectPath; } set { // 只有当值发生变化时才进行同步 if (_projectPath != value) { _projectPath = value; // 如果DeviceManager已经初始化,则同步更新其ProjectPath属性 if (_deviceManager != null) { _deviceManager.ProjectPath = _projectPath; } } } } // 显示错误的定时器 /// /// 更新设计模式状态到DeviceManager /// private void UpdateDesignModeStatus() { DeviceManager.IsDesignMode = DesignMode; Console.WriteLine($"相机控件设计模式状态已更新: {DesignMode}"); } /// /// 更新InfoImage显示 /// 1. 如果暂停,显示暂停信息 /// 2. 否则如果Ping不通或断开,显示重连信息 /// 3. 否则清空InfoImage /// 4. 最后调用更新UI /// private void UpdateInfo() { // 更新Ping状态到Info文本 Console.WriteLine($"Ping状态更新: {(IsDevicePingable ? "可Ping通" : "不可Ping通")}"); if (DesignMode) return; try { lock (_infoImageLock) { // 检查连接状态 bool isDisconnected = _deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Disconnected; bool isReconnecting = _deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Reconnecting; bool isPaused = _isPaused; // 使用_isPaused标志判断暂停状态 bool isPingFailed = !IsDevicePingable; // 根据用户要求的优先级显示信息:先检查暂停状态,然后再检查Ping状态和连接状态 if (isPaused) { // 暂停状态 - 最高优先级 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.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); } } } else if (isPingFailed || isDisconnected || isReconnecting) { // 非暂停状态下,检查Ping状态和连接状态 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; if (isReconnecting) { text = "正在重连..."; textColor = Color.Yellow; } else if (isDisconnected) { text = "连接断开"; textColor = Color.Red; } else if (isPingFailed) { 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); } } } // 否则清空InfoImage(已在开头处理) // 设置显示标志 _isDisplayingInfo = (isPaused || isDisconnected || isReconnecting); } // 调用更新UI UpdateImageOnUI(); } catch (Exception ex) { Console.WriteLine($"更新Info显示时出错: {ex.Message}"); } } /// /// 暂停/恢复检测菜单项点击事件处理 /// 1、暂停或恢复时,设置暂停状态,调用更新Info /// private void PauseDetectionToolStripMenuItem_Click(object sender, EventArgs e) { if (DesignMode) return; try { // 切换暂停状态 _isPaused = !_isPaused; if (_isPaused) { // 设置暂停状态 pauseDetectionToolStripMenuItem.Text = "恢复检测"; // 暂停时停止图像接收并更新DeviceManager的暂停检测状态 if (_deviceManager != null) { _deviceManager.IsDetectionPaused = true; if (_isReceivingImage) { _deviceManager.StopImageReceiving(); _isReceivingImage = false; } } Console.WriteLine("检测已暂停"); } else { // 设置恢复状态 pauseDetectionToolStripMenuItem.Text = "暂停检测"; // 恢复时更新DeviceManager的暂停检测状态并重新开始图像接收 if (_deviceManager != null) { _deviceManager.IsDetectionPaused = false; if (_deviceManager.ConnectionStatus == ConnectionStatus.Connected) { _deviceManager.StopImageReceiving(); _deviceManager.StartImageReceiving(); _isReceivingImage = true; // 恢复检测后,启动连接检查以确保连接正常 _deviceManager.StartConnectionCheck(); } // 如果当前是断开状态但启用了自动重连,尝试启动重连 else if (_deviceManager.AutoReconnectEnabled) { _deviceManager.StartAutoReconnect(); } } 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(); } } /// /// Camera控件加载事件 /// 在控件加载时自动启动相机显示热图 /// private void Camera_Load(object sender, EventArgs e) { // 只有在非设计模式下才启动相机 if (!DesignMode) { try { StartCamera(); } catch (Exception ex) { ShowError($"自动启动相机失败: {ex.Message}"); } } } // 注意:UserControl不支持FormClosing事件,资源清理已在Dispose方法中处理 /// /// 初始化设备管理器 /// 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; } } /// /// 初始化图像缓冲区和相关图像资源 /// 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.Transparent); } // 初始化InfoImage为透明bitmap lock (_infoImageLock) { _infoImage = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT); using (Graphics g = Graphics.FromImage(_infoImage)) { g.Clear(Color.Transparent); } Console.WriteLine("InfoImage已初始化为透明bitmap"); } // 初始化图像框的bitmap为透明 if (imageBox != null && !imageBox.IsDisposed) { imageBox.Image = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT); using (Graphics g = Graphics.FromImage(imageBox.Image)) { g.Clear(Color.Transparent); } Console.WriteLine("图像框bitmap已初始化为透明"); } } catch (Exception ex) { Console.WriteLine($"初始化图像资源失败: {ex.Message}"); // 发生异常时释放已创建的资源 if (_imageBuffer != null) { _imageBuffer.Dispose(); _imageBuffer = null; } lock (_infoImageLock) { if (_infoImage != null) { _infoImage.Dispose(); _infoImage = null; } } } } /// /// 启动相机 /// 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); } } /// /// 开始接收图像(使用HTTP方式) /// 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}"); } } /// /// 停止接收图像 /// 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一次 /// /// 获取设备是否可Ping通 /// public bool IsDevicePingable { get { return _isDevicePingable; } private set { if (_isDevicePingable != value) { _isDevicePingable = value; Console.WriteLine($"设备Ping状态变更: {(_isDevicePingable ? "可Ping通" : "不可Ping通")}"); // 状态变化时调用更新Info UpdateInfo(); } } } /// /// 设备管理器图像接收事件处理 /// 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中 // 不释放旧的LastImage,仅在控件Dispose时释放 lock (_lastImageLock) { // 如果是第一次设置LastImage,直接赋值 if (_lastImage == null) { _lastImage = newImage; } else { // 后续更新时,使用临时变量保存新图像,然后替换引用 Image temp = newImage; newImage = _lastImage; _lastImage = temp; // 注意:这里不Dispose旧图像,只在控件Dispose时统一释放 } } // 按照用户要求:调用更新到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}"); } } /// /// 在UI线程上更新图像 - 新实现,按照用户要求: /// 1. 先将LastImage绘制到全局缓冲 /// 2. 再将InfoImage绘制到缓冲 /// 3. 最后一次性绘制到图像框的bitmap /// private void UpdateImageOnUI() { if (DesignMode) return; // 线程安全检查 - 确保在UI线程上执行 if (this.InvokeRequired) { try { this.BeginInvoke(new Action(UpdateImageOnUI)); } catch (ObjectDisposedException) { Console.WriteLine("控件已释放,跳过UI更新"); } return; } // 一次性控件有效性检查,避免重复检查 if (this.IsDisposed || imageBox == null || imageBox.IsDisposed) { Console.WriteLine("控件已释放,无法更新图像"); return; } Image lastImage = null; Image infoImage = null; Image oldImage = null; Bitmap displayImage = null; try { // 检查图像缓冲区是否有效 if (_imageBuffer == null) { Console.WriteLine("图像缓冲区未初始化,尝试重新初始化"); InitializeImageBuffer(); if (_imageBuffer == null) { 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.Transparent); // 步骤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); } } } // 在同一个锁内创建缓冲区的副本,避免重复锁定 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) { try { imageBox.Image = oldImage; } catch { // 如果设置旧图像也失败,释放它 DisposeImage(oldImage); } } } catch (Exception ex) { Console.WriteLine($"更新图像UI异常: {ex.Message}"); } finally { // 确保在任何情况下都释放资源 DisposeImage(lastImage); DisposeImage(infoImage); // 只有当旧图像不再被使用时才释放 if (oldImage != null && oldImage != imageBox.Image) { DisposeImage(oldImage); } } } /// /// 安全释放图像资源的辅助方法 /// private void DisposeImage(Image image) { if (image != null) { try { image.Dispose(); } catch { } } } /// /// 设备管理器连接状态变更事件处理 /// 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(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}"); } } /// /// 处理连接状态变更 /// 2、断开或连接时,设置连接状态,调用更新Info /// private void HandleConnectionStatusChanged(ConnectionStatusChangedEventArgs e) { if (DesignMode) return; try { // 确保在UI线程上更新UI状态 if (this.InvokeRequired) { this.Invoke(new Action(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 {} } } /// /// 更新UI状态 /// /// 是否已连接 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}"); } } /// /// 设备管理器连接异常事件处理 /// 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}"); } } /// /// 显示错误信息 /// private void ShowError(string message) { if (DesignMode) return; Console.WriteLine(message); // 错误消息仅写入日志即可,不需要在UI上显示 } /// /// 右键菜单显示前的事件处理方法 /// 用于更新色彩模式菜单项的选中状态 /// /// /// 右键菜单打开事件处理 /// private void ContextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e) { // 暂停菜单项的文本已经在点击事件中更新,这里无需再次更新 if (DesignMode) return; try { // 检查是否处于暂停状态 bool isPaused = pauseDetectionToolStripMenuItem.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 色彩模式切换方法 /// /// 白热色彩模式 /// 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}"); } } /// /// 黑热色彩模式 /// 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}"); } } /// /// 铁红色彩模式 /// 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}"); } } /// /// 熔岩色彩模式 /// 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}"); } } /// /// 彩虹色彩模式 /// 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}"); } } /// /// 铁灰色彩模式 /// 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}"); } } /// /// 红热色彩模式 /// 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}"); } } /// /// 彩虹2色彩模式 /// 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 视频模式切换方法 /// /// 红外模式 /// 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相关方法 /// /// Ping定时器的回调方法 /// /// 状态对象 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(UpdatePingState), pingResult); } else { UpdatePingState(pingResult); } } catch (ObjectDisposedException) { // 控件可能已被释放,忽略此更新 } }); } } /// /// 执行Ping操作 /// /// 要Ping的IP地址 /// 是否Ping通 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; } } /// /// 更新Ping状态 /// /// 是否可Ping通 private void UpdatePingState(bool isPingable) { IsDevicePingable = isPingable; } /// /// 开始设备Ping /// private void StartDevicePing() { try { if (_pingTimer != null) { _pingTimer.Change(0, _pingInterval); // 立即开始,然后按间隔执行 Console.WriteLine("设备Ping已启动"); } } catch (Exception ex) { Console.WriteLine($"启动设备Ping失败: {ex.Message}"); } } /// /// 停止设备Ping /// 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 /// /// 清理资源 /// 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); } } }