This commit is contained in:
JoyD
2025-10-28 19:45:30 +08:00
10 changed files with 7310 additions and 18 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -0,0 +1,662 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace JoyD.Windows.CS.Toprie
{
public class A8SDK
{
// 结构体定义
public struct AreaPos
{
public int enable;
public int x;
public int y;
public int width;
public int height;
}
public struct SpotPos
{
public int enable;
public int x;
public int y;
}
public struct LinePos
{
public int enable;
public int sta_x;
public int sta_y;
public int end_x;
public int end_y;
}
public struct ImagePos
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public AreaPos[] area;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public SpotPos[] spot;
public LinePos line;
}
public struct AreaTemp
{
public int enable;
public int max_temp;
public int max_temp_x;
public int max_temp_y;
public int min_temp;
public int min_temp_x;
public int min_temp_y;
public int ave_temp;
}
public struct SpotTemp
{
public int enable;
public int temp;
}
public struct LineTemp
{
public int enable;
public int max_temp;
public int max_temp_x;
public int max_temp_y;
public int min_temp;
public int min_temp_x;
public int min_temp_y;
public int ave_temp;
}
public struct GlobaTemp
{
public int max_temp;
public int max_temp_x;
public int max_temp_y;
public int min_temp;
public int min_temp_x;
public int min_temp_y;
}
public struct ImageTemp
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public AreaTemp[] area;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public SpotTemp[] spot;
public LineTemp line;
public GlobaTemp globa;
}
public struct EmailServer
{
public int enable;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] recv_addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] send_addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] send_pwd;
}
public struct TftpServer
{
public int enable;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] tftp_addr;
}
public struct NetworkEth
{
public int enable;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] static_ip;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] netmask;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] gateway;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] dns1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] dns2;
}
public struct EnvirParam
{
public int method;
public int num;
public float emissivity;
public float airTemp;
public float targetTemp;
public float atmosTrans;
public float distance;
public float infraredTemp;
public float infraredRadia;
}
public struct AlarmParam
{
public int method;
public int num;
public int active;
public int condition;
public int captrue;
public int disableCalib;
public int email;
public int digital;
public int ftp;
public float threshold;
public float hysteresis;
public int thresholeTime;
}
public struct FocusParam
{
public int method;
public int x;
public int y;
public int width;
public int height;
}
public struct TimeParam
{
public int year;
public char month;
public char day;
public char hour;
public char minute;
public char second;
}
// 使用V8类实例来实现功能
private readonly string deviceIp;
private readonly V8 v8Instance;
public A8SDK(string ip)
{
deviceIp = ip;
v8Instance = new V8(ip);
}
~A8SDK()
{
// V8类的析构函数会自动处理资源释放
}
public static int SDK_initialize()
{
return V8.SDK_initialize();
}
public static int SDK_destroy()
{
return V8.SDK_destroy();
}
public static String SDK_serch_device(int list_len)
{
return V8.SDK_serch_device(list_len);
}
// 快门校正方法
public void Shutter_correction()
{
v8Instance.Shutter_correction();
}
// 快门次数属性
public int Shutter_times
{
get
{
return v8Instance.Shutter_times;
}
set
{
v8Instance.Shutter_times = value;
}
}
// 色板属性
public int Color_plate
{
get
{
return v8Instance.Color_plate;
}
set
{
v8Instance.Color_plate = value;
}
}
// 镜像模式属性
public int Mirror_mode
{
get
{
return v8Instance.Mirror_mode;
}
set
{
v8Instance.Mirror_mode = value;
}
}
// 视频模式属性
public int Video_mode
{
get
{
return v8Instance.Video_mode;
}
set
{
v8Instance.Video_mode = value;
}
}
// 设置区域位置
public void Set_area_pos(int index, SharedStructures.AreaPos area_data)
{
v8Instance.Set_area_pos(index, area_data);
}
// 获取区域位置
public SharedStructures.AreaPos Get_area_pos(int index)
{
return v8Instance.Get_area_pos(index);
}
// 设置点位置
public void Set_spot_pos(int index, SharedStructures.SpotPos spot_data)
{
v8Instance.Set_spot_pos(index, spot_data);
}
// 获取点位置
public SharedStructures.SpotPos Get_spot_pos(int index)
{
return v8Instance.Get_spot_pos(index);
}
// 线位置属性
public SharedStructures.LinePos Line_pos
{
set
{
v8Instance.Line_pos = value;
}
get
{
return v8Instance.Line_pos;
}
}
// 所有位置属性
public SharedStructures.ImagePos All_pos
{
get
{
return v8Instance.All_pos;
}
}
// 温度范围属性
public int Temp_range
{
set
{
v8Instance.Temp_range = value;
}
get
{
return v8Instance.Temp_range;
}
}
// 视频ISP X偏移属性
public int Video_isp_x_offset
{
set
{
v8Instance.Video_isp_x_offset = value;
}
get
{
return v8Instance.Video_isp_x_offset;
}
}
// 视频ISP Y偏移属性
public int Video_isp_y_offset
{
set
{
v8Instance.Video_isp_y_offset = value;
}
get
{
return v8Instance.Video_isp_y_offset;
}
}
// 视频ISP X缩放属性
public int Video_isp_x_scale
{
set
{
v8Instance.Video_isp_x_scale = value;
}
get
{
return v8Instance.Video_isp_x_scale;
}
}
// 视频ISP Y缩放属性
public int Video_isp_y_scale
{
set
{
v8Instance.Video_isp_y_scale = value;
}
get
{
return v8Instance.Video_isp_y_scale;
}
}
// LED设置属性
public int Set_led
{
set
{
v8Instance.Set_led = value;
}
get
{
return v8Instance.Set_led;
}
}
// 邮件服务器属性
public SharedStructures.EmailServer Email_server
{
set
{
v8Instance.Email_server = value;
}
get
{
return v8Instance.Email_server;
}
}
// TFTP服务器属性
public SharedStructures.TftpServer Tftp_server
{
set
{
v8Instance.Tftp_server = value;
}
get
{
return v8Instance.Tftp_server;
}
}
// 网络以太网属性
public SharedStructures.NetworkEth Network_eth
{
set
{
v8Instance.Network_eth = value;
}
get
{
return v8Instance.Network_eth;
}
}
// 融合距离属性
public int Fusion_distance
{
set
{
v8Instance.Fusion_distance = value;
}
get
{
return v8Instance.Fusion_distance;
}
}
// 设置环境参数
public void Set_envir_param(SharedStructures.EnvirParam data)
{
v8Instance.Set_envir_param(data);
}
// 获取区域环境参数
public SharedStructures.EnvirParam Get_area_envir_param(int index)
{
return v8Instance.Get_area_envir_param(index);
}
// 获取点环境参数
public SharedStructures.EnvirParam Get_spot_envir_param(int index)
{
return v8Instance.Get_spot_envir_param(index);
}
// 获取线环境参数
public SharedStructures.EnvirParam Get_line_envir_param()
{
return v8Instance.Get_line_envir_param();
}
// 获取全局环境参数
public SharedStructures.EnvirParam Get_globa_envir_param()
{
return v8Instance.Get_globa_envir_param();
}
// 设置报警参数
public void Set_alarm_param(SharedStructures.AlarmParam data)
{
v8Instance.Set_alarm_param(data);
}
// 获取区域报警参数
public SharedStructures.AlarmParam Get_area_alarm_param(int index)
{
return v8Instance.Get_area_alarm_param(index);
}
// 获取点报警参数
public SharedStructures.AlarmParam Get_spot_alarm_param(int index)
{
return v8Instance.Get_spot_alarm_param(index);
}
// 获取全局报警参数
public SharedStructures.AlarmParam Get_globa_alarm_param()
{
return v8Instance.Get_globa_alarm_param();
}
// 获取线报警参数
public SharedStructures.AlarmParam Get_line_alarm_param()
{
return v8Instance.Get_line_alarm_param();
}
// 获取区域温度
public SharedStructures.AreaTemp Get_area_temp(int index)
{
return v8Instance.Get_area_temp(index);
}
// 获取点温度
public SharedStructures.SpotTemp Get_spot_temp(int index)
{
return v8Instance.Get_spot_temp(index);
}
// 获取线温度
public SharedStructures.LineTemp Get_line_temp()
{
return v8Instance.Get_line_temp();
}
// 获取全局温度
public SharedStructures.GlobaTemp Get_globa_temp()
{
return v8Instance.Get_globa_temp();
}
// 获取所有温度
public SharedStructures.ImageTemp Get_all_temp()
{
return v8Instance.Get_all_temp();
}
// 电源重启
public void Power_reboot()
{
v8Instance.Power_reboot();
}
// 参数恢复
public void Param_recover()
{
v8Instance.Param_recover();
}
// 更新
public void Update()
{
v8Instance.Update();
}
// 心跳检测
public int Heartbeat()
{
return v8Instance.Heartbeat();
}
// 设置串口
public void Set_uart(int nSpeed, int nBits, char nEvent, int nStop)
{
v8Instance.Set_uart(nSpeed, nBits, nEvent, nStop);
}
// 发送串口命令
public void Send_uart_command(byte[] cmd)
{
v8Instance.Send_uart_command(cmd);
}
// 自动对焦
public void Autofocus(SharedStructures.FocusParam data)
{
v8Instance.Autofocus(data);
}
// 设置设备名称
public void Set_device_name(string data)
{
v8Instance.Set_device_name(data);
}
// 获取设备名称
public string Get_device_name()
{
return v8Instance.Get_device_name();
}
// 获取检测数量
public byte[] Get_detect_number()
{
return v8Instance.Get_detect_number();
}
// 温度帧属性
public char Temp_frame
{
set
{
v8Instance.Temp_frame = value;
}
get
{
return v8Instance.Temp_frame;
}
}
// 报警属性
public char Alarm
{
set
{
v8Instance.Alarm = value;
}
get
{
return v8Instance.Alarm;
}
}
// 补偿温度属性
public float Comp_temp
{
get
{
return v8Instance.Comp_temp;
}
}
// 设置时间
public void Set_time(SharedStructures.TimeParam data)
{
v8Instance.Set_time(data);
}
// 设置图像模式
public void SetImageMode(int mode)
{
v8Instance.SetImageMode(mode);
}
// 获取图像数据
public byte[] GetImageData()
{
return v8Instance.GetImageData();
}
// 设备搜索
public static List<string> SearchDevices()
{
List<string> devices = new List<string>();
const int maxDevices = 10;
string searchResult = SDK_serch_device(maxDevices);
if (!string.IsNullOrEmpty(searchResult))
{
string[] deviceList = searchResult.Split(';');
foreach (string device in deviceList)
{
if (!string.IsNullOrEmpty(device))
{
devices.Add(device);
}
}
}
return devices;
}
}
}

View File

@@ -7,19 +7,6 @@
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
@@ -28,10 +15,35 @@
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
this.imageBox = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.imageBox)).BeginInit();
this.SuspendLayout();
//
// imageBox
//
this.imageBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.imageBox.Location = new System.Drawing.Point(0, 0);
this.imageBox.Name = "imageBox";
this.imageBox.Size = new System.Drawing.Size(512, 384);
this.imageBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.imageBox.TabIndex = 0;
this.imageBox.TabStop = false;
//
// Camera
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.imageBox);
this.Name = "Camera";
this.Size = new System.Drawing.Size(512, 384);
this.Load += new System.EventHandler(this.Camera_Load);
((System.ComponentModel.ISupportInitialize)(this.imageBox)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.PictureBox imageBox;
}
}

View File

@@ -1,19 +1,914 @@
using System;
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace JoyD.Windows.CS.Toprie
{
public partial class Camera : UserControl
{
// 设备管理器实例
private DeviceManager _deviceManager;
// 是否正在接收图像
private bool _isReceivingImage = false;
// 显示错误的定时器
private System.Windows.Forms.Timer _errorDisplayTimer;
public Camera()
{
{
InitializeComponent();
// 将设计模式状态传递给DeviceManager
DeviceManager.IsDesignMode = DesignMode;
// 只有在非设计模式下才初始化设备管理器和错误定时器
if (!DesignMode)
{
// 清空现有日志
try
{
string logFile = Path.Combine(Application.StartupPath, "camera.log");
if (File.Exists(logFile))
{
File.WriteAllText(logFile, string.Empty);
}
}
catch { }
InitializeDeviceManager();
InitializeErrorTimer();
}
}
/// <summary>
/// Camera控件加载事件
/// 在控件加载时自动启动相机显示热图
/// </summary>
private void Camera_Load(object sender, EventArgs e)
{
// 只有在非设计模式下才启动相机
if (!DesignMode)
{
try
{
StartCamera();
}
catch (Exception ex)
{
ShowError($"自动启动相机失败: {ex.Message}");
}
}
}
// 注意UserControl不支持FormClosing事件资源清理已在Dispose方法中处理
/// <summary>
/// 初始化设备管理器
/// </summary>
private void InitializeDeviceManager()
{
_deviceManager = new DeviceManager
{
AutoReconnectEnabled = true,
ReconnectInterval = 2000 // 2秒
};
// 确保DeviceManager的设计模式状态与控件一致
DeviceManager.IsDesignMode = DesignMode;
// 设置静态属性
DeviceManager.MaxReconnectAttempts = 5;
// 注册图像接收事件
_deviceManager.ImageReceived += DeviceManager_ImageReceived;
// 注册连接状态变更事件
_deviceManager.ConnectionStatusChanged += DeviceManager_ConnectionStatusChanged;
// 注册连接异常事件
_deviceManager.ConnectionException += DeviceManager_ConnectionException;
}
/// <summary>
/// 初始化错误显示定时器
/// </summary>
private void InitializeErrorTimer()
{
_errorDisplayTimer = new System.Windows.Forms.Timer { Interval = 3000 }; // 显示3秒后清除
_errorDisplayTimer.Tick += ErrorDisplayTimer_Tick;
}
/// <summary>
/// 启动相机
/// </summary>
public void StartCamera()
{
try
{
// 只有在没有接收图像时才启动
if (!_isReceivingImage)
{
// 清理错误显示
ShowError(string.Empty);
// 使用异步方式连接设备和设置模式避免UI线程阻塞
ThreadPool.QueueUserWorkItem(delegate
{
try
{
// 设置为热图模式
bool modeSet = false;
try
{
_deviceManager.SetImageMode(ImageMode.Thermal);
modeSet = true;
Console.WriteLine("已设置热图模式");
}
catch (Exception ex)
{
Console.WriteLine($"设置热图模式失败: {ex.Message}");
}
// 启用自动重连
_deviceManager.AutoReconnectEnabled = true;
// 尝试连接设备
if (_deviceManager.ConnectDevice())
{
Console.WriteLine("设备连接成功");
// 如果热图模式未成功设置,在连接成功后再次尝试
if (!modeSet)
{
try
{
_deviceManager.SetImageMode(ImageMode.Thermal);
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()
{
try
{
if (!_isReceivingImage && _deviceManager.ConnectionStatus == ConnectionStatus.Connected)
{
Console.WriteLine("Camera开始使用HTTP方式接收图像");
// 直接调用HTTP方式的图像接收
_deviceManager.StartImageReceiving();
_isReceivingImage = true;
}
}
catch (Exception ex)
{
ShowError($"开始接收图像失败: {ex.Message}");
}
}
/// <summary>
/// 停止接收图像
/// </summary>
public void StopCamera()
{
try
{
if (_isReceivingImage)
{
_deviceManager.StopReceiveImage();
_isReceivingImage = false;
}
}
catch (Exception ex)
{
Console.WriteLine($"停止相机失败: {ex.Message}");
}
}
/// <summary>
/// 设备管理器图像接收事件处理
/// </summary>
private void DeviceManager_ImageReceived(object sender, ImageReceivedEventArgs e)
{
Image image = null;
try
{
if (e.ImageData != null && e.ImageData.Length > 0)
{
// 创建内存流并从流中创建图像
using (MemoryStream ms = new MemoryStream(e.ImageData))
{
// 检查流是否可读且有效
if (ms.CanRead && ms.Length > 0)
{
// 从流中创建图像
using (Image tempImage = System.Drawing.Image.FromStream(ms))
{
// 创建一个全新的位图而不仅仅是克隆,确保数据完整性
image = new Bitmap(tempImage);
// 立即验证新创建的图像是否有效
try
{
// 访问Width和Height属性来验证图像是否有效
int width = image.Width;
int height = image.Height;
if (width <= 0 || height <= 0)
{
Console.WriteLine("创建的图像尺寸无效");
image.Dispose();
image = null;
}
}
catch (Exception)
{
Console.WriteLine("创建的图像无效");
image.Dispose();
image = null;
}
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"创建图像失败: {ex.Message}");
// 确保在异常情况下释放资源
if (image != null)
{
image.Dispose();
image = null;
}
}
if (image == null)
{
Console.WriteLine("接收到空或无效的图像数据");
return;
}
try
{
// 尝试创建图像的克隆以传递给UI线程
Image clonedImage = null;
try
{
clonedImage = (Image)image.Clone();
// 验证克隆的图像是否有效
if (clonedImage.Width <= 0 || clonedImage.Height <= 0)
{
Console.WriteLine("克隆的图像尺寸无效");
clonedImage.Dispose();
image.Dispose(); // 确保原始图像也被释放
return;
}
}
catch (Exception ex)
{
Console.WriteLine($"克隆图像失败: {ex.Message}");
image.Dispose(); // 确保原始图像被释放
return;
}
// 确保在UI线程上更新并且检查控件是否已被释放
if (!this.IsDisposed && !this.Disposing)
{
if (this.InvokeRequired)
{
try
{
// 创建本地副本以避免闭包问题
Image imageForUI = clonedImage;
clonedImage = null; // 防止在异步操作期间被修改
// 使用BeginInvoke在UI线程上更新图像避免可能的死锁问题
this.BeginInvoke(new Action(() =>
{
try
{
UpdateImageOnUI(imageForUI);
}
catch (Exception ex)
{
Console.WriteLine($"更新UI图像失败: {ex.Message}");
// 如果UI更新失败确保克隆的图像被释放
if (imageForUI != null)
{
try
{
imageForUI.Dispose();
}
catch {}
}
}
}));
}
catch (Exception)
{
// 异常情况下确保释放图像资源
if (clonedImage != null)
{
try
{
clonedImage.Dispose();
}
catch {}
clonedImage = null;
}
throw;
}
}
else
{
// 在UI线程上直接更新图像
UpdateImageOnUI(clonedImage);
}
}
else
{
// 如果控件已释放,确保释放图像资源
clonedImage.Dispose();
}
// 释放原始图像资源
image.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"处理图像接收事件失败: {ex.Message}");
// 确保在任何异常情况下都释放原始图像
if (image != null)
{
try
{
image.Dispose();
}
catch {}
}
}
}
/// <summary>
/// 在UI线程上更新图像
/// </summary>
private void UpdateImageOnUI(Image image)
{
// 线程安全检查 - 确保在UI线程上执行
if (this.InvokeRequired)
{
try
{
this.BeginInvoke(new Action<Image>(UpdateImageOnUI), image);
}
catch (ObjectDisposedException)
{
// 如果控件已被释放,确保释放图像资源
if (image != null)
{
try { image.Dispose(); } catch {}
}
Console.WriteLine("控件已释放跳过UI更新");
}
return;
}
// 空值检查
if (image == null)
{
Console.WriteLine("传入UpdateImageOnUI的图像为空");
return;
}
// 连接状态检查 - 只在设备实际连接时处理图像
if (_deviceManager.ConnectionStatus != ConnectionStatus.Connected)
{
Console.WriteLine("设备未连接,跳过图像更新");
try { image.Dispose(); } catch {}
return;
}
// 增强图像有效性检查
bool isImageValid = false;
Image safeImage = null;
try
{
// 创建一个安全的图像副本,确保原图像不被破坏
safeImage = new Bitmap(image);
// 预先验证图像是否有效
if (safeImage.Width > 0 && safeImage.Height > 0)
{
// 尝试访问图像的像素数据,这是更严格的验证方式
using (Graphics g = Graphics.FromImage(new Bitmap(1, 1)))
{
// 简单绘制操作验证图像是否可绘制
g.DrawImage(safeImage, 0, 0, 1, 1);
}
isImageValid = true;
Console.WriteLine($"图像验证成功: {safeImage.Width}x{safeImage.Height}");
}
else
{
Console.WriteLine($"图像尺寸无效: {safeImage.Width}x{safeImage.Height}");
}
}
catch (Exception ex)
{
Console.WriteLine($"图像验证失败: {ex.Message}");
// 释放临时创建的安全图像
if (safeImage != null)
{
try { safeImage.Dispose(); } catch {}
}
// 释放原始图像
try { image.Dispose(); } catch {}
return;
}
try
{
// 更新图像前先检查控件是否存在/已释放
if (this.IsDisposed || imageBox == null || imageBox.IsDisposed)
{
// 如果控件已释放,确保图像也被释放
try { safeImage.Dispose(); } catch {}
try { image.Dispose(); } catch {}
Console.WriteLine("控件已释放,无法更新图像");
return;
}
// 保存旧图像引用,以便在设置新图像后释放
Image oldImage = imageBox.Image;
try
{
// 确保控件仍处于有效状态
if (imageBox.IsDisposed || !isImageValid || safeImage == null)
{
try { safeImage.Dispose(); } catch {}
try { image.Dispose(); } catch {}
return;
}
// 尝试设置新图像(使用验证过的安全副本)
imageBox.Image = safeImage;
// 验证图像是否成功设置
if (imageBox.Image != safeImage)
{
Console.WriteLine("图像设置失败,可能参数无效");
try { safeImage.Dispose(); } catch {}
try { image.Dispose(); } catch {}
return;
}
// 释放原始图像,因为我们现在使用的是安全副本
try { image.Dispose(); } catch {}
// 仅在新图像成功设置后释放旧图像
if (oldImage != null && oldImage != safeImage) // 防止自引用释放
{
try
{
oldImage.Dispose();
}
catch {}
}
}
catch (ArgumentException ex) when (ex.Message.Contains("参数无效"))
{
// 特别处理"参数无效"异常
Console.WriteLine($"图像参数无效异常: {ex.Message}");
try { safeImage.Dispose(); } catch {}
try { image.Dispose(); } catch {}
// 尝试设置旧图像回来,如果可能的话
if (oldImage != null)
{
try
{
imageBox.Image = oldImage;
}
catch
{
// 如果设置旧图像也失败,释放它
try { oldImage.Dispose(); } catch {}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"更新图像UI异常: {ex.Message}");
// 确保在任何异常情况下都释放所有图像资源
try { safeImage.Dispose(); } catch {}
try { image.Dispose(); } catch {}
}
}
/// <summary>
/// 设备管理器连接状态变更事件处理
/// </summary>
private void DeviceManager_ConnectionStatusChanged(object sender, ConnectionStatusChangedEventArgs e)
{
// 确保在UI线程上更新并且检查控件是否已被释放
if (!this.IsDisposed && !this.Disposing)
{
if (this.InvokeRequired)
{
try
{
// 使用BeginInvoke代替Invoke避免可能的死锁问题
this.BeginInvoke(new Action(() => HandleConnectionStatusChanged(e)));
}
catch (ObjectDisposedException)
{
// 捕获控件已释放异常,避免程序崩溃
Console.WriteLine("控件已释放跳过UI更新");
}
}
else
{
HandleConnectionStatusChanged(e);
}
}
}
/// <summary>
/// 处理连接状态变更
/// </summary>
private void HandleConnectionStatusChanged(ConnectionStatusChangedEventArgs e)
{
try
{
// 确保在UI线程上更新UI状态
if (this.InvokeRequired)
{
this.Invoke(new Action<ConnectionStatusChangedEventArgs>(HandleConnectionStatusChanged), e);
return;
}
// 更新UI状态
UpdateUIState(e.Status == ConnectionStatus.Connected);
// 检查_deviceManager是否为空
if (_deviceManager == null)
{
Console.WriteLine("设备管理器未初始化");
return;
}
switch (e.Status)
{
case ConnectionStatus.Connected:
Console.WriteLine("设备已连接");
// 清除错误信息
ShowError(string.Empty);
// 确保设置为热图模式
try
{
_deviceManager.SetImageMode(ImageMode.Thermal);
Console.WriteLine("连接成功后确认热图模式");
}
catch (Exception ex)
{
Console.WriteLine($"连接成功后设置热图模式失败: {ex.Message}");
}
// 开始接收图像包含在try-catch中
if (!_isReceivingImage)
{
StartReceiveImage();
}
break;
case ConnectionStatus.Disconnected:
Console.WriteLine("设备已断开连接");
// 停止接收图像(添加空检查和异常处理)
if (_isReceivingImage)
{
try
{
_deviceManager.StopReceiveImage();
_isReceivingImage = false;
}
catch (Exception ex)
{
Console.WriteLine($"停止接收图像失败: {ex.Message}");
_isReceivingImage = false; // 确保状态更新
}
}
if (!string.IsNullOrEmpty(e.DeviceInfo))
{
ShowError(e.DeviceInfo);
}
else
{
ShowError("设备连接已断开");
}
break;
case ConnectionStatus.Connecting:
case ConnectionStatus.Reconnecting:
Console.WriteLine($"正在连接设备...{(!string.IsNullOrEmpty(e.DeviceInfo) ? " " + e.DeviceInfo : "")}");
ShowError(string.Empty); // 清除之前的错误信息
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)
{
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)
{
// 确保在UI线程上更新并且检查控件是否已被释放
if (!this.IsDisposed && !this.Disposing)
{
if (this.InvokeRequired)
{
try
{
// 使用BeginInvoke在UI线程上显示错误
this.BeginInvoke(new Action(() =>
{
// 记录异常日志
Console.WriteLine($"连接异常发生于 {DateTime.Now}: {e.Message}\n{e.Exception.Message}\n{e.Exception.StackTrace}");
ShowError($"连接异常: {e.Message}");
}));
}
catch (ObjectDisposedException)
{
// 捕获控件已释放异常,避免程序崩溃
Console.WriteLine("控件已释放,跳过异常处理");
}
}
else
{
// 记录异常日志
Console.WriteLine($"连接异常发生于 {DateTime.Now}: {e.Message}\n{e.Exception.Message}\n{e.Exception.StackTrace}");
ShowError($"连接异常: {e.Message}");
}
}
}
/// <summary>
/// 显示错误信息
/// </summary>
private void ShowError(string message)
{
Console.WriteLine(message);
// 可以根据需要添加UI上的错误显示
// 这里简化处理,只在控制台输出
// 检查imageBox是否存在且尺寸有效
if (imageBox == null || imageBox.Width <= 0 || imageBox.Height <= 0)
{
Console.WriteLine("imageBox尺寸无效跳过错误显示图像创建");
return;
}
// 确保使用有效的尺寸创建Bitmap
int width = Math.Max(100, imageBox.Width);
int height = Math.Max(100, imageBox.Height);
// 如果需要在图像区域显示错误文字,可以使用以下代码
using (Bitmap errorBitmap = new Bitmap(width, height))
using (Graphics g = Graphics.FromImage(errorBitmap))
{
g.Clear(Color.Black);
using (Font font = new Font("Arial", 10))
using (Brush brush = new SolidBrush(Color.Red))
{
SizeF textSize = g.MeasureString(message, font);
PointF textLocation = new PointF(
(errorBitmap.Width - textSize.Width) / 2,
(errorBitmap.Height - textSize.Height) / 2);
g.DrawString(message, font, brush, textLocation);
}
UpdateImageOnUI(errorBitmap);
}
// 启动定时器3秒后清除错误显示
_errorDisplayTimer.Stop();
_errorDisplayTimer.Start();
}
/// <summary>
/// 错误显示定时器事件
/// </summary>
private void ErrorDisplayTimer_Tick(object sender, EventArgs e)
{
_errorDisplayTimer.Stop();
// 清除错误显示,恢复到等待图像状态
// 检查imageBox是否存在且尺寸有效
if (imageBox == null || imageBox.Width <= 0 || imageBox.Height <= 0)
{
Console.WriteLine("imageBox尺寸无效跳过等待图像创建");
return;
}
// 确保使用有效的尺寸创建Bitmap
int width = Math.Max(100, imageBox.Width);
int height = Math.Max(100, imageBox.Height);
using (Bitmap waitingBitmap = new Bitmap(width, height))
using (Graphics g = Graphics.FromImage(waitingBitmap))
{
g.Clear(Color.Black);
using (Font font = new Font("Arial", 10))
using (Brush brush = new SolidBrush(Color.White))
{
string waitingText = "正在等待热图数据...";
SizeF textSize = g.MeasureString(waitingText, font);
PointF textLocation = new PointF(
(waitingBitmap.Width - textSize.Width) / 2,
(waitingBitmap.Height - textSize.Height) / 2);
g.DrawString(waitingText, font, brush, textLocation);
}
UpdateImageOnUI(waitingBitmap);
}
}
/// <summary>
/// 清理资源
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
// 检查是否处于设计模式
if (!DesignMode)
{
// 停止相机并释放相关资源
try
{
StopCamera();
}
catch (Exception ex)
{
Console.WriteLine("关闭相机时出错: " + ex.Message);
}
}
// 取消注册事件并释放设备管理器
if (_deviceManager != null)
{
// 移除所有事件监听
_deviceManager.ImageReceived -= DeviceManager_ImageReceived;
_deviceManager.ConnectionStatusChanged -= DeviceManager_ConnectionStatusChanged;
_deviceManager.ConnectionException -= DeviceManager_ConnectionException;
// 释放设备管理器资源
_deviceManager.Dispose();
_deviceManager = null;
}
// 无论是否在设计模式下,都需要释放定时器
if (_errorDisplayTimer != null)
{
_errorDisplayTimer.Stop();
_errorDisplayTimer.Dispose();
_errorDisplayTimer = null;
}
// 无论是否在设计模式下,都需要释放图像资源
if (imageBox != null && !imageBox.IsDisposed && imageBox.Image != null)
{
imageBox.Image.Dispose();
imageBox.Image = null;
}
// 释放组件资源
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,179 @@
using System;
using System.Runtime.InteropServices;
namespace JoyD.Windows.CS.Toprie
{
/// <summary>
/// 共享数据结构定义类用于在V8、A8SDK和DeviceManager之间共享
/// </summary>
public static class SharedStructures
{
// 位置相关结构体
public struct AreaPos
{
public int enable;
public int x;
public int y;
public int width;
public int height;
}
public struct SpotPos
{
public int enable;
public int x;
public int y;
}
public struct LinePos
{
public int enable;
public int sta_x;
public int sta_y;
public int end_x;
public int end_y;
}
public struct ImagePos
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public AreaPos[] area;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public SpotPos[] spot;
public LinePos line;
}
// 温度相关结构体
public struct AreaTemp
{
public int enable;
public int max_temp;
public int max_temp_x;
public int max_temp_y;
public int min_temp;
public int min_temp_x;
public int min_temp_y;
public int ave_temp;
}
public struct SpotTemp
{
public int enable;
public int temp;
}
public struct LineTemp
{
public int enable;
public int max_temp;
public int max_temp_x;
public int max_temp_y;
public int min_temp;
public int min_temp_x;
public int min_temp_y;
public int ave_temp;
}
public struct GlobaTemp
{
public int max_temp;
public int max_temp_x;
public int max_temp_y;
public int min_temp;
public int min_temp_x;
public int min_temp_y;
}
public struct ImageTemp
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public AreaTemp[] area;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public SpotTemp[] spot;
public LineTemp line;
public GlobaTemp globa;
}
// 配置相关结构体
public struct EmailServer
{
public int enable;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] recv_addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] send_addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] send_pwd;
}
public struct TftpServer
{
public int enable;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] tftp_addr;
}
public struct NetworkEth
{
public int enable;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] static_ip;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] netmask;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] gateway;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] dns1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] dns2;
}
public struct EnvirParam
{
public int method;
public int num;
public float emissivity;
public float airTemp;
public float targetTemp;
public float atmosTrans;
public float distance;
public float infraredTemp;
public float infraredRadia;
}
public struct AlarmParam
{
public int method;
public int num;
public int active;
public int condition;
public int captrue;
public int disableCalib;
public int email;
public int digital;
public int ftp;
public float threshold;
public float hysteresis;
public int thresholeTime;
}
public struct FocusParam
{
public int method;
public int x;
public int y;
public int width;
public int height;
}
public struct TimeParam
{
public int year;
public char month;
public char day;
public char hour;
public char minute;
public char second;
}
}
}

View File

@@ -25,6 +25,9 @@
<IncludeBuildOutput>true</IncludeBuildOutput>
<IncludeContentInPack>true</IncludeContentInPack>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Toprie.ico</ApplicationIcon>
</PropertyGroup>
<!-- 添加NuGet打包支持 -->
<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
<!-- 确保项目支持pack目标 -->
@@ -60,13 +63,25 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="A8SDK.cs" />
<Compile Include="Camera.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Camera.Designer.cs">
<DependentUpon>Camera.cs</DependentUpon>
</Compile>
<Compile Include="DeviceManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SharedStructures.cs" />
<Compile Include="V8.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Camera.resx">
<DependentUpon>Camera.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Include="Toprie.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because it is too large Load Diff