优化温度数据保存功能:1. 在DeviceManager.cs中新增接受TemperatureData参数的SaveTemperatureDataToCsv方法重载 2. 修改Camera.cs以直接使用缓存的温度数据进行保存 3. 更新相关注释说明实现逻辑

This commit is contained in:
zqm
2025-11-05 17:24:42 +08:00
parent b44d94a25e
commit f9f4b73ff8
3 changed files with 98 additions and 392 deletions

View File

@@ -2173,54 +2173,27 @@ namespace JoyD.Windows.CS.Toprie
// 如果用户选择了文件路径
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
// 现在直接从设备管理器获取温度数据,不再使用缓存的温度数据
// 调用设备管理器的方法获取最新的温度数据
// 获取已有的温度数据从DeviceManager缓存中获取
TemperatureData temperatureData = _deviceManager.LastTemperature;
if (temperatureData == null)
{
MessageBox.Show("获取温度数据失败,请确保设备已连接且正在接收数据。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 保存温度数据到CSV文件
using (StreamWriter writer = new StreamWriter(saveFileDialog.FileName))
if (temperatureData == null)
{
// 写入文件头部信息(以#开头的注释行不会被CSV解析器当作数据
writer.WriteLine("# 温度数据文件 - CSV格式逗号分隔值");
writer.WriteLine($"# 生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
writer.WriteLine($"# 分辨率: {temperatureData.Width} x {temperatureData.Height}");
writer.WriteLine($"# 最高温度: {temperatureData.MaxTemperature:F2} °C");
writer.WriteLine($"# 最低温度: {temperatureData.MinTemperature:F2} °C");
writer.WriteLine($"# 平均温度: {temperatureData.AverageTemperature:F2} °C");
writer.WriteLine();
// 写入温度数据矩阵CSV格式
for (int i = 0; i < temperatureData.Height; i++)
{
StringBuilder lineBuilder = new StringBuilder();
for (int j = 0; j < temperatureData.Width; j++)
{
// 获取温度值
string tempValue = $"{temperatureData.TemperatureMatrix[i, j]:F2}";
// CSV标准格式如果值包含逗号、引号或换行符需要用引号包围并转义内部引号
if (tempValue.Contains(',') || tempValue.Contains('"') || tempValue.Contains('\n'))
{
tempValue = $"\"{tempValue.Replace("\"", "\"\"")}\"";
}
lineBuilder.Append(tempValue);
if (j < temperatureData.Width - 1)
{
lineBuilder.Append(",");
}
}
writer.WriteLine(lineBuilder.ToString());
}
MessageBox.Show("获取温度数据失败,请确保设备已连接且正在接收数据。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 显示保存成功消息
MessageBox.Show($"温度数据已成功保存到:\n{saveFileDialog.FileName}", "保存成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
// 使用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)

View File

@@ -257,8 +257,6 @@ namespace JoyD.Windows.CS.Toprie
private bool _isAutoReconnectEnabled = true;
// 最大重连次数
private int _maxReconnectAttempts = 5;
// 是否已连接
private bool _isConnected = false;
// 连接超时设置
private readonly int _connectTimeout = 5000;
// 当前重连尝试次数
@@ -1599,6 +1597,8 @@ namespace JoyD.Windows.CS.Toprie
private const int HEADER_SIZE = 24; // 24字节头部结构体
private const int WIDTH = 256;
private const int HEIGHT = 192;
private const int FixedWidth = 512;
private const int FixedHeight = 384;
/// <param name="dataAccumulator">累积的温度数据包列表</param>
// 重入保护标志
@@ -4181,7 +4181,6 @@ namespace JoyD.Windows.CS.Toprie
Log("设计模式下跳过实际设备连接");
// 不设置真实的连接状态,避免触发图像接收
_currentDeviceId = deviceId;
_isConnected = false;
_connectionStatus = ConnectionStatus.Disconnected;
UpdateConnectionStatus(ConnectionStatus.Disconnected, "设计模式:跳过设备连接");
return;
@@ -4375,8 +4374,6 @@ namespace JoyD.Windows.CS.Toprie
if (result)
{
_isConnected = true;
// 启动心跳检测和连接检查
StartHeartbeat();
StartConnectionCheck();
@@ -5440,17 +5437,16 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
/// <param name="filePath">CSV文件路径</param>
/// <returns>是否保存成功</returns>
public bool SaveTemperatureDataToCsv(string filePath)
/// <summary>
/// 保存温度数据到CSV文件接受已有的温度数据
/// </summary>
/// <param name="filePath">CSV文件路径</param>
/// <param name="temperatureData">温度数据对象</param>
/// <returns>是否保存成功</returns>
public bool SaveTemperatureDataToCsv(string filePath, TemperatureData temperatureData)
{
try
{
// 检查设备是否连接
if (!_isConnected || _a8Sdk == null)
{
Console.WriteLine("设备未连接,无法保存温度数据");
return false;
}
// 检查文件路径是否有效
if (string.IsNullOrEmpty(filePath))
{
@@ -5475,91 +5471,13 @@ namespace JoyD.Windows.CS.Toprie
}
}
// 尝试从设备获取温度数据
List<TemperatureDataPoint> temperatureData = new List<TemperatureDataPoint>();
try
{
// 使用A8SDK的Get_all_temp方法获取所有温度数据
Console.WriteLine("正在获取设备温度数据...");
SharedStructures.ImageTemp imageTemp = _a8Sdk.Get_all_temp();
// 检查是否获取到温度数据
bool hasTemperatureData = false;
// 处理全局温度数据
if (imageTemp.globa.max_temp > -273.0f) // 检查是否有有效温度值(高于绝对零度)
{
Console.WriteLine($"全局温度数据: 最高={imageTemp.globa.max_temp}°C, 最低={imageTemp.globa.min_temp}°C");
// 添加全局温度点
temperatureData.Add(new TemperatureDataPoint(0, 0, imageTemp.globa.max_temp));
temperatureData.Add(new TemperatureDataPoint(0, 1, imageTemp.globa.min_temp));
// 添加最高温度点的坐标
temperatureData.Add(new TemperatureDataPoint(
imageTemp.globa.max_temp_x,
imageTemp.globa.max_temp_y,
imageTemp.globa.max_temp));
// 添加最低温度点的坐标
temperatureData.Add(new TemperatureDataPoint(
imageTemp.globa.min_temp_x,
imageTemp.globa.min_temp_y,
imageTemp.globa.min_temp));
hasTemperatureData = true;
}
// 处理区域温度数据
for (int i = 0; i < imageTemp.area.Length; i++)
{
if (imageTemp.area[i].enable == 1 && imageTemp.area[i].ave_temp > -273.0f)
{
Console.WriteLine($"区域 {i+1} 温度: {imageTemp.area[i].ave_temp}°C");
temperatureData.Add(new TemperatureDataPoint(i + 1, 0, imageTemp.area[i].ave_temp));
hasTemperatureData = true;
}
}
// 处理点温度数据
for (int i = 0; i < imageTemp.spot.Length; i++)
{
if (imageTemp.spot[i].enable == 1 && imageTemp.spot[i].temp > -273.0f)
{
Console.WriteLine($"点 {i+1} 温度: {imageTemp.spot[i].temp}°C");
temperatureData.Add(new TemperatureDataPoint(i + 1, 1, imageTemp.spot[i].temp));
hasTemperatureData = true;
}
}
if (!hasTemperatureData)
{
Console.WriteLine("未获取到有效温度数据,使用模拟数据");
// 如果无法获取真实数据,使用模拟数据
GenerateMockTemperatureData(temperatureData);
}
else
{
Console.WriteLine($"成功获取到 {temperatureData.Count} 个温度数据点");
}
}
catch (Exception ex)
{
Console.WriteLine($"获取温度数据失败: {ex.Message}");
// 如果获取真实数据失败,使用模拟数据
Console.WriteLine("使用模拟温度数据作为备选");
GenerateMockTemperatureData(temperatureData);
}
// 写入CSV文件
try
{
// 确保有温度数据
if (temperatureData.Count == 0)
if (temperatureData == null || temperatureData.TemperatureMatrix == null || temperatureData.Width == 0 || temperatureData.Height == 0)
{
Console.WriteLine("没有温度数据可保存");
Console.WriteLine("没有有效的温度数据可保存");
return false;
}
@@ -5568,31 +5486,30 @@ namespace JoyD.Windows.CS.Toprie
// 写入CSV头部和元信息
writer.WriteLine("# 温度数据导出");
writer.WriteLine($"# 导出时间: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
writer.WriteLine($"# 设备IP: {_deviceIp}");
writer.WriteLine($"# 数据点数: {temperatureData.Count}");
writer.WriteLine($"# 采样分辨率: {temperatureData.Width}x{temperatureData.Height}");
writer.WriteLine($"# 修正分辨率: {FixedWidth}x{FixedHeight}");
writer.WriteLine($"# 最高温度: {temperatureData.MaxTemperature:F2}°C");
writer.WriteLine($"# 最低温度: {temperatureData.MinTemperature:F2}°C");
writer.WriteLine($"# 平均温度: {temperatureData.AverageTemperature:F2}°C");
writer.WriteLine("#");
// 写入数据列头
writer.WriteLine("数据类型,X坐标,Y坐标,温度值(°C)");
writer.WriteLine("X坐标,Y坐标,温度值(°C)");
// 写入温度数据
foreach (var dataPoint in temperatureData)
// 写入温度矩阵数据
for (int y = 0; y < FixedHeight; y++)
{
// 根据X坐标判断数据类型
string dataType = "未知";
if (dataPoint.X == 0 && dataPoint.Y == 0) dataType = "全局最高";
else if (dataPoint.X == 0 && dataPoint.Y == 1) dataType = "全局最低";
else if (dataPoint.X == 0 && dataPoint.Y == 2) dataType = "全局平均";
else if (dataPoint.Y == 0 && dataPoint.X > 0 && dataPoint.X <= 6) dataType = $"区域{dataPoint.X}";
else if (dataPoint.Y == 1 && dataPoint.X > 0 && dataPoint.X <= 6) dataType = $"点{dataPoint.X}";
else dataType = "温度点";
writer.WriteLine($"{dataType},{dataPoint.X},{dataPoint.Y},{dataPoint.Temperature:F2}");
for (int x = 0; x < FixedWidth; x++)
{
// 获取温度值
float temperature = temperatureData.RealTemperatureMatrix[y, x];
writer.WriteLine($"{x},{y},{temperature:F2}");
}
}
}
Console.WriteLine($"温度数据已成功保存到: {filePath}");
Console.WriteLine($"共保存 {temperatureData.Count} 个温度数据点");
Console.WriteLine($"采样分辨率: {temperatureData.Width}x{temperatureData.Height}");
return true;
}
catch (IOException ioEx)
@@ -5641,190 +5558,6 @@ namespace JoyD.Windows.CS.Toprie
}
}
}
/// <summary>
/// 获取当前温度数据
/// </summary>
/// <returns>当前温度数据对象如果获取失败则返回null</returns>
public TemperatureData GetCurrentTemperatureData()
{
try
{
// 检查设备是否连接
if (!_isConnected || _a8Sdk == null)
{
Log("设备未连接,无法获取温度数据");
return null;
}
// 尝试从设备获取温度数据
try
{
// 使用A8SDK的Get_all_temp方法获取所有温度数据
Log("正在获取设备温度数据...");
SharedStructures.ImageTemp imageTemp = _a8Sdk.Get_all_temp();
// 检查是否获取到温度数据使用默认值比较而不是null比较
if (imageTemp.Equals(default(SharedStructures.ImageTemp)))
{
Log("未获取到温度数据返回null");
return null;
}
// 获取温度补偿值
float compensationValue = _a8Sdk.Comp_temp;
Log($"获取到的温度补偿值: {compensationValue}");
// 创建温度数据对象
// 这里使用模拟的温度矩阵,实际项目中应该使用真实的温度数据
// 根据设备实际分辨率创建温度矩阵
const int width = 640; // 假设设备分辨率为640x480
const int height = 480;
// 生成模拟的温度数据矩阵
float[,] temperatureMatrix = new float[height, width];
// 填充温度矩阵,使用获取到的最大和最小温度作为参考
float maxTemp = imageTemp.globa.max_temp > -273.0f ? imageTemp.globa.max_temp : 30.0f;
float minTemp = imageTemp.globa.min_temp > -273.0f ? imageTemp.globa.min_temp : 20.0f;
// 生成温度矩阵,在最大和最小温度之间变化
Random rand = new Random();
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 生成在最小和最大温度之间的随机值
temperatureMatrix[i, j] = minTemp + (float)rand.NextDouble() * (maxTemp - minTemp);
// 添加一些变化模式,使温度分布更自然
temperatureMatrix[i, j] += (float)Math.Sin(i * 0.02) * 2.0f + (float)Math.Cos(j * 0.02) * 2.0f;
}
}
// 确保最大和最小温度点存在
if (imageTemp.globa.max_temp_x >= 0 && imageTemp.globa.max_temp_y >= 0)
{
int x = Math.Min(imageTemp.globa.max_temp_x, width - 1);
int y = Math.Min(imageTemp.globa.max_temp_y, height - 1);
temperatureMatrix[y, x] = maxTemp;
}
if (imageTemp.globa.min_temp_x >= 0 && imageTemp.globa.min_temp_y >= 0)
{
int x = Math.Min(imageTemp.globa.min_temp_x, width - 1);
int y = Math.Min(imageTemp.globa.min_temp_y, height - 1);
temperatureMatrix[y, x] = minTemp;
}
// 创建TemperatureData对象
// 使用Toprie.TemperatureData类的构造函数
// 首先创建符合构造函数要求的rawData格式
List<byte> rawDataList = new List<byte>();
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 将温度值转换回原始格式(不包含补偿值)
float tempValue = temperatureMatrix[i, j] - compensationValue;
int tempInt = (int)(tempValue * 10.0f);
rawDataList.Add((byte)((tempInt >> 8) & 0xFF)); // 高字节
rawDataList.Add((byte)(tempInt & 0xFF)); // 低字节
}
}
// 使用全局TemperatureData类的构造函数
TemperatureData temperatureData = new TemperatureData(
rawDataList.ToArray(),
width,
height,
compensationValue
);
Log($"成功创建温度数据对象,分辨率: {width}x{height}, 最大温度: {temperatureData.MaxTemperature:F2}°C, 最小温度: {temperatureData.MinTemperature:F2}°C");
return temperatureData;
}
catch (Exception ex)
{
Log($"获取温度数据失败: {ex.Message}");
// 如果获取真实数据失败,尝试生成模拟数据
return GenerateMockTemperatureData();
}
}
catch (Exception ex)
{
Log($"GetCurrentTemperatureData方法异常: {ex.Message}");
return null;
}
}
/// <summary>
/// 生成完整的模拟温度数据对象
/// </summary>
/// <returns>模拟的温度数据对象</returns>
private TemperatureData GenerateMockTemperatureData()
{
try
{
Log("生成模拟温度数据...");
// 假设分辨率为640x480
const int width = 640;
const int height = 480;
// 创建温度矩阵
float[,] temperatureMatrix = new float[height, width];
Random rand = new Random();
// 生成有一定模式的模拟温度数据
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 基础温度25度加上一些变化模式
float baseTemp = 25.0f;
// 添加波浪形变化
float waveVariation = (float)(Math.Sin(i * 0.05) * Math.Cos(j * 0.05)) * 10.0f;
// 添加随机噪声
float noise = (float)(rand.NextDouble() - 0.5) * 2.0f;
// 组合生成最终温度
temperatureMatrix[i, j] = baseTemp + waveVariation + noise;
}
}
// 创建并返回模拟温度数据对象
// 首先创建符合构造函数要求的rawData格式
List<byte> rawDataList = new List<byte>();
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 将温度值转换回原始格式
float tempValue = temperatureMatrix[i, j];
int tempInt = (int)(tempValue * 10.0f);
rawDataList.Add((byte)((tempInt >> 8) & 0xFF)); // 高字节
rawDataList.Add((byte)(tempInt & 0xFF)); // 低字节
}
}
// 使用全局TemperatureData类的构造函数
TemperatureData temperatureData = new TemperatureData(
rawDataList.ToArray(),
width,
height,
0 // 模拟数据不需要补偿值
);
Log($"模拟温度数据生成完成,最大温度: {temperatureData.MaxTemperature:F2}°C, 最小温度: {temperatureData.MinTemperature:F2}°C");
return temperatureData;
}
catch (Exception ex)
{
Log($"生成模拟温度数据失败: {ex.Message}");
return null;
}
}
#region IDisposable
private bool _disposed = false;

View File

@@ -80,69 +80,69 @@ namespace JoyD.Windows.CS.Toprie
}
/// <summary>
/// 解析原始温度数据
/// </summary>
/// <param name="rawData">原始温度数据</param>
/// <param name="compensationValue">温度补偿值</param>
private void ParseRawData(byte[] rawData, float compensationValue)
{
// 检查数据是否有效
if (rawData == null || rawData.Length == 0)
/// 解析原始温度数据
/// </summary>
/// <param name="rawData">原始温度数据</param>
/// <param name="compensationValue">温度补偿值</param>
private void ParseRawData(byte[] rawData, float compensationValue)
{
throw new ArgumentNullException(nameof(rawData), "原始温度数据为空");
}
// 根据SDK文档要求使用24字节头部
const int HEADER_SIZE = 24;
// 确保数据长度足够(至少包含头部)
if (rawData.Length < HEADER_SIZE)
{
throw new ArgumentException("数据长度不足,无法解析");
}
// 计算温度数据长度和像素总数
int dataLength = rawData.Length - HEADER_SIZE;
int pixelCount = dataLength / 2; // 每个像素2字节
// 计算最大可处理的像素数
int maxPixels = Math.Min(pixelCount, Width * Height);
// 跳过24字节头部按照行优先顺序解析温度数据
for (int i = 0; i < maxPixels; i++)
{
// 计算行列索引
int row = i / Width;
int col = i % Width;
if (row < Height && col < Width)
// 检查数据是否有效
if (rawData == null || rawData.Length == 0)
{
// 计算数据偏移量跳过24字节头部
int offset = HEADER_SIZE + i * 2;
throw new ArgumentNullException(nameof(rawData), "原始温度数据为空");
}
if (offset + 1 < rawData.Length)
// 根据SDK文档要求使用24字节头部
const int HEADER_SIZE = 24;
// 确保数据长度足够(至少包含头部)
if (rawData.Length < HEADER_SIZE)
{
throw new ArgumentException("数据长度不足,无法解析");
}
// 计算温度数据长度和像素总数
int dataLength = rawData.Length - HEADER_SIZE;
int pixelCount = dataLength / 2; // 每个像素2字节
// 计算最大可处理的像素数
int maxPixels = Math.Min(pixelCount, Width * Height);
// 跳过24字节头部按照行优先顺序解析温度数据
for (int i = 0; i < maxPixels; i++)
{
// 计算行列索引
int row = i / Width;
int col = i % Width;
if (row < Height && col < Width)
{
// 根据SDK文档要求温度值计算方法(H×256+L)/10单位为摄氏度
// H为高8位L为低8位
int highByte = rawData[offset + 1]; // 高8位
int lowByte = rawData[offset]; // 低8位
int tempValue = (highByte << 8) | lowByte;
// 计算数据偏移量跳过24字节头部
int offset = HEADER_SIZE + i * 2;
// 计算实际温度值:(H×256+L)/10
float rawTemperature = tempValue / 10.0f;
if (offset + 1 < rawData.Length)
{
// 根据SDK文档要求温度值计算方法(H×256+L)/10单位为摄氏度
// H为高8位L为低8位
int highByte = rawData[offset + 1]; // 高8位
int lowByte = rawData[offset]; // 低8位
int tempValue = (highByte << 8) | lowByte;
// 应用温度补偿值
float compensatedTemperature = rawTemperature + compensationValue;
// 计算实际温度值:(H×256+L)/10
float rawTemperature = tempValue / 10.0f;
// 原始温度存入TemperatureMatrix未温补
TemperatureMatrix[row, col] = rawTemperature;
// 应用温度补偿值
float compensatedTemperature = rawTemperature + compensationValue;
// 温补后的温度存入RealTemperatureMatrix映射到512×384矩阵
MapToRealTemperatureMatrix(row, col, compensatedTemperature);
// 原始温度存入TemperatureMatrix未温补
TemperatureMatrix[row, col] = rawTemperature;
// 温补后的温度存入RealTemperatureMatrix映射到512×384矩阵
MapToRealTemperatureMatrix(row, col, compensatedTemperature);
}
}
}
}
}
/// <summary>
/// 将原始温度数据映射到512×384的实际温度矩阵