Files
JoyD/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs
2026-01-13 14:29:37 +08:00

3498 lines
147 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.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
namespace JoyD.Windows.CS.Toprie
{
/// <summary>
/// 相机控件类,用于显示和处理相机图像
/// </summary>
public partial class Camera : UserControl
{
// 测温区配置类
private class TemperatureZone
{
public int Index { get; set; }
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public Color Color { get; set; }
}
// 温差配置类
private class TemperatureDiffConfig
{
public List<Tuple<double, Color>> TemperatureLegend { get; set; }
public Dictionary<Point, double> PixelTemperatureData { get; set; }
public TemperatureDiffConfig()
{
TemperatureLegend = new List<Tuple<double, Color>>();
PixelTemperatureData = new Dictionary<Point, double>();
}
}
// 设备管理器实例
private DeviceManager _deviceManager;
// 是否正在接收图像
private bool _isReceivingImage = false;
// 全局图像缓冲区bitmap用于在其上绘制图像和mask信息
private Bitmap _imageBuffer = null;
private const int BUFFER_WIDTH = 512;
// 右键菜单菜单项
private System.Windows.Forms.ToolStripMenuItem modifyConfigToolStripMenuItem;
private const int BUFFER_HEIGHT = 384;
// 预览窗口引用
private JoyD.Windows.CS.preview _previewForm;
// 最后接收的图像
private Image _lastImage = null;
// 信息图像,用于显示额外信息
private Image _infoImage = null;
// 实时信息图像,用于显示温度等实时数据
private Image _displayImage = null;
private readonly object _displayImageLock = new object();
// 最近一次获取到的温度数据
// 温度显示状态标志
private bool _showGlobalTemperature = false; // 是否显示全局温度
private bool _showAreaTemperature = false; // 是否显示区域温度
private bool _showMaxTemperature = false; // 是否显示最高温度默认为false
private bool _showAverageTemperature = false; // 是否显示平均温度
private bool _showMinTemperature = false; // 是否显示最低温度
// 日志保存状态标志
// 不再需要_saveLogEnabled字段直接使用DeviceManager.LogToFile静态属性控制日志记录
// 用于保护_lastImage的线程锁
private readonly object _lastImageLock = new object();
// 用于保护_infoImage的线程锁
private readonly object _infoImageLock = new object();
// 是否显示信息图像
private bool _isDisplayingInfo = false;
// 是否暂停检测
private bool _isPaused = false;
/// <summary>
/// 首次ping通设备并更新信息
/// </summary>
private volatile bool _isFirst = true;
// 项目路径,用于数据文件的存取
private string _projectPath = "";
// 配置是否已经加载的标志位
private bool _isConfigLoaded = false;
// 自动配置标志位,自动加载或保存区域、温度和温差信息
private bool _autoConfig = true;
// 加载的测温区配置
private readonly List<TemperatureZone> _loadedTemperatureZones = new List<TemperatureZone>();
// 加载的温差配置
private TemperatureDiffConfig _loadedTemperatureDiffConfig = new TemperatureDiffConfig();
// 检测区配置
private DetectionZone _detectionZone = new DetectionZone();
/// <summary>
/// 获取或设置项目路径,控件所需的数据文件将在此目录中进行存取
/// </summary>
[Category("配置")]
[Description("设置项目路径,控件所需的数据文件将在此目录中进行存取")]
[DefaultValue("")]
[DisplayName("项目路径")]
[Editor(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(System.Drawing.Design.UITypeEditor))]
public string ProjectPath
{
get { return _projectPath; }
set
{
// 只有当值发生变化时才进行同步
if (_projectPath != value)
{
_projectPath = value;
// 如果DeviceManager已经初始化则同步更新其ProjectPath属性
if (_deviceManager != null)
{
_deviceManager.ProjectPath = _projectPath;
}
// ProjectPath改变时无论是否为自动配置都要加载菜单
LoadMenuConfig();
// 只有当自动配置开启时,才加载配置文件
if (_autoConfig && !_isConfigLoaded)
{
LoadZoneConfig();
LoadTemperatureDiffConfig();
// 设置配置已加载标志
_isConfigLoaded = true;
}
}
}
}
/// <summary>
/// 获取或设置是否自动配置,自动加载或保存区域、温度和温差信息
/// </summary>
[Category("配置")]
[DisplayName("自动配置")]
[Description("自动加载或保存区域、温度和温差信息")]
[DefaultValue(true)]
public bool AutoConfig
{
get { return _autoConfig; }
set
{
_autoConfig = value;
// 更新Setting窗口的自动配置状态
JoyD.Windows.CS.Setting.Form.AutoConfig = _autoConfig;
// 如果自动配置开启且配置尚未加载,则加载配置
if (_autoConfig && !_isConfigLoaded)
{
LoadAllConfigs();
_isConfigLoaded = true;
}
}
}
/// <summary>
/// 温度类型枚举
/// </summary>
public enum TemperatureType
{
/// <summary>
/// 最高温度
/// </summary>
Max = 0,
/// <summary>
/// 最低温度
/// </summary>
Min = 1,
/// <summary>
/// 平均温度
/// </summary>
Average = 2
}
/// <summary>
/// 获取或设置检测区配置
/// </summary>
[Category("配置")]
[DisplayName("检测区配置")]
[Description("设置检测区的位置和大小")]
public DetectionZone CurrentDetectionZone
{
get { return _detectionZone; }
set
{
_detectionZone = value;
// 同时更新DeviceManager的检测区配置
if (_deviceManager != null)
{
_deviceManager.DetectionZone = value;
}
}
}
/// <summary>
/// 关闭相机并完全释放所有资源
/// </summary>
[Category("控制")]
[DisplayName("关闭相机")]
[Description("关闭相机并完全释放所有资源")]
public void Close()
{
try
{
// 停止相机并释放相关资源
StopCamera();
// 关闭设置窗口
Setting.Form.Close();
// 取消注册事件并释放设备管理器
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)
{
_imageBuffer.Dispose();
_imageBuffer = null;
}
// 释放LastImage资源
lock (_lastImageLock)
{
if (_lastImage != null)
{
_lastImage.Dispose();
_lastImage = null;
}
}
// 释放InfoImage资源
lock (_infoImageLock)
{
if (_infoImage != null)
{
_infoImage.Dispose();
_infoImage = null;
}
}
// 释放DisplayImage资源
lock (_displayImageLock)
{
if (_displayImage != null)
{
_displayImage.Dispose();
_displayImage = null;
}
}
}
catch (Exception ex)
{
Console.WriteLine($"关闭相机时出错: {ex.Message}");
}
}
/// <summary>
/// 获取指定测温区的温度值
/// </summary>
/// <param name="tempType">温度类型0表示最高温1表示最低温2表示平均温</param>
/// <param name="zoneIndex">测温区编号0表示全局温度</param>
/// <returns>温度值</returns>
public float GetAreaTemp(int tempType, int zoneIndex)
{
// 检查DeviceManager是否为空或未连接
if (_deviceManager == null || _deviceManager.ConnectionStatus != ConnectionStatus.Connected)
{
return float.NaN;
}
// 获取当前温度数据
TemperatureData temperatureData = _deviceManager.LastTemperature;
if (temperatureData == null)
{
return float.NaN;
}
// 全局温度
if (zoneIndex == 0)
{
switch (tempType)
{
case 0: // 最高温
return temperatureData.MaxTemperature;
case 1: // 最低温
return temperatureData.MinTemperature;
case 2: // 平均温
return temperatureData.AverageTemperature;
default:
return float.NaN;
}
}
// 特定测温区温度
else
{
if (temperatureData.ZoneTemperatures.TryGetValue(zoneIndex, out var zoneTempData))
{
switch (tempType)
{
case 0: // 最高温
return zoneTempData.MaxTemperature;
case 1: // 最低温
return zoneTempData.MinTemperature;
case 2: // 平均温
return zoneTempData.AverageTemperature;
default:
return float.NaN;
}
}
return float.NaN;
}
}
/// <summary>
/// 加载测温区配置文件
/// </summary>
private void LoadZoneConfig()
{
try
{
if (string.IsNullOrWhiteSpace(_projectPath)) _projectPath = "";
// 配置文件存储在Config子目录中
string configDir = Path.Combine(_projectPath, "Config");
string configPath = Path.Combine(configDir, "测温区信息.csv");
if (!File.Exists(configPath))
return;
// 清空已加载的测温区
_loadedTemperatureZones.Clear();
// 读取CSV文件
using (StreamReader reader = new StreamReader(configPath, Encoding.UTF8))
{
// 跳过标题行
reader.ReadLine();
// 读取数据行
string line;
while ((line = reader.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(line))
continue;
string[] parts = line.Split(',');
if (parts.Length < 6)
continue;
try
{
// 解析颜色支持HTML格式和十六进制格式
TemperatureZone zone = new TemperatureZone
{
Index = int.Parse(parts[0]),
X = int.Parse(parts[1]),
Y = int.Parse(parts[2]),
Width = int.Parse(parts[3]),
Height = int.Parse(parts[4]),
Color = ColorTranslator.FromHtml(parts[5])
};
_loadedTemperatureZones.Add(zone);
}
catch (Exception ex)
{
Console.WriteLine($"解析测温区配置行失败: {line}, 错误: {ex.Message}");
}
}
}
Console.WriteLine($"成功加载 {_loadedTemperatureZones.Count} 个测温区配置");
}
catch (Exception ex)
{
Console.WriteLine($"加载测温区配置文件失败: {ex.Message}");
}
}
/// <summary>
/// 加载温差配置文件
/// </summary>
private void LoadTemperatureDiffConfig()
{
try
{
if (string.IsNullOrWhiteSpace(_projectPath)) _projectPath = "";
// 配置文件存储在Config子目录中
string configDir = Path.Combine(_projectPath, "Config");
string configPath = Path.Combine(configDir, "温差数据.csv");
if (!File.Exists(configPath))
return;
// 创建新的温差配置实例
TemperatureDiffConfig config = new TemperatureDiffConfig();
// 读取CSV文件
using (StreamReader reader = new StreamReader(configPath, Encoding.UTF8))
{
string line;
bool readingLegend = false;
bool readingPixelData = false;
while ((line = reader.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(line))
continue;
// 检查部分标题
if (line == "温差图例信息")
{
readingLegend = true;
readingPixelData = false;
reader.ReadLine(); // 跳过温度图例的标题行
continue;
}
else if (line == "像素温度数据")
{
readingLegend = false;
readingPixelData = true;
reader.ReadLine(); // 跳过像素温度数据的标题行
continue;
}
if (readingLegend)
{
// 解析温差图例数据
string[] parts = line.Split(',');
if (parts.Length >= 2)
{
try
{
double temperature = double.Parse(parts[0]);
Color color = ColorTranslator.FromHtml(parts[1]);
config.TemperatureLegend.Add(new Tuple<double, Color>(temperature, color));
}
catch (Exception ex)
{
Console.WriteLine($"解析温差图例数据失败: {line}, 错误: {ex.Message}");
}
}
}
else if (readingPixelData)
{
// 解析像素温度数据
string[] parts = line.Split(',');
if (parts.Length >= 3)
{
try
{
int x = int.Parse(parts[0]);
int y = int.Parse(parts[1]);
double temperature = double.Parse(parts[2]);
config.PixelTemperatureData[new Point(x, y)] = temperature;
}
catch (Exception ex)
{
Console.WriteLine($"解析像素温度数据失败: {line}, 错误: {ex.Message}");
}
}
}
}
}
// 更新加载的温差配置
_loadedTemperatureDiffConfig = config;
Console.WriteLine($"成功加载温差配置,包含 {_loadedTemperatureDiffConfig.TemperatureLegend.Count} 个图例项和 {_loadedTemperatureDiffConfig.PixelTemperatureData.Count} 个像素温度数据");
}
catch (Exception ex)
{
Console.WriteLine($"加载温差配置文件失败: {ex.Message}");
}
}
/// <summary>
/// 加载所有配置文件
/// </summary>
public void LoadAllConfigs()
{
LoadMenuConfig();
LoadZoneConfig();
LoadTemperatureDiffConfig();
}
/// <summary>
/// 更新设计模式状态到DeviceManager
/// </summary>
private void UpdateDesignModeStatus()
{
DeviceManager.IsDesignMode = DesignMode;
Console.WriteLine($"相机控件设计模式状态已更新: {DesignMode}");
}
/// <summary>
/// 更新InfoImage显示 - 按照用户要求的详细步骤:
/// 1. 以透明色清空Info
/// 2. 如果暂停显示暂停信息否则如果Ping不通或断开显示重连信息否则满足就绪条件
/// 3. 在就绪条件下,如果有温度数据,显示最高温度
/// 4. 最后调用更新UI
/// </summary>
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 isConnected = _deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Connected;
bool isPaused = _isPaused; // 使用_isPaused标志判断暂停状态
bool isPingFailed = !IsDevicePingable;
bool isReady = !isPaused && !isPingFailed && isConnected; // 就绪条件非暂停、可Ping通、已连接
using (Graphics g = Graphics.FromImage(_infoImage))
{
// 步骤1以透明色清空Info
g.Clear(Color.Transparent);
// 步骤2检查暂停状态、Ping状态和连接状态
if (isPaused)
{
// 暂停状态 - 最高优先级
// 绘制暂停文本
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() { 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状态和连接状态
// 确定显示的文本和颜色
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() { Alignment = StringAlignment.Center };
// 将主文本居中显示
g.DrawString(text, font, textBrush,
new RectangleF(0, BUFFER_HEIGHT / 3, BUFFER_WIDTH, BUFFER_HEIGHT / 3),
format);
}
Console.WriteLine($"当前状态:{text}");
}
}
// 设置显示标志
_isDisplayingInfo = isPaused || isDisconnected || isReconnecting || _showGlobalTemperature || _showAreaTemperature;
// 步骤3无论就绪状态如何都调用更新UI以显示状态信息
UpdateImageOnUI();
}
}
catch (Exception ex)
{
Console.WriteLine($"更新Info显示时出错: {ex.Message}");
}
}
/// <summary>
/// 暂停/恢复检测菜单项点击事件处理
/// 1、暂停或恢复时设置暂停状态调用更新Info
/// </summary>
private void PauseDetectionToolStripMenuItem_Click(object sender, EventArgs e)
{
if (DesignMode) return;
try
{
// 记录操作前的暂停状态
Console.WriteLine($"[PauseDetection] 操作前状态: _isPaused = {_isPaused}");
// 切换暂停状态
_isPaused = !_isPaused;
// 记录操作后的暂停状态
Console.WriteLine($"[PauseDetection] 操作后状态: _isPaused = {_isPaused}");
if (_isPaused)
{
// 设置暂停状态
pauseDetectionToolStripMenuItem.Text = "恢复检测";
// 暂停时停止图像接收并更新DeviceManager的暂停检测状态
if (_deviceManager != null)
{
_deviceManager.IsDetectionPaused = true;
if (_isReceivingImage)
{
_deviceManager.StopImageReceiving();
_deviceManager.PauseTemperatureDataReceiving();
_isReceivingImage = false;
}
}
Console.WriteLine($"[PauseDetection] 检测已暂停 - DeviceManager状态更新完成当前时间: {DateTime.Now:HH:mm:ss.fff}");
}
else
{
// 设置恢复状态
pauseDetectionToolStripMenuItem.Text = "暂停检测";
// 恢复时更新DeviceManager的暂停检测状态并重新开始图像接收
if (_deviceManager != null)
{
_deviceManager.IsDetectionPaused = false;
if (_deviceManager.ConnectionStatus == ConnectionStatus.Connected)
{
_deviceManager.StopImageReceiving();
_deviceManager.StartImageReceiving();
_deviceManager.ResumeTemperatureDataReceiving();
_isReceivingImage = true;
// 恢复检测后,启动连接检查以确保连接正常
_deviceManager.StartConnectionCheck();
}
// 如果当前是断开状态但启用了自动重连,尝试启动重连
else if (_deviceManager.AutoReconnectEnabled)
{
_deviceManager.StartAutoReconnect();
}
}
Console.WriteLine($"[PauseDetection] 检测已恢复 - DeviceManager状态更新完成连接状态: {_deviceManager?.ConnectionStatus}, 当前时间: {DateTime.Now:HH:mm:ss.fff}");
}
// 修改流程第1点和第5点暂停或恢复时设置暂停状态调用更新Info在暂停状态下会显示暂停信息
UpdateInfo();
// 保存菜单状态
SaveMenuConfig();
}
catch (Exception ex)
{
Console.WriteLine($"处理暂停/恢复检测时出错: {ex.Message}");
}
}
/// <summary>
/// 相机控件构造函数
/// </summary>
public Camera()
{
InitializeComponent();
// 为右键菜单添加Opening事件用于在菜单显示前更新色彩模式的选中状态
this.contextMenuStrip1.Opening += ContextMenuStrip1_Opening;
// 初始化日志保存状态,确保与菜单项状态同步
// 直接使用DeviceManager.LogToFile静态属性控制日志记录无需额外的_saveLogEnabled字段
saveLogToolStripMenuItem.Checked = DeviceManager.LogToFile;
// 将设计模式状态传递给DeviceManager
UpdateDesignModeStatus();
// 初始化Ping定时器
_pingTimer = new System.Threading.Timer(PingTimer_Tick, null, Timeout.Infinite, Timeout.Infinite);
InitializeImageBuffer();
// 异步初始化图像缓冲区和加载菜单配置避免阻塞UI线程
ThreadPool.QueueUserWorkItem(delegate
{
try
{
LoadMenuConfig();
Console.WriteLine("图像缓冲区和菜单配置已异步初始化完成");
}
catch (Exception ex)
{
Console.WriteLine($"异步初始化失败: {ex.Message}");
}
});
// 只有在非设计模式下才初始化设备管理器和错误定时器
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
{
Console.WriteLine("正在加载并启动相机");
// 启动设备Ping
StartDevicePing();
// 延迟启动相机,避免界面卡顿
ThreadPool.QueueUserWorkItem(delegate
{
while (!IsDevicePingable)
Thread.Sleep(3000); // 延迟3秒后启动
_isFirst = false;
// 确保窗口句柄已经创建避免Invoke时抛出异常
while (!this.IsHandleCreated)
Thread.Sleep(100); // 等待窗口句柄创建
// 使用Invoke或BeginInvoke调用UI线程操作
if (this.IsHandleCreated)
{
this.BeginInvoke(new Action(() =>
{
try
{
StartCamera();
}
catch (Exception ex)
{
ShowError($"自动启动相机失败: {ex.Message}");
}
}));
}
});
}
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;
// 异步加载配置文件,避免阻塞主线程
if (!_isConfigLoaded)
{
ThreadPool.QueueUserWorkItem(delegate
{
try
{
LoadAllConfigs();
_isConfigLoaded = true;
Console.WriteLine("配置文件已异步加载完成");
}
catch (Exception ex)
{
Console.WriteLine($"异步加载配置文件失败: {ex.Message}");
}
});
}
// 注册图像接收事件
_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);
}
// 初始化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");
}
// 初始化DisplayImage为透明bitmap
lock (_displayImageLock)
{
_displayImage = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT);
using (Graphics g = Graphics.FromImage(_displayImage))
{
g.Clear(Color.Transparent);
}
Console.WriteLine("DisplayImage已初始化为透明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;
}
}
}
}
/// <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方式接收图像");
// 记录日志
WriteLog("开始使用HTTP方式接收图像");
// 直接调用HTTP方式的图像接收
_deviceManager.StartImageReceiving();
// 不再需要温度数据实时通知机制,移除相关代码
_isReceivingImage = true;
WriteLog("图像接收已启动");
}
}
catch (Exception ex)
{
ShowError($"开始接收图像失败: {ex.Message}");
WriteLog($"开始接收图像失败: {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}");
}
}
// 温度数据相关变量已移除,不再需要温度数据实时通知机制
// 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通")}");
// 按照README中要求的修改流程第3点和第6点Ping通状态变化时只在非暂停状态下调用更新Info
if (!_isPaused)
{
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 newImage = System.Drawing.Image.FromStream(ms))
{
// 立即验证新创建的图像是否有效
try
{
// 访问Width和Height属性来验证图像是否有效
int width = newImage.Width;
int height = newImage.Height;
if (width <= 0 || height <= 0)
{
Console.WriteLine("创建的图像尺寸无效");
return;
}
if (_lastImage == null) _lastImage = new Bitmap(newImage);
else
{
using (Graphics g = Graphics.FromImage(_lastImage))
{
g.DrawImage(newImage, Point.Empty);
}
}
}
catch (Exception)
{
Console.WriteLine("创建的图像无效");
return;
}
// 按照README中要求图像更新时保存LastImage调用更新UI不调用更新Info
this.BeginInvoke(new Action(() =>
{
try
{
if (!_isPaused)
{
UpdateImageOnUI(); // 只调用更新UI不调用更新Info
}
}
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;
}
// 一次性控件有效性检查,避免重复检查
if (this.IsDisposed || imageBox == null || imageBox.IsDisposed)
{
Console.WriteLine("控件已释放,无法更新图像");
return;
}
// 调用温度显示更新方法
// 在就绪条件下,调用更新实时信息
bool isReady = _deviceManager != null && (_isFirst || (!_isPaused && IsDevicePingable && _deviceManager.ConnectionStatus == ConnectionStatus.Connected));
if (isReady)
{
UpdateRealTimeInfoOnUI();
}
Image lastImage = null;
Image infoImage = null;
Image oldImage = null;
Bitmap tempImage = null;
try
{
// 检查图像缓冲区是否有效
if (_imageBuffer == null)
{
InitializeImageBuffer();
if (_imageBuffer == null)
{
return;
}
}
// 保存旧图像引用,以便在设置新图像后释放
oldImage = imageBox.Image;
if (!_isFirst)
{
// 获取当前的LastImage引用
lock (_lastImageLock)
{
if (_lastImage == null)
{
return;
}
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);
}
}
// 步骤2.1绘制DisplayImage实时温度信息等 - 仅在就绪条件下执行
if (isReady)
{
lock (_displayImageLock)
{
if (_displayImage != null)
{
g.DrawImage(_displayImage, 0, 0);
}
}
}
// 注意:温度信息不再直接在这里绘制
// 而是在UpdateRealTimeInfoOnUI方法中绘制到_displayImage然后在这里绘制到_imageBuffer
}
// 在同一个锁内创建缓冲区的副本,避免重复锁定
tempImage = (Bitmap)_imageBuffer.Clone();
}
// 将全局缓冲一次性绘制到图像框的bitmap
imageBox.Image = tempImage;
// 通知预览窗口更新图像
if (_previewForm != null && !_previewForm.IsDisposed)
{
_previewForm.UpdateImage(tempImage);
}
// 步骤5同步更新检测配置窗口的实时图像属性
// 创建LastImage的副本并通过UpdateRealTimeImage方法传递给Setting窗口
lock (_lastImageLock)
{
if (_lastImage != null)
{
// 直接创建LastImage的副本以避免线程安全问题
Image lastImageCopy = (Image)_lastImage.Clone();
// 调用Setting窗口的方法更新实时温度图像
Setting.Form.UpdateRealTimeImage(lastImageCopy);
}
}
//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);
}
}
}
/// <summary>
/// 安全释放图像资源的辅助方法
/// </summary>
private void DisposeImage(Image image)
{
if (image != null)
{
try { image.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;
}
// 捕获所有可能的异常,确保事件处理器不会崩溃
try
{
// 首先检查控件状态
if (this.IsDisposed || this.Disposing)
{
return;
}
// 线程安全处理 - 确保在UI线程上更新
if (this.InvokeRequired)
{
try
{
// 使用BeginInvoke代替Invoke避免可能的死锁问题
this.BeginInvoke(new Action<ConnectionStatusChangedEventArgs>(args =>
{
// 再次检查控件状态,防止在异步调用期间控件被释放
if (!this.IsDisposed && !this.Disposing)
{
HandleConnectionStatusChanged(args);
}
}), 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.BeginInvoke(new Action<ConnectionStatusChangedEventArgs>(HandleConnectionStatusChanged), e);
return;
}
// 更新UI状态
UpdateUIState(e.Status == ConnectionStatus.Connected);
// 检查_deviceManager是否为空
if (_deviceManager == null)
{
return;
}
switch (e.Status)
{
case ConnectionStatus.Connected:
Console.WriteLine("设备已连接");
WriteLog("设备已连接成功");
// 仅在首次连接时设置为热图模式,重连时保留之前的模式
if (!_isReceivingImage) // 首次连接时_isReceivingImage为false
{
try
{
_deviceManager.SetImageMode(ImageMode.Infrared);
Console.WriteLine("首次连接,设置热图模式");
WriteLog("首次连接,设置热图模式");
}
catch (Exception ex)
{
Console.WriteLine($"设置热图模式失败: {ex.Message}");
WriteLog($"设置热图模式失败: {ex.Message}");
}
}
else
{
Console.WriteLine("重连成功,保留当前图像模式");
WriteLog("重连成功,保留当前图像模式");
}
// 注意色彩模式同步现在在DeviceManager内部的连接成功处理中自动完成
// 无需在此处重复调用
// 开始接收图像包含在try-catch中
if (!_isReceivingImage)
{
try
{
StartReceiveImage();
}
catch (Exception ex)
{
Console.WriteLine($"开始接收图像失败: {ex.Message}");
WriteLog($"开始接收图像失败: {ex.Message}");
}
}
// 按照README中要求的修改流程第2点和第5点断开或连接时设置连接状态只在非暂停状态下调用更新Info
if (!_isPaused)
{
UpdateInfo();
}
break;
case ConnectionStatus.Disconnected:
Console.WriteLine("设备已断开连接");
WriteLog("设备连接已断开");
// 停止接收图像(添加空检查和异常处理)
if (_isReceivingImage)
{
try
{
_deviceManager.StopReceiveImage();
_isReceivingImage = false;
}
catch (Exception ex)
{
Console.WriteLine($"停止接收图像失败: {ex.Message}");
WriteLog($"停止接收图像失败: {ex.Message}");
_isReceivingImage = false; // 确保状态更新
}
}
// 按照README中要求的修改流程第2点和第5点断开或连接时设置连接状态只在非暂停状态下调用更新Info
if (!_isPaused)
{
UpdateInfo();
}
if (!string.IsNullOrEmpty(e.DeviceInfo))
{
ShowError(e.DeviceInfo);
WriteLog($"断开原因: {e.DeviceInfo}");
}
else
{
ShowError("设备连接已断开");
WriteLog("设备连接已断开");
}
break;
case ConnectionStatus.Connecting:
Console.WriteLine($"正在连接设备...{(!string.IsNullOrEmpty(e.DeviceInfo) ? " " + e.DeviceInfo : "")}");
WriteLog($"正在连接设备...{(!string.IsNullOrEmpty(e.DeviceInfo) ? " " + e.DeviceInfo : "")}");
ShowError(string.Empty); // 清除之前的错误信息
break;
case ConnectionStatus.Reconnecting:
Console.WriteLine($"正在重新连接设备...{(!string.IsNullOrEmpty(e.DeviceInfo) ? " " + e.DeviceInfo : "")}");
WriteLog($"正在重新连接设备...{(!string.IsNullOrEmpty(e.DeviceInfo) ? " " + e.DeviceInfo : "")}");
ShowError(string.Empty); // 清除之前的错误信息
// 按照README中要求的修改流程第2点和第6点连接状态变化时只在非暂停状态下调用更新Info
if (_isFirst || !_isPaused)
{
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 (globalTemperatureToolStripMenuItem.Checked && areaTemperatureToolStripMenuItem.Checked)
{
areaTemperatureToolStripMenuItem.Checked = false;
}
// 暂停菜单项的文本已经在点击事件中更新,这里无需再次更新
if (DesignMode) return;
try
{
// 检查是否处于暂停状态
bool isPaused = pauseDetectionToolStripMenuItem.Text == "恢复检测";
// 检查设备是否已连接
bool isConnected = _deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Connected;
// 在暂停状态或未连接状态下,隐藏图像模式根菜单和色彩模式菜单
// 注意:根菜单隐藏后,其所有子菜单会自动隐藏,不需要单独设置
var currentImageMode = _deviceManager.CurrentImageMode;
bool isInfraredMode = currentImageMode == ImageMode.Infrared;
colorModeToolStripMenuItem.Visible = isInfraredMode;
saveTemperatureToolStripMenuItem.Visible = isInfraredMode;
if (isPaused || !isConnected)
{
// 隐藏图像模式根菜单
if (imageModeToolStripMenuItem != null)
imageModeToolStripMenuItem.Visible = false;
// 隐藏色彩模式菜单
colorModeToolStripMenuItem.Visible = false;
saveTemperatureToolStripMenuItem.Visible = false;
// 在暂停状态下隐藏温度显示根菜单
temperatureDisplayToolStripMenuItem.Visible = false;
// 当只有一个菜单项可见时,隐藏分隔符
toolStripSeparator1.Visible = false;
}
else
{
// 在非暂停状态且已连接状态下,显示图像模式根菜单
// 注意:根菜单显示后,其所有子菜单会自动显示,不需要单独设置
// 只在红外模式下显示温度显示根菜单
temperatureDisplayToolStripMenuItem.Visible = isInfraredMode;
if (imageModeToolStripMenuItem != null)
imageModeToolStripMenuItem.Visible = true;
// 在非暂停状态且已连接状态下,显示分隔符
toolStripSeparator1.Visible = true;
// 根据当前图像模式控制色彩模式菜单的可见性
// 只有在红外模式下才显示色彩模式菜单和保存温度菜单
// 清除视频模式菜单项的选中状态
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枚举
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("切换到红外模式失败");
}
finally
{
// 菜单状态需要保存和恢复
SaveMenuConfig();
}
}
private void VisibleModeToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
_deviceManager.SetImageMode(ImageMode.Natural);
}
catch (Exception ex)
{
Console.WriteLine("切换到自然模式失败: " + ex.Message);
ShowError("切换到自然模式失败");
}
finally
{
// 菜单状态需要保存和恢复
SaveMenuConfig();
}
}
#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.BeginInvoke(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, 1000); // 1秒超时与DeviceManager保持一致
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)
{
// 按照README中要求的修改流程第3点Ping通状态变化时修改Ping状态
// 注意UpdateInfo的调用已在IsDevicePingable的setter中实现只在非暂停状态下
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);
}
}
Setting.Form.Close();
// 取消注册事件并释放设备管理器
if (_deviceManager != null)
{
// 移除所有事件监听
_deviceManager.ImageReceived -= DeviceManager_ImageReceived;
_deviceManager.ConnectionStatusChanged -= DeviceManager_ConnectionStatusChanged;
_deviceManager.ConnectionException -= DeviceManager_ConnectionException;
// 已移除对TemperatureReceived事件的订阅不再需要取消订阅
// 释放设备管理器资源
_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}");
}
}
}
// 释放DisplayImage资源
lock (_displayImageLock)
{
if (_displayImage != null)
{
try
{
_displayImage.Dispose();
_displayImage = null;
Console.WriteLine("DisplayImage资源已释放");
}
catch (Exception ex)
{
Console.WriteLine($"清理DisplayImage资源异常: {ex.Message}");
}
}
}
// 释放组件资源
components?.Dispose();
}
base.Dispose(disposing);
}
/// <summary>
/// 保存温度菜单项点击事件处理程序
/// </summary>
/// <param name="sender">事件发送者</param>
/// <param name="e">事件参数</param>
/// <summary>
/// 全局温度菜单项点击事件处理
/// 当全局温度被勾选时,自动取消区域温度的勾选
/// </summary>
private void GlobalTemperatureToolStripMenuItem_Click(object sender, EventArgs e)
{
// 更新全局温度显示状态标志
_showGlobalTemperature = globalTemperatureToolStripMenuItem.Checked;
// 当全局温度被勾选时,自动取消区域温度的勾选
if (_showGlobalTemperature)
{
_showAreaTemperature = false;
areaTemperatureToolStripMenuItem.Checked = false;
}
// 修改流程第6点数据显示菜单勾选变化时只在非暂停状态下调用更新实时信息
if (!_isPaused)
{
UpdateRealTimeInfoOnUI();
}
// 菜单状态变更时自动静默保存配置
SaveMenuConfig();
}
/// <summary>
/// 区域温度菜单项点击事件处理
/// 当区域温度被勾选时,自动取消全局温度的勾选
/// </summary>
private void AreaTemperatureToolStripMenuItem_Click(object sender, EventArgs e)
{
// 更新区域温度显示状态标志
_showAreaTemperature = areaTemperatureToolStripMenuItem.Checked;
// 当区域温度被勾选时,自动取消全局温度的勾选
if (_showAreaTemperature)
{
_showGlobalTemperature = false;
globalTemperatureToolStripMenuItem.Checked = false;
}
// 修改流程第6点数据显示菜单勾选变化时只在非暂停状态下调用更新实时信息
if (!_isPaused)
{
UpdateRealTimeInfoOnUI();
}
// 菜单状态变更时自动静默保存配置
SaveMenuConfig();
}
/// <summary>
/// 最高温度菜单项点击事件处理
/// </summary>
private void MaxTemperatureToolStripMenuItem_Click(object sender, EventArgs e)
{
// 更新最高温度显示状态标志
_showMaxTemperature = maxTemperatureToolStripMenuItem.Checked;
// 修改流程第6点数据显示菜单勾选变化时只在非暂停状态下调用更新实时信息
if (!_isPaused)
{
UpdateRealTimeInfoOnUI();
}
// 菜单状态变更时自动静默保存配置
SaveMenuConfig();
}
/// <summary>
/// 平均温度菜单项点击事件处理
/// </summary>
private void AvgTemperatureToolStripMenuItem_Click(object sender, EventArgs e)
{
// 更新平均温度显示状态标志
_showAverageTemperature = avgTemperatureToolStripMenuItem.Checked;
// 修改流程第6点数据显示菜单勾选变化时只在非暂停状态下调用更新实时信息
if (!_isPaused)
{
UpdateRealTimeInfoOnUI();
}
// 菜单状态变更时自动静默保存配置
SaveMenuConfig();
}
/// <summary>
/// 最低温度菜单项点击事件处理
/// </summary>
private void MinTemperatureToolStripMenuItem_Click(object sender, EventArgs e)
{
// 更新最低温度显示状态标志
_showMinTemperature = minTemperatureToolStripMenuItem.Checked;
// 修改流程第6点数据显示菜单勾选变化时只在非暂停状态下调用更新实时信息
if (!_isPaused)
{
UpdateRealTimeInfoOnUI();
}
// 菜单状态变更时自动静默保存配置
SaveMenuConfig();
}
/// <summary>
/// 保存日志菜单项点击事件处理
/// </summary>
private void SaveLogToolStripMenuItem_Click(object sender, EventArgs e)
{
// 更新DeviceManager.LogToFile静态属性这是控制所有日志记录的单一来源
DeviceManager.LogToFile = saveLogToolStripMenuItem.Checked;
// 可以在这里添加日志记录的初始化或清理逻辑
if (DeviceManager.LogToFile)
{
// 日志启用时的初始化
WriteLog("日志保存功能已启用");
}
else
{
// 日志禁用时的清理(如果需要)
WriteLog("日志保存功能已禁用");
}
// 菜单状态变更时自动静默保存配置
SaveMenuConfig();
}
/// <summary>
/// 保存菜单配置到CSV文件
/// </summary>
private void SaveMenuConfig()
{
try
{
WriteLog("开始保存菜单配置");
string configFilePath;
// 如果项目路径存在使用项目路径下的Config目录
if (!string.IsNullOrEmpty(_projectPath) && Directory.Exists(_projectPath))
{
try
{
// 创建Config目录如果不存在
string configDir = Path.Combine(_projectPath, "Config");
Directory.CreateDirectory(configDir);
// 配置文件路径
configFilePath = Path.Combine(configDir, "menu_config.csv");
WriteLog($"使用项目路径保存配置: {configFilePath}");
}
catch (UnauthorizedAccessException accessEx)
{
string errorMsg = $"项目路径访问权限不足: {accessEx.Message}";
WriteLog($"{errorMsg} - {accessEx.StackTrace}");
Console.WriteLine(errorMsg);
// 回退到应用程序目录
configFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", "menu_config.csv");
Directory.CreateDirectory(Path.GetDirectoryName(configFilePath));
WriteLog($"回退到应用程序目录保存: {configFilePath}");
}
}
else
{
// 如果项目路径不存在使用应用程序目录下的Config目录
string configDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config");
Directory.CreateDirectory(configDir);
configFilePath = Path.Combine(configDir, "menu_config.csv");
WriteLog($"使用应用程序目录保存配置: {configFilePath}");
}
// 创建CSV内容
StringBuilder csvContent = new StringBuilder();
// 添加标题行
csvContent.AppendLine("MenuName,Status,Checked");
try
{
// 保存暂停检测菜单状态
if (pauseDetectionToolStripMenuItem != null)
{
csvContent.AppendLine($"pauseDetection,{pauseDetectionToolStripMenuItem.Text},{_isPaused}");
}
else
{
WriteLog("警告: pauseDetectionToolStripMenuItem未初始化");
}
// 保存日志保存菜单状态
if (saveLogToolStripMenuItem != null)
{
csvContent.AppendLine($"saveLog,{saveLogToolStripMenuItem.Text},{saveLogToolStripMenuItem.Checked}");
}
else
{
WriteLog("警告: saveLogToolStripMenuItem未初始化");
}
// 保存温度显示菜单状态
if (globalTemperatureToolStripMenuItem != null)
csvContent.AppendLine($"globalTemperature,{globalTemperatureToolStripMenuItem.Text},{globalTemperatureToolStripMenuItem.Checked}");
if (areaTemperatureToolStripMenuItem != null)
csvContent.AppendLine($"areaTemperature,{areaTemperatureToolStripMenuItem.Text},{areaTemperatureToolStripMenuItem.Checked}");
if (maxTemperatureToolStripMenuItem != null)
csvContent.AppendLine($"maxTemperature,{maxTemperatureToolStripMenuItem.Text},{maxTemperatureToolStripMenuItem.Checked}");
if (avgTemperatureToolStripMenuItem != null)
csvContent.AppendLine($"avgTemperature,{avgTemperatureToolStripMenuItem.Text},{avgTemperatureToolStripMenuItem.Checked}");
if (minTemperatureToolStripMenuItem != null)
csvContent.AppendLine($"minTemperature,{minTemperatureToolStripMenuItem.Text},{minTemperatureToolStripMenuItem.Checked}");
// 保存色彩模式菜单状态
if (whiteHotToolStripMenuItem != null)
csvContent.AppendLine($"whiteHot,{whiteHotToolStripMenuItem.Text},{whiteHotToolStripMenuItem.Checked}");
if (blackHotToolStripMenuItem != null)
csvContent.AppendLine($"blackHot,{blackHotToolStripMenuItem.Text},{blackHotToolStripMenuItem.Checked}");
if (ironRedToolStripMenuItem != null)
csvContent.AppendLine($"ironRed,{ironRedToolStripMenuItem.Text},{ironRedToolStripMenuItem.Checked}");
if (lavaToolStripMenuItem != null)
csvContent.AppendLine($"lava,{lavaToolStripMenuItem.Text},{lavaToolStripMenuItem.Checked}");
if (rainbowToolStripMenuItem != null)
csvContent.AppendLine($"rainbow,{rainbowToolStripMenuItem.Text},{rainbowToolStripMenuItem.Checked}");
if (ironGrayToolStripMenuItem != null)
csvContent.AppendLine($"ironGray,{ironGrayToolStripMenuItem.Text},{ironGrayToolStripMenuItem.Checked}");
if (redHotToolStripMenuItem != null)
csvContent.AppendLine($"redHot,{redHotToolStripMenuItem.Text},{redHotToolStripMenuItem.Checked}");
if (rainbow2ToolStripMenuItem != null)
csvContent.AppendLine($"rainbow2,{rainbow2ToolStripMenuItem.Text},{rainbow2ToolStripMenuItem.Checked}");
// 保存图像模式菜单状态
if (thermalModeToolStripMenuItem != null)
csvContent.AppendLine($"thermalMode,{thermalModeToolStripMenuItem.Text},{thermalModeToolStripMenuItem.Checked}");
if (visibleModeToolStripMenuItem != null)
csvContent.AppendLine($"visibleMode,{visibleModeToolStripMenuItem.Text},{visibleModeToolStripMenuItem.Checked}");
// 保存检测区配置
csvContent.AppendLine(); // 添加空行
csvContent.AppendLine($"detectionZone,{_detectionZone.X},{_detectionZone.Y},{_detectionZone.Width},{_detectionZone.Height},{ColorTranslator.ToHtml(_detectionZone.Color)}");
}
catch (Exception ex)
{
// 捕获配置数据收集过程中的异常
string errorMsg = $"收集菜单状态数据失败: {ex.Message}";
WriteLog($"{errorMsg} - {ex.StackTrace}");
Console.WriteLine(errorMsg);
throw;
}
// 写入文件
try
{
File.WriteAllText(configFilePath, csvContent.ToString(), Encoding.UTF8);
string successMsg = $"菜单配置已成功保存到CSV文件: {configFilePath}";
Console.WriteLine(successMsg);
WriteLog(successMsg);
}
catch (IOException ioEx)
{
// 捕获IO异常并记录详细信息
string errorMsg = $"配置文件IO异常: {ioEx.Message}";
WriteLog($"{errorMsg} - {ioEx.StackTrace}");
Console.WriteLine(errorMsg);
}
catch (UnauthorizedAccessException accessEx)
{
// 捕获权限异常并记录详细信息
string errorMsg = $"配置文件访问权限异常: {accessEx.Message}";
WriteLog($"{errorMsg} - {accessEx.StackTrace}");
Console.WriteLine(errorMsg);
}
}
catch (Exception ex)
{
// 记录保存失败的综合日志
string errorMsg = $"保存菜单配置过程中发生未预期的错误: {ex.Message}";
WriteLog($"{errorMsg} - {ex.StackTrace}");
Console.WriteLine(errorMsg);
}
finally
{
WriteLog("菜单配置保存操作完成");
}
}
/// <summary>
/// 从CSV文件加载菜单配置
/// </summary>
private void LoadMenuConfig()
{
WriteLog("开始加载菜单配置");
try
{
string configFilePath = null;
// 检查项目路径下的配置文件
if (!string.IsNullOrEmpty(_projectPath) && Directory.Exists(_projectPath))
{
try
{
configFilePath = Path.Combine(_projectPath, "Config", "menu_config.csv");
WriteLog($"检查项目路径配置文件: {configFilePath}");
// 如果项目路径下没有配置文件,检查应用程序目录
if (!File.Exists(configFilePath))
{
configFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", "menu_config.csv");
WriteLog($"项目路径配置文件不存在,检查应用程序目录: {configFilePath}");
}
}
catch (UnauthorizedAccessException accessEx)
{
string errorMsg = $"项目路径访问权限不足: {accessEx.Message}";
WriteLog($"{errorMsg} - {accessEx.StackTrace}");
Console.WriteLine(errorMsg);
// 回退到应用程序目录
configFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", "menu_config.csv");
WriteLog($"回退到应用程序目录: {configFilePath}");
}
}
else
{
// 直接检查应用程序目录下的配置文件
configFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", "menu_config.csv");
WriteLog($"检查应用程序目录配置文件: {configFilePath}");
}
// 如果配置文件不存在,直接返回
if (!File.Exists(configFilePath))
{
string infoMsg = $"菜单配置文件不存在,使用默认配置: {configFilePath}";
Console.WriteLine(infoMsg);
WriteLog(infoMsg);
return;
}
// 读取CSV文件内容
string[] lines = null;
try
{
lines = File.ReadAllLines(configFilePath, Encoding.UTF8);
WriteLog($"成功读取配置文件,共{lines.Length}行");
}
catch (IOException ioEx)
{
string errorMsg = $"配置文件读取失败: {ioEx.Message}";
WriteLog($"{errorMsg} - {ioEx.StackTrace}");
Console.WriteLine(errorMsg);
return;
}
catch (UnauthorizedAccessException accessEx)
{
string errorMsg = $"配置文件访问权限不足: {accessEx.Message}";
WriteLog($"{errorMsg} - {accessEx.StackTrace}");
Console.WriteLine(errorMsg);
return;
}
// 创建菜单状态字典
Dictionary<string, Dictionary<string, string>> menuStates = new Dictionary<string, Dictionary<string, string>>();
// 解析CSV内容
try
{
for (int i = 1; i < lines.Length; i++) // 跳过标题行
{
string line = lines[i];
if (string.IsNullOrWhiteSpace(line))
{
WriteLog($"警告: 配置文件第{i + 1}行为空,已跳过");
continue;
}
try
{
// 简单的CSV解析这里假设没有包含逗号的字段
string[] parts = line.Split(',');
if (parts.Length >= 3)
{
string menuName = parts[0];
// 检测区配置处理
if (menuName == "detectionZone")
{
if (parts.Length >= 6)
{
int x = int.Parse(parts[1]);
int y = int.Parse(parts[2]);
int width = int.Parse(parts[3]);
int height = int.Parse(parts[4]);
// 解析检测区颜色,支持颜色名称和十六进制颜色代码
Color color = CommonUtils.ParseColor(parts[5]);
_detectionZone = new DetectionZone
{
X = x,
Y = y,
Width = width,
Height = height,
Color = color
};
WriteLog($"成功加载检测区配置: X={x}, Y={y}, Width={width}, Height={height}, Color={color.Name}");
}
continue;
}
string status = parts[1];
string isChecked = parts[2];
menuStates[menuName] = new Dictionary<string, string>
{
{ "status", status },
{ "checked", isChecked }
};
}
else
{
WriteLog($"警告: 配置文件第{i + 1}行格式不正确,字段数量不足: {line}");
}
}
catch (Exception ex)
{
WriteLog($"解析配置文件第{i + 1}行时出错: {ex.Message} - {ex.StackTrace}");
// 继续处理下一行,不中断整个解析过程
}
}
WriteLog($"成功解析配置文件,共加载{menuStates.Count}个菜单状态");
}
catch (Exception ex)
{
string errorMsg = $"配置文件解析过程中发生错误: {ex.Message}";
WriteLog($"{errorMsg} - {ex.StackTrace}");
Console.WriteLine(errorMsg);
return;
}
// 应用菜单状态
try
{
// 暂停检测菜单
if (menuStates.ContainsKey("pauseDetection") && pauseDetectionToolStripMenuItem != null)
{
try
{
_isPaused = bool.Parse(menuStates["pauseDetection"]["checked"]);
pauseDetectionToolStripMenuItem.Text = menuStates["pauseDetection"]["status"];
WriteLog($"应用暂停检测菜单状态: {_isPaused}");
}
catch (FormatException ex)
{
WriteLog($"暂停检测菜单状态格式错误: {ex.Message}");
}
}
// 日志保存菜单
if (menuStates.ContainsKey("saveLog") && saveLogToolStripMenuItem != null)
{
try
{
saveLogToolStripMenuItem.Checked = bool.Parse(menuStates["saveLog"]["checked"]);
saveLogToolStripMenuItem.Text = menuStates["saveLog"]["status"];
// 如果有DeviceManager实例同步状态
if (_deviceManager != null)
{
DeviceManager.LogToFile = saveLogToolStripMenuItem.Checked;
WriteLog($"同步日志保存状态到DeviceManager: {saveLogToolStripMenuItem.Checked}");
}
WriteLog($"应用日志保存菜单状态: {saveLogToolStripMenuItem.Checked}");
}
catch (FormatException ex)
{
WriteLog($"日志保存菜单状态格式错误: {ex.Message}");
}
}
// 温度显示菜单 - 添加空值检查
if (globalTemperatureToolStripMenuItem != null)
ApplyMenuState(menuStates, "globalTemperature", globalTemperatureToolStripMenuItem);
if (areaTemperatureToolStripMenuItem != null)
ApplyMenuState(menuStates, "areaTemperature", areaTemperatureToolStripMenuItem);
if (maxTemperatureToolStripMenuItem != null)
ApplyMenuState(menuStates, "maxTemperature", maxTemperatureToolStripMenuItem);
if (avgTemperatureToolStripMenuItem != null)
ApplyMenuState(menuStates, "avgTemperature", avgTemperatureToolStripMenuItem);
if (minTemperatureToolStripMenuItem != null)
ApplyMenuState(menuStates, "minTemperature", minTemperatureToolStripMenuItem);
// 同步菜单状态到对应的私有字段,确保与显示逻辑一致
if (globalTemperatureToolStripMenuItem != null)
_showGlobalTemperature = globalTemperatureToolStripMenuItem.Checked;
if (areaTemperatureToolStripMenuItem != null)
_showAreaTemperature = areaTemperatureToolStripMenuItem.Checked;
if (maxTemperatureToolStripMenuItem != null)
_showMaxTemperature = maxTemperatureToolStripMenuItem.Checked;
if (avgTemperatureToolStripMenuItem != null)
_showAverageTemperature = avgTemperatureToolStripMenuItem.Checked;
if (minTemperatureToolStripMenuItem != null)
_showMinTemperature = minTemperatureToolStripMenuItem.Checked;
// 处理温度显示菜单项的互斥逻辑
if (_showGlobalTemperature && _showAreaTemperature)
{
// 如果两者都被选中,默认保持全局温度选中,取消区域温度
_showAreaTemperature = false;
if (areaTemperatureToolStripMenuItem != null)
areaTemperatureToolStripMenuItem.Checked = false;
}
// 更新实时温度信息显示,只在非暂停状态下调用
if (!_isPaused)
{
UpdateRealTimeInfoOnUI();
}
// 色彩模式菜单 - 添加空值检查
if (whiteHotToolStripMenuItem != null)
ApplyMenuState(menuStates, "whiteHot", whiteHotToolStripMenuItem);
if (blackHotToolStripMenuItem != null)
ApplyMenuState(menuStates, "blackHot", blackHotToolStripMenuItem);
if (ironRedToolStripMenuItem != null)
ApplyMenuState(menuStates, "ironRed", ironRedToolStripMenuItem);
if (lavaToolStripMenuItem != null)
ApplyMenuState(menuStates, "lava", lavaToolStripMenuItem);
if (rainbowToolStripMenuItem != null)
ApplyMenuState(menuStates, "rainbow", rainbowToolStripMenuItem);
if (ironGrayToolStripMenuItem != null)
ApplyMenuState(menuStates, "ironGray", ironGrayToolStripMenuItem);
if (redHotToolStripMenuItem != null)
ApplyMenuState(menuStates, "redHot", redHotToolStripMenuItem);
if (rainbow2ToolStripMenuItem != null)
ApplyMenuState(menuStates, "rainbow2", rainbow2ToolStripMenuItem);
// 应用选中的色彩模式到设备
if (_deviceManager != null)
{
try
{
if (whiteHotToolStripMenuItem != null && whiteHotToolStripMenuItem.Checked)
{
_deviceManager.SetPaletteType(PaletteType.WhiteHot);
Console.WriteLine("应用白热色彩模式到设备");
}
else if (blackHotToolStripMenuItem != null && blackHotToolStripMenuItem.Checked)
{
_deviceManager.SetPaletteType(PaletteType.BlackHot);
Console.WriteLine("应用黑热色彩模式到设备");
}
else if (ironRedToolStripMenuItem != null && ironRedToolStripMenuItem.Checked)
{
_deviceManager.SetPaletteType(PaletteType.IronRed);
Console.WriteLine("应用铁红色彩模式到设备");
}
else if (lavaToolStripMenuItem != null && lavaToolStripMenuItem.Checked)
{
_deviceManager.SetPaletteType(PaletteType.Lava);
Console.WriteLine("应用熔岩色彩模式到设备");
}
else if (rainbowToolStripMenuItem != null && rainbowToolStripMenuItem.Checked)
{
_deviceManager.SetPaletteType(PaletteType.Rainbow);
Console.WriteLine("应用彩虹色彩模式到设备");
}
else if (ironGrayToolStripMenuItem != null && ironGrayToolStripMenuItem.Checked)
{
_deviceManager.SetPaletteType(PaletteType.IronGray);
Console.WriteLine("应用铁灰色彩模式到设备");
}
else if (redHotToolStripMenuItem != null && redHotToolStripMenuItem.Checked)
{
_deviceManager.SetPaletteType(PaletteType.RedHot);
Console.WriteLine("应用红热色彩模式到设备");
}
else if (rainbow2ToolStripMenuItem != null && rainbow2ToolStripMenuItem.Checked)
{
_deviceManager.SetPaletteType(PaletteType.Rainbow2);
Console.WriteLine("应用彩虹2色彩模式到设备");
}
}
catch (Exception ex)
{
Console.WriteLine($"应用色彩模式到设备失败: {ex.Message}");
}
}
// 图像模式菜单 - 添加空值检查
if (thermalModeToolStripMenuItem != null)
ApplyMenuState(menuStates, "thermalMode", thermalModeToolStripMenuItem);
if (visibleModeToolStripMenuItem != null)
ApplyMenuState(menuStates, "visibleMode", visibleModeToolStripMenuItem);
// 应用选中的图像模式到设备
if (_deviceManager != null)
{
try
{
if (thermalModeToolStripMenuItem != null && thermalModeToolStripMenuItem.Checked)
{
_deviceManager.SetImageMode(ImageMode.Infrared);
Console.WriteLine("应用红外图像模式到设备");
}
else if (visibleModeToolStripMenuItem != null && visibleModeToolStripMenuItem.Checked)
{
_deviceManager.SetImageMode(ImageMode.Natural);
Console.WriteLine("应用自然图像模式到设备");
}
}
catch (Exception ex)
{
Console.WriteLine($"应用图像模式到设备失败: {ex.Message}");
}
}
string successMsg = "菜单配置已从CSV文件成功加载并应用";
Console.WriteLine(successMsg);
WriteLog(successMsg);
}
catch (Exception ex)
{
string errorMsg = $"应用菜单状态过程中发生错误: {ex.Message}";
WriteLog($"{errorMsg} - {ex.StackTrace}");
Console.WriteLine(errorMsg);
}
}
catch (Exception ex)
{
// 捕获未预期的异常
string errorMsg = $"加载菜单配置过程中发生未预期的错误: {ex.Message}";
WriteLog($"{errorMsg} - {ex.StackTrace}");
Console.WriteLine(errorMsg);
}
finally
{
WriteLog("菜单配置加载操作完成");
}
}
/// <summary>
/// 应用菜单状态的辅助方法
/// </summary>
/// <param name="menuStates">菜单状态字典</param>
/// <param name="menuName">菜单名称</param>
/// <param name="menuItem">菜单项控件</param>
private void ApplyMenuState(Dictionary<string, Dictionary<string, string>> menuStates, string menuName, ToolStripMenuItem menuItem)
{
try
{
// 参数验证
if (menuStates == null)
{
WriteLog($"警告: 应用菜单状态时menuStates参数为null菜单名称: {menuName}");
return;
}
if (string.IsNullOrEmpty(menuName))
{
WriteLog("警告: 应用菜单状态时menuName参数为空");
return;
}
if (menuItem == null)
{
WriteLog($"警告: 应用菜单状态时菜单项为null菜单名称: {menuName}");
return;
}
// 检查菜单状态字典中是否包含该菜单
if (menuStates.ContainsKey(menuName))
{
WriteLog($"应用菜单状态: {menuName}");
try
{
// 尝试解析和应用Checked状态
string checkedValue = menuStates[menuName].ContainsKey("checked") ? menuStates[menuName]["checked"] : "false";
bool isChecked = bool.Parse(checkedValue);
menuItem.Checked = isChecked;
WriteLog($"菜单 {menuName} 选中状态已设置为: {isChecked}");
}
catch (FormatException ex)
{
string errorMsg = $"菜单 {menuName} 选中状态格式错误: {ex.Message}";
WriteLog(errorMsg);
// 保留默认值,不抛出异常以允许其他菜单状态继续应用
}
try
{
// 尝试设置菜单项文本
if (menuStates[menuName].ContainsKey("status"))
{
menuItem.Text = menuStates[menuName]["status"];
WriteLog($"菜单 {menuName} 文本已设置为: {menuStates[menuName]["status"]}");
}
}
catch (Exception ex)
{
string errorMsg = $"设置菜单 {menuName} 文本时出错: {ex.Message}";
WriteLog(errorMsg);
// 保留默认值,不抛出异常以允许其他菜单状态继续应用
}
}
else
{
WriteLog($"菜单配置中未找到菜单项: {menuName}");
}
}
catch (Exception ex)
{
// 捕获所有其他未预期的异常
string errorMsg = $"应用菜单 {menuName} 状态时发生未预期的错误: {ex.Message}";
WriteLog($"{errorMsg} - {ex.StackTrace}");
// 不重新抛出异常,避免影响其他菜单状态的应用
}
}
/// <summary>
/// 写入日志信息到文件
/// 只有在DeviceManager.LogToFile为true时才会保存
/// </summary>
/// <param name="logMessage">日志消息</param>
private void WriteLog(string logMessage)
{
// 只有当日志保存功能启用时才执行写入
// 直接使用DeviceManager.LogToFile静态属性保持与系统其他部分一致
if (DeviceManager.LogToFile)
{
try
{
string logFilePath;
// 如果项目路径存在使用项目路径下的Logs目录
if (!string.IsNullOrEmpty(_projectPath) && Directory.Exists(_projectPath))
{
// 创建Logs目录如果不存在
string logDir = Path.Combine(_projectPath, "Logs");
Directory.CreateDirectory(logDir);
// 生成日志文件名(按日期)
string logFileName = $"Log_{DateTime.Now:yyyyMMdd}.txt";
logFilePath = Path.Combine(logDir, logFileName);
}
else
{
// 如果项目路径不存在使用应用程序目录下的Log.txt
logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log.txt");
// 确保目录存在
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
}
// 写入日志(带时间戳)
string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {logMessage}";
File.AppendAllText(logFilePath, logEntry + Environment.NewLine);
}
catch (Exception ex)
{
// 记录日志写入失败的异常(但不抛出,以免影响主程序)
Console.WriteLine($"写入日志失败: {ex.Message}");
}
}
}
/// <summary>
/// 更新实时信息显示
/// 实现温度数据及其他实时信息的显示功能
/// 根据README.md要求实现10项功能
/// </summary>
/// <summary>
/// 在指定区域内居中显示文本
/// </summary>
/// <param name="g">用于绘制的Graphics对象</param>
/// <param name="texts">要显示的文本数组</param>
/// <param name="color">文本颜色,默认为白色</param>
/// <param name="area">显示文本的区域默认为null此时使用整个图像框</param>
private void DrawTextInAreaCentered(Graphics g, string[] texts, Color? color = null, RectangleF? area = null)
{
if (g == null || texts == null || texts.Length == 0)
return;
// 设置默认颜色为白色
Color displayColor = color ?? Color.White;
// 创建固定的字体和格式对象
using (Font font = new Font("微软雅黑", 12, FontStyle.Bold))
using (Brush brush = new SolidBrush(displayColor))
{
StringFormat format = new StringFormat
{
Alignment = StringAlignment.Near, // 左对齐
LineAlignment = StringAlignment.Near // 顶部对齐,由代码控制垂直居中
};
// 如果没有指定区域,使用整个图像框作为默认区域
RectangleF displayArea = area ?? new RectangleF(0, 0, _displayImage.Width, _displayImage.Height);
// 计算文本区域大小
SizeF[] textSizes = new SizeF[texts.Length];
float maxTextWidth = 0;
float totalTextHeight = 0;
const float lineSpacing = 5; // 行间距
for (int i = 0; i < texts.Length; i++)
{
textSizes[i] = g.MeasureString(texts[i], font);
maxTextWidth = Math.Max(maxTextWidth, textSizes[i].Width);
totalTextHeight += textSizes[i].Height;
}
// 添加文本间距
totalTextHeight += (texts.Length - 1) * lineSpacing;
// 计算文本在区域内的居中位置
float textAreaX = displayArea.X + (displayArea.Width - maxTextWidth) / 2;
float textAreaY = displayArea.Y + (displayArea.Height - totalTextHeight) / 2;
// 绘制温度文本
float currentY = textAreaY;
for (int i = 0; i < texts.Length; i++)
{
g.DrawString(texts[i], font, brush, textAreaX, currentY, format);
currentY += textSizes[i].Height + lineSpacing; // 加上行间距
}
}
}
private void UpdateRealTimeInfoOnUI()
{
lock (_displayImageLock)
{
// 1. 以透明色清空DisplayImage
// 遵循README要求中途不进行Dispose和设置为null只在上面进行绘制
if (_displayImage == null)
return;
using (Graphics g = Graphics.FromImage(_displayImage))
{
// 清除DisplayImage为透明色
g.Clear(Color.Transparent);
if (_deviceManager.CurrentImageMode == ImageMode.Infrared)
{
// 1. 先绘制区域框线,不受温度数据影响,只受显示设置影响
if (_showAreaTemperature)
{
try
{
// 使用固定的字体和格式对象绘制区域编号
using (Font font = new Font("微软雅黑", 10, FontStyle.Bold))
{
// 遍历已加载的测温区列表,绘制每个区域的框线和编号
foreach (TemperatureZone zone in _loadedTemperatureZones)
{
// 将相对坐标转换为绝对坐标
int absoluteX = _detectionZone.X + zone.X;
int absoluteY = _detectionZone.Y + zone.Y;
// 创建画笔,使用区域的颜色作为框线颜色
using (Pen pen = new Pen(zone.Color, 2))
{
// 绘制区域框线
g.DrawRectangle(pen, absoluteX, absoluteY, zone.Width, zone.Height);
}
// 创建画刷,使用区域的颜色作为文字颜色
using (Brush brush = new SolidBrush(zone.Color))
{
// 绘制区域编号,编号显示在区域左上角
PointF numberPosition = new PointF(absoluteX + 5, absoluteY + 5);
g.DrawString(zone.Index.ToString(), font, brush, numberPosition);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"绘制区域框时发生异常: {ex.Message}");
}
}
// 2. 温度数据绘制逻辑,仍受温度数据有效性影响
TemperatureData temperatureData = _deviceManager.LastTemperature;
if (temperatureData != null && temperatureData.Timestamp != null)
{
TimeSpan timeDiff = DateTime.Now - temperatureData.Timestamp;
if (timeDiff.TotalSeconds <= 3)
{
// 3. 温度显示菜单下如果未勾选区域温度和全局温度,则不显示任何温度信息
if (!_showGlobalTemperature && !_showAreaTemperature)
return;
// 4. 如果勾选了全局温度且未勾选区域温度,则显示全局温度(居中显示),否则显示区域温度(居中显示)
bool isGlobalTemperatureMode = _showGlobalTemperature && !_showAreaTemperature;
// 5. 根据温度模式显示温度数据
if (isGlobalTemperatureMode)
{
// 准备温度文本
List<string> temperatureTexts = new List<string>();
if (_showAverageTemperature)
{
temperatureTexts.Add($"平均: {temperatureData.AverageTemperature:F2} °C");
}
if (_showMinTemperature)
{
temperatureTexts.Add($"最低: {temperatureData.MinTemperature:F2} °C");
}
if (_showMaxTemperature)
{
temperatureTexts.Add($"最高: {temperatureData.MaxTemperature:F2} °C");
}
// 记录温度数据日志
if (temperatureTexts.Count > 0)
{
WriteLog($"全局温度数据 - 平均: {temperatureData.AverageTemperature:F2} °C, 最低: {temperatureData.MinTemperature:F2} °C, 最高: {temperatureData.MaxTemperature:F2} °C");
}
// 如果没有要显示的温度文本,直接返回
if (temperatureTexts.Count == 0)
return;
// 将List<string>转换为string[]以便传递给DrawTextInAreaCentered方法
string[] textsArray = temperatureTexts.ToArray();
// 调用DrawTextInAreaCentered方法绘制温度文本
DrawTextInAreaCentered(g, textsArray);
}
else if (_showAreaTemperature)
{
// 区域温度模式:为每个区域计算并显示温度数据
foreach (TemperatureZone zone in _loadedTemperatureZones)
{
// 准备温度文本
List<string> areaTemperatureTexts = new List<string>();
// 直接从ZoneTemperatures字典获取已计算好的区域温度数据
if (temperatureData.ZoneTemperatures.TryGetValue(zone.Index, out var zoneTempData))
{
if (_showMaxTemperature)
{
areaTemperatureTexts.Add($"最高: {zoneTempData.MaxTemperature:F2} °C");
}
if (_showMinTemperature)
{
areaTemperatureTexts.Add($"最低: {zoneTempData.MinTemperature:F2} °C");
}
if (_showAverageTemperature)
{
areaTemperatureTexts.Add($"平均: {zoneTempData.AverageTemperature:F2} °C");
}
}
// 将相对坐标转换为绝对坐标
int absoluteX = _detectionZone.X + zone.X;
int absoluteY = _detectionZone.Y + zone.Y;
// 如果有温度文本需要绘制
if (areaTemperatureTexts.Count > 0)
{
// 创建表示测温区的矩形区域
RectangleF zoneArea = new RectangleF(absoluteX, absoluteY, zone.Width, zone.Height);
// 将List<string>转换为string[]以便传递给DrawTextInAreaCentered方法
string[] textsArray = areaTemperatureTexts.ToArray();
// 调用DrawTextInAreaCentered方法绘制温度文本文本会在区域内垂直居中
DrawTextInAreaCentered(g, textsArray, zone.Color, zoneArea);
}
}
}
}
}
// 设置显示状态标志
_isDisplayingInfo = true;
}
// 标记信息正在显示
_isDisplayingInfo = true;
}
}
}
private void SaveTemperatureToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
// 检查设备管理器是否初始化且设备是否已连接
if (_deviceManager == null || _deviceManager.ConnectionStatus != ConnectionStatus.Connected)
{
MessageBox.Show("设备未连接,请先连接设备后再保存温度数据。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
// 使用SaveFileDialog让用户选择保存路径
SaveFileDialog saveFileDialog = new SaveFileDialog
{
Title = "保存温度数据",
Filter = "CSV文件 (*.csv)|*.csv|文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*",
DefaultExt = "csv",
FileName = $"温度数据_{DateTime.Now:yyyyMMdd_HHmmss}"
};
// 设置保存对话框的初始目录为ProjectPath属性的值
String SavePath = String.IsNullOrWhiteSpace(ProjectPath) ? Application.StartupPath : ProjectPath;
if (!string.IsNullOrEmpty(SavePath))
{
// 确保目录存在
if (Directory.Exists(SavePath))
{
saveFileDialog.InitialDirectory = SavePath;
}
else
{
// 尝试创建目录
try
{
Directory.CreateDirectory(SavePath);
saveFileDialog.InitialDirectory = SavePath;
}
catch (Exception ex)
{
Console.WriteLine($"创建保存路径目录失败: {ex.Message}");
// 如果创建目录失败,则不设置初始目录,使用默认行为
}
}
}
// 如果用户选择了文件路径
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
// 获取已有的温度数据从DeviceManager缓存中获取
TemperatureData temperatureData = _deviceManager.LastTemperature;
if (temperatureData == null)
{
MessageBox.Show("获取温度数据失败,请确保设备已连接且正在接收数据。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 使用DeviceManager的新重载方法直接传入已获取的温度数据
bool saveResult = _deviceManager.SaveTemperatureDataToCsv(saveFileDialog.FileName, temperatureData);
if (saveResult)
{
// 显示保存成功消息
MessageBox.Show($"温度数据已成功保存到:\n{saveFileDialog.FileName}", "保存成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
// 显示保存失败消息
MessageBox.Show("保存温度数据失败,请检查文件路径是否有效且有写入权限。", "保存失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
catch (Exception ex)
{
// 显示错误消息
MessageBox.Show($"保存温度数据时发生错误:\n{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
Console.WriteLine($"保存温度数据异常: {ex.Message}\n{ex.StackTrace}");
}
}
/// <summary>
/// 验证菜单功能是否正常工作 - 用于测试
/// 可以在调试时手动调用此方法来测试菜单配置的保存和加载
/// </summary>
public void ValidateMenuFunctionality()
{
try
{
WriteLog("开始验证菜单功能");
// 1. 保存当前菜单配置
SaveMenuConfig();
WriteLog("菜单配置已保存");
// 2. 模拟菜单状态更改并再次保存
if (globalTemperatureToolStripMenuItem != null)
{
globalTemperatureToolStripMenuItem.Checked = !globalTemperatureToolStripMenuItem.Checked;
WriteLog($"临时更改全局温度菜单状态为: {globalTemperatureToolStripMenuItem.Checked}");
}
// 3. 重新加载配置
LoadMenuConfig();
WriteLog("菜单配置已重新加载");
// 4. 输出验证信息
WriteLog("菜单功能验证完成");
Console.WriteLine("菜单功能验证完成: 保存和加载配置正常工作");
}
catch (Exception ex)
{
WriteLog($"验证菜单功能时出错: {ex.Message}");
Console.WriteLine($"验证菜单功能失败: {ex.Message}");
}
}
/// <summary>
/// imageBox双击事件处理方法
/// 双击后弹出预览窗口
/// </summary>
private void ImageBox_DoubleClick(object sender, EventArgs e)
{
try
{
// 关闭已存在的预览窗口
if (_previewForm != null && !_previewForm.IsDisposed)
{
_previewForm.Close();
}
// 创建新的预览窗口实例
_previewForm = new JoyD.Windows.CS.preview();
// 设置初始图像
_previewForm.UpdateImage(_lastImage);
// 模态显示预览窗口
_previewForm.ShowDialog();
// 预览窗口关闭后清空引用
_previewForm = null;
}
catch (Exception ex)
{
Console.WriteLine($"打开预览窗口时出错: {ex.Message}");
MessageBox.Show($"打开预览窗口时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// "修改配置"菜单项点击事件处理方法
/// 点击后弹出检测配置窗口
/// </summary>
private void ModifyConfigToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
// 显示配置窗口使用完整命名空间引用Setting类
JoyD.Windows.CS.Setting.Form.AutoConfig = this.AutoConfig;
JoyD.Windows.CS.Setting.Form.ProjectPath = this.ProjectPath;
JoyD.Windows.CS.Setting.Form.CurrentDetectionZone = this.CurrentDetectionZone;
JoyD.Windows.CS.Setting.Form.ShowDialog();
// 获取修改后的检测区配置
this.CurrentDetectionZone = JoyD.Windows.CS.Setting.Form.CurrentDetectionZone;
// 保存修改后的检测区配置到文件
SaveMenuConfig();
// 重新加载测温区和温差数据,应用新的配置
LoadZoneConfig();
LoadTemperatureDiffConfig();
// 触发DeviceManager重新加载配置确保DeviceManager使用最新的测温区配置
if (_deviceManager != null)
{
_deviceManager.ProjectPath = this.ProjectPath;
}
}
catch (Exception ex)
{
Console.WriteLine($"打开配置窗口时出错: {ex.Message}");
MessageBox.Show($"打开配置窗口时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}