Merge branch 'main' of http://47.109.191.115:3000/ZengQingming/JoyD
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
// 获取图像数据
|
||||
|
||||
146
Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs
generated
146
Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs
generated
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
504
Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs
Normal file
504
Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs
Normal 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
Reference in New Issue
Block a user