修改Camera.cs和Camera.Designer.cs,移除不必要的菜单配置保存和ImageBox双击事件处理
This commit is contained in:
@@ -58,7 +58,6 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
this.imageBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
this.imageBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||||
this.imageBox.TabIndex = 0;
|
this.imageBox.TabIndex = 0;
|
||||||
this.imageBox.TabStop = false;
|
this.imageBox.TabStop = false;
|
||||||
this.imageBox.DoubleClick += new System.EventHandler(this.ImageBox_DoubleClick);
|
|
||||||
//
|
//
|
||||||
// contextMenuStrip1
|
// contextMenuStrip1
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
@@ -266,6 +266,9 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
|
|
||||||
// 修改流程第1点和第5点:暂停或恢复时,设置暂停状态,调用更新Info(在暂停状态下会显示暂停信息)
|
// 修改流程第1点和第5点:暂停或恢复时,设置暂停状态,调用更新Info(在暂停状态下会显示暂停信息)
|
||||||
UpdateInfo();
|
UpdateInfo();
|
||||||
|
|
||||||
|
// 保存菜单状态
|
||||||
|
SaveMenuConfig();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -290,6 +293,9 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
// 初始化图像缓冲区
|
// 初始化图像缓冲区
|
||||||
InitializeImageBuffer();
|
InitializeImageBuffer();
|
||||||
|
|
||||||
|
// 加载保存的菜单配置
|
||||||
|
LoadMenuConfig();
|
||||||
|
|
||||||
// 初始化Ping定时器
|
// 初始化Ping定时器
|
||||||
_pingTimer = new System.Threading.Timer(PingTimer_Tick, null, Timeout.Infinite, Timeout.Infinite);
|
_pingTimer = new System.Threading.Timer(PingTimer_Tick, null, Timeout.Infinite, Timeout.Infinite);
|
||||||
|
|
||||||
@@ -1578,6 +1584,11 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
Console.WriteLine("切换到红外模式失败: " + ex.Message);
|
Console.WriteLine("切换到红外模式失败: " + ex.Message);
|
||||||
ShowError("切换到红外模式失败");
|
ShowError("切换到红外模式失败");
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// 菜单状态需要保存和恢复
|
||||||
|
SaveMenuConfig();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void VisibleModeToolStripMenuItem_Click(object sender, EventArgs e)
|
private void VisibleModeToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
@@ -1591,6 +1602,11 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
Console.WriteLine("切换到自然模式失败: " + ex.Message);
|
Console.WriteLine("切换到自然模式失败: " + ex.Message);
|
||||||
ShowError("切换到自然模式失败");
|
ShowError("切换到自然模式失败");
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// 菜单状态需要保存和恢复
|
||||||
|
SaveMenuConfig();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -1856,6 +1872,9 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
{
|
{
|
||||||
UpdateRealTimeInfoOnUI();
|
UpdateRealTimeInfoOnUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 菜单状态变更时自动静默保存配置
|
||||||
|
SaveMenuConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1879,6 +1898,9 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
{
|
{
|
||||||
UpdateRealTimeInfoOnUI();
|
UpdateRealTimeInfoOnUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 菜单状态变更时自动静默保存配置
|
||||||
|
SaveMenuConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1894,6 +1916,9 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
{
|
{
|
||||||
UpdateRealTimeInfoOnUI();
|
UpdateRealTimeInfoOnUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 菜单状态变更时自动静默保存配置
|
||||||
|
SaveMenuConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1909,6 +1934,9 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
{
|
{
|
||||||
UpdateRealTimeInfoOnUI();
|
UpdateRealTimeInfoOnUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 菜单状态变更时自动静默保存配置
|
||||||
|
SaveMenuConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1924,6 +1952,9 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
{
|
{
|
||||||
UpdateRealTimeInfoOnUI();
|
UpdateRealTimeInfoOnUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 菜单状态变更时自动静默保存配置
|
||||||
|
SaveMenuConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1945,6 +1976,466 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
// 日志禁用时的清理(如果需要)
|
// 日志禁用时的清理(如果需要)
|
||||||
WriteLog("日志保存功能已禁用");
|
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}");
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
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 (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 (thermalModeToolStripMenuItem != null)
|
||||||
|
ApplyMenuState(menuStates, "thermalMode", thermalModeToolStripMenuItem);
|
||||||
|
if (visibleModeToolStripMenuItem != null)
|
||||||
|
ApplyMenuState(menuStates, "visibleMode", visibleModeToolStripMenuItem);
|
||||||
|
|
||||||
|
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>
|
/// <summary>
|
||||||
@@ -2217,6 +2708,44 @@ namespace JoyD.Windows.CS.Toprie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>
|
/// <summary>
|
||||||
/// imageBox双击事件处理方法
|
/// imageBox双击事件处理方法
|
||||||
/// 双击后弹出检测配置窗口
|
/// 双击后弹出检测配置窗口
|
||||||
|
|||||||
@@ -1,220 +0,0 @@
|
|||||||
# JoyD
|
|
||||||
|
|
||||||
## 图像处理相关
|
|
||||||
|
|
||||||
### InfoImage, ImageBuffer, 图像框的bitmap, LastImage, DisplayImage
|
|
||||||
1. 初始化时,都创建成512x384的透明bitmap
|
|
||||||
2. 中途不进行Dispose和设置为null,只在上面进行绘制
|
|
||||||
3. 仅当控件被Dispose时,才进行Dispose和设置为null
|
|
||||||
|
|
||||||
## 操作流程
|
|
||||||
|
|
||||||
### 修改流程
|
|
||||||
1. 暂停或恢复时,设置暂停状态,调用更新Info
|
|
||||||
2. 断开或连接时,设置连接状态,调用更新Info
|
|
||||||
3. Ping通状态变化时,修改Ping状态,调用更新Info
|
|
||||||
4. 图像更新时,保存LastImage,调用更新UI
|
|
||||||
5. 2-4 只在非暂停状态下调用更新,暂停状态下不更新Info和UI
|
|
||||||
6. 数据显示菜单勾选变化时,只在非暂停状态下调用更新实时信息
|
|
||||||
|
|
||||||
### 更新Info
|
|
||||||
1. 以透明色清空Info
|
|
||||||
2. 如果暂停,显示暂停信息,否则如果Ping不通或断开,显示重连信息,否则就绪
|
|
||||||
3. 如果未就绪,调用更新UI
|
|
||||||
|
|
||||||
### 更新UI
|
|
||||||
1. 先将LastImage绘制到全局缓冲
|
|
||||||
2. 再将InfoImage绘制到缓冲
|
|
||||||
3. 在就绪条件下,调用更新实时信息,并将DisplayImage绘制到缓冲
|
|
||||||
4. 最后一次性绘制到图像框的bitmap
|
|
||||||
5. 同步更新检测配置窗口的实时图像属性
|
|
||||||
|
|
||||||
### 更新实时信息
|
|
||||||
1. 以透明色清空DisplayImage
|
|
||||||
2. 如果没有温度数据或温度数据的时间3秒之前,返回
|
|
||||||
3. 温度显示菜单下如果未勾选区域温度和全局温度,则不显示任何温度信息
|
|
||||||
4. 如果勾选了全局温度,则显示全局温度(居中显示),否则显示区域温度(居中显示)
|
|
||||||
5. 如果勾选了区域温度,则显示区域框,否则不显示区域框
|
|
||||||
6. 如果勾选了平均温度,显示平均温度(居中显示)
|
|
||||||
7. 如果勾选了最低温度,显示最低温度(居中显示)
|
|
||||||
8. 如果勾选了最高温度,显示最高温度(居中显示)
|
|
||||||
9. 如果是全局温度时,最低温度和最高温度、平均温度,显示三行(左对齐),整体水平和垂直相对于图像框居中显示
|
|
||||||
10. 如果是区域温度时,最低温度和最高温度、平均温度,显示三行(左对齐),整体水平和垂直相对于区域框居中显示
|
|
||||||
|
|
||||||
## TCP温度数据接收
|
|
||||||
|
|
||||||
1. 系统初始化时,创建后台线程
|
|
||||||
2. 然后线程循环执行
|
|
||||||
3. 判断是否存在tcp连接实例
|
|
||||||
4. 如果不存在连接实例,则判断是否暂停。如果暂停,则Sleep 1秒后继续执行。否则创建一个新的连接实例。
|
|
||||||
5. 如果存在连接实例,则判断是否连接。如果未连接,则重新连接
|
|
||||||
6. 同步接收和处理数据
|
|
||||||
7. 收到数据后,如果暂停,接收后丢弃,Sleep 1秒。否则同步将收到的数组复制放到待处理列表(线程安全列表)。
|
|
||||||
8. 通知处理线程,有新数据到达
|
|
||||||
9. 跳转到6
|
|
||||||
10. Dispose时,关闭后台线程
|
|
||||||
|
|
||||||
### 温度数据接收时的补充说明
|
|
||||||
|
|
||||||
总之就是不主动断开连接:
|
|
||||||
|
|
||||||
1. 同步机制简单,不用考虑阻塞,因为在后台线程里
|
|
||||||
2. 不用心跳机制,协议约定是每秒1帧数据
|
|
||||||
3. 异常情况处理,出现异常就跳转到第3步
|
|
||||||
4. 不用考虑波动问题,这是在一个循环中,它会下次重试
|
|
||||||
5. 在异常出现且需要重新创建对象前,先释放旧的资源
|
|
||||||
6. 数据完整性验证放在了数据处理环节
|
|
||||||
7. 连接失败,会跳转到第3步。下一循环会自动重试
|
|
||||||
|
|
||||||
### 温度数据处理逻辑
|
|
||||||
|
|
||||||
1. 收到的数组放到待处理列表中
|
|
||||||
2. 开始循环处理列表
|
|
||||||
3. 如果列表为空,退出循环
|
|
||||||
4. 从列表中读取第一个数组,查看是否有头信息
|
|
||||||
5. 如果没有头信息,将数组从列表中移除,跳转到3
|
|
||||||
6. 如果有头信息,查看头信息中数据长度信息
|
|
||||||
7. 检查数组中是否有足够的数据长度
|
|
||||||
8. 如果没有足够的数据长度,看列表中是否有下一个数组
|
|
||||||
9. 如果没有,则退出循环,否则将下一个数组合并到当前数组中尾部,将下一个数组从列表中移除,然后跳转到7
|
|
||||||
10. 如果有足够的数据长度,则检查数据长度范围内是否又出现了头信息
|
|
||||||
11. 如果有,则将数组中第二个头信息之前的数据移除,然后跳转到6
|
|
||||||
12. 如果没有,则将数据长度范围内的数据从数组取出进行解析处理,剩余部分保留到数组中,跳转到4
|
|
||||||
13. 处理完毕后,跳转到3
|
|
||||||
14. 循环完成,进入等待状态
|
|
||||||
|
|
||||||
### 温度数据解析处理逻辑
|
|
||||||
|
|
||||||
根据热像仪SDK文档,原始温度数据的解析处理需遵循以下逻辑:
|
|
||||||
|
|
||||||
#### 原始温度数据格式说明
|
|
||||||
- 通过TCP端口8081获取温度数据
|
|
||||||
- 数据包含24字节头部和温度数据两部分
|
|
||||||
- 头部结构体(header)包含:
|
|
||||||
- mark[5]: 固定标识"+TEMP"
|
|
||||||
- payload_length: 温度数据长度
|
|
||||||
- timestamp: 时间戳(前4字节为秒级时间,后2字节为毫秒级时间)
|
|
||||||
- 温度数据长度计算:宽×高×2字节,例如240×180分辨率时为240×180×2字节
|
|
||||||
- 数据按字节低位在前读取,如读取为80 51 01 00时实际值为00 01 51 80的16进制值
|
|
||||||
|
|
||||||
#### 原始温度数据解析方法
|
|
||||||
1. **单像素温度计算**:
|
|
||||||
- 每个像素点温度由两个字节组成(L为低8位,H为高8位)
|
|
||||||
- 公式:摄氏温度×10 = H×256 + L
|
|
||||||
- 实际温度:摄氏温度 = (H×256 + L)/10
|
|
||||||
|
|
||||||
2. **温度数据布局**:
|
|
||||||
- 数据按行优先顺序排列
|
|
||||||
- 例如分辨率为4×3时,数据顺序为:L1 H1 L2 H2 L3 H3 L4 H4 L5 H5 ...
|
|
||||||
|
|
||||||
#### 温补值处理
|
|
||||||
1. **温补值获取**:
|
|
||||||
- 通过SDK函数`sdk_get_comp_temp`获取温补值
|
|
||||||
- 函数原型:`sdk_get_comp_temp(const char* ip, int* comp_temp)`
|
|
||||||
- 返回值为0表示获取成功
|
|
||||||
|
|
||||||
2. **温补值应用**:
|
|
||||||
- 温补值也是摄氏度乘以10的结果
|
|
||||||
- 最终温度计算公式:
|
|
||||||
```
|
|
||||||
最终温度 = 原始温度 + 温补值
|
|
||||||
```
|
|
||||||
- 示例:原始温度为243(24.3°C),温补值为5(0.5°C),则最终温度为248(24.8°C)
|
|
||||||
|
|
||||||
#### 温度数据解析处理流程
|
|
||||||
1. 接收TCP数据并解析头部信息
|
|
||||||
2. 验证mark标识是否为"+TEMP"
|
|
||||||
3. 根据payload_length确定温度数据长度
|
|
||||||
4. 按像素点顺序解析每个像素的温度值
|
|
||||||
5. 获取温补值并应用到每个像素的温度上
|
|
||||||
6. 计算得到最终的温度数据
|
|
||||||
|
|
||||||
#### 注意事项
|
|
||||||
1. 时间戳转换时需注意低字节在前、高字节在后的顺序
|
|
||||||
2. 温补值是设备补偿值,必须应用到原始温度上才能得到与网页计算结果一致的温度
|
|
||||||
3. 分辨率不同时,温度数据长度会相应变化,解析时需根据实际分辨率计算
|
|
||||||
4. 原始温度数据更新频率约为每秒一条
|
|
||||||
|
|
||||||
### 温度显示菜单
|
|
||||||
1. 在右键菜单中增加温度显示菜单
|
|
||||||
2. 温度显示菜单下显示全局温度(可勾选)
|
|
||||||
3. 温度显示菜单下显示区域温度(可勾选)
|
|
||||||
4. 区域温度和全局温度只能二选一显示
|
|
||||||
5. 温度显示菜单下显示最高温度、平均温度和最低温度(可勾选)
|
|
||||||
|
|
||||||
### 检测配置
|
|
||||||
#### 区域绘制逻辑
|
|
||||||
1. 创建独立的叠加层图像 :专门维护一个与显示图像同尺寸的Image对象作为矩形框的叠加层
|
|
||||||
2. 分离绘制逻辑 :将临时绘制和最终绘制分离,临时矩形仍通过Paint事件显示,完成的矩形绘制到叠加层
|
|
||||||
3. 图像合并机制 :在Paint事件中先绘制叠加层,再绘制临时矩形。
|
|
||||||
4. 保存的矩形框位置和大小信息应该是相对于图像的,而不是相对于控件的。
|
|
||||||
5. 当btnDrawRegion按下后,处于绘制状态, btnSelectColor才显示出来
|
|
||||||
6. 当绘制状态时,右击鼠标,退出绘制状态,且清除所有临时绘制。
|
|
||||||
7. 当绘制状态时,双击鼠标,随机生成一个颜色,用于绘制矩形框。
|
|
||||||
8. 当就绪状态时,鼠标移到区域内,该区域内填充半透明色,当有多个重叠时,填充索引号最大的区域
|
|
||||||
9. 当鼠标在半透明区域内单击时,该区域填充半透明色,且显示八个句柄,表示选中该区域。
|
|
||||||
10. 选中区域时,工具栏显示按钮btnDeleteRegion和btnSelectColor;隐藏btnDrawRegion。
|
|
||||||
11. 当选中区域时,btnSelectColor用于改变选中区域的颜色。
|
|
||||||
12. 当选中区域时,btnDeleteRegion用于删除该区域,删除后btnSelectColor隐藏,btnDrawRegion显示。
|
|
||||||
13. 当选中区域时,鼠标移动到八个句柄上,显示对应的光标,用于调整区域大小。
|
|
||||||
14. 当选中区域时,鼠标可以移动区域,用于调整区域位置。
|
|
||||||
15. 当选中区域时,右击鼠标,退出选中状态,转为就绪状态
|
|
||||||
16. 当就绪状态时,工具栏显示绘制温差图按钮(btnDrawTempDiff)
|
|
||||||
17. 当绘制温差图按钮按下时,进入绘制温差图状态,显示添加和删除温差图例按钮(btnAddTempDiff、btnDeleteTempDiff),显示温差图例列表(dataGridViewTempDiff)
|
|
||||||
18. 当绘制温差图状态时,右击鼠标进入就绪状态
|
|
||||||
19. 初始状态/就绪状态,显示dataGridViewTempDiff,但只允许查看
|
|
||||||
|
|
||||||
### btnNewTempRegion(新建测温区)
|
|
||||||
1. 移除所有已有的测温区列表
|
|
||||||
2. 用透明色清空叠加层图像
|
|
||||||
### btnLoadTempRegion(加载测温区)
|
|
||||||
1. 弹出用户打开文件对话框,用户选择要加载的csv文件
|
|
||||||
2. 移除所有已有的测温区列表,用透明色清空叠加层图像
|
|
||||||
3. 从csv文件中读取所有测温区位置大小和颜色信息,添加到测温区列表中
|
|
||||||
4. 用读取的颜色填充叠加层图像对应的区域
|
|
||||||
### btnSaveTempRegion(保存测温区)
|
|
||||||
1. 弹出用户保存文件对话框,用户选择保存路径和文件名
|
|
||||||
2. 保存测温区列表中的所有测温区位置大小和颜色信息为csv文件
|
|
||||||
### btnNewTempDiff(新建温差图)
|
|
||||||
1. 用透明色清空温差层图像
|
|
||||||
2. 移除所有已有的温差图例列表
|
|
||||||
### btnLoadTempDiff(加载温差图)
|
|
||||||
1. 弹出用户打开文件对话框,用户选择要加载的csv文件
|
|
||||||
2. 用透明色清空温差层图像
|
|
||||||
3. 移除所有已有的温差图例列表
|
|
||||||
4. 从csv文件中读取所有温差图例信息,添加到温差图例列表中
|
|
||||||
5. 从csv文件中读取所有像素的温度值,绘制温差层图像对应的像素
|
|
||||||
### btnSaveTempDiff(保存温差图)
|
|
||||||
1. 弹出用户保存文件对话框,用户选择保存路径和文件名
|
|
||||||
2. 保存温差图例列表中的所有温差图例信息到csv文件
|
|
||||||
3. 每个温差图例信息占一行,格式为:
|
|
||||||
- 温度(如20°C)
|
|
||||||
- 颜色(如#FF0000表示红色)
|
|
||||||
4. 保存温差图每个像素的温度值到csv文件
|
|
||||||
- 每个像素温度值占一行,格式为:
|
|
||||||
- X坐标(如100)
|
|
||||||
- Y坐标(如300)
|
|
||||||
- 温度值(°C)(如25.5)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 配置状态说明
|
|
||||||
1. 初始状态/就绪状态:
|
|
||||||
- 显示btnDrawRegion和btnDrawTempDiff按钮、dataGridViewTempDiff
|
|
||||||
- 显示btnNewTempRegion按钮、btnLoadTempRegion按钮、btnSaveTempRegion按钮、btnNewTempDiff按钮、btnLoadTempDiff按钮、btnSaveTempDiff按钮
|
|
||||||
- 隐藏btnSelectColor、btnDeleteRegion按钮、btnAddTempDiff、btnDeleteTempDiff、btnBrushSize1、btnBrushSize3、btnBrushSize5、btnBrushSize10、btnBrushSize15、btnBrushSize25、txtRegionNumber
|
|
||||||
2. 选中区域状态:
|
|
||||||
- 显示btnDeleteRegion和btnSelectColor按钮、txtRegionNumber
|
|
||||||
- 隐藏btnDrawRegion和btnDrawTempDiff按钮、dataGridViewTempDiff、btnAddTempDiff、btnDeleteTempDiff、btnBrushSize1、btnBrushSize3、btnBrushSize5、btnBrushSize10、btnBrushSize15、btnBrushSize25
|
|
||||||
- 隐藏btnNewTempRegion按钮、btnLoadTempRegion按钮、btnSaveTempRegion按钮、btnNewTempDiff按钮、btnLoadTempDiff按钮、btnSaveTempDiff按钮
|
|
||||||
3. 绘制状态:
|
|
||||||
- 显示btnSelectColor按钮
|
|
||||||
- 显示btnDrawRegion按钮、txtRegionNumber
|
|
||||||
- 隐藏btnDeleteRegion按钮
|
|
||||||
- 隐藏btnDrawTempDiff按钮、dataGridViewTempDiff、btnAddTempDiff、btnDeleteTempDiff、btnBrushSize1、btnBrushSize3、btnBrushSize5、btnBrushSize10、btnBrushSize15、btnBrushSize25
|
|
||||||
- 隐藏btnNewTempRegion按钮、btnLoadTempRegion按钮、btnSaveTempRegion按钮、btnNewTempDiff按钮、btnLoadTempDiff按钮、btnSaveTempDiff按钮
|
|
||||||
4. 绘制温差图状态:
|
|
||||||
- 显示btnDrawTempDiff按钮,显示dataGridViewTempDiff、btnAddTempDiff、btnDeleteTempDiff
|
|
||||||
- 当选定温差图例后,显示btnBrushSize1、btnBrushSize3、btnBrushSize5、btnBrushSize10、btnBrushSize15、btnBrushSize25
|
|
||||||
- 隐藏btnSelectColor、btnDeleteRegion、btnDrawRegion按钮
|
|
||||||
- 隐藏btnNewTempRegion按钮、btnLoadTempRegion按钮、btnSaveTempRegion按钮、btnNewTempDiff按钮、btnLoadTempDiff按钮、btnSaveTempDiff按钮
|
|
||||||
@@ -146,7 +146,6 @@ namespace JoyD.Windows.CS
|
|||||||
// toolStrip
|
// toolStrip
|
||||||
//
|
//
|
||||||
this.toolStrip.AllowDrop = true;
|
this.toolStrip.AllowDrop = true;
|
||||||
this.toolStrip.CanOverflow = true;
|
|
||||||
this.toolStrip.Dock = System.Windows.Forms.DockStyle.None;
|
this.toolStrip.Dock = System.Windows.Forms.DockStyle.None;
|
||||||
this.toolStrip.ImageScalingSize = new System.Drawing.Size(20, 20);
|
this.toolStrip.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||||
this.toolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.toolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
@@ -390,6 +389,7 @@ namespace JoyD.Windows.CS
|
|||||||
this.ClientSize = new System.Drawing.Size(793, 450);
|
this.ClientSize = new System.Drawing.Size(793, 450);
|
||||||
this.Controls.Add(this.splitContainer);
|
this.Controls.Add(this.splitContainer);
|
||||||
this.Name = "Setting";
|
this.Name = "Setting";
|
||||||
|
this.ShowInTaskbar = false;
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||||
this.Text = "检测配置";
|
this.Text = "检测配置";
|
||||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Setting_FormClosing);
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Setting_FormClosing);
|
||||||
|
|||||||
@@ -120,20 +120,4 @@
|
|||||||
<metadata name="toolStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="toolStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
<value>516, 17</value>
|
<value>516, 17</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
|
||||||
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>
|
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
|
||||||
YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
|
||||||
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
|
||||||
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
|
||||||
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
|
||||||
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
|
||||||
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
|
||||||
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
|
||||||
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
|
||||||
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
|
||||||
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
|
||||||
</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
</root>
|
||||||
@@ -1,54 +1,220 @@
|
|||||||
# 托普瑞热像仪应用开发任务列表
|
# JoyD
|
||||||
|
|
||||||
## 1. SDK集成准备
|
## 图像处理相关
|
||||||
|
|
||||||
- [x] 1.1 复制托普瑞SDK文件到项目目录
|
### InfoImage, ImageBuffer, 图像框的bitmap, LastImage, DisplayImage
|
||||||
- [x] 1.2 在Visual Studio项目中添加SDK引用
|
1. 初始化时,都创建成512x384的透明bitmap
|
||||||
- [x] 1.3 阅读SDK文档,了解API接口
|
2. 中途不进行Dispose和设置为null,只在上面进行绘制
|
||||||
|
3. 仅当控件被Dispose时,才进行Dispose和设置为null
|
||||||
|
|
||||||
## 2. 设备连接功能
|
## 操作流程
|
||||||
|
|
||||||
- [x] 2.1 实现设备连接模块
|
### 修改流程
|
||||||
- [x] 2.2 完善连接状态管理
|
1. 暂停或恢复时,设置暂停状态,调用更新Info
|
||||||
|
2. 断开或连接时,设置连接状态,调用更新Info
|
||||||
|
3. Ping通状态变化时,修改Ping状态,调用更新Info
|
||||||
|
4. 图像更新时,保存LastImage,调用更新UI
|
||||||
|
5. 2-4 只在非暂停状态下调用更新,暂停状态下不更新Info和UI
|
||||||
|
6. 数据显示菜单勾选变化时,只在非暂停状态下调用更新实时信息
|
||||||
|
|
||||||
## 3. 温度数据处理
|
### 更新Info
|
||||||
|
1. 以透明色清空Info
|
||||||
|
2. 如果暂停,显示暂停信息,否则如果Ping不通或断开,显示重连信息,否则就绪
|
||||||
|
3. 如果未就绪,调用更新UI
|
||||||
|
|
||||||
### 3.1 设备连接建立与管理
|
### 更新UI
|
||||||
- [ ] 3.1.1 实现设备连接建立流程
|
1. 先将LastImage绘制到全局缓冲
|
||||||
- 使用SDK的`sdk_initialize()`初始化套接字库
|
2. 再将InfoImage绘制到缓冲
|
||||||
- 利用`sdk_search_device()`搜索设备IP地址
|
3. 在就绪条件下,调用更新实时信息,并将DisplayImage绘制到缓冲
|
||||||
- 验证设备IP地址是否为默认地址(192.168.100.2)或自定义地址
|
4. 最后一次性绘制到图像框的bitmap
|
||||||
- 建立与热像仪的TCP连接,使用8081端口获取原始温度数据
|
5. 同步更新检测配置窗口的实时图像属性
|
||||||
- [ ] 3.1.2 连接状态维护机制
|
|
||||||
- 利用现有的`_heartbeatTimer`实现SDK心跳检测(`sdk_heartbeat`)
|
|
||||||
- 扩展`UpdateConnectionStatus()`方法,支持温度数据连接状态管理
|
|
||||||
- 实现基于`_connectionCheckTimer`的定时连接状态检测
|
|
||||||
- 利用现有的自动重连机制,当温度数据连接断开时自动重连
|
|
||||||
- [ ] 3.1.3 连接条件判断
|
|
||||||
- 调用`IsNetworkAvailable()`验证网络连接状态
|
|
||||||
- 检查设备型号兼容性,支持IRAY-A8系列设备
|
|
||||||
- 验证设备版本信息,确保SDK兼容性
|
|
||||||
- 在获取温度数据前检查设备是否处于正常工作状态
|
|
||||||
|
|
||||||
### 3.2 原始温度数据获取
|
### 更新实时信息
|
||||||
- [ ] 3.2.1 温度数据获取方法实现
|
1. 以透明色清空DisplayImage
|
||||||
- 在`DeviceManager.cs`中添加`GetRawTemperatureData()`方法
|
2. 如果没有温度数据或温度数据的时间3秒之前,返回
|
||||||
- 创建TCP客户端连接到设备的8081端口
|
3. 温度显示菜单下如果未勾选区域温度和全局温度,则不显示任何温度信息
|
||||||
- 实现数据接收线程,使用类似现有的`_imageReceiveThread`模式
|
4. 如果勾选了全局温度,则显示全局温度(居中显示),否则显示区域温度(居中显示)
|
||||||
- 实现24字节头部结构体解析,提取mark("+TEMP")、payload_length和timestamp
|
5. 如果勾选了区域温度,则显示区域框,否则不显示区域框
|
||||||
- [ ] 3.2.2 温度数据解析与处理
|
6. 如果勾选了平均温度,显示平均温度(居中显示)
|
||||||
- 根据设备分辨率计算温度数据总长度(宽*高*2字节)
|
7. 如果勾选了最低温度,显示最低温度(居中显示)
|
||||||
- 实现温度数据解码算法:摄氏温度 = (H*256 + L)/10,其中L是低8位,H是高8位
|
8. 如果勾选了最高温度,显示最高温度(居中显示)
|
||||||
- 调用`sdk_get_comp_temp()`获取设备温补值并进行叠加修正
|
9. 如果是全局温度时,最低温度和最高温度、平均温度,显示三行(左对齐),整体水平和垂直相对于图像框居中显示
|
||||||
- 创建温度数据模型类,存储解析后的温度矩阵
|
10. 如果是区域温度时,最低温度和最高温度、平均温度,显示三行(左对齐),整体水平和垂直相对于区域框居中显示
|
||||||
- [ ] 3.2.3 数据获取条件控制与异常处理
|
|
||||||
- 添加连接状态验证,确保`_connectionStatus`为`Connected`
|
## TCP温度数据接收
|
||||||
- 实现数据接收超时处理,基于`_lastDataReceivedTime`和`DataReceivedTimeout`
|
|
||||||
- 添加数据校验机制,确保接收到的温度数据格式正确
|
1. 系统初始化时,创建后台线程
|
||||||
- 实现断线自动重连,利用现有的`_reconnectTimer`机制
|
2. 然后线程循环执行
|
||||||
- 添加异常捕获和日志记录,使用现有的`Log()`方法
|
3. 判断是否存在tcp连接实例
|
||||||
- [ ] 3.2.4 温度数据接口设计
|
4. 如果不存在连接实例,则判断是否暂停。如果暂停,则Sleep 1秒后继续执行。否则创建一个新的连接实例。
|
||||||
- 添加温度数据接收事件`TemperatureDataReceived`
|
5. 如果存在连接实例,则判断是否连接。如果未连接,则重新连接
|
||||||
- 实现线程安全的数据访问方法
|
6. 同步接收和处理数据
|
||||||
- 添加温度数据缓存机制,保留最近几帧数据
|
7. 收到数据后,如果暂停,接收后丢弃,Sleep 1秒。否则同步将收到的数组复制放到待处理列表(线程安全列表)。
|
||||||
- 提供获取特定区域温度数据的辅助方法
|
8. 通知处理线程,有新数据到达
|
||||||
|
9. 跳转到6
|
||||||
|
10. Dispose时,关闭后台线程
|
||||||
|
|
||||||
|
### 温度数据接收时的补充说明
|
||||||
|
|
||||||
|
总之就是不主动断开连接:
|
||||||
|
|
||||||
|
1. 同步机制简单,不用考虑阻塞,因为在后台线程里
|
||||||
|
2. 不用心跳机制,协议约定是每秒1帧数据
|
||||||
|
3. 异常情况处理,出现异常就跳转到第3步
|
||||||
|
4. 不用考虑波动问题,这是在一个循环中,它会下次重试
|
||||||
|
5. 在异常出现且需要重新创建对象前,先释放旧的资源
|
||||||
|
6. 数据完整性验证放在了数据处理环节
|
||||||
|
7. 连接失败,会跳转到第3步。下一循环会自动重试
|
||||||
|
|
||||||
|
### 温度数据处理逻辑
|
||||||
|
|
||||||
|
1. 收到的数组放到待处理列表中
|
||||||
|
2. 开始循环处理列表
|
||||||
|
3. 如果列表为空,退出循环
|
||||||
|
4. 从列表中读取第一个数组,查看是否有头信息
|
||||||
|
5. 如果没有头信息,将数组从列表中移除,跳转到3
|
||||||
|
6. 如果有头信息,查看头信息中数据长度信息
|
||||||
|
7. 检查数组中是否有足够的数据长度
|
||||||
|
8. 如果没有足够的数据长度,看列表中是否有下一个数组
|
||||||
|
9. 如果没有,则退出循环,否则将下一个数组合并到当前数组中尾部,将下一个数组从列表中移除,然后跳转到7
|
||||||
|
10. 如果有足够的数据长度,则检查数据长度范围内是否又出现了头信息
|
||||||
|
11. 如果有,则将数组中第二个头信息之前的数据移除,然后跳转到6
|
||||||
|
12. 如果没有,则将数据长度范围内的数据从数组取出进行解析处理,剩余部分保留到数组中,跳转到4
|
||||||
|
13. 处理完毕后,跳转到3
|
||||||
|
14. 循环完成,进入等待状态
|
||||||
|
|
||||||
|
### 温度数据解析处理逻辑
|
||||||
|
|
||||||
|
根据热像仪SDK文档,原始温度数据的解析处理需遵循以下逻辑:
|
||||||
|
|
||||||
|
#### 原始温度数据格式说明
|
||||||
|
- 通过TCP端口8081获取温度数据
|
||||||
|
- 数据包含24字节头部和温度数据两部分
|
||||||
|
- 头部结构体(header)包含:
|
||||||
|
- mark[5]: 固定标识"+TEMP"
|
||||||
|
- payload_length: 温度数据长度
|
||||||
|
- timestamp: 时间戳(前4字节为秒级时间,后2字节为毫秒级时间)
|
||||||
|
- 温度数据长度计算:宽×高×2字节,例如240×180分辨率时为240×180×2字节
|
||||||
|
- 数据按字节低位在前读取,如读取为80 51 01 00时实际值为00 01 51 80的16进制值
|
||||||
|
|
||||||
|
#### 原始温度数据解析方法
|
||||||
|
1. **单像素温度计算**:
|
||||||
|
- 每个像素点温度由两个字节组成(L为低8位,H为高8位)
|
||||||
|
- 公式:摄氏温度×10 = H×256 + L
|
||||||
|
- 实际温度:摄氏温度 = (H×256 + L)/10
|
||||||
|
|
||||||
|
2. **温度数据布局**:
|
||||||
|
- 数据按行优先顺序排列
|
||||||
|
- 例如分辨率为4×3时,数据顺序为:L1 H1 L2 H2 L3 H3 L4 H4 L5 H5 ...
|
||||||
|
|
||||||
|
#### 温补值处理
|
||||||
|
1. **温补值获取**:
|
||||||
|
- 通过SDK函数`sdk_get_comp_temp`获取温补值
|
||||||
|
- 函数原型:`sdk_get_comp_temp(const char* ip, int* comp_temp)`
|
||||||
|
- 返回值为0表示获取成功
|
||||||
|
|
||||||
|
2. **温补值应用**:
|
||||||
|
- 温补值也是摄氏度乘以10的结果
|
||||||
|
- 最终温度计算公式:
|
||||||
|
```
|
||||||
|
最终温度 = 原始温度 + 温补值
|
||||||
|
```
|
||||||
|
- 示例:原始温度为243(24.3°C),温补值为5(0.5°C),则最终温度为248(24.8°C)
|
||||||
|
|
||||||
|
#### 温度数据解析处理流程
|
||||||
|
1. 接收TCP数据并解析头部信息
|
||||||
|
2. 验证mark标识是否为"+TEMP"
|
||||||
|
3. 根据payload_length确定温度数据长度
|
||||||
|
4. 按像素点顺序解析每个像素的温度值
|
||||||
|
5. 获取温补值并应用到每个像素的温度上
|
||||||
|
6. 计算得到最终的温度数据
|
||||||
|
|
||||||
|
#### 注意事项
|
||||||
|
1. 时间戳转换时需注意低字节在前、高字节在后的顺序
|
||||||
|
2. 温补值是设备补偿值,必须应用到原始温度上才能得到与网页计算结果一致的温度
|
||||||
|
3. 分辨率不同时,温度数据长度会相应变化,解析时需根据实际分辨率计算
|
||||||
|
4. 原始温度数据更新频率约为每秒一条
|
||||||
|
|
||||||
|
### 温度显示菜单
|
||||||
|
1. 在右键菜单中增加温度显示菜单
|
||||||
|
2. 温度显示菜单下显示全局温度(可勾选)
|
||||||
|
3. 温度显示菜单下显示区域温度(可勾选)
|
||||||
|
4. 区域温度和全局温度只能二选一显示
|
||||||
|
5. 温度显示菜单下显示最高温度、平均温度和最低温度(可勾选)
|
||||||
|
|
||||||
|
### 检测配置
|
||||||
|
#### 区域绘制逻辑
|
||||||
|
1. 创建独立的叠加层图像 :专门维护一个与显示图像同尺寸的Image对象作为矩形框的叠加层
|
||||||
|
2. 分离绘制逻辑 :将临时绘制和最终绘制分离,临时矩形仍通过Paint事件显示,完成的矩形绘制到叠加层
|
||||||
|
3. 图像合并机制 :在Paint事件中先绘制叠加层,再绘制临时矩形。
|
||||||
|
4. 保存的矩形框位置和大小信息应该是相对于图像的,而不是相对于控件的。
|
||||||
|
5. 当btnDrawRegion按下后,处于绘制状态, btnSelectColor才显示出来
|
||||||
|
6. 当绘制状态时,右击鼠标,退出绘制状态,且清除所有临时绘制。
|
||||||
|
7. 当绘制状态时,双击鼠标,随机生成一个颜色,用于绘制矩形框。
|
||||||
|
8. 当就绪状态时,鼠标移到区域内,该区域内填充半透明色,当有多个重叠时,填充索引号最大的区域
|
||||||
|
9. 当鼠标在半透明区域内单击时,该区域填充半透明色,且显示八个句柄,表示选中该区域。
|
||||||
|
10. 选中区域时,工具栏显示按钮btnDeleteRegion和btnSelectColor;隐藏btnDrawRegion。
|
||||||
|
11. 当选中区域时,btnSelectColor用于改变选中区域的颜色。
|
||||||
|
12. 当选中区域时,btnDeleteRegion用于删除该区域,删除后btnSelectColor隐藏,btnDrawRegion显示。
|
||||||
|
13. 当选中区域时,鼠标移动到八个句柄上,显示对应的光标,用于调整区域大小。
|
||||||
|
14. 当选中区域时,鼠标可以移动区域,用于调整区域位置。
|
||||||
|
15. 当选中区域时,右击鼠标,退出选中状态,转为就绪状态
|
||||||
|
16. 当就绪状态时,工具栏显示绘制温差图按钮(btnDrawTempDiff)
|
||||||
|
17. 当绘制温差图按钮按下时,进入绘制温差图状态,显示添加和删除温差图例按钮(btnAddTempDiff、btnDeleteTempDiff),显示温差图例列表(dataGridViewTempDiff)
|
||||||
|
18. 当绘制温差图状态时,右击鼠标进入就绪状态
|
||||||
|
19. 初始状态/就绪状态,显示dataGridViewTempDiff,但只允许查看
|
||||||
|
|
||||||
|
### btnNewTempRegion(新建测温区)
|
||||||
|
1. 移除所有已有的测温区列表
|
||||||
|
2. 用透明色清空叠加层图像
|
||||||
|
### btnLoadTempRegion(加载测温区)
|
||||||
|
1. 弹出用户打开文件对话框,用户选择要加载的csv文件
|
||||||
|
2. 移除所有已有的测温区列表,用透明色清空叠加层图像
|
||||||
|
3. 从csv文件中读取所有测温区位置大小和颜色信息,添加到测温区列表中
|
||||||
|
4. 用读取的颜色填充叠加层图像对应的区域
|
||||||
|
### btnSaveTempRegion(保存测温区)
|
||||||
|
1. 弹出用户保存文件对话框,用户选择保存路径和文件名
|
||||||
|
2. 保存测温区列表中的所有测温区位置大小和颜色信息为csv文件
|
||||||
|
### btnNewTempDiff(新建温差图)
|
||||||
|
1. 用透明色清空温差层图像
|
||||||
|
2. 移除所有已有的温差图例列表
|
||||||
|
### btnLoadTempDiff(加载温差图)
|
||||||
|
1. 弹出用户打开文件对话框,用户选择要加载的csv文件
|
||||||
|
2. 用透明色清空温差层图像
|
||||||
|
3. 移除所有已有的温差图例列表
|
||||||
|
4. 从csv文件中读取所有温差图例信息,添加到温差图例列表中
|
||||||
|
5. 从csv文件中读取所有像素的温度值,绘制温差层图像对应的像素
|
||||||
|
### btnSaveTempDiff(保存温差图)
|
||||||
|
1. 弹出用户保存文件对话框,用户选择保存路径和文件名
|
||||||
|
2. 保存温差图例列表中的所有温差图例信息到csv文件
|
||||||
|
3. 每个温差图例信息占一行,格式为:
|
||||||
|
- 温度(如20°C)
|
||||||
|
- 颜色(如#FF0000表示红色)
|
||||||
|
4. 保存温差图每个像素的温度值到csv文件
|
||||||
|
- 每个像素温度值占一行,格式为:
|
||||||
|
- X坐标(如100)
|
||||||
|
- Y坐标(如300)
|
||||||
|
- 温度值(°C)(如25.5)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 配置状态说明
|
||||||
|
1. 初始状态/就绪状态:
|
||||||
|
- 显示btnDrawRegion和btnDrawTempDiff按钮、dataGridViewTempDiff
|
||||||
|
- 显示btnNewTempRegion按钮、btnLoadTempRegion按钮、btnSaveTempRegion按钮、btnNewTempDiff按钮、btnLoadTempDiff按钮、btnSaveTempDiff按钮
|
||||||
|
- 隐藏btnSelectColor、btnDeleteRegion按钮、btnAddTempDiff、btnDeleteTempDiff、btnBrushSize1、btnBrushSize3、btnBrushSize5、btnBrushSize10、btnBrushSize15、btnBrushSize25、txtRegionNumber
|
||||||
|
2. 选中区域状态:
|
||||||
|
- 显示btnDeleteRegion和btnSelectColor按钮、txtRegionNumber
|
||||||
|
- 隐藏btnDrawRegion和btnDrawTempDiff按钮、dataGridViewTempDiff、btnAddTempDiff、btnDeleteTempDiff、btnBrushSize1、btnBrushSize3、btnBrushSize5、btnBrushSize10、btnBrushSize15、btnBrushSize25
|
||||||
|
- 隐藏btnNewTempRegion按钮、btnLoadTempRegion按钮、btnSaveTempRegion按钮、btnNewTempDiff按钮、btnLoadTempDiff按钮、btnSaveTempDiff按钮
|
||||||
|
3. 绘制状态:
|
||||||
|
- 显示btnSelectColor按钮
|
||||||
|
- 显示btnDrawRegion按钮、txtRegionNumber
|
||||||
|
- 隐藏btnDeleteRegion按钮
|
||||||
|
- 隐藏btnDrawTempDiff按钮、dataGridViewTempDiff、btnAddTempDiff、btnDeleteTempDiff、btnBrushSize1、btnBrushSize3、btnBrushSize5、btnBrushSize10、btnBrushSize15、btnBrushSize25
|
||||||
|
- 隐藏btnNewTempRegion按钮、btnLoadTempRegion按钮、btnSaveTempRegion按钮、btnNewTempDiff按钮、btnLoadTempDiff按钮、btnSaveTempDiff按钮
|
||||||
|
4. 绘制温差图状态:
|
||||||
|
- 显示btnDrawTempDiff按钮,显示dataGridViewTempDiff、btnAddTempDiff、btnDeleteTempDiff
|
||||||
|
- 当选定温差图例后,显示btnBrushSize1、btnBrushSize3、btnBrushSize5、btnBrushSize10、btnBrushSize15、btnBrushSize25
|
||||||
|
- 隐藏btnSelectColor、btnDeleteRegion、btnDrawRegion按钮
|
||||||
|
- 隐藏btnNewTempRegion按钮、btnLoadTempRegion按钮、btnSaveTempRegion按钮、btnNewTempDiff按钮、btnLoadTempDiff按钮、btnSaveTempDiff按钮
|
||||||
Reference in New Issue
Block a user