diff --git a/README.md b/README.md
index f0e9b72..71b9659 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,15 @@
# JoyD
-久鼎代码库
\ No newline at end of file
+久鼎代码库
+### 修改流程:
+ 1. 暂停或恢复时,设置暂停状态,调用更新Info
+ 2. 断开或连接时,设置连接状态,调用更新Info
+ 3. Ping通状态变化时,修改Ping状态,调用更新Info
+ 4. 图像更新时, 保存LastImage, 调用更新到UI
+ ### 更新Info:
+ 1. 如果暂停,显示暂停信息,否则如果Ping不通或断开,显示重连信息,否则清空InfoImage
+ 2. 最后调用更新UI
+ ### 更新UI:
+ 1. 先将LastImage绘制到全局缓冲
+ 2. 再将InfoImage绘制到缓冲
+ 3. 最后一次性绘制到图像框的bitmap
\ No newline at end of file
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs
index 0e05154..49d6ec0 100644
--- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs
@@ -35,6 +35,8 @@ namespace JoyD.Windows.CS.Toprie
this.ironGrayToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.redHotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.rainbow2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+ this.pauseDetectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
((System.ComponentModel.ISupportInitialize)(this.imageBox)).BeginInit();
this.contextMenuStrip1.SuspendLayout();
this.SuspendLayout();
@@ -54,9 +56,11 @@ namespace JoyD.Windows.CS.Toprie
//
this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.imageModeToolStripMenuItem,
- this.colorModeToolStripMenuItem});
+ this.colorModeToolStripMenuItem,
+ this.toolStripSeparator1,
+ this.pauseDetectionToolStripMenuItem});
this.contextMenuStrip1.Name = "contextMenuStrip1";
- this.contextMenuStrip1.Size = new System.Drawing.Size(161, 74);
+ this.contextMenuStrip1.Size = new System.Drawing.Size(161, 100);
//
// imageModeToolStripMenuItem
//
@@ -72,14 +76,14 @@ namespace JoyD.Windows.CS.Toprie
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);
+ 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);
+ this.visibleModeToolStripMenuItem.Click += new System.EventHandler(this.VisibleModeToolStripMenuItem_Click);
//
// colorModeToolStripMenuItem
//
@@ -92,6 +96,18 @@ namespace JoyD.Windows.CS.Toprie
this.ironGrayToolStripMenuItem,
this.redHotToolStripMenuItem,
this.rainbow2ToolStripMenuItem});
+ //
+ // toolStripSeparator1
+ //
+ this.toolStripSeparator1.Name = "toolStripSeparator1";
+ this.toolStripSeparator1.Size = new System.Drawing.Size(157, 6);
+ //
+ // pauseDetectionToolStripMenuItem
+ //
+ this.pauseDetectionToolStripMenuItem.Name = "pauseDetectionToolStripMenuItem";
+ this.pauseDetectionToolStripMenuItem.Size = new System.Drawing.Size(160, 22);
+ this.pauseDetectionToolStripMenuItem.Text = "暂停检测";
+ this.pauseDetectionToolStripMenuItem.Click += new System.EventHandler(this.PauseDetectionToolStripMenuItem_Click);
this.colorModeToolStripMenuItem.Name = "colorModeToolStripMenuItem";
this.colorModeToolStripMenuItem.Size = new System.Drawing.Size(160, 22);
this.colorModeToolStripMenuItem.Text = "色彩模式";
@@ -101,56 +117,56 @@ namespace JoyD.Windows.CS.Toprie
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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ this.blackHotToolStripMenuItem.Click += new System.EventHandler(this.BlackHotToolStripMenuItem_Click);
//
// 已移除蓝红菜单项(不在SDK的8种标准色板中)
//
@@ -158,6 +174,7 @@ namespace JoyD.Windows.CS.Toprie
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ // 添加imageBox
this.Controls.Add(this.imageBox);
this.Name = "Camera";
this.Size = new System.Drawing.Size(512, 384);
@@ -172,6 +189,8 @@ namespace JoyD.Windows.CS.Toprie
private System.Windows.Forms.PictureBox imageBox;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
+ private System.Windows.Forms.ToolStripMenuItem pauseDetectionToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
private System.Windows.Forms.ToolStripMenuItem colorModeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem whiteHotToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem blackHotToolStripMenuItem;
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs
index cfe5736..aa822b0 100644
--- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
+using System.Threading.Tasks;
namespace JoyD.Windows.CS.Toprie
{
@@ -19,6 +20,26 @@ namespace JoyD.Windows.CS.Toprie
// 是否正在接收图像
private bool _isReceivingImage = false;
+ // 全局图像缓冲区bitmap,用于在其上绘制图像和mask信息
+ private Bitmap _imageBuffer = null;
+ private const int BUFFER_WIDTH = 512;
+ private const int BUFFER_HEIGHT = 384;
+
+ // 最后接收的图像
+ private Image _lastImage = null;
+
+ // 信息图像,用于显示额外信息
+ private Image _infoImage = null;
+
+ // 用于保护_lastImage的线程锁
+ private readonly object _lastImageLock = new object();
+
+ // 用于保护_infoImage的线程锁
+ private readonly object _infoImageLock = new object();
+
+ // 是否显示信息图像
+ private bool _isDisplayingInfo = false;
+
// 项目路径,用于数据文件的存取
private string _projectPath = "";
@@ -30,24 +51,24 @@ namespace JoyD.Windows.CS.Toprie
[DefaultValue("")]
public string ProjectPath
{
- get { return _projectPath; }
+ get { return _projectPath; }
set
{
// 只有当值发生变化时才进行同步
if (_projectPath != value)
- {
- _projectPath = value;
+ {
+ _projectPath = value;
// 如果DeviceManager已经初始化,则同步更新其ProjectPath属性
- if (_deviceManager != null)
- {
- _deviceManager.ProjectPath = _projectPath;
+ if (_deviceManager != null)
+ {
+ _deviceManager.ProjectPath = _projectPath;
}
- }
}
- }
+ }
+ }
// 显示错误的定时器
- private System.Windows.Forms.Timer _errorDisplayTimer;
+
///
/// 更新设计模式状态到DeviceManager
@@ -57,7 +78,177 @@ namespace JoyD.Windows.CS.Toprie
DeviceManager.IsDesignMode = DesignMode;
Console.WriteLine($"相机控件设计模式状态已更新: {DesignMode}");
}
+
+ ///
+ /// 更新InfoImage显示
+ /// 1. 如果暂停,显示暂停信息
+ /// 2. 否则如果Ping不通或断开,显示重连信息
+ /// 3. 否则清空InfoImage
+ /// 4. 最后调用更新UI
+ ///
+ private void UpdateInfo()
+ {
+ // 更新Ping状态到Info文本
+ Console.WriteLine($"Ping状态更新: {(IsDevicePingable ? "可Ping通" : "不可Ping通")}");
+ if (DesignMode) return;
+
+ try
+ {
+ lock (_infoImageLock)
+ {
+ // 检查连接状态
+ bool isDisconnected = _deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Disconnected;
+ bool isReconnecting = _deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Reconnecting;
+ bool isPaused = _isPaused; // 使用_isPaused标志判断暂停状态
+ bool isPingFailed = !IsDevicePingable;
+ // 根据用户要求的优先级显示信息:先检查暂停状态,然后再检查Ping状态和连接状态
+
+ using (Graphics g = Graphics.FromImage(_infoImage))
+ {
+ // 设置半透明背景
+ g.Clear(Color.FromArgb(128, Color.Transparent));
+ if (isPaused)
+ {
+ // 暂停状态 - 最高优先级
+ // 绘制暂停文本
+ string text = "暂停";
+ Color textColor = Color.Red;
+
+ using (Font font = new Font("Arial", 48, FontStyle.Bold))
+ using (SolidBrush textBrush = new SolidBrush(textColor))
+ {
+ StringFormat format = new StringFormat() { Alignment = StringAlignment.Center };
+
+ // 将主文本居中显示
+ g.DrawString(text, font, textBrush,
+ new RectangleF(0, BUFFER_HEIGHT / 3, BUFFER_WIDTH, BUFFER_HEIGHT / 3),
+ format);
+ }
+ }
+ else if (isPingFailed || isDisconnected || isReconnecting)
+ {
+ // 非暂停状态下,检查Ping状态和连接状态
+
+ // 确定显示的文本和颜色
+ string text = "";
+ Color textColor = Color.White;
+
+ if (isReconnecting)
+ {
+ text = "正在重连...";
+ textColor = Color.Yellow;
+ }
+ else if (isDisconnected)
+ {
+ text = "连接断开";
+ textColor = Color.Red;
+ }
+ else if (isPingFailed)
+ {
+ text = "网络断开";
+ textColor = Color.Red;
+ }
+
+ // 绘制文本
+ using (Font font = new Font("Arial", 48, FontStyle.Bold))
+ using (SolidBrush textBrush = new SolidBrush(textColor))
+ {
+ StringFormat format = new StringFormat() { Alignment = StringAlignment.Center };
+
+ // 将主文本居中显示
+ g.DrawString(text, font, textBrush,
+ new RectangleF(0, BUFFER_HEIGHT / 3, BUFFER_WIDTH, BUFFER_HEIGHT / 3),
+ format);
+ }
+ }
+ else g.Clear(Color.Transparent);
+ }
+ // 否则清空InfoImage(已在开头处理)
+
+ // 设置显示标志
+ _isDisplayingInfo = (isPaused || isDisconnected || isReconnecting);
+ }
+
+ // 调用更新UI
+ UpdateImageOnUI();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"更新Info显示时出错: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 暂停/恢复检测菜单项点击事件处理
+ /// 1、暂停或恢复时,设置暂停状态,调用更新Info
+ ///
+ private void PauseDetectionToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ if (DesignMode) return;
+
+ try
+ {
+ // 切换暂停状态
+ _isPaused = !_isPaused;
+
+ if (_isPaused)
+ {
+ // 设置暂停状态
+ pauseDetectionToolStripMenuItem.Text = "恢复检测";
+
+ // 暂停时停止图像接收并更新DeviceManager的暂停检测状态
+ if (_deviceManager != null)
+ {
+ _deviceManager.IsDetectionPaused = true;
+
+ if (_isReceivingImage)
+ {
+ _deviceManager.StopImageReceiving();
+ _isReceivingImage = false;
+ }
+ }
+
+ Console.WriteLine("检测已暂停");
+ }
+ else
+ {
+ // 设置恢复状态
+ pauseDetectionToolStripMenuItem.Text = "暂停检测";
+
+ // 恢复时更新DeviceManager的暂停检测状态并重新开始图像接收
+ if (_deviceManager != null)
+ {
+ _deviceManager.IsDetectionPaused = false;
+
+ if (_deviceManager.ConnectionStatus == ConnectionStatus.Connected)
+ {
+ _deviceManager.StopImageReceiving();
+ _deviceManager.StartImageReceiving();
+ _isReceivingImage = true;
+
+ // 恢复检测后,启动连接检查以确保连接正常
+ _deviceManager.StartConnectionCheck();
+ }
+ // 如果当前是断开状态但启用了自动重连,尝试启动重连
+ else if (_deviceManager.AutoReconnectEnabled)
+ {
+ _deviceManager.StartAutoReconnect();
+ }
+ }
+
+ Console.WriteLine("检测已恢复");
+ }
+
+ // 按照用户要求:暂停或恢复时,设置暂停状态,调用更新Info
+ UpdateInfo();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"处理暂停/恢复检测时出错: {ex.Message}");
+ }
+ }
+
public Camera()
{
InitializeComponent();
@@ -68,6 +259,12 @@ namespace JoyD.Windows.CS.Toprie
// 将设计模式状态传递给DeviceManager
UpdateDesignModeStatus();
+ // 初始化图像缓冲区
+ InitializeImageBuffer();
+
+ // 初始化Ping定时器
+ _pingTimer = new System.Threading.Timer(PingTimer_Tick, null, Timeout.Infinite, Timeout.Infinite);
+
// 只有在非设计模式下才初始化设备管理器和错误定时器
if (!DesignMode)
{
@@ -89,7 +286,6 @@ namespace JoyD.Windows.CS.Toprie
catch { }
InitializeDeviceManager();
- InitializeErrorTimer();
}
}
@@ -143,21 +339,75 @@ namespace JoyD.Windows.CS.Toprie
_deviceManager.ConnectionException += DeviceManager_ConnectionException;
}
}
-
+
///
- /// 初始化错误显示定时器
+ /// 初始化图像缓冲区和相关图像资源
///
- private void InitializeErrorTimer()
- {
- _errorDisplayTimer = new System.Windows.Forms.Timer { Interval = 3000 }; // 显示3秒后清除
- _errorDisplayTimer.Tick += ErrorDisplayTimer_Tick;
+ private void InitializeImageBuffer()
+ {
+ try
+ {
+ // 创建512*384大小的透明bitmap作为图像缓冲区
+ _imageBuffer = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT);
+ Console.WriteLine($"图像缓冲区已初始化: {BUFFER_WIDTH}x{BUFFER_HEIGHT}");
+
+ // 初始化缓冲区为黑色背景
+ using (Graphics g = Graphics.FromImage(_imageBuffer))
+ {
+ g.Clear(Color.Black);
+ }
+
+ // 初始化InfoImage为透明bitmap
+ lock (_infoImageLock)
+ {
+ _infoImage = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT);
+ using (Graphics g = Graphics.FromImage(_infoImage))
+ {
+ g.Clear(Color.Transparent);
+ }
+ Console.WriteLine("InfoImage已初始化为透明bitmap");
+ }
+
+ // 初始化图像框的bitmap为透明
+ if (imageBox != null && !imageBox.IsDisposed)
+ {
+ imageBox.Image = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT);
+ using (Graphics g = Graphics.FromImage(imageBox.Image))
+ {
+ g.Clear(Color.Transparent);
+ }
+ Console.WriteLine("图像框bitmap已初始化为透明");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"初始化图像资源失败: {ex.Message}");
+ // 发生异常时释放已创建的资源
+ if (_imageBuffer != null)
+ {
+ _imageBuffer.Dispose();
+ _imageBuffer = null;
+ }
+ lock (_infoImageLock)
+ {
+ if (_infoImage != null)
+ {
+ _infoImage.Dispose();
+ _infoImage = null;
+ }
+ }
+ }
}
+
+
///
/// 启动相机
///
public void StartCamera()
- {
+ {
+ // 启动设备Ping
+ StartDevicePing();
if (DesignMode) return;
try
{
@@ -266,7 +516,9 @@ namespace JoyD.Windows.CS.Toprie
/// 停止接收图像
///
public void StopCamera()
- {
+ {
+ // 停止设备Ping
+ StopDevicePing();
if (DesignMode) return;
try
{
@@ -282,327 +534,270 @@ namespace JoyD.Windows.CS.Toprie
}
}
+ private bool _isPaused = false; // 暂停状态标志
+
+ // Ping相关字段
+ private System.Threading.Timer _pingTimer;
+ private bool _isDevicePingable = false;
+ private const int _pingInterval = 500; // 0.5秒Ping一次
+
+ ///
+ /// 获取设备是否可Ping通
+ ///
+ public bool IsDevicePingable
+ {
+ get { return _isDevicePingable; }
+ private set
+ {
+ if (_isDevicePingable != value)
+ {
+ _isDevicePingable = value;
+ Console.WriteLine($"设备Ping状态变更: {(_isDevicePingable ? "可Ping通" : "不可Ping通")}");
+ // 状态变化时调用更新Info
+ UpdateInfo();
+ }
+ }
+ }
+
///
/// 设备管理器图像接收事件处理
///
private void DeviceManager_ImageReceived(object sender, ImageReceivedEventArgs e)
- {
+ {
if (DesignMode) return;
- 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))
+ using (Image newImage = System.Drawing.Image.FromStream(ms))
{
- // 创建一个全新的位图而不仅仅是克隆,确保数据完整性
- image = new Bitmap(tempImage);
-
// 立即验证新创建的图像是否有效
try
- {
+ {
// 访问Width和Height属性来验证图像是否有效
- int width = image.Width;
- int height = image.Height;
+ int width = newImage.Width;
+ int height = newImage.Height;
if (width <= 0 || height <= 0)
- {
+ {
Console.WriteLine("创建的图像尺寸无效");
- image.Dispose();
- image = null;
+ return;
+ }
+ if (_lastImage == null) _lastImage = new Bitmap(newImage);
+ else
+ {
+ using(Graphics g= Graphics.FromImage(_lastImage))
+ {
+ g.DrawImage(newImage,Point.Empty);
+ }
}
}
catch (Exception)
- {
+ {
Console.WriteLine("创建的图像无效");
- image.Dispose();
- image = null;
+ return;
}
- }
- }
- }
- }
- }
- 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)
- {
+
+ // 按照用户要求:调用更新到UI
+ // 只有当图像更新未暂停时才更新UI
+ if (!_isPaused)
+ {
+ this.BeginInvoke(new Action(() =>
+ {
try
- {
- imageForUI.Dispose();
+ {
+ UpdateImageOnUI();
}
- catch {}
- }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"更新UI图像失败: {ex.Message}");
+ }
+ }));
}
- }));
- }
- 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 {}
- }
+ {
+ Console.WriteLine($"处理接收到的图像时出错: {ex.Message}");
}
}
+
+
///
- /// 在UI线程上更新图像
+ /// 在UI线程上更新图像 - 新实现,按照用户要求:
+ /// 1. 先将LastImage绘制到全局缓冲
+ /// 2. 再将InfoImage绘制到缓冲
+ /// 3. 最后一次性绘制到图像框的bitmap
///
- private void UpdateImageOnUI(Image image)
- {
+ private void UpdateImageOnUI()
+ {
if (DesignMode) return;
+
// 线程安全检查 - 确保在UI线程上执行
if (this.InvokeRequired)
- {
+ {
try
- {
- this.BeginInvoke(new Action(UpdateImageOnUI), image);
+ {
+ this.BeginInvoke(new Action(UpdateImageOnUI));
}
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 {}
+
+ // 一次性控件有效性检查,避免重复检查
+ if (this.IsDisposed || imageBox == null || imageBox.IsDisposed)
+ {
+ Console.WriteLine("控件已释放,无法更新图像");
return;
}
- // 增强图像有效性检查
- bool isImageValid = false;
- Image safeImage = null;
+ Image lastImage = null;
+ Image infoImage = null;
+ Image oldImage = null;
+ Bitmap displayImage = 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);
+ {
+ // 检查图像缓冲区是否有效
+ if (_imageBuffer == null)
+ {
+ Console.WriteLine("图像缓冲区未初始化,尝试重新初始化");
+ InitializeImageBuffer();
+ if (_imageBuffer == null)
+ {
+ Console.WriteLine("重新初始化图像缓冲区失败");
+ return;
}
- 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;
+ 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 {}
+ // 获取当前的LastImage引用
+ lock (_lastImageLock)
+ {
+ if (_lastImage != null)
+ {
+ lastImage = (Image)_lastImage.Clone();
}
}
- catch (ArgumentException ex) when (ex.Message.Contains("参数无效"))
- {
- // 特别处理"参数无效"异常
- Console.WriteLine($"图像参数无效异常: {ex.Message}");
- try { safeImage.Dispose(); } catch {}
- try { image.Dispose(); } catch {}
+
+ // 获取当前的InfoImage引用
+ lock (_infoImageLock)
+ {
+ if (_infoImage != null)
+ {
+ infoImage = (Image)_infoImage.Clone();
+ }
+ }
+
+ // 合并锁定,减少锁的数量,提高性能
+ lock (_imageBuffer)
+ {
+ using (Graphics g = Graphics.FromImage(_imageBuffer))
+ {
+ // 清除缓冲区背景为黑色
+ g.Clear(Color.Black);
+
+ // 步骤1:先将LastImage绘制到全局缓冲
+ if (lastImage != null)
+ {
+ // 保持原始图像比例,居中显示
+ float scale = Math.Min((float)BUFFER_WIDTH / lastImage.Width, (float)BUFFER_HEIGHT / lastImage.Height);
+ int scaledWidth = (int)(lastImage.Width * scale);
+ int scaledHeight = (int)(lastImage.Height * scale);
+ int x = (BUFFER_WIDTH - scaledWidth) / 2;
+ int y = (BUFFER_HEIGHT - scaledHeight) / 2;
+
+ g.DrawImage(lastImage, x, y, scaledWidth, scaledHeight);
+ }
+
+ // 步骤2:再将InfoImage绘制到缓冲
+ if (infoImage != null)
+ {
+ g.DrawImage(infoImage, 0, 0);
+ }
+ else if (_isDisplayingInfo)
+ {
+ // 如果没有InfoImage但需要显示信息,则绘制默认信息
+ using (Font font = new Font("微软雅黑", 12, FontStyle.Bold))
+ using (Brush textBrush = new SolidBrush(Color.Red))
+ {
+ g.DrawString("测试信息", font, textBrush, 10, 10);
+ }
+ }
+ }
- // 尝试设置旧图像回来,如果可能的话
- if (oldImage != null)
- {
- try
- {
- imageBox.Image = oldImage;
- }
- catch
- {
- // 如果设置旧图像也失败,释放它
- try { oldImage.Dispose(); } catch {}
- }
+ // 在同一个锁内创建缓冲区的副本,避免重复锁定
+ displayImage = (Bitmap)_imageBuffer.Clone();
+ }
+
+ // 将全局缓冲一次性绘制到图像框的bitmap
+ imageBox.Image = displayImage;
+
+ if (lastImage != null)
+ {
+ Console.WriteLine($"图像更新成功: {lastImage.Width}x{lastImage.Height}");
+ }
+ }
+ catch (ArgumentException ex) when (ex.Message.Contains("参数无效"))
+ {
+ // 特别处理"参数无效"异常
+ Console.WriteLine($"图像参数无效异常: {ex.Message}");
+
+ // 尝试设置旧图像回来,不需要再次检查控件状态(已在方法开始处检查)
+ if (oldImage != null)
+ {
+ try
+ {
+ imageBox.Image = oldImage;
+ }
+ catch
+ {
+ // 如果设置旧图像也失败,释放它
+ DisposeImage(oldImage);
}
}
}
catch (Exception ex)
- {
+ {
Console.WriteLine($"更新图像UI异常: {ex.Message}");
- // 确保在任何异常情况下都释放所有图像资源
- try { safeImage.Dispose(); } catch {}
- try { image.Dispose(); } catch {}
+ }
+ finally
+ {
+ // 确保在任何情况下都释放资源
+ DisposeImage(lastImage);
+ DisposeImage(infoImage);
+ // 只有当旧图像不再被使用时才释放
+ if (oldImage != null && oldImage != imageBox.Image)
+ {
+ DisposeImage(oldImage);
+ }
+ }
+ }
+
+ ///
+ /// 安全释放图像资源的辅助方法
+ ///
+ private void DisposeImage(Image image)
+ {
+ if (image != null)
+ {
+ try { image.Dispose(); } catch { }
}
}
@@ -683,6 +878,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 处理连接状态变更
+ /// 2、断开或连接时,设置连接状态,调用更新Info
///
private void HandleConnectionStatusChanged(ConnectionStatusChangedEventArgs e)
{
@@ -711,9 +907,6 @@ namespace JoyD.Windows.CS.Toprie
case ConnectionStatus.Connected:
Console.WriteLine("设备已连接");
- // 清除错误信息
- ShowError(string.Empty);
-
// 仅在首次连接时设置为热图模式,重连时保留之前的模式
if (!_isReceivingImage) // 首次连接时_isReceivingImage为false
{
@@ -737,9 +930,19 @@ namespace JoyD.Windows.CS.Toprie
// 开始接收图像(包含在try-catch中)
if (!_isReceivingImage)
- {
- StartReceiveImage();
+ {
+ try
+ {
+ StartReceiveImage();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"开始接收图像失败: {ex.Message}");
+ }
}
+
+ // 设置连接状态后,调用更新Info
+ UpdateInfo();
break;
case ConnectionStatus.Disconnected:
Console.WriteLine("设备已断开连接");
@@ -759,6 +962,9 @@ namespace JoyD.Windows.CS.Toprie
}
}
+ // 设置连接状态后,调用更新Info
+ UpdateInfo();
+
if (!string.IsNullOrEmpty(e.DeviceInfo))
{
ShowError(e.DeviceInfo);
@@ -769,10 +975,16 @@ namespace JoyD.Windows.CS.Toprie
}
break;
case ConnectionStatus.Connecting:
- case ConnectionStatus.Reconnecting:
Console.WriteLine($"正在连接设备...{(!string.IsNullOrEmpty(e.DeviceInfo) ? " " + e.DeviceInfo : "")}");
ShowError(string.Empty); // 清除之前的错误信息
break;
+ case ConnectionStatus.Reconnecting:
+ Console.WriteLine($"正在重新连接设备...{(!string.IsNullOrEmpty(e.DeviceInfo) ? " " + e.DeviceInfo : "")}");
+ ShowError(string.Empty); // 清除之前的错误信息
+
+ // 设置重连状态后,调用更新Info
+ UpdateInfo();
+ break;
}
}
catch (Exception ex)
@@ -935,176 +1147,134 @@ namespace JoyD.Windows.CS.Toprie
{
if (DesignMode) return;
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();
+ // 错误消息仅写入日志即可,不需要在UI上显示
}
- ///
- /// 错误显示定时器事件
- ///
- private void ErrorDisplayTimer_Tick(object sender, EventArgs e)
- {
- if (DesignMode) return;
- _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);
- }
- }
///
/// 右键菜单显示前的事件处理方法
/// 用于更新色彩模式菜单项的选中状态
///
+ ///
+ /// 右键菜单打开事件处理
+ ///
private void ContextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
+ // 暂停菜单项的文本已经在点击事件中更新,这里无需再次更新
if (DesignMode) return;
try
{
- // 根据当前图像模式控制色彩模式菜单的可见性
- if (_deviceManager != null)
+ // 检查是否处于暂停状态
+ bool isPaused = pauseDetectionToolStripMenuItem.Text == "恢复检测";
+
+ // 检查设备是否已连接
+ bool isConnected = _deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Connected;
+
+ // 在暂停状态或未连接状态下,隐藏图像模式根菜单和色彩模式菜单
+ // 注意:根菜单隐藏后,其所有子菜单会自动隐藏,不需要单独设置
+ if (isPaused || !isConnected)
{
- 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);
- }
+ // 隐藏图像模式根菜单
+ if (imageModeToolStripMenuItem != null)
+ imageModeToolStripMenuItem.Visible = false;
- // 更新视频模式菜单项的选中状态
- try
+ // 隐藏色彩模式菜单
+ colorModeToolStripMenuItem.Visible = false;
+
+ // 当只有一个菜单项可见时,隐藏分隔符
+ toolStripSeparator1.Visible = false;
+ }
+ else
+ {
+ // 在非暂停状态且已连接状态下,显示图像模式根菜单
+ // 注意:根菜单显示后,其所有子菜单会自动显示,不需要单独设置
+ if (imageModeToolStripMenuItem != null)
+ imageModeToolStripMenuItem.Visible = true;
+
+ // 在非暂停状态且已连接状态下,显示分隔符
+ toolStripSeparator1.Visible = true;
+
+ // 根据当前图像模式控制色彩模式菜单的可见性
+ colorModeToolStripMenuItem.Visible = true;
+ // 清除视频模式菜单项的选中状态
+ thermalModeToolStripMenuItem.Checked = false;
+ visibleModeToolStripMenuItem.Checked = false;
+ fusionMode1ToolStripMenuItem.Checked = false;
+ fusionMode2ToolStripMenuItem.Checked = false;
+ fusionMode3ToolStripMenuItem.Checked = false;
+ fusionMode4ToolStripMenuItem.Checked = false;
+ fusionMode5ToolStripMenuItem.Checked = false;
+
+ // 清除色彩模式菜单项的选中状态
+ whiteHotToolStripMenuItem.Checked = false;
+ blackHotToolStripMenuItem.Checked = false;
+ ironRedToolStripMenuItem.Checked = false;
+ lavaToolStripMenuItem.Checked = false;
+ rainbowToolStripMenuItem.Checked = false;
+ ironGrayToolStripMenuItem.Checked = false;
+ redHotToolStripMenuItem.Checked = false;
+ rainbow2ToolStripMenuItem.Checked = false;
+
+ // 尝试获取当前色彩模式并更新对应菜单项的选中状态
+ if (_deviceManager != null && _deviceManager.ConnectionStatus == ConnectionStatus.Connected)
{
- // 更改为使用ImageMode枚举
- var currentImageMode = _deviceManager.CurrentImageMode;
- thermalModeToolStripMenuItem.Checked = currentImageMode == ImageMode.Infrared;
- visibleModeToolStripMenuItem.Checked = currentImageMode == ImageMode.Natural;
- }
- catch (Exception ex)
- {
- Console.WriteLine("获取当前图像模式失败: " + ex.Message);
+ 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);
+ Console.WriteLine($"更新右键菜单选中状态失败: {ex.Message}");
}
}
@@ -1113,7 +1283,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 白热色彩模式
///
- private void whiteHotToolStripMenuItem_Click(object sender, EventArgs e)
+ private void WhiteHotToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
@@ -1132,7 +1302,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 黑热色彩模式
///
- private void blackHotToolStripMenuItem_Click(object sender, EventArgs e)
+ private void BlackHotToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
@@ -1151,7 +1321,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 铁红色彩模式
///
- private void ironRedToolStripMenuItem_Click(object sender, EventArgs e)
+ private void IronRedToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
@@ -1170,7 +1340,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 熔岩色彩模式
///
- private void lavaToolStripMenuItem_Click(object sender, EventArgs e)
+ private void LavaToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
@@ -1189,7 +1359,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 彩虹色彩模式
///
- private void rainbowToolStripMenuItem_Click(object sender, EventArgs e)
+ private void RainbowToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
@@ -1208,7 +1378,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 铁灰色彩模式
///
- private void ironGrayToolStripMenuItem_Click(object sender, EventArgs e)
+ private void IronGrayToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
@@ -1227,7 +1397,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 红热色彩模式
///
- private void redHotToolStripMenuItem_Click(object sender, EventArgs e)
+ private void RedHotToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
@@ -1246,7 +1416,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 彩虹2色彩模式
///
- private void rainbow2ToolStripMenuItem_Click(object sender, EventArgs e)
+ private void Rainbow2ToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
@@ -1269,7 +1439,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 红外模式
///
- private void thermalModeToolStripMenuItem_Click(object sender, EventArgs e)
+ private void ThermalModeToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
@@ -1282,7 +1452,7 @@ namespace JoyD.Windows.CS.Toprie
}
}
- private void visibleModeToolStripMenuItem_Click(object sender, EventArgs e)
+ private void VisibleModeToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
@@ -1297,6 +1467,110 @@ namespace JoyD.Windows.CS.Toprie
#endregion
+ #region 设备Ping相关方法
+
+ ///
+ /// Ping定时器的回调方法
+ ///
+ /// 状态对象
+ private void PingTimer_Tick(object state)
+ {
+ if (_deviceManager != null && !string.IsNullOrEmpty(_deviceManager.IPAddress))
+ {
+ Task.Factory.StartNew(() =>
+ {
+ bool pingResult = PingDevice(_deviceManager.IPAddress);
+ try
+ {
+ // 在线程安全的方式下更新状态
+ if (this.InvokeRequired)
+ {
+ this.Invoke(new Action(UpdatePingState), pingResult);
+ }
+ else
+ {
+ UpdatePingState(pingResult);
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ // 控件可能已被释放,忽略此更新
+ }
+ });
+ }
+ }
+
+ ///
+ /// 执行Ping操作
+ ///
+ /// 要Ping的IP地址
+ /// 是否Ping通
+ private bool PingDevice(string ipAddress)
+ {
+ try
+ {
+ using (var ping = new System.Net.NetworkInformation.Ping())
+ {
+ var reply = ping.Send(ipAddress, 2000); // 2秒超时
+ return reply != null && reply.Status == System.Net.NetworkInformation.IPStatus.Success;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Ping设备失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 更新Ping状态
+ ///
+ /// 是否可Ping通
+ private void UpdatePingState(bool isPingable)
+ {
+ IsDevicePingable = isPingable;
+ }
+
+ ///
+ /// 开始设备Ping
+ ///
+ private void StartDevicePing()
+ {
+ try
+ {
+ if (_pingTimer != null)
+ {
+ _pingTimer.Change(0, _pingInterval); // 立即开始,然后按间隔执行
+ Console.WriteLine("设备Ping已启动");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"启动设备Ping失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 停止设备Ping
+ ///
+ private void StopDevicePing()
+ {
+ try
+ {
+ if (_pingTimer != null)
+ {
+ _pingTimer.Change(Timeout.Infinite, Timeout.Infinite);
+ Console.WriteLine("设备Ping已停止");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"停止设备Ping失败: {ex.Message}");
+ }
+ }
+
+ #endregion
+
///
/// 清理资源
///
@@ -1331,21 +1605,73 @@ namespace JoyD.Windows.CS.Toprie
_deviceManager = null;
}
- // 无论是否在设计模式下,都需要释放定时器
- if (_errorDisplayTimer != null)
- {
- _errorDisplayTimer.Stop();
- _errorDisplayTimer.Dispose();
- _errorDisplayTimer = null;
- }
+
// 无论是否在设计模式下,都需要释放图像资源
+
+ // 释放Ping定时器
+ if (_pingTimer != null)
+ {
+ _pingTimer.Dispose();
+ _pingTimer = null;
+ }
if (imageBox != null && !imageBox.IsDisposed && imageBox.Image != null)
- {
+ {
imageBox.Image.Dispose();
imageBox.Image = null;
}
+ // 释放图像缓冲区资源
+ if (_imageBuffer != null)
+ {
+ try
+ {
+ _imageBuffer.Dispose();
+ _imageBuffer = null;
+ Console.WriteLine("图像缓冲区资源已释放");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"清理ImageBuffer资源异常: {ex.Message}");
+ }
+ }
+
+ // 释放LastImage资源
+ lock (_lastImageLock)
+ {
+ if (_lastImage != null)
+ {
+ try
+ {
+ _lastImage.Dispose();
+ _lastImage = null;
+ Console.WriteLine("LastImage资源已释放");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"清理LastImage资源异常: {ex.Message}");
+ }
+ }
+ }
+
+ // 释放InfoImage资源
+ lock (_infoImageLock)
+ {
+ if (_infoImage != null)
+ {
+ try
+ {
+ _infoImage.Dispose();
+ _infoImage = null;
+ Console.WriteLine("InfoImage资源已释放");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"清理InfoImage资源异常: {ex.Message}");
+ }
+ }
+ }
+
// 释放组件资源
if (components != null)
{
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs
index 2f31099..830af27 100644
--- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs
@@ -153,6 +153,9 @@ namespace JoyD.Windows.CS.Toprie
// 设计模式标志,用于在设计模式下跳过实际的设备连接和初始化
public static bool IsDesignMode { get; set; } = true;
+ // 暂停检测标志,用于控制是否进行连接检测和重连操作
+ public bool IsDetectionPaused { get; set; } = false;
+
// 项目路径,用于数据文件的存取
private string _projectPath = "";
@@ -165,7 +168,7 @@ namespace JoyD.Windows.CS.Toprie
private List _deviceList = new List();
// 目标设备ID,用于指定ID连接
- private int _targetDeviceId = -1;
+ private readonly int _targetDeviceId = -1;
// 当前设备ID
private int _currentDeviceId = -1;
// 默认设备IP
@@ -177,7 +180,7 @@ namespace JoyD.Windows.CS.Toprie
// 是否已初始化
private bool _isInitialized = false;
// 是否已释放
- private bool _isDisposed = false;
+ private readonly bool _isDisposed = false;
// 图像模式
private ImageMode _currentImageMode = ImageMode.Infrared;
// 当前色彩模式
@@ -185,14 +188,12 @@ namespace JoyD.Windows.CS.Toprie
// 当前视频模式
private VideoMode _currentVideoMode = VideoMode.Infrared; // 默认红外模式
// 自动重连是否启用
- private bool _autoReconnectEnabled = true;
// 自动重连定时器
private System.Threading.Timer _reconnectTimer;
// 重连间隔(毫秒)
private int _reconnectInterval = 2000;
- private int _connectionCheckInterval = 5000;
// 重连尝试次数
- private int _reconnectAttempts = 0;
+
// 最大重连尝试次数
public static int MaxReconnectAttempts = 5;
// 连接检查定时器
@@ -211,22 +212,17 @@ namespace JoyD.Windows.CS.Toprie
private const int DataReceivedTimeout = 15000; // 15秒内未收到数据则认为连接可能断开
// TCP客户端
// 该变量已在文件上方定义,删除重复实现
- // 网络流
- private NetworkStream _imageNetworkStream;
- // 图像接收任务
- private Task _imageReceivingTask;
- // 取消令牌源
- private CancellationTokenSource _imageReceivingCts;
+
// 停止请求事件
private ManualResetEvent _stopRequested = new ManualResetEvent(false);
// 缓冲区
- private byte[] _imageBuffer = new byte[4096];
+ private readonly byte[] _imageBuffer = new byte[4096];
// 图像数据累积缓冲区
- private byte[] _imageDataAccumulator = new byte[0];
+ private readonly byte[] _imageDataAccumulator = new byte[0];
// 多部分请求边界
- private string _multipartBoundary = string.Empty;
+ private readonly string _multipartBoundary = string.Empty;
// 锁对象
- private object _lockObject = new object();
+ private readonly object _lockObject = new object();
// 是否启用自动重连
private bool _isAutoReconnectEnabled = true;
// 最大重连次数
@@ -234,7 +230,7 @@ namespace JoyD.Windows.CS.Toprie
// 是否已连接
private bool _isConnected = false;
// 连接超时设置
- private int _connectTimeout = 5000;
+ private readonly int _connectTimeout = 5000;
// 当前重连尝试次数
private int _currentReconnectAttempt = 0;
// 连接取消令牌源
@@ -384,7 +380,7 @@ namespace JoyD.Windows.CS.Toprie
// 保存状态变更相关信息供后续处理
ConnectionStatus finalNewStatus = newStatus;
- bool shouldReconnect = (newStatus == ConnectionStatus.Disconnected && _autoReconnectEnabled && oldStatus != ConnectionStatus.Connecting);
+ bool shouldReconnect = (newStatus == ConnectionStatus.Disconnected && _isAutoReconnectEnabled && oldStatus != ConnectionStatus.Connecting);
bool shouldReset = (newStatus == ConnectionStatus.Connected);
// 添加状态转换验证,避免不合理的状态切换
@@ -872,7 +868,7 @@ namespace JoyD.Windows.CS.Toprie
///
/// 启动连接状态检查
///
- private void StartConnectionCheck()
+ public void StartConnectionCheck()
{
lock (_lockObject) // 添加线程同步锁
{
@@ -885,6 +881,13 @@ namespace JoyD.Windows.CS.Toprie
return;
}
+ // 在暂停检测模式下,跳过连接检查
+ if (IsDetectionPaused)
+ {
+ Log("暂停检测模式下跳过连接检查");
+ return;
+ }
+
// 首先停止现有的连接检查,确保资源释放
StopConnectionCheck();
@@ -931,7 +934,7 @@ namespace JoyD.Windows.CS.Toprie
Log("重新初始化失败,确认连接已断开");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备未初始化,连接已断开");
// 启动自动重连
- if (_autoReconnectEnabled)
+ if (_isAutoReconnectEnabled)
{
StartAutoReconnect();
}
@@ -980,8 +983,8 @@ namespace JoyD.Windows.CS.Toprie
{
UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接检查异常", ex);
_isInitialized = false;
- // 启动自动重连
- if (_autoReconnectEnabled)
+ // 启动自动重连,只有在未暂停检测时才执行
+ if (_isAutoReconnectEnabled && !IsDetectionPaused)
{
StartAutoReconnect();
}
@@ -1033,9 +1036,11 @@ namespace JoyD.Windows.CS.Toprie
// 先检查对象状态,避免不必要的操作
lock (_lockObject)
{
- if (_isDisposed || _connectionStatus != ConnectionStatus.Connected)
+ if (_isDisposed || _connectionStatus != ConnectionStatus.Connected || IsDetectionPaused)
{
- Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 对象已释放或连接状态非Connected,跳过检查");
+ string reason = _isDisposed ? "对象已释放" :
+ (_connectionStatus != ConnectionStatus.Connected ? "连接状态非Connected" : "检测已暂停");
+ Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - {reason},跳过检查");
return;
}
}
@@ -1092,8 +1097,8 @@ namespace JoyD.Windows.CS.Toprie
UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接已断开:设备离线");
- // 断开连接后自动启动重连,但在锁外执行
- if (_autoReconnectEnabled)
+ // 断开连接后自动启动重连,但在锁外执行,只有在未暂停检测时才执行
+ if (_isAutoReconnectEnabled && !IsDetectionPaused)
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 连接断开,将启动自动重连");
// 在锁外启动自动重连,避免潜在的死锁
@@ -1120,6 +1125,13 @@ namespace JoyD.Windows.CS.Toprie
Log("设计模式下跳过实际的连接有效性检查,模拟连接有效");
return true;
}
+
+ // 在暂停检测模式下,跳过连接有效性检查,直接返回连接有效
+ if (IsDetectionPaused)
+ {
+ Log("暂停检测模式下跳过连接有效性检查,模拟连接有效");
+ return true;
+ }
// 注意:此方法被CheckConnectionWrapper调用,已经在线程安全的上下文中
// 不需要额外加锁,但需要确保SDK实例的访问是线程安全的
@@ -1598,7 +1610,6 @@ namespace JoyD.Windows.CS.Toprie
}
Log("开始使用HTTP方式接收图像数据");
- bool existed = false;
try
{
// 确保之前的连接已关闭
@@ -2448,29 +2459,52 @@ namespace JoyD.Windows.CS.Toprie
{
try
{
- // 确保设备已连接且SDK实例有效
- if (_connectionStatus == ConnectionStatus.Connected && _a8Sdk != null)
+ // 使用SDK操作锁保护对_a8Sdk的访问
+ lock (_sdkOperationLock)
{
- // 获取当前设备的色彩模式值
- int currentValue = _a8Sdk.GetColorPlate();
- Log($"从设备读取的色彩模式值: {currentValue}");
-
- // 尝试将读取到的值转换为PaletteType枚举并更新内部状态
- if (Enum.IsDefined(typeof(PaletteType), currentValue))
+ // 确保设备已连接且SDK实例有效
+ if (_connectionStatus == ConnectionStatus.Connected && _a8Sdk != null)
{
- PaletteType actualPalette = (PaletteType)currentValue;
- _currentPaletteType = actualPalette;
- Log($"已更新内部状态为设备实际值: {actualPalette}");
+ try
+ {
+ // 获取当前设备的色彩模式值
+ int currentValue = _a8Sdk.GetColorPlate();
+ Log($"从设备读取的色彩模式值: {currentValue}");
+
+ // 尝试将读取到的值转换为PaletteType枚举并更新内部状态
+ if (Enum.IsDefined(typeof(PaletteType), currentValue))
+ {
+ PaletteType actualPalette = (PaletteType)currentValue;
+ _currentPaletteType = actualPalette;
+ Log($"已更新内部状态为设备实际值: {actualPalette}");
+ }
+ else
+ {
+ Log($"警告:设备返回的色彩模式值 {currentValue} 不在枚举定义范围内,使用默认值");
+ }
+ }
+ catch (Exception ex)
+ {
+ Log($"从设备读取色彩模式时出错: {ex.Message}");
+ // 尝试重新创建SDK实例
+ Log("尝试重新创建SDK实例...");
+ try
+ {
+ _a8Sdk = new A8SDK(_deviceIp);
+ Log("SDK实例已重新创建");
+ }
+ catch (Exception recreateEx)
+ {
+ Log($"重新创建SDK实例失败: {recreateEx.Message}");
+ }
+ throw;
+ }
}
else
{
- Log($"警告:设备返回的色彩模式值 {currentValue} 不在枚举定义范围内,使用默认值");
+ Log($"同步色彩模式失败:设备未连接或SDK未初始化");
}
}
- else
- {
- Log($"同步色彩模式失败:设备未连接或SDK未初始化");
- }
}
catch (Exception ex)
{
@@ -2504,11 +2538,27 @@ namespace JoyD.Windows.CS.Toprie
// 重试间隔(毫秒)
const int retryDelayMs = 100;
+ // 确保SDK实例存在,如果不存在则尝试创建
+ if (_a8Sdk == null)
+ {
+ Log("SDK实例为空,尝试创建新实例...");
+ try
+ {
+ _a8Sdk = new A8SDK(_deviceIp);
+ Log("SDK实例创建成功");
+ }
+ catch (Exception ex)
+ {
+ Log($"创建SDK实例失败: {ex.Message}");
+ return false;
+ }
+ }
+
// 先获取原始值,只读取一次,避免嵌套调用
int originalValue = -1;
try
{
- if (_a8Sdk != null && _connectionStatus == ConnectionStatus.Connected)
+ if (_connectionStatus == ConnectionStatus.Connected)
{
originalValue = _a8Sdk.Color_plate;
Log($"成功读取当前色彩模式值: {originalValue}");
@@ -2517,6 +2567,17 @@ namespace JoyD.Windows.CS.Toprie
catch (Exception ex)
{
Log($"获取当前色彩模式值时出错: {ex.Message}");
+ // 尝试重新创建SDK实例
+ Log("尝试重新创建SDK实例...");
+ try
+ {
+ _a8Sdk = new A8SDK(_deviceIp);
+ Log("SDK实例已重新创建");
+ }
+ catch (Exception recreateEx)
+ {
+ Log($"重新创建SDK实例失败: {recreateEx.Message}");
+ }
// 即使获取失败,仍尝试设置新值
}
@@ -2534,8 +2595,24 @@ namespace JoyD.Windows.CS.Toprie
{
try
{
- // 检查对象状态和连接状态
- if (_a8Sdk == null || _connectionStatus != ConnectionStatus.Connected)
+ // 再次确保SDK实例存在
+ if (_a8Sdk == null)
+ {
+ Log("SDK实例为空,尝试重新创建...");
+ try
+ {
+ _a8Sdk = new A8SDK(_deviceIp);
+ Log("SDK实例重新创建成功");
+ }
+ catch (Exception ex)
+ {
+ Log($"重新创建SDK实例失败: {ex.Message}");
+ continue; // 继续下一次尝试
+ }
+ }
+
+ // 检查连接状态
+ if (_connectionStatus != ConnectionStatus.Connected)
{
Log($"色彩模式设置失败: {(attempt > 0 ? "重试中" : "")}SDK实例为空或设备未连接");
Thread.Sleep(retryDelayMs);
@@ -3195,7 +3272,6 @@ namespace JoyD.Windows.CS.Toprie
if (result)
{
_isConnected = true;
- _reconnectAttempts = 0;
// 启动心跳检测和连接检查
StartHeartbeat();
@@ -3383,7 +3459,7 @@ namespace JoyD.Windows.CS.Toprie
// 连接进行中标志(用于防止重连期间再次触发连接)
private volatile bool _isConnecting = false;
- private void StartAutoReconnect()
+ public void StartAutoReconnect()
{
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 开始执行");
@@ -3394,6 +3470,13 @@ namespace JoyD.Windows.CS.Toprie
return;
}
+ // 在暂停检测模式下,跳过重连启动
+ if (IsDetectionPaused)
+ {
+ Log("暂停检测模式下跳过重连启动");
+ return;
+ }
+
// 检查对象是否已释放
if (_isDisposed)
{
@@ -3599,6 +3682,13 @@ namespace JoyD.Windows.CS.Toprie
return;
}
+ // 在暂停检测模式下,跳过重连操作
+ if (IsDetectionPaused)
+ {
+ Log("暂停检测模式下跳过实际的重连操作");
+ return;
+ }
+
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 开始执行");
// 使用Interlocked.Exchange实现原子操作检查,防止重入
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/README.md b/Windows/CS/Framework4.0/Toprie/Toprie/README.md
new file mode 100644
index 0000000..44edfae
--- /dev/null
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/README.md
@@ -0,0 +1,18 @@
+# JoyD
+
+### InfoImage, ImageBuffer, 图像框的bitmap, LastImage
+1. 初始化时,都创建成512x384的透明bitmap
+2. 中途不进行Dispose和设置为null,只在上面进行绘制
+3. 仅当控件被Dispose时,才进行Dispose和设置为null
+### 修改流程:
+ 1. 暂停或恢复时,设置暂停状态,调用更新Info
+ 2. 断开或连接时,设置连接状态,调用更新Info
+ 3. Ping通状态变化时,修改Ping状态,调用更新Info
+ 4. 图像更新时, 保存LastImage, 调用更新到UI
+ ### 更新Info:
+ 1. 如果暂停,显示暂停信息,否则如果Ping不通或断开,显示重连信息,否则清空InfoImage
+ 2. 最后调用更新UI
+ ### 更新UI:
+ 1. 先将LastImage绘制到全局缓冲
+ 2. 再将InfoImage绘制到缓冲
+ 3. 最后一次性绘制到图像框的bitmap
\ No newline at end of file
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs
index 6f5bf00..46b4d53 100644
--- a/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs
@@ -69,8 +69,7 @@ namespace JoyD.Windows.CS.Toprie
// 启动工作线程
_isRunning = true;
- _workerThread = new Thread(ProcessRequests);
- _workerThread.IsBackground = true;
+ _workerThread = new Thread(ProcessRequests) { IsBackground = true };
_workerThread.Start();
Console.WriteLine("UDP通信管理器已初始化");
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs
index 01d0bdc..970386b 100644
--- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs
@@ -128,16 +128,14 @@ namespace JoyD.Windows.CS.Toprie
}
// 私有字段
- private string deviceIp;
- private Socket socket;
- private bool isConnected;
+ private readonly string deviceIp;
+ private Socket socket = null;
private static bool isSdkInitialized = false;
- private static Dictionary deviceInstances = new Dictionary();
+ private static readonly Dictionary deviceInstances = new Dictionary();
public V8(string ip)
{
deviceIp = ip;
- isConnected = false;
}
~V8()
@@ -158,10 +156,9 @@ namespace JoyD.Windows.CS.Toprie
if (socket != null)
{
socket.Close();
- socket = null;
- }
- isConnected = false;
- Console.WriteLine("UDP通信状态已重置");
+ socket = null;
+ }
+ Console.WriteLine("UDP通信状态已重置");
}
catch (Exception ex)
{
@@ -1132,9 +1129,10 @@ namespace JoyD.Windows.CS.Toprie
if (SendCommand(command, out string response))
{
// 创建默认的ImagePos对象
- SharedStructures.ImagePos data = new SharedStructures.ImagePos();
- data.area = new SharedStructures.AreaPos[6];
- data.spot = new SharedStructures.SpotPos[6];
+ SharedStructures.ImagePos data = new SharedStructures.ImagePos() {
+ area = new SharedStructures.AreaPos[6],
+ spot = new SharedStructures.SpotPos[6]
+ };
// 这里应该解析完整的响应数据
// 简化实现,返回默认值
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/todolist.md b/Windows/CS/Framework4.0/Toprie/Toprie/todolist.md
new file mode 100644
index 0000000..fef0a0e
--- /dev/null
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/todolist.md
@@ -0,0 +1,54 @@
+# 托普瑞热像仪应用开发任务列表
+
+## 1. SDK集成准备
+
+- [x] 1.1 复制托普瑞SDK文件到项目目录
+- [x] 1.2 在Visual Studio项目中添加SDK引用
+- [x] 1.3 阅读SDK文档,了解API接口
+
+## 2. 设备连接功能
+
+- [x] 2.1 实现设备连接模块
+- [x] 2.2 完善连接状态管理
+
+## 3. 温度数据处理
+
+### 3.1 设备连接建立与管理
+- [ ] 3.1.1 实现设备连接建立流程
+ - 使用SDK的`sdk_initialize()`初始化套接字库
+ - 利用`sdk_search_device()`搜索设备IP地址
+ - 验证设备IP地址是否为默认地址(192.168.100.2)或自定义地址
+ - 建立与热像仪的TCP连接,使用8081端口获取原始温度数据
+- [ ] 3.1.2 连接状态维护机制
+ - 利用现有的`_heartbeatTimer`实现SDK心跳检测(`sdk_heartbeat`)
+ - 扩展`UpdateConnectionStatus()`方法,支持温度数据连接状态管理
+ - 实现基于`_connectionCheckTimer`的定时连接状态检测
+ - 利用现有的自动重连机制,当温度数据连接断开时自动重连
+- [ ] 3.1.3 连接条件判断
+ - 调用`IsNetworkAvailable()`验证网络连接状态
+ - 检查设备型号兼容性,支持IRAY-A8系列设备
+ - 验证设备版本信息,确保SDK兼容性
+ - 在获取温度数据前检查设备是否处于正常工作状态
+
+### 3.2 原始温度数据获取
+- [ ] 3.2.1 温度数据获取方法实现
+ - 在`DeviceManager.cs`中添加`GetRawTemperatureData()`方法
+ - 创建TCP客户端连接到设备的8081端口
+ - 实现数据接收线程,使用类似现有的`_imageReceiveThread`模式
+ - 实现24字节头部结构体解析,提取mark("+TEMP")、payload_length和timestamp
+- [ ] 3.2.2 温度数据解析与处理
+ - 根据设备分辨率计算温度数据总长度(宽*高*2字节)
+ - 实现温度数据解码算法:摄氏温度 = (H*256 + L)/10,其中L是低8位,H是高8位
+ - 调用`sdk_get_comp_temp()`获取设备温补值并进行叠加修正
+ - 创建温度数据模型类,存储解析后的温度矩阵
+- [ ] 3.2.3 数据获取条件控制与异常处理
+ - 添加连接状态验证,确保`_connectionStatus`为`Connected`
+ - 实现数据接收超时处理,基于`_lastDataReceivedTime`和`DataReceivedTimeout`
+ - 添加数据校验机制,确保接收到的温度数据格式正确
+ - 实现断线自动重连,利用现有的`_reconnectTimer`机制
+ - 添加异常捕获和日志记录,使用现有的`Log()`方法
+- [ ] 3.2.4 温度数据接口设计
+ - 添加温度数据接收事件`TemperatureDataReceived`
+ - 实现线程安全的数据访问方法
+ - 添加温度数据缓存机制,保留最近几帧数据
+ - 提供获取特定区域温度数据的辅助方法
\ No newline at end of file