This commit is contained in:
JoyD
2025-10-29 22:02:19 +08:00
7 changed files with 3473 additions and 746 deletions

View File

@@ -226,13 +226,32 @@ namespace JoyD.Windows.CS.Toprie
{
get
{
return v8Instance.Color_plate;
return GetColorPlate();
}
set
{
v8Instance.Color_plate = value;
SetColorPlate(value);
}
}
/// <summary>
/// 获取当前色彩模式
/// </summary>
/// <returns>当前色彩模式的值</returns>
public int GetColorPlate()
{
return v8Instance.GetColorPlate();
}
/// <summary>
/// 设置色彩模式
/// </summary>
/// <param name="value">要设置的色彩模式值</param>
/// <returns>设置是否成功</returns>
public bool SetColorPlate(int value)
{
return v8Instance.SetColorPlate(value);
}
// 镜像模式属性
public int Mirror_mode
@@ -627,10 +646,16 @@ namespace JoyD.Windows.CS.Toprie
v8Instance.Set_time(data);
}
// 设置图像模式
public void SetImageMode(int mode)
// 获取视频模式
public int GetVideoMode()
{
v8Instance.SetImageMode(mode);
return v8Instance.GetVideoMode();
}
// 设置视频模式
public void SetVideoMode(int mode)
{
v8Instance.SetVideoMode(mode);
}
// 获取图像数据

View File

@@ -1,4 +1,4 @@
namespace JoyD.Windows.CS.Toprie
namespace JoyD.Windows.CS.Toprie
{
partial class Camera
{
@@ -15,12 +15,33 @@
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.imageBox = new System.Windows.Forms.PictureBox();
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
this.imageModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.thermalModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.visibleModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fusionMode1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fusionMode2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fusionMode3ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fusionMode4ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fusionMode5ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.colorModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.whiteHotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.blackHotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ironRedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.lavaToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.rainbowToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ironGrayToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.redHotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.rainbow2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
((System.ComponentModel.ISupportInitialize)(this.imageBox)).BeginInit();
this.contextMenuStrip1.SuspendLayout();
this.SuspendLayout();
//
// imageBox
//
this.imageBox.ContextMenuStrip = this.contextMenuStrip1;
this.imageBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.imageBox.Location = new System.Drawing.Point(0, 0);
this.imageBox.Name = "imageBox";
@@ -29,6 +50,110 @@
this.imageBox.TabIndex = 0;
this.imageBox.TabStop = false;
//
// contextMenuStrip1
//
this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.imageModeToolStripMenuItem,
this.colorModeToolStripMenuItem});
this.contextMenuStrip1.Name = "contextMenuStrip1";
this.contextMenuStrip1.Size = new System.Drawing.Size(161, 74);
//
// imageModeToolStripMenuItem
//
this.imageModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.thermalModeToolStripMenuItem,
this.visibleModeToolStripMenuItem});
this.imageModeToolStripMenuItem.Name = "imageModeToolStripMenuItem";
this.imageModeToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.imageModeToolStripMenuItem.Text = "图像模式";
//
// thermalModeToolStripMenuItem
//
this.thermalModeToolStripMenuItem.Name = "thermalModeToolStripMenuItem";
this.thermalModeToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.thermalModeToolStripMenuItem.Text = "红外模式";
this.thermalModeToolStripMenuItem.Click += new System.EventHandler(this.thermalModeToolStripMenuItem_Click);
//
// visibleModeToolStripMenuItem
//
this.visibleModeToolStripMenuItem.Name = "visibleModeToolStripMenuItem";
this.visibleModeToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.visibleModeToolStripMenuItem.Text = "自然模式";
this.visibleModeToolStripMenuItem.Click += new System.EventHandler(this.visibleModeToolStripMenuItem_Click);
//
// colorModeToolStripMenuItem
//
this.colorModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.whiteHotToolStripMenuItem,
this.blackHotToolStripMenuItem,
this.ironRedToolStripMenuItem,
this.lavaToolStripMenuItem,
this.rainbowToolStripMenuItem,
this.ironGrayToolStripMenuItem,
this.redHotToolStripMenuItem,
this.rainbow2ToolStripMenuItem});
this.colorModeToolStripMenuItem.Name = "colorModeToolStripMenuItem";
this.colorModeToolStripMenuItem.Size = new System.Drawing.Size(160, 22);
this.colorModeToolStripMenuItem.Text = "色彩模式";
//
// rainbowToolStripMenuItem
//
this.rainbowToolStripMenuItem.Name = "rainbowToolStripMenuItem";
this.rainbowToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.rainbowToolStripMenuItem.Text = "彩虹";
this.rainbowToolStripMenuItem.Click += new System.EventHandler(this.rainbowToolStripMenuItem_Click);
//
// ironRedToolStripMenuItem
//
this.ironRedToolStripMenuItem.Name = "ironRedToolStripMenuItem";
this.ironRedToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.ironRedToolStripMenuItem.Text = "铁红";
this.ironRedToolStripMenuItem.Click += new System.EventHandler(this.ironRedToolStripMenuItem_Click);
//
// lavaToolStripMenuItem
//
this.lavaToolStripMenuItem.Name = "lavaToolStripMenuItem";
this.lavaToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.lavaToolStripMenuItem.Text = "熔岩";
this.lavaToolStripMenuItem.Click += new System.EventHandler(this.lavaToolStripMenuItem_Click);
//
// ironGrayToolStripMenuItem
//
this.ironGrayToolStripMenuItem.Name = "ironGrayToolStripMenuItem";
this.ironGrayToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.ironGrayToolStripMenuItem.Text = "铁灰";
this.ironGrayToolStripMenuItem.Click += new System.EventHandler(this.ironGrayToolStripMenuItem_Click);
//
// redHotToolStripMenuItem
//
this.redHotToolStripMenuItem.Name = "redHotToolStripMenuItem";
this.redHotToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.redHotToolStripMenuItem.Text = "红热";
this.redHotToolStripMenuItem.Click += new System.EventHandler(this.redHotToolStripMenuItem_Click);
//
// rainbow2ToolStripMenuItem
//
this.rainbow2ToolStripMenuItem.Name = "rainbow2ToolStripMenuItem";
this.rainbow2ToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.rainbow2ToolStripMenuItem.Text = "彩虹2";
this.rainbow2ToolStripMenuItem.Click += new System.EventHandler(this.rainbow2ToolStripMenuItem_Click);
//
// whiteHotToolStripMenuItem
//
this.whiteHotToolStripMenuItem.Name = "whiteHotToolStripMenuItem";
this.whiteHotToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.whiteHotToolStripMenuItem.Text = "白热";
this.whiteHotToolStripMenuItem.Click += new System.EventHandler(this.whiteHotToolStripMenuItem_Click);
//
// blackHotToolStripMenuItem
//
this.blackHotToolStripMenuItem.Name = "blackHotToolStripMenuItem";
this.blackHotToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.blackHotToolStripMenuItem.Text = "黑热";
this.blackHotToolStripMenuItem.Click += new System.EventHandler(this.blackHotToolStripMenuItem_Click);
//
// 已移除蓝红菜单项不在SDK的8种标准色板中
//
// Camera
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
@@ -38,6 +163,7 @@
this.Size = new System.Drawing.Size(512, 384);
this.Load += new System.EventHandler(this.Camera_Load);
((System.ComponentModel.ISupportInitialize)(this.imageBox)).EndInit();
this.contextMenuStrip1.ResumeLayout(false);
this.ResumeLayout(false);
}
@@ -45,5 +171,23 @@
#endregion
private System.Windows.Forms.PictureBox imageBox;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
private System.Windows.Forms.ToolStripMenuItem colorModeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem whiteHotToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem blackHotToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ironRedToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem lavaToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem rainbowToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ironGrayToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem redHotToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem rainbow2ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem imageModeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem thermalModeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem visibleModeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem fusionMode1ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem fusionMode2ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem fusionMode3ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem fusionMode4ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem fusionMode5ToolStripMenuItem;
}
}

View File

@@ -19,15 +19,54 @@ namespace JoyD.Windows.CS.Toprie
// 是否正在接收图像
private bool _isReceivingImage = false;
// 项目路径,用于数据文件的存取
private string _projectPath = "";
/// <summary>
/// 获取或设置项目路径,控件所需的数据文件将在此目录中进行存取
/// </summary>
[Category("配置")]
[Description("设置项目路径,控件所需的数据文件将在此目录中进行存取")]
[DefaultValue("")]
public string ProjectPath
{
get { return _projectPath; }
set
{
// 只有当值发生变化时才进行同步
if (_projectPath != value)
{
_projectPath = value;
// 如果DeviceManager已经初始化则同步更新其ProjectPath属性
if (_deviceManager != null)
{
_deviceManager.ProjectPath = _projectPath;
}
}
}
}
// 显示错误的定时器
private System.Windows.Forms.Timer _errorDisplayTimer;
/// <summary>
/// 更新设计模式状态到DeviceManager
/// </summary>
private void UpdateDesignModeStatus()
{
DeviceManager.IsDesignMode = DesignMode;
Console.WriteLine($"相机控件设计模式状态已更新: {DesignMode}");
}
public Camera()
{
InitializeComponent();
// 为右键菜单添加Opening事件用于在菜单显示前更新色彩模式的选中状态
this.contextMenuStrip1.Opening += ContextMenuStrip1_Opening;
// 将设计模式状态传递给DeviceManager
DeviceManager.IsDesignMode = DesignMode;
UpdateDesignModeStatus();
// 只有在非设计模式下才初始化设备管理器和错误定时器
if (!DesignMode)
@@ -35,7 +74,13 @@ namespace JoyD.Windows.CS.Toprie
// 清空现有日志
try
{
string logFile = Path.Combine(Application.StartupPath, "camera.log");
string logFile = Path.Combine(Application.StartupPath, "log.txt");
// 确保日志文件目录存在
string logDir = Path.GetDirectoryName(logFile);
if (!Directory.Exists(logDir))
{
Directory.CreateDirectory(logDir);
}
if (File.Exists(logFile))
{
File.WriteAllText(logFile, string.Empty);
@@ -75,28 +120,28 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
private void InitializeDeviceManager()
{
_deviceManager = new DeviceManager
// 只有在非设计模式下才初始化设备管理器
if (!DesignMode)
{
AutoReconnectEnabled = true,
ReconnectInterval = 2000 // 2秒
};
_deviceManager = new DeviceManager
{
AutoReconnectEnabled = true,
ReconnectInterval = 2000, // 2秒
ProjectPath = !string.IsNullOrEmpty(ProjectPath) ? ProjectPath : Application.StartupPath
};
// 确保DeviceManager的设计模式状态与控件一致
DeviceManager.IsDesignMode = DesignMode;
// 设置静态属性
DeviceManager.MaxReconnectAttempts = 5;
// 设置静态属性
DeviceManager.MaxReconnectAttempts = 5;
// 注册图像接收事件
_deviceManager.ImageReceived += DeviceManager_ImageReceived;
// 注册图像接收事件
_deviceManager.ImageReceived += DeviceManager_ImageReceived;
// 注册连接状态变更事件
_deviceManager.ConnectionStatusChanged += DeviceManager_ConnectionStatusChanged;
// 注册连接状态变更事件
_deviceManager.ConnectionStatusChanged += DeviceManager_ConnectionStatusChanged;
// 注册连接异常事件
_deviceManager.ConnectionException += DeviceManager_ConnectionException;
// 注册连接异常事件
_deviceManager.ConnectionException += DeviceManager_ConnectionException;
}
}
/// <summary>
@@ -113,6 +158,7 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
public void StartCamera()
{
if (DesignMode) return;
try
{
// 只有在没有接收图像时才启动
@@ -128,16 +174,6 @@ namespace JoyD.Windows.CS.Toprie
{
// 设置为热图模式
bool modeSet = false;
try
{
_deviceManager.SetImageMode(ImageMode.Thermal);
modeSet = true;
Console.WriteLine("已设置热图模式");
}
catch (Exception ex)
{
Console.WriteLine($"设置热图模式失败: {ex.Message}");
}
// 启用自动重连
_deviceManager.AutoReconnectEnabled = true;
@@ -152,7 +188,7 @@ namespace JoyD.Windows.CS.Toprie
{
try
{
_deviceManager.SetImageMode(ImageMode.Thermal);
_deviceManager.SetImageMode(ImageMode.Infrared);
Console.WriteLine("连接后已设置热图模式");
}
catch (Exception ex)
@@ -209,6 +245,7 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
private void StartReceiveImage()
{
if (DesignMode) return;
try
{
if (!_isReceivingImage && _deviceManager.ConnectionStatus == ConnectionStatus.Connected)
@@ -230,6 +267,7 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
public void StopCamera()
{
if (DesignMode) return;
try
{
if (_isReceivingImage)
@@ -249,6 +287,7 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
private void DeviceManager_ImageReceived(object sender, ImageReceivedEventArgs e)
{
if (DesignMode) return;
Image image = null;
try
{
@@ -414,6 +453,7 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
private void UpdateImageOnUI(Image image)
{
if (DesignMode) return;
// 线程安全检查 - 确保在UI线程上执行
if (this.InvokeRequired)
{
@@ -571,27 +611,74 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
private void DeviceManager_ConnectionStatusChanged(object sender, ConnectionStatusChangedEventArgs e)
{
// 确保在UI线程上更新并且检查控件是否已被释放
if (!this.IsDisposed && !this.Disposing)
{
if (DesignMode) return;
// 参数验证
if (e == null)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 警告: 接收到空的连接状态变更事件参数");
return;
}
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 接收连接状态变更事件: {e.Status}");
// 捕获所有可能的异常,确保事件处理器不会崩溃
try
{
// 首先检查控件状态
if (this.IsDisposed || this.Disposing)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 控件已释放或正在释放跳过UI更新");
return;
}
// 检查事件参数中的状态信息
string statusMessage = $"连接状态变更为: {e.Status}";
if (!string.IsNullOrEmpty(e.DeviceInfo))
{
statusMessage += $" (设备信息: {e.DeviceInfo})";
}
// 线程安全处理 - 确保在UI线程上更新
if (this.InvokeRequired)
{
{
try
{
{
// 使用BeginInvoke代替Invoke避免可能的死锁问题
this.BeginInvoke(new Action(() => HandleConnectionStatusChanged(e)));
this.BeginInvoke(new Action<ConnectionStatusChangedEventArgs>(args =>
{
// 再次检查控件状态,防止在异步调用期间控件被释放
if (!this.IsDisposed && !this.Disposing)
{
HandleConnectionStatusChanged(args);
}
else
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 异步调用期间控件已释放,跳过处理");
}
}), e);
}
catch (ObjectDisposedException)
{
catch (ObjectDisposedException ode)
{
// 捕获控件已释放异常,避免程序崩溃
Console.WriteLine("控件已释放跳过UI更新");
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 控件已释放无法进行UI线程调用: {ode.Message}");
}
catch (InvalidOperationException ioe)
{
// 捕获无效操作异常,通常发生在控件状态异常时
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] UI线程调用无效: {ioe.Message}");
}
}
else
{
{
// 直接在UI线程上处理
HandleConnectionStatusChanged(e);
}
}
catch (Exception ex)
{
// 捕获所有其他异常,记录并继续
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 处理连接状态变更事件异常: {ex.Message}\n{ex.StackTrace}");
}
}
/// <summary>
@@ -599,6 +686,7 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
private void HandleConnectionStatusChanged(ConnectionStatusChangedEventArgs e)
{
if (DesignMode) return;
try
{
// 确保在UI线程上更新UI状态
@@ -626,20 +714,30 @@ namespace JoyD.Windows.CS.Toprie
// 清除错误信息
ShowError(string.Empty);
// 确保设置为热图模式
try
{
_deviceManager.SetImageMode(ImageMode.Thermal);
Console.WriteLine("连接成功后确认热图模式");
// 仅在首次连接时设置为热图模式,重连时保留之前的模式
if (!_isReceivingImage) // 首次连接时_isReceivingImage为false
{
try
{
_deviceManager.SetImageMode(ImageMode.Infrared);
Console.WriteLine("首次连接,设置热图模式");
}
catch (Exception ex)
{
Console.WriteLine($"设置热图模式失败: {ex.Message}");
}
}
catch (Exception ex)
{
Console.WriteLine($"连接成功后设置热图模式失败: {ex.Message}");
else
{
Console.WriteLine("重连成功,保留当前图像模式");
}
// 注意色彩模式同步现在在DeviceManager内部的连接成功处理中自动完成
// 无需在此处重复调用
// 开始接收图像包含在try-catch中
if (!_isReceivingImage)
{
{
StartReceiveImage();
}
break;
@@ -696,6 +794,7 @@ namespace JoyD.Windows.CS.Toprie
/// <param name="isConnected">是否已连接</param>
private void UpdateUIState(bool isConnected)
{
if (DesignMode) return;
try
{
// 根据连接状态更新图像框状态
@@ -739,36 +838,94 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
private void DeviceManager_ConnectionException(object sender, ConnectionExceptionEventArgs e)
{
// 确保在UI线程上更新并且检查控件是否已被释放
if (!this.IsDisposed && !this.Disposing)
{
if (DesignMode) return;
// 参数验证
if (e == null)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 警告: 接收到空的连接异常事件参数");
return;
}
// 记录详细的异常信息但避免在UI线程上执行耗时操作
string exceptionMessage = e.Exception != null ? e.Exception.Message : "无详细异常信息";
string stackTrace = e.Exception != null ? e.Exception.StackTrace : "无堆栈信息";
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 接收连接异常事件: {e.Message}\n{exceptionMessage}\n{stackTrace}");
// 捕获所有可能的异常,确保异常处理不会导致程序崩溃
try
{
// 首先检查控件状态
if (this.IsDisposed || this.Disposing)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 控件已释放或正在释放,跳过异常显示");
return;
}
// 创建用于UI显示的错误消息
string uiErrorMessage = $"连接异常: {e.Message}";
if (string.IsNullOrEmpty(e.Message) && e.Exception != null)
{
uiErrorMessage = $"连接异常: {exceptionMessage}";
}
// 线程安全处理 - 确保在UI线程上更新
if (this.InvokeRequired)
{
{
try
{
// 使用BeginInvoke在UI线程上显示错误
this.BeginInvoke(new Action(() =>
{
// 记录异常日志
Console.WriteLine($"连接异常发生于 {DateTime.Now}: {e.Message}\n{e.Exception.Message}\n{e.Exception.StackTrace}");
ShowError($"连接异常: {e.Message}");
{
// 创建局部变量保存错误消息,避免闭包问题
string errorMsg = uiErrorMessage;
// 使用BeginInvoke代替Invoke避免可能的死锁问题
this.BeginInvoke(new Action(() =>
{
// 再次检查控件状态,防止在异步调用期间控件被释放
if (!this.IsDisposed && !this.Disposing)
{
try
{
ShowError(errorMsg);
}
catch (Exception showEx)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 显示错误消息异常: {showEx.Message}");
}
}
else
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 异步调用期间控件已释放,跳过错误显示");
}
}));
}
catch (ObjectDisposedException)
{
catch (ObjectDisposedException ode)
{
// 捕获控件已释放异常,避免程序崩溃
Console.WriteLine("控件已释放,跳过异常处理");
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 控件已释放无法进行UI线程调用: {ode.Message}");
}
catch (InvalidOperationException ioe)
{
// 捕获无效操作异常,通常发生在控件状态异常时
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] UI线程调用无效: {ioe.Message}");
}
}
else
{
// 记录异常日志
Console.WriteLine($"连接异常发生于 {DateTime.Now}: {e.Message}\n{e.Exception.Message}\n{e.Exception.StackTrace}");
ShowError($"连接异常: {e.Message}");
{
// 直接在UI线程上处理
try
{
ShowError(uiErrorMessage);
}
catch (Exception showEx)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 显示错误消息异常: {showEx.Message}");
}
}
}
catch (Exception ex)
{
// 捕获所有其他异常,记录并继续
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 处理连接异常事件异常: {ex.Message}\n{ex.StackTrace}");
}
}
/// <summary>
@@ -776,6 +933,7 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
private void ShowError(string message)
{
if (DesignMode) return;
Console.WriteLine(message);
// 可以根据需要添加UI上的错误显示
@@ -820,6 +978,7 @@ namespace JoyD.Windows.CS.Toprie
/// </summary>
private void ErrorDisplayTimer_Tick(object sender, EventArgs e)
{
if (DesignMode) return;
_errorDisplayTimer.Stop();
// 清除错误显示,恢复到等待图像状态
@@ -853,6 +1012,291 @@ namespace JoyD.Windows.CS.Toprie
}
}
/// <summary>
/// 右键菜单显示前的事件处理方法
/// 用于更新色彩模式菜单项的选中状态
/// </summary>
private void ContextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
if (DesignMode) return;
try
{
// 根据当前图像模式控制色彩模式菜单的可见性
if (_deviceManager != null)
{
colorModeToolStripMenuItem.Visible = _deviceManager.CurrentImageMode == ImageMode.Infrared;
}
// 清除视频模式菜单项的选中状态
thermalModeToolStripMenuItem.Checked = false;
visibleModeToolStripMenuItem.Checked = false;
fusionMode1ToolStripMenuItem.Checked = false;
fusionMode2ToolStripMenuItem.Checked = false;
fusionMode3ToolStripMenuItem.Checked = false;
fusionMode4ToolStripMenuItem.Checked = false;
fusionMode5ToolStripMenuItem.Checked = false;
// 清除色彩模式菜单项的选中状态
whiteHotToolStripMenuItem.Checked = false;
blackHotToolStripMenuItem.Checked = false;
ironRedToolStripMenuItem.Checked = false;
lavaToolStripMenuItem.Checked = false;
rainbowToolStripMenuItem.Checked = false;
ironGrayToolStripMenuItem.Checked = false;
redHotToolStripMenuItem.Checked = false;
rainbow2ToolStripMenuItem.Checked = false;
// 尝试获取当前色彩模式并更新对应菜单项的选中状态
if (_deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Connected)
{
try
{
// 获取当前色彩模式
PaletteType currentPalette = _deviceManager.CurrentPaletteType;
// 根据当前色彩模式设置对应菜单项的选中状态
switch (currentPalette)
{
case PaletteType.WhiteHot:
whiteHotToolStripMenuItem.Checked = true;
break;
case PaletteType.BlackHot:
blackHotToolStripMenuItem.Checked = true;
break;
case PaletteType.IronRed:
ironRedToolStripMenuItem.Checked = true;
break;
case PaletteType.Lava:
lavaToolStripMenuItem.Checked = true;
break;
case PaletteType.Rainbow:
rainbowToolStripMenuItem.Checked = true;
break;
case PaletteType.IronGray:
ironGrayToolStripMenuItem.Checked = true;
break;
case PaletteType.RedHot:
redHotToolStripMenuItem.Checked = true;
break;
case PaletteType.Rainbow2:
rainbow2ToolStripMenuItem.Checked = true;
break;
}
}
catch (Exception ex)
{
Console.WriteLine("获取当前色彩模式失败: " + ex.Message);
}
// 更新视频模式菜单项的选中状态
try
{
// 更改为使用ImageMode枚举
var currentImageMode = _deviceManager.CurrentImageMode;
thermalModeToolStripMenuItem.Checked = currentImageMode == ImageMode.Infrared;
visibleModeToolStripMenuItem.Checked = currentImageMode == ImageMode.Natural;
}
catch (Exception ex)
{
Console.WriteLine("获取当前图像模式失败: " + ex.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine("更新右键菜单选中状态失败: " + ex.Message);
}
}
#region
/// <summary>
/// 白热色彩模式
/// </summary>
private void whiteHotToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (_deviceManager != null)
{
Console.WriteLine("切换到白热色彩模式");
_deviceManager.SetPaletteType(PaletteType.WhiteHot);
}
}
catch (Exception ex)
{
Console.WriteLine($"切换到白热色彩模式失败: {ex.Message}");
}
}
/// <summary>
/// 黑热色彩模式
/// </summary>
private void blackHotToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (_deviceManager != null)
{
Console.WriteLine("切换到黑热色彩模式");
_deviceManager.SetPaletteType(PaletteType.BlackHot);
}
}
catch (Exception ex)
{
Console.WriteLine($"切换到黑热色彩模式失败: {ex.Message}");
}
}
/// <summary>
/// 铁红色彩模式
/// </summary>
private void ironRedToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (_deviceManager != null)
{
Console.WriteLine("切换到铁红色彩模式");
_deviceManager.SetPaletteType(PaletteType.IronRed);
}
}
catch (Exception ex)
{
Console.WriteLine($"切换到铁红色彩模式失败: {ex.Message}");
}
}
/// <summary>
/// 熔岩色彩模式
/// </summary>
private void lavaToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (_deviceManager != null)
{
Console.WriteLine("切换到熔岩色彩模式");
_deviceManager.SetPaletteType(PaletteType.Lava);
}
}
catch (Exception ex)
{
Console.WriteLine($"切换到熔岩色彩模式失败: {ex.Message}");
}
}
/// <summary>
/// 彩虹色彩模式
/// </summary>
private void rainbowToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (_deviceManager != null)
{
Console.WriteLine("切换到彩虹色彩模式");
_deviceManager.SetPaletteType(PaletteType.Rainbow);
}
}
catch (Exception ex)
{
Console.WriteLine($"切换到彩虹色彩模式失败: {ex.Message}");
}
}
/// <summary>
/// 铁灰色彩模式
/// </summary>
private void ironGrayToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (_deviceManager != null)
{
Console.WriteLine("切换到铁灰色彩模式");
_deviceManager.SetPaletteType(PaletteType.IronGray);
}
}
catch (Exception ex)
{
Console.WriteLine($"切换到铁灰色彩模式失败: {ex.Message}");
}
}
/// <summary>
/// 红热色彩模式
/// </summary>
private void redHotToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (_deviceManager != null)
{
Console.WriteLine("切换到红热色彩模式");
_deviceManager.SetPaletteType(PaletteType.RedHot);
}
}
catch (Exception ex)
{
Console.WriteLine($"切换到红热色彩模式失败: {ex.Message}");
}
}
/// <summary>
/// 彩虹2色彩模式
/// </summary>
private void rainbow2ToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (_deviceManager != null)
{
Console.WriteLine("切换到彩虹2色彩模式");
_deviceManager.SetPaletteType(PaletteType.Rainbow2);
}
}
catch (Exception ex)
{
Console.WriteLine($"切换到彩虹2色彩模式失败: {ex.Message}");
}
}
#endregion
#region
/// <summary>
/// 红外模式
/// </summary>
private void thermalModeToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
_deviceManager.SetImageMode(ImageMode.Infrared);
}
catch (Exception ex)
{
Console.WriteLine("切换到红外模式失败: " + ex.Message);
ShowError("切换到红外模式失败");
}
}
private void visibleModeToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
_deviceManager.SetImageMode(ImageMode.Natural);
}
catch (Exception ex)
{
Console.WriteLine("切换到自然模式失败: " + ex.Message);
ShowError("切换到自然模式失败");
}
}
#endregion
/// <summary>
/// 清理资源
/// </summary>

File diff suppressed because it is too large Load Diff

View File

@@ -73,6 +73,7 @@
<Compile Include="DeviceManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SharedStructures.cs" />
<Compile Include="UdpCommunicationManager.cs" />
<Compile Include="V8.cs" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,504 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace JoyD.Windows.CS.Toprie
{
/// <summary>
/// UDP通信管理器负责管理所有UDP通信实现请求-响应匹配和状态管理
/// 防止异步数据相互影响
/// </summary>
public class UdpCommunicationManager : IDisposable
{
// 单例实例
private static readonly UdpCommunicationManager _instance = new UdpCommunicationManager();
// 命令队列
private readonly Queue<UdpRequest> _requestQueue = new Queue<UdpRequest>();
// 请求映射表,用于匹配请求和响应
private readonly Dictionary<string, UdpRequest> _activeRequests = new Dictionary<string, UdpRequest>();
// 锁对象,保护共享资源
private readonly object _queueLock = new object();
private readonly object _requestLock = new object();
// UDP客户端实例
private UdpClient _udpClient;
// 工作线程
private Thread _workerThread;
private bool _isRunning;
// 信号量,用于通知工作线程有新请求
private AutoResetEvent _requestAvailable = new AutoResetEvent(false);
// 默认端口
private const int DEFAULT_UDP_PORT = 18890;
// 请求超时时间(毫秒)
private const int DEFAULT_TIMEOUT = 500;
/// <summary>
/// 获取UdpCommunicationManager的单例实例
/// </summary>
public static UdpCommunicationManager Instance => _instance;
/// <summary>
/// 私有构造函数初始化UDP通信管理器
/// </summary>
private UdpCommunicationManager()
{
Initialize();
}
/// <summary>
/// 初始化UDP通信管理器
/// </summary>
private void Initialize()
{
try
{
// 创建UDP客户端
_udpClient = new UdpClient();
_udpClient.Client.ReceiveTimeout = DEFAULT_TIMEOUT;
// 启动工作线程
_isRunning = true;
_workerThread = new Thread(ProcessRequests);
_workerThread.IsBackground = true;
_workerThread.Start();
Console.WriteLine("UDP通信管理器已初始化");
}
catch (Exception ex)
{
Console.WriteLine($"UDP通信管理器初始化失败: {ex.Message}");
}
}
/// <summary>
/// 处理请求队列的工作线程函数
/// </summary>
private void ProcessRequests()
{
while (_isRunning)
{
// 等待请求可用信号
_requestAvailable.WaitOne();
if (!_isRunning) break;
UdpRequest request = null;
// 从队列中获取请求
lock (_queueLock)
{
if (_requestQueue.Count > 0)
{
request = _requestQueue.Dequeue();
}
}
if (request != null)
{
// 处理请求
ProcessRequest(request);
}
}
}
/// <summary>
/// 处理单个UDP请求
/// </summary>
/// <param name="request">UDP请求对象</param>
private void ProcessRequest(UdpRequest request)
{
try
{
Console.WriteLine($"处理UDP请求: {request.RequestId}, 目标: {request.TargetIp}:{request.Port}");
// 将请求添加到活动请求列表
lock (_requestLock)
{
_activeRequests[request.RequestId] = request;
}
// 发送UDP数据报
_udpClient.Send(request.Data, request.Data.Length, request.TargetIp, request.Port);
Console.WriteLine($"UDP命令已发送: {Encoding.ASCII.GetString(request.Data)}");
// 启动异步接收响应
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
IAsyncResult ar = _udpClient.BeginReceive(result =>
{
try
{
byte[] responseData = _udpClient.EndReceive(result, ref remoteEndPoint);
HandleResponse(request.RequestId, responseData);
}
catch (Exception ex)
{
Console.WriteLine($"接收UDP响应异常: {ex.Message}");
HandleTimeout(request.RequestId);
}
}, null);
// 设置超时处理(兼容.NET Framework 4.0
ThreadPool.QueueUserWorkItem(state =>
{
string requestId = (string)state;
Thread.Sleep(request.Timeout);
HandleTimeout(requestId);
}, request.RequestId);
}
catch (Exception ex)
{
Console.WriteLine($"处理UDP请求异常: {ex.Message}");
request.OnResponse?.Invoke(null, true); // 通知请求失败
// 从活动请求列表中移除
lock (_requestLock)
{
_activeRequests.Remove(request.RequestId);
}
}
}
/// <summary>
/// 处理接收到的UDP响应
/// </summary>
/// <param name="requestId">请求ID</param>
/// <param name="responseData">响应数据</param>
private void HandleResponse(string requestId, byte[] responseData)
{
UdpRequest request = null;
// 查找对应的请求
lock (_requestLock)
{
if (_activeRequests.TryGetValue(requestId, out request))
{
_activeRequests.Remove(requestId);
}
}
if (request != null)
{
Console.WriteLine($"收到UDP响应: {requestId}, 长度: {responseData?.Length ?? 0}");
request.OnResponse?.Invoke(responseData, false); // 通知请求成功
}
}
/// <summary>
/// 处理请求超时
/// </summary>
/// <param name="requestId">请求ID</param>
private void HandleTimeout(string requestId)
{
UdpRequest request = null;
// 查找对应的请求
lock (_requestLock)
{
if (_activeRequests.TryGetValue(requestId, out request))
{
_activeRequests.Remove(requestId);
}
}
if (request != null)
{
Console.WriteLine($"UDP请求超时: {requestId}");
request.OnResponse?.Invoke(null, true); // 通知请求超时
}
}
/// <summary>
/// 发送UDP请求
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="data">要发送的数据</param>
/// <param name="port">目标端口默认为18890</param>
/// <param name="timeout">超时时间毫秒默认为500</param>
/// <returns>包含响应数据的Task</returns>
public Task<byte[]> SendRequestAsync(string targetIp, byte[] data, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT)
{
var tcs = new TaskCompletionSource<byte[]>();
string requestId = Guid.NewGuid().ToString();
var request = new UdpRequest
{
RequestId = requestId,
TargetIp = targetIp,
Port = port,
Data = data,
Timeout = timeout,
OnResponse = (response, isTimeout) =>
{
if (isTimeout)
{
tcs.TrySetResult(null);
}
else
{
tcs.TrySetResult(response);
}
}
};
// 将请求添加到队列
lock (_queueLock)
{
_requestQueue.Enqueue(request);
}
// 通知工作线程有新请求
_requestAvailable.Set();
return tcs.Task;
}
/// <summary>
/// UDP请求结果枚举
/// </summary>
public enum RequestResult
{
Success,
Timeout,
NetworkError,
ProcessingError,
InvalidResponse
}
/// <summary>
/// 使用独立UDP客户端发送请求并接收响应即用即销毁模式
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="data">要发送的数据</param>
/// <param name="port">目标端口</param>
/// <param name="timeout">超时时间(毫秒)</param>
/// <returns>响应数据如果超时或出错则返回null</returns>
private byte[] SendRequestWithDisposableUdp(string targetIp, byte[] data, int port, int timeout)
{
using (UdpClient udpClient = new UdpClient())
{
try
{
// 设置超时时间
udpClient.Client.ReceiveTimeout = timeout;
// 发送数据
IPEndPoint targetEndPoint = new IPEndPoint(IPAddress.Parse(targetIp), port);
udpClient.Send(data, data.Length, targetEndPoint);
// 接收响应
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] responseData = udpClient.Receive(ref remoteEndPoint);
return responseData;
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.TimedOut)
{
Console.WriteLine($"UDP请求执行超时({timeout}ms)");
}
else
{
Console.WriteLine($"UDP通信错误: {ex.Message}");
}
return null;
}
catch (Exception ex)
{
Console.WriteLine($"发送UDP请求时发生错误: {ex.Message}");
return null;
}
}
}
/// <summary>
/// 同步发送UDP请求
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="data">要发送的数据</param>
/// <param name="port">目标端口默认为18890</param>
/// <param name="timeout">超时时间毫秒默认为500</param>
/// <param name="response">返回的响应数据</param>
/// <returns>请求结果</returns>
public RequestResult SendRequest(string targetIp, byte[] data, out byte[] response, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT)
{
response = null;
try
{
// 使用新的即用即销毁UDP方法
response = SendRequestWithDisposableUdp(targetIp, data, port, timeout);
if (response != null)
{
return RequestResult.Success;
}
// 如果响应为null可能是超时或网络错误
// 在SendRequestWithDisposableUdp方法中已经记录了具体错误
return RequestResult.NetworkError;
}
catch (Exception ex)
{
Console.WriteLine($"处理UDP请求结果时发生错误: {ex.Message}");
return RequestResult.ProcessingError;
}
}
/// <summary>
/// 同步发送UDP请求兼容旧版本
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="data">要发送的数据</param>
/// <param name="port">目标端口默认为18890</param>
/// <param name="timeout">超时时间毫秒默认为500</param>
/// <returns>响应数据如果超时则返回null</returns>
public byte[] SendRequest(string targetIp, byte[] data, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT)
{
SendRequest(targetIp, data, out byte[] response, port, timeout);
return response;
}
/// <summary>
/// 异步发送字符串命令的UDP请求
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="command">要发送的命令字符串</param>
/// <param name="port">目标端口默认为18890</param>
/// <param name="timeout">超时时间毫秒默认为500</param>
/// <returns>包含响应字符串的Task</returns>
public Task<string> SendRequestAsync(string targetIp, string command, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT)
{
byte[] data = Encoding.ASCII.GetBytes(command);
Task<byte[]> dataTask = SendRequestAsync(targetIp, data, port, timeout);
// 创建一个新的Task用于返回字符串结果
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
dataTask.ContinueWith(task => {
if (task.IsFaulted)
{
tcs.SetException(task.Exception);
}
else if (task.IsCanceled)
{
tcs.SetCanceled();
}
else
{
byte[] result = task.Result;
string stringResult = result != null ? Encoding.ASCII.GetString(result) : null;
tcs.SetResult(stringResult);
}
});
return tcs.Task;
}
/// <summary>
/// 同步发送字符串命令的UDP请求
/// </summary>
/// <param name="targetIp">目标IP地址</param>
/// <param name="command">要发送的命令</param>
/// <param name="timeoutMs">超时时间(毫秒)</param>
/// <param name="response">返回的响应内容</param>
/// <returns>请求结果</returns>
public RequestResult SendRequest(string targetIp, string command, int timeoutMs, out string response)
{
response = null;
try
{
var task = SendRequestAsync(targetIp, command, DEFAULT_UDP_PORT, timeoutMs);
bool completed = task.Wait(timeoutMs + 50); // 额外添加50ms作为缓冲
if (completed)
{
response = task.Result;
if (response != null)
{
// 验证响应格式是否符合预期
if (response.Length > 0 && (response.StartsWith("+RET:") || response.Contains(":")))
{
return RequestResult.Success;
}
Console.WriteLine($"命令 '{command}' 收到无效响应: '{response}'");
return RequestResult.InvalidResponse;
}
return RequestResult.NetworkError;
}
Console.WriteLine($"命令 '{command}' 执行超时({timeoutMs}ms)");
return RequestResult.Timeout;
}
catch (TimeoutException)
{
Console.WriteLine($"命令 '{command}' 执行超时异常");
return RequestResult.Timeout;
}
catch (Exception ex)
{
Console.WriteLine($"发送命令 '{command}' 时发生错误: {ex.Message}");
return RequestResult.ProcessingError;
}
}
/// <summary>
/// 清理资源
/// </summary>
public void Dispose()
{
_isRunning = false;
_requestAvailable.Set(); // 唤醒工作线程以便它可以退出
if (_workerThread != null && _workerThread.IsAlive)
{
_workerThread.Join(1000); // 等待工作线程退出
}
if (_udpClient != null)
{
_udpClient.Close();
_udpClient = null;
}
if (_requestAvailable != null)
{
_requestAvailable.Dispose();
_requestAvailable = null;
}
// 清空请求队列和活动请求
lock (_queueLock)
{
_requestQueue.Clear();
}
lock (_requestLock)
{
_activeRequests.Clear();
}
Console.WriteLine("UDP通信管理器已释放");
}
/// <summary>
/// UDP请求类封装单个UDP请求的信息
/// </summary>
private class UdpRequest
{
public string RequestId { get; set; }
public string TargetIp { get; set; }
public int Port { get; set; }
public byte[] Data { get; set; }
public int Timeout { get; set; }
public Action<byte[], bool> OnResponse { get; set; }
}
}
}

File diff suppressed because it is too large Load Diff