From 6da609c10fc4080914812fa9a74c28fee6c3493c Mon Sep 17 00:00:00 2001 From: zqm Date: Tue, 28 Oct 2025 10:29:11 +0800 Subject: [PATCH 01/17] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Toprie/Toprie/Camera.Designer.cs | 104 ++++++ .../CS/Framework4.0/Toprie/Toprie/Camera.cs | 156 ++++++++ .../Toprie/Toprie/DeviceManager.cs | 42 +++ .../Framework4.0/Toprie/Toprie/Toprie.csproj | 1 + .../Toprie/Toprie/UdpCommunicationManager.cs | 341 ++++++++++++++++++ Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 143 +++----- 6 files changed, 698 insertions(+), 89 deletions(-) create mode 100644 Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs index a27acbe..7cba02f 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs @@ -15,12 +15,25 @@ /// 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.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 +42,86 @@ this.imageBox.TabIndex = 0; this.imageBox.TabStop = false; // + // contextMenuStrip1 + // + this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.colorModeToolStripMenuItem}); + this.contextMenuStrip1.Name = "contextMenuStrip1"; + this.contextMenuStrip1.Size = new System.Drawing.Size(161, 48); + // + // 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 +131,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 +139,15 @@ #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; } } diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index b052b60..3eea4bc 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -853,6 +853,162 @@ namespace JoyD.Windows.CS.Toprie } } + #region 色彩模式切换方法 + + /// + /// 白热色彩模式 + /// + 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}"); + } + } + + /// + /// 黑热色彩模式 + /// + 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}"); + } + } + + /// + /// 铁红色彩模式 + /// + 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}"); + } + } + + /// + /// 熔岩色彩模式 + /// + 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}"); + } + } + + /// + /// 彩虹色彩模式 + /// + 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}"); + } + } + + /// + /// 铁灰色彩模式 + /// + 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}"); + } + } + + /// + /// 红热色彩模式 + /// + 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}"); + } + } + + /// + /// 彩虹2色彩模式 + /// + 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 + /// /// 清理资源 /// diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 0d8b01e..3d356ba 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -37,6 +37,22 @@ namespace JoyD.Windows.CS.Toprie Fusion } + /// + /// 色彩模式枚举 + /// 对应SDK文档中的8种色板模式:白热、黑热、铁红、熔岩、彩虹、铁灰、红热、彩虹2 + /// + public enum PaletteType + { + WhiteHot, // 白热,对应SDK参数0 + BlackHot, // 黑热,对应SDK参数1 + IronRed, // 铁红,对应SDK参数2 + Lava, // 熔岩,对应SDK参数3 + Rainbow, // 彩虹,对应SDK参数4 + IronGray, // 铁灰,对应SDK参数5 + RedHot, // 红热,对应SDK参数6 + Rainbow2 // 彩虹2,对应SDK参数7 + } + /// /// 连接状态改变事件参数 /// @@ -1711,6 +1727,32 @@ namespace JoyD.Windows.CS.Toprie CurrentImageMode = mode; } + /// + /// 设置色彩模式 + /// + /// 色彩模式 + public void SetPaletteType(PaletteType paletteType) + { + try + { + if (_a8Sdk != null) + { + // 将PaletteType枚举转换为int类型并发送命令 + // 按照SDK文档中的参数映射:白热(0)、黑热(1)、铁红(2)、熔岩(3)、彩虹(4)、铁灰(5)、红热(6)、彩虹2(7) + int paletteValue = (int)paletteType; + + // 尝试设置色彩模式 + _a8Sdk.Color_plate = paletteValue; + Console.WriteLine($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); + } + } + catch (Exception ex) + { + Console.WriteLine($"设置色彩模式失败: {ex.Message}"); + // 发生异常时可以考虑恢复到之前的设置或记录错误 + } + } + /// /// 发送模式切换命令 /// diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj b/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj index fdd4ec2..817a1a5 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj @@ -73,6 +73,7 @@ + diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs new file mode 100644 index 0000000..615d5f5 --- /dev/null +++ b/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs @@ -0,0 +1,341 @@ +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 +{ + /// + /// UDP通信管理器,负责管理所有UDP通信,实现请求-响应匹配和状态管理 + /// 防止异步数据相互影响 + /// + public class UdpCommunicationManager : IDisposable + { + // 单例实例 + private static readonly UdpCommunicationManager _instance = new UdpCommunicationManager(); + + // 命令队列 + private readonly Queue _requestQueue = new Queue(); + + // 请求映射表,用于匹配请求和响应 + private readonly Dictionary _activeRequests = new Dictionary(); + + // 锁对象,保护共享资源 + 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; + + /// + /// 获取UdpCommunicationManager的单例实例 + /// + public static UdpCommunicationManager Instance => _instance; + + /// + /// 私有构造函数,初始化UDP通信管理器 + /// + private UdpCommunicationManager() + { + Initialize(); + } + + /// + /// 初始化UDP通信管理器 + /// + 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}"); + } + } + + /// + /// 处理请求队列的工作线程函数 + /// + 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); + } + } + } + + /// + /// 处理单个UDP请求 + /// + /// UDP请求对象 + 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); + } + } + } + + /// + /// 处理接收到的UDP响应 + /// + /// 请求ID + /// 响应数据 + 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); // 通知请求成功 + } + } + + /// + /// 处理请求超时 + /// + /// 请求ID + 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); // 通知请求超时 + } + } + + /// + /// 发送UDP请求 + /// + /// 目标IP地址 + /// 要发送的数据 + /// 目标端口,默认为18890 + /// 超时时间(毫秒),默认为500 + /// 包含响应数据的Task + public Task SendRequestAsync(string targetIp, byte[] data, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT) + { + var tcs = new TaskCompletionSource(); + 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; + } + + /// + /// 同步发送UDP请求 + /// + /// 目标IP地址 + /// 要发送的数据 + /// 目标端口,默认为18890 + /// 超时时间(毫秒),默认为500 + /// 响应数据,如果超时则返回null + public byte[] SendRequest(string targetIp, byte[] data, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT) + { + try + { + var task = SendRequestAsync(targetIp, data, port, timeout); + task.Wait(timeout); + return task.Result; + } + catch (Exception ex) + { + Console.WriteLine($"同步发送UDP请求异常: {ex.Message}"); + return null; + } + } + + /// + /// 清理资源 + /// + 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通信管理器已释放"); + } + + /// + /// UDP请求类,封装单个UDP请求的信息 + /// + 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 OnResponse { get; set; } + } + } +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index e45592e..6fc3661 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; -using System.Net.Sockets; using System.Text; +using System.Net.Sockets; +using System.Net; using System.Threading; using System.IO; using System.Runtime.InteropServices; -using System.Net; +// 导入UDP通信管理器 +using JoyD.Windows.CS.Toprie; namespace JoyD.Windows.CS.Toprie { @@ -187,25 +189,20 @@ namespace JoyD.Windows.CS.Toprie try { - // 修改为UDP协议,与SDK保持一致 - // SDK中使用的是UDP协议(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) - // 端口使用18890,与SDK中的CMD_SERVER_UDP_PORT保持一致 - using (UdpClient udpClient = new UdpClient()) + // 使用UDP通信管理器发送请求 + response = UdpCommunicationManager.Instance.SendRequest(deviceIp, command, 18890, 200); + + if (response != null) { - // 设置接收超时,与SDK中的超时保持一致 - udpClient.Client.ReceiveTimeout = 200; // SDK中普通命令超时为200ms - - // 发送UDP数据报,端口使用18890与SDK保持一致 - udpClient.Send(command, command.Length, deviceIp, 18890); Console.WriteLine($"UDP命令已发送到 {deviceIp}:18890"); - - // 尝试接收响应 - IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); - response = udpClient.Receive(ref remoteEndPoint); - Console.WriteLine($"收到UDP命令响应,长度: {response.Length}"); return true; } + else + { + Console.WriteLine($"UDP命令发送后未收到响应或超时"); + return false; + } } catch (Exception ex) { @@ -221,28 +218,23 @@ namespace JoyD.Windows.CS.Toprie try { - // 修改为UDP协议,与SDK保持一致 - // SDK中使用的是UDP协议(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) - // 端口使用18890,与SDK中的CMD_SERVER_UDP_PORT保持一致 - using (UdpClient udpClient = new UdpClient()) + // 使用UDP通信管理器发送请求 + byte[] responseBytes = UdpCommunicationManager.Instance.SendRequest(deviceIp, + Encoding.ASCII.GetBytes(cmd), 18890, 200); + + if (responseBytes != null) { - // 设置接收超时,与SDK中的超时保持一致 - udpClient.Client.ReceiveTimeout = 200; // SDK中普通命令超时为200ms - - // 发送UDP数据报,端口使用18890与SDK保持一致 - byte[] commandBytes = Encoding.ASCII.GetBytes(cmd); - udpClient.Send(commandBytes, commandBytes.Length, deviceIp, 18890); + response = Encoding.ASCII.GetString(responseBytes); Console.WriteLine($"UDP命令已发送: {cmd}"); Console.WriteLine($"目标IP: {deviceIp}:18890"); - - // 尝试接收响应 - IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); - byte[] responseBytes = udpClient.Receive(ref remoteEndPoint); - response = Encoding.ASCII.GetString(responseBytes); - Console.WriteLine($"收到UDP命令响应: {response}"); return true; } + else + { + Console.WriteLine($"UDP命令发送后未收到响应或超时: {cmd}"); + return false; + } } catch (Exception ex) @@ -1666,10 +1658,7 @@ namespace JoyD.Windows.CS.Toprie // 使用UDP协议发送心跳命令,与SDK保持一致 public int Heartbeat() { - // 关键修改:与SDK保持一致,使用UDP协议发送心跳命令 - // SDK中使用的是UDP协议(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) - // 之前的实现使用了TCP协议,这是心跳失败的根本原因 - // 端口修改为18890,与SDK中的CMD_SERVER_UDP_PORT保持一致 + // 使用UDP通信管理器进行心跳检测,防止异步数据相互影响 try { // 根据SDK实际实现,心跳命令格式是 +CMD:24$ @@ -1683,71 +1672,47 @@ namespace JoyD.Windows.CS.Toprie Console.WriteLine($"心跳命令: {command}"); Console.WriteLine($"目标IP: {deviceIp}:18890"); - using (UdpClient udpClient = new UdpClient()) + try { - try + // 使用UDP通信管理器发送心跳请求,设置500ms超时 + byte[] responseBytes = UdpCommunicationManager.Instance.SendRequest(deviceIp, + commandBytes, 18890, 500); + + Console.WriteLine("UDP心跳命令已发送,等待响应..."); + + if (responseBytes != null) { - // 注意:SDK中tv_out.tv_sec=50实际表示50秒,这是SDK的实现方式 - // 这里我们设置一个更合理的值500毫秒,既保证能接收到响应,又不会等待太久 - udpClient.Client.ReceiveTimeout = 500; + string response = Encoding.ASCII.GetString(responseBytes); - // 发送UDP数据报,端口使用18890与SDK保持一致 - udpClient.Send(commandBytes, commandBytes.Length, deviceIp, 18890); - Console.WriteLine("UDP心跳命令已发送,等待响应..."); - - // 尝试接收响应 - IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); - - // 使用BeginReceive和EndReceive进行非阻塞接收,更好地处理超时 - IAsyncResult ar = udpClient.BeginReceive(null, null); - if (ar.AsyncWaitHandle.WaitOne(500)) // 额外的超时检查,双重保险 + Console.WriteLine($"收到UDP心跳响应: {response}"); + // 不使用LINQ的Select方法,避免缺少System.Linq命名空间的问题 + string[] asciiValues = new string[response.Length]; + for (int i = 0; i < response.Length; i++) { - try - { - byte[] responseBytes = udpClient.EndReceive(ar, ref remoteEndPoint); - string response = Encoding.ASCII.GetString(responseBytes); - - Console.WriteLine($"收到UDP心跳响应: {response}"); - // 不使用LINQ的Select方法,避免缺少System.Linq命名空间的问题 - string[] asciiValues = new string[response.Length]; - for (int i = 0; i < response.Length; i++) - { - asciiValues[i] = ((int)response[i]).ToString(); - } - Console.WriteLine($"响应长度: {response.Length} 字节,响应ASCII码: {string.Join(",", asciiValues)}"); - - // SDK要求响应中必须包含 ":ok" 字符串才算成功 - if (!string.IsNullOrEmpty(response) && response.Contains(":ok")) - { - Console.WriteLine("心跳成功: 响应包含':ok'"); - return 1; // 返回1表示成功,与DeviceManager中的heartbeatResult > 0判断一致 - } - else - { - Console.WriteLine($"心跳响应不包含':ok',验证失败。收到的响应: '{response}'"); - } - } - catch (Exception ex) - { - Console.WriteLine($"处理UDP响应时发生异常: {ex.Message}"); - } + asciiValues[i] = ((int)response[i]).ToString(); + } + Console.WriteLine($"响应长度: {response.Length} 字节,响应ASCII码: {string.Join(",", asciiValues)}"); + + // SDK要求响应中必须包含 ":ok" 字符串才算成功 + if (!string.IsNullOrEmpty(response) && response.Contains(":ok")) + { + Console.WriteLine("心跳成功: 响应包含':ok'"); + return 1; // 返回1表示成功,与DeviceManager中的heartbeatResult > 0判断一致 } else { - Console.WriteLine("UDP接收超时,取消接收操作"); - udpClient.Close(); // 关闭以取消接收操作 + Console.WriteLine($"心跳响应不包含':ok',验证失败。收到的响应: '{response}'"); } } - catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut) + else { - // 超时异常,继续重试 - Console.WriteLine("UDP心跳接收超时"); - } - catch (Exception ex) - { - Console.WriteLine($"UDP心跳异常: {ex.Message}"); + Console.WriteLine("UDP心跳未收到响应或超时"); } } + catch (Exception ex) + { + Console.WriteLine($"UDP心跳异常: {ex.Message}"); + } // 如果不是最后一次尝试,短暂延迟后重试 if (retry < 2) From eb96e4f15188a2c618898408147013563f26909f Mon Sep 17 00:00:00 2001 From: zqm Date: Tue, 28 Oct 2025 13:08:56 +0800 Subject: [PATCH 02/17] =?UTF-8?q?=E9=87=8D=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CS/Framework4.0/Toprie/Toprie/Camera.cs | 169 +- .../Toprie/Toprie/DeviceManager.cs | 1483 +++++++++++++---- .../Toprie/Toprie/UdpCommunicationManager.cs | 134 +- Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 67 +- 4 files changed, 1469 insertions(+), 384 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index 3eea4bc..a58df52 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -35,7 +35,7 @@ namespace JoyD.Windows.CS.Toprie // 清空现有日志 try { - string logFile = Path.Combine(Application.StartupPath, "camera.log"); + string logFile = Path.Combine(Application.StartupPath, "log.txt"); if (File.Exists(logFile)) { File.WriteAllText(logFile, string.Empty); @@ -570,28 +570,74 @@ namespace JoyD.Windows.CS.Toprie /// 设备管理器连接状态变更事件处理 /// private void DeviceManager_ConnectionStatusChanged(object sender, ConnectionStatusChangedEventArgs e) - { - // 确保在UI线程上更新,并且检查控件是否已被释放 - if (!this.IsDisposed && !this.Disposing) - { + { + // 参数验证 + 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(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}"); + } } /// @@ -738,37 +784,94 @@ namespace JoyD.Windows.CS.Toprie /// 设备管理器连接异常事件处理 /// private void DeviceManager_ConnectionException(object sender, ConnectionExceptionEventArgs e) - { - // 确保在UI线程上更新,并且检查控件是否已被释放 - if (!this.IsDisposed && !this.Disposing) - { + { + // 参数验证 + 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}"); + } } /// diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 3d356ba..08852cb 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -182,6 +182,10 @@ namespace JoyD.Windows.CS.Toprie private const int ConnectionCheckInterval = 5000; // 心跳间隔(毫秒) private int _heartbeatInterval = 5000; + // 最后接收数据时间戳 + private DateTime _lastDataReceivedTime = DateTime.MinValue; + // 数据接收超时时间(毫秒) + private const int DataReceivedTimeout = 15000; // 15秒内未收到数据则认为连接可能断开 // TCP客户端 // 该变量已在文件上方定义,删除重复实现 // 网络流 @@ -233,7 +237,7 @@ namespace JoyD.Windows.CS.Toprie } catch (Exception ex) { - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 捕获异常: {ex.Message}"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] IsNetworkAvailable() - 捕获异常: {ex.Message}"); return false; } } @@ -300,66 +304,169 @@ namespace JoyD.Windows.CS.Toprie /// 相关异常(如果有) private void UpdateConnectionStatus(ConnectionStatus newStatus, string message = null, Exception exception = null) { - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 尝试更新状态: {_connectionStatus} -> {newStatus}, 消息: {message}"); - // 检查对象是否已被释放 - if (_isDisposed) + // 使用线程安全的方式记录方法进入 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 开始执行"); + + lock (_lockObject) // 添加线程同步锁 { - Log("警告: 尝试在已释放对象上更新连接状态"); - return; + // 检查对象是否已被释放 + if (_isDisposed) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 对象已释放,忽略状态更新"); + return; + } + + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 尝试更新状态: {_connectionStatus} -> {newStatus}, 消息: {message}"); + + bool statusChanged = (_connectionStatus != newStatus); + ConnectionStatus oldStatus = _connectionStatus; + + if (statusChanged) + { + _connectionStatus = newStatus; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 状态已更新: {oldStatus} -> {newStatus}"); + + // 触发连接状态变更事件前再次检查对象是否已被释放 + if (!_isDisposed) + { + try + { + OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(newStatus, message)); + } + catch (Exception ex) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 触发连接状态变更事件异常: {ex.Message}"); + } + } + + Log($"连接状态变更: {oldStatus} -> {newStatus}"); + if (!string.IsNullOrEmpty(message)) + { + Log($"状态消息: {message}"); + } + if (exception != null) + { + Log($"异常信息: {exception.Message}"); + } + + // 保存状态变更相关信息供后续处理 + ConnectionStatus finalNewStatus = newStatus; + bool shouldReconnect = (newStatus == ConnectionStatus.Disconnected && _autoReconnectEnabled && oldStatus != ConnectionStatus.Connecting); + bool shouldReset = (newStatus == ConnectionStatus.Connected); + + // 添加状态转换验证,避免不合理的状态切换 + bool isValidTransition = ValidateStatusTransition(oldStatus, newStatus); + if (!isValidTransition) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 无效的状态转换: {oldStatus} -> {newStatus},已忽略"); + return; + } + + // 添加额外的保护:防止短时间内状态频繁变化导致的闪烁问题 + // 1. 当从Connected转换到Reconnecting时,检查是否真的需要重连 + if (oldStatus == ConnectionStatus.Connected && newStatus == ConnectionStatus.Reconnecting) + { + // 检查当前是否有其他重连操作正在进行 + if (Interlocked.CompareExchange(ref _isReconnecting, 1, 1) == 1) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 检测到重连操作正在进行,推迟状态转换: {oldStatus} -> {newStatus}"); + return; + } + } + + // 2. 当从Reconnecting快速切换回Connected时,确保消息不会引起混淆 + if (oldStatus == ConnectionStatus.Reconnecting && newStatus == ConnectionStatus.Connected && message.Contains("正在进行自动重连")) + { + // 修改消息内容,使其更准确 + message = "重连成功,设备已恢复连接"; + } + + // 在锁外执行可能耗时的操作 + ThreadPool.QueueUserWorkItem((state) => + { + try + { + if (shouldReconnect) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 在锁外启动自动重连"); + StartAutoReconnect(); + } + else if (shouldReset) + { + lock (_lockObject) // 重置时需要加锁 + { + if (!_isDisposed) + { + _currentReconnectAttempt = 0; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 连接成功,已重置重连计数"); + } + } + } + } + catch (Exception ex) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 后续操作异常: {ex.Message}"); + } + }); + } + else + { + // 优化:只有在调试模式且有异常时才记录调用堆栈,减少日志量 + if (System.Diagnostics.Debugger.IsAttached || exception != null || (!string.IsNullOrEmpty(message) && !message.Contains("正在进行自动重连"))) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 状态未变化({_connectionStatus}),消息: {message}"); + + // 仅在调试模式且有异常时才记录调用堆栈,避免频繁的堆栈记录 + #if DEBUG + if (exception != null) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 调用堆栈: {Environment.StackTrace}"); + } + #endif + } + } } - if (_connectionStatus != newStatus) - { - ConnectionStatus oldStatus = _connectionStatus; - _connectionStatus = newStatus; - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 状态已更新: {oldStatus} -> {newStatus}"); - - // 触发连接状态变更事件前再次检查对象是否已被释放 - if (!_isDisposed) - { - try - { - OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(newStatus, message)); - } - catch (Exception ex) - { - Log($"触发连接状态变更事件异常: {ex.Message}"); - } - } - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 异常处理完成"); - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 连接检查正常完成"); - - Log($"连接状态变更: {oldStatus} -> {newStatus}"); - if (!string.IsNullOrEmpty(message)) - { - Log($"状态消息: {message}"); - } - if (exception != null) - { - Log($"异常信息: {exception.Message}"); - } - - // 如果断开连接且启用了自动重连,启动重连机制 - if (newStatus == ConnectionStatus.Disconnected && _autoReconnectEnabled && oldStatus != ConnectionStatus.Connecting) - { - StartAutoReconnect(); - } - // 如果连接成功,重置重连计数并启动图像接收 - else if (newStatus == ConnectionStatus.Connected) - { - _currentReconnectAttempt = 0; - StopAutoReconnect(); - // 启动图像接收 - StartImageReceiving(); - } - // 如果断开连接,停止图像接收 - else if (newStatus == ConnectionStatus.Disconnected) - { - StopImageReceiving(); - } - } + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] UpdateConnectionStatus() - 执行完成"); } + /// + /// 验证状态转换的有效性 + /// + /// 当前状态 + /// 目标状态 + /// 是否为有效的状态转换 + private bool ValidateStatusTransition(ConnectionStatus fromStatus, ConnectionStatus toStatus) + { + // 如果状态相同,认为是有效的(但会在主方法中跳过实际变更) + if (fromStatus == toStatus) + return true; + + // 定义有效的状态转换规则 + switch (fromStatus) + { + case ConnectionStatus.Disconnected: + // 断开状态可以转换为连接中或重连中 + return toStatus == ConnectionStatus.Connecting || toStatus == ConnectionStatus.Reconnecting; + + case ConnectionStatus.Connecting: + // 连接中状态可以转换为已连接或断开 + return toStatus == ConnectionStatus.Connected || toStatus == ConnectionStatus.Disconnected; + + case ConnectionStatus.Connected: + // 已连接状态可以转换为断开或重连中 + // 允许直接从Connected转换到Reconnecting,以支持更灵活的重连机制 + return toStatus == ConnectionStatus.Disconnected || toStatus == ConnectionStatus.Reconnecting; + + case ConnectionStatus.Reconnecting: + // 重连中状态可以转换为已连接或断开 + return toStatus == ConnectionStatus.Connected || toStatus == ConnectionStatus.Disconnected; + + default: + return false; + } + } + /// /// 初始化设备管理器 /// @@ -384,8 +491,18 @@ namespace JoyD.Windows.CS.Toprie return true; } - try + // 加锁进行线程安全的初始化 + lock (_lockObject) { + // 再次检查,因为可能在获取锁的过程中已被其他线程初始化 + if (_isInitialized) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 已被其他线程初始化,直接返回成功"); + return true; + } + + try + { // 清理旧资源(如果有) Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 清理旧资源"); CleanupResources(); @@ -407,6 +524,9 @@ namespace JoyD.Windows.CS.Toprie return false; } + // 更新状态为连接中 + UpdateConnectionStatus(ConnectionStatus.Connecting, "正在初始化设备连接"); + // 首先调用SDK静态初始化方法 - 这是连接成功的关键步骤! // 添加重试机制,增强初始化可靠性 int initResult = -1; @@ -451,15 +571,37 @@ namespace JoyD.Windows.CS.Toprie _isInitialized = true; Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化成功,_isInitialized设为true"); + + // 更新状态为已连接 + UpdateConnectionStatus(ConnectionStatus.Connected, "SDK初始化成功"); + + // 连接成功后,停止自动重连(如果正在运行) + StopAutoReconnect(); + + // 启动图像接收 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 连接成功,启动图像接收"); + StartImageReceiving(); + return true; } catch (Exception ex) { Log($"SDK初始化失败: {ex.Message},堆栈: {ex.StackTrace}"); _isInitialized = false; - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化异常,_isInitialized保持false: {ex.Message}"); - OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败")); - return false; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化异常,_isInitialized保持false: {ex.Message}"); + OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败")); + // 更新状态为断开 + UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化失败", ex); + return false; + } + finally + { + // 确保在异常情况下也能清理资源 + if (!_isInitialized && _connectionStatus == ConnectionStatus.Connecting) + { + UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化未完成"); + } + } } } @@ -582,6 +724,8 @@ namespace JoyD.Windows.CS.Toprie break; } } + // 添加心跳检测异常处理,但位置应该在CheckConnectionValidity方法中 + // 这里保持PingDevice方法的纯净性 if (isInSameSubnet) { @@ -654,8 +798,43 @@ namespace JoyD.Windows.CS.Toprie /// /// 事件参数 protected virtual void OnConnectionStatusChanged(ConnectionStatusChangedEventArgs e) - { - ConnectionStatusChanged?.Invoke(this, e); + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionStatusChanged() - 开始执行,状态: {e.Status}"); + + // 检查参数有效性 + if (e == null) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionStatusChanged() - 参数为空,跳过事件触发"); + return; + } + + // 获取事件处理程序的快照,避免在多线程环境中出现竞态条件 + EventHandler handler = ConnectionStatusChanged; + + // 检查是否有订阅者 + if (handler != null) + { + // 获取所有订阅的委托,单独处理每个处理器 + Delegate[] invocationList = handler.GetInvocationList(); + + foreach (Delegate d in invocationList) + { + try + { + // 安全地转换并调用每个处理器 + EventHandler invoker = (EventHandler)d; + invoker(this, e); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionStatusChanged() - 成功触发一个事件处理器: {d.Method.Name}"); + } + catch (Exception ex) + { + // 捕获单个事件处理器的异常,确保其他处理器仍能被调用 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionStatusChanged() - 事件处理器异常: {ex.Message},堆栈: {ex.StackTrace}"); + } + } + } + + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionStatusChanged() - 执行完成"); } /// @@ -663,39 +842,105 @@ namespace JoyD.Windows.CS.Toprie /// private void StartConnectionCheck() { - try + lock (_lockObject) // 添加线程同步锁 { - // 首先停止现有的连接检查,确保资源释放 - StopConnectionCheck(); - - Console.WriteLine("启动连接状态检查,每5秒检查一次"); - - // 使用try-catch包装定时器创建,避免异常 try { - // 每5秒检查一次连接状态 - _connectionCheckTimer = new System.Threading.Timer(state => + // 首先停止现有的连接检查,确保资源释放 + StopConnectionCheck(); + + Log("启动连接状态检查,每5秒检查一次"); // 统一使用Log方法 + + // 使用try-catch包装定时器创建,避免异常 + try { - // 确保定时器状态检查在工作线程中安全执行 - try + // 使用非重复模式的定时器,避免前一个检查未完成就开始新的检查 + _connectionCheckTimer = new System.Threading.Timer(state => { - // 避免在定时器回调中可能的资源访问冲突 - if (_connectionCheckTimer == null) - return; - - // 关键修改:即使_isInitialized为false,也需要检查连接状态 - // 当显示为已连接但初始化状态为false时,必须检查连接 - if (_connectionStatus == ConnectionStatus.Connected) + // 确保定时器状态检查在工作线程中安全执行 + try { - // 如果初始化失败但显示已连接,尝试重新初始化 - if (!_isInitialized) + // 避免在定时器回调中可能的资源访问冲突 + if (_connectionCheckTimer == null) + return; + + lock (_lockObject) // 添加线程同步锁保护状态访问 { - Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 警告: 显示已连接但初始化状态为false,尝试重新初始化..."); - Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 当前状态: _connectionStatus={_connectionStatus}, _isInitialized={_isInitialized}, _a8Sdk={(_a8Sdk == null ? "null" : "已初始化")}, _currentDeviceId={_currentDeviceId}"); - if (!Initialize()) + if (_isDisposed) + return; + + // 关键修改:即使_isInitialized为false,也需要检查连接状态 + // 当显示为已连接但初始化状态为false时,必须检查连接 + if (_connectionStatus == ConnectionStatus.Connected) { - Console.WriteLine("重新初始化失败,确认连接已断开"); - UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备未初始化,连接已断开"); + // 如果初始化失败但显示已连接,尝试重新初始化 + if (!_isInitialized) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 警告: 显示已连接但初始化状态为false,尝试重新初始化..."); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 当前状态: _connectionStatus={_connectionStatus}, _isInitialized={_isInitialized}, _a8Sdk={(_a8Sdk == null ? "null" : "已初始化")}, _currentDeviceId={_currentDeviceId}"); + + // 避免在定时器回调中直接调用可能阻塞的方法 + // 使用线程池执行初始化 + ThreadPool.QueueUserWorkItem((obj) => + { + lock (_lockObject) + { + if (!_isDisposed && _connectionStatus == ConnectionStatus.Connected && !_isInitialized) + { + if (!Initialize()) + { + Log("重新初始化失败,确认连接已断开"); + UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备未初始化,连接已断开"); + // 启动自动重连 + if (_autoReconnectEnabled) + { + StartAutoReconnect(); + } + } + } + } + }); + } + + // 无论初始化状态如何,只要显示为已连接就进行检查 + // 复制状态变量到局部变量以避免在检查过程中状态被修改 + bool isConnected = _connectionStatus == ConnectionStatus.Connected; + bool isInitialized = _isInitialized; + A8SDK sdkInstance = _a8Sdk; + int deviceId = _currentDeviceId; + + // 在锁外执行检查以避免阻塞 + ThreadPool.QueueUserWorkItem((obj) => + { + CheckConnectionWrapper(); + }); + } + // 正常情况下的连接检查 + else if (_isInitialized && _a8Sdk != null && _currentDeviceId != -1) + { + // 复制状态变量到局部变量以避免在检查过程中状态被修改 + bool isInitialized = _isInitialized; + A8SDK sdkInstance = _a8Sdk; + int deviceId = _currentDeviceId; + + // 在锁外执行检查以避免阻塞 + ThreadPool.QueueUserWorkItem((obj) => + { + CheckConnectionWrapper(); + }); + } + } + } + catch (Exception ex) + { + Log($"定时器回调异常: {ex.Message}"); + // 异常时如果是已连接状态,将其设为断开 + lock (_lockObject) + { + if (!_isDisposed && _connectionStatus == ConnectionStatus.Connected) + { + UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接检查异常", ex); + _isInitialized = false; // 启动自动重连 if (_autoReconnectEnabled) { @@ -703,98 +948,125 @@ namespace JoyD.Windows.CS.Toprie } } } - - // 无论初始化状态如何,只要显示为已连接就进行检查 - CheckConnectionWrapper(); + // 发生异常时停止定时器,避免持续报错 + StopConnectionCheck(); } - // 正常情况下的连接检查 - else if (_isInitialized && _a8Sdk != null && _currentDeviceId != -1) + finally { - CheckConnectionWrapper(); - } - } - catch (Exception ex) - { - Log($"定时器回调异常: {ex.Message}"); - // 异常时如果是已连接状态,将其设为断开 - if (_connectionStatus == ConnectionStatus.Connected) - { - UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接检查异常", ex); - _isInitialized = false; - // 启动自动重连 - if (_autoReconnectEnabled) + // 检查完成后,如果定时器仍存在且未释放,重新启动定时器 + // 这样可以确保一个检查完成后才开始下一个检查 + lock (_lockObject) { - StartAutoReconnect(); + if (!_isDisposed && _connectionCheckTimer != null) + { + try + { + _connectionCheckTimer.Change(5000, Timeout.Infinite); + } + catch (ObjectDisposedException) + { + // 忽略已释放对象的异常 + } + } } } - // 发生异常时停止定时器,避免持续报错 - StopConnectionCheck(); - } - }, null, 5000, 5000); + }, null, 5000, Timeout.Infinite); + } + catch (Exception ex) + { + Log($"创建连接检查定时器失败: {ex.Message}"); + _connectionCheckTimer = null; + } } catch (Exception ex) { - Log($"创建连接检查定时器失败: {ex.Message}"); - _connectionCheckTimer = null; + Log($"启动连接检查异常: {ex.Message}"); + StopConnectionCheck(); } } - catch (Exception ex) - { - Log($"启动连接检查异常: {ex.Message}"); - StopConnectionCheck(); - } } // 连接检查的安全包装方法 private void CheckConnectionWrapper() { Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 开始连接检查"); + + // 先检查对象状态,避免不必要的操作 + lock (_lockObject) + { + if (_isDisposed || _connectionStatus != ConnectionStatus.Connected) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 对象已释放或连接状态非Connected,跳过检查"); + return; + } + } + + // 在锁外执行连接有效性检查,避免长时间阻塞 + bool isStillConnected; try { - // 检查连接有效性 - bool isStillConnected = CheckConnectionValidity(); + isStillConnected = CheckConnectionValidity(); Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - CheckConnectionValidity返回: {isStillConnected}"); - - // 连接状态变化时更新状态 - if (!isStillConnected && _connectionStatus == ConnectionStatus.Connected) - { - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 检测到连接已断开: _connectionStatus={_connectionStatus}, isStillConnected={isStillConnected}, _isInitialized={_isInitialized}"); - // 断开连接时重置初始化状态和SDK实例 - _isInitialized = false; - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 重置初始化状态为false"); - _a8Sdk = null; - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 释放SDK实例"); - UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接已断开:设备离线"); - // 断开连接后自动启动重连 - if (_autoReconnectEnabled) - { - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 连接断开,启动自动重连"); - StartAutoReconnect(); - } - } - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 连接检查正常完成"); } catch (Exception ex) { Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 检查连接状态异常: {ex.Message}"); - // 检查异常时,将状态设为断开 - if (_connectionStatus == ConnectionStatus.Connected) + isStillConnected = false; + } + + // 如果连接无效,需要在锁内更新状态 + if (!isStillConnected) + { + lock (_lockObject) { - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 异常时连接状态为Connected,需要重置"); - // 异常时重置初始化状态和SDK实例 - _isInitialized = false; - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 异常时重置初始化状态为false"); - _a8Sdk = null; - UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接状态检查异常", ex); - // 异常时启动自动重连 - if (_autoReconnectEnabled) + if (!_isDisposed && _connectionStatus == ConnectionStatus.Connected) { - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 检查异常,启动自动重连"); - StartAutoReconnect(); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 检测到连接已断开: _connectionStatus={_connectionStatus}, isStillConnected={isStillConnected}, _isInitialized={_isInitialized}"); + // 断开连接时重置初始化状态和SDK实例 + _isInitialized = false; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 重置初始化状态为false"); + + // 安全释放SDK实例 + A8SDK oldSdk = _a8Sdk; + _a8Sdk = null; + + // 在锁外释放资源 + if (oldSdk != null) + { + ThreadPool.QueueUserWorkItem((obj) => + { + try + { + // 注意:A8SDK类没有Dispose方法,这里只记录日志 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - SDK实例引用已处理"); + } + catch (Exception disposeEx) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] SDK资源释放异常: {disposeEx.Message}"); + } + }); + } + else + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - SDK实例已为空,无需释放"); + } + + UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接已断开:设备离线"); + + // 断开连接后自动启动重连,但在锁外执行 + if (_autoReconnectEnabled) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 连接断开,将启动自动重连"); + // 在锁外启动自动重连,避免潜在的死锁 + ThreadPool.QueueUserWorkItem((obj) => + { + StartAutoReconnect(); + }); + } } } - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 异常处理完成"); } + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionWrapper() - 连接检查完成"); } /// @@ -810,6 +1082,9 @@ namespace JoyD.Windows.CS.Toprie return true; } + // 注意:此方法被CheckConnectionWrapper调用,已经在线程安全的上下文中 + // 不需要额外加锁,但需要确保SDK实例的访问是线程安全的 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - 开始检查连接有效性"); try { @@ -847,7 +1122,9 @@ namespace JoyD.Windows.CS.Toprie if (_a8Sdk == null) { Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK实例不存在,重新初始化..."); - _a8Sdk = new A8SDK(_deviceIp); + // 先创建新实例,再原子赋值,避免中间状态 + A8SDK newSdk = new A8SDK(_deviceIp); + _a8Sdk = newSdk; Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] CheckConnectionValidity() - SDK实例已创建"); } @@ -1005,8 +1282,43 @@ namespace JoyD.Windows.CS.Toprie /// /// 事件参数 protected virtual void OnConnectionException(ConnectionExceptionEventArgs e) - { - ConnectionException?.Invoke(this, e); + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionException() - 开始执行"); + + // 检查参数有效性 + if (e == null) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionException() - 参数为空,跳过事件触发"); + return; + } + + // 获取事件处理程序的快照,避免在多线程环境中出现竞态条件 + EventHandler handler = ConnectionException; + + // 检查是否有订阅者 + if (handler != null) + { + // 获取所有订阅的委托,单独处理每个处理器 + Delegate[] invocationList = handler.GetInvocationList(); + + foreach (Delegate d in invocationList) + { + try + { + // 安全地转换并调用每个处理器 + EventHandler invoker = (EventHandler)d; + invoker(this, e); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionException() - 成功触发一个事件处理器: {d.Method.Name}"); + } + catch (Exception ex) + { + // 捕获单个事件处理器的异常,确保其他处理器仍能被调用 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionException() - 事件处理器异常: {ex.Message},堆栈: {ex.StackTrace}"); + } + } + } + + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnConnectionException() - 执行完成"); } /// @@ -1014,8 +1326,56 @@ namespace JoyD.Windows.CS.Toprie /// /// 事件参数 protected virtual void OnImageReceived(ImageReceivedEventArgs e) - { - ImageReceived?.Invoke(this, e); + { + // 图像事件可能高频触发,只记录重要日志以避免性能影响 + bool isDebugMode = false; // 可配置是否启用详细日志 + + if (isDebugMode) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnImageReceived() - 开始执行"); + } + + // 检查参数有效性 + if (e == null) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnImageReceived() - 警告: 参数为空,跳过事件触发"); + return; + } + + // 获取事件处理程序的快照,避免在多线程环境中出现竞态条件 + EventHandler handler = ImageReceived; + + // 检查是否有订阅者 + if (handler != null) + { + // 获取所有订阅的委托,单独处理每个处理器 + Delegate[] invocationList = handler.GetInvocationList(); + + foreach (Delegate d in invocationList) + { + try + { + // 安全地转换并调用每个处理器 + EventHandler invoker = (EventHandler)d; + invoker(this, e); + + if (isDebugMode) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnImageReceived() - 成功触发处理器: {d.Method.Name}"); + } + } + catch (Exception ex) + { + // 捕获单个事件处理器的异常,确保其他处理器仍能被调用 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnImageReceived() - 处理器异常: {ex.Message},堆栈: {ex.StackTrace}"); + } + } + } + + if (isDebugMode) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] OnImageReceived() - 执行完成"); + } } #region 公共属性 @@ -1177,14 +1537,34 @@ namespace JoyD.Windows.CS.Toprie /// public void StartImageReceiving() { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartImageReceiving() - 开始执行"); + // 在设计模式下,跳过实际的图像接收 if (IsDesignMode) { Log("设计模式下跳过实际的图像接收"); return; } + + lock (_lockObject) + { + // 检查对象是否已被释放 + if (_isDisposed) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartImageReceiving() - 对象已释放,忽略操作"); + return; + } + + // 检查连接状态 + if (_connectionStatus != ConnectionStatus.Connected) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartImageReceiving() - 连接未建立,无法开始图像接收"); + return; + } + } - Console.WriteLine("开始使用HTTP方式接收图像数据"); + Log("开始使用HTTP方式接收图像数据"); + bool existed = false; try { // 确保之前的连接已关闭 @@ -1192,7 +1572,10 @@ namespace JoyD.Windows.CS.Toprie // 重置停止事件 _stopImageEvent = new ManualResetEvent(false); - _isReceivingImages = true; + lock (_lockObject) + { + _isReceivingImages = true; + } // 创建并启动图像接收线程 _imageReceiveThread = new Thread(ReceiveImageDataWithHttpWebRequest) @@ -1205,8 +1588,19 @@ namespace JoyD.Windows.CS.Toprie catch (Exception ex) { // 记录异常 - Console.WriteLine("StartImageReceiving error: " + ex.Message); - _isReceivingImages = false; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartImageReceiving() - 异常: {ex.Message}"); + lock (_lockObject) + { + _isReceivingImages = false; + } + + // 如果连接状态异常,触发异常事件 + OnConnectionException(new ConnectionExceptionEventArgs(ex, "启动图像接收失败")); + } + finally + { + // 确保方法执行完成时记录日志 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartImageReceiving() - 执行完成"); } } @@ -1215,47 +1609,140 @@ namespace JoyD.Windows.CS.Toprie /// public void StopImageReceiving() { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopImageReceiving() - 开始执行"); + + if (_isDisposed) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopImageReceiving() - 对象已释放,跳过操作"); + return; + } + try { - _isReceivingImages = false; + // 线程安全地更新状态 + lock (_lockObject) + { + _isReceivingImages = false; + } // 通知线程停止 - if (_stopImageEvent != null) + ManualResetEvent stopEvent = null; + lock (_lockObject) { - _stopImageEvent.Set(); + stopEvent = _stopImageEvent; + } + + if (stopEvent != null) + { + try + { + stopEvent.Set(); + } + catch (ObjectDisposedException) + { + // 忽略已释放的事件 + } } // 等待线程结束 - if (_imageReceiveThread != null && _imageReceiveThread.IsAlive) + Thread imageThread = null; + lock (_lockObject) { - _imageReceiveThread.Join(3000); // 最多等待3秒 - _imageReceiveThread = null; + imageThread = _imageReceiveThread; + } + + if (imageThread != null && imageThread.IsAlive) + { + try + { + imageThread.Join(3000); // 最多等待3秒 + lock (_lockObject) + { + if (_imageReceiveThread == imageThread) // 确保没有被其他线程修改 + { + _imageReceiveThread = null; + } + } + } + catch (ThreadStateException) + { + // 忽略线程状态异常 + } } // 停止重连线程 - if (_imageReconnectThread != null && _imageReconnectThread.IsAlive) + Thread reconnectThread = null; + lock (_lockObject) { - _imageReconnectThread.Join(1000); - _imageReconnectThread = null; + reconnectThread = _imageReconnectThread; + } + + if (reconnectThread != null && reconnectThread.IsAlive) + { + try + { + reconnectThread.Join(1000); + lock (_lockObject) + { + if (_imageReconnectThread == reconnectThread) // 确保没有被其他线程修改 + { + _imageReconnectThread = null; + } + } + } + catch (ThreadStateException) + { + // 忽略线程状态异常 + } } // 释放资源 - if (_imageStream != null) + Stream stream = null; + lock (_lockObject) { - _imageStream.Close(); - _imageStream.Dispose(); + stream = _imageStream; _imageStream = null; } - - if (_imageTcpClient != null) + + if (stream != null) { - _imageTcpClient.Close(); + try + { + stream.Close(); + stream.Dispose(); + } + catch (Exception) + { + // 忽略释放资源时的异常 + } + } + + TcpClient tcpClient = null; + lock (_lockObject) + { + tcpClient = _imageTcpClient; _imageTcpClient = null; } + + if (tcpClient != null) + { + try + { + tcpClient.Close(); + } + catch (Exception) + { + // 忽略关闭连接时的异常 + } + } } catch (Exception ex) { - Console.WriteLine("StopImageReceiving error: " + ex.Message); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopImageReceiving() - 异常: {ex.Message}"); + } + finally + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopImageReceiving() - 执行完成"); } } @@ -1347,6 +1834,9 @@ namespace JoyD.Windows.CS.Toprie { if (data == null || data.Length == 0) return; + + // 更新最后接收数据时间戳 + _lastDataReceivedTime = DateTime.Now; // 检查是否为HTTP响应 if (IsHttpResponse(data)) @@ -1731,7 +2221,8 @@ namespace JoyD.Windows.CS.Toprie /// 设置色彩模式 /// /// 色彩模式 - public void SetPaletteType(PaletteType paletteType) + /// 设置是否成功 + public bool SetPaletteType(PaletteType paletteType) { try { @@ -1742,14 +2233,29 @@ namespace JoyD.Windows.CS.Toprie int paletteValue = (int)paletteType; // 尝试设置色彩模式 + int originalValue = _a8Sdk.Color_plate; // 先获取当前值 _a8Sdk.Color_plate = paletteValue; - Console.WriteLine($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); + + // 验证设置是否成功(通过再次读取确认) + int currentValue = _a8Sdk.Color_plate; + if (currentValue == paletteValue) + { + Console.WriteLine($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); + return true; + } + else + { + Console.WriteLine($"色彩模式设置失败: 未能确认设置生效,当前值: {currentValue}"); + return false; + } } + Console.WriteLine("色彩模式设置失败: SDK实例为空"); + return false; } catch (Exception ex) { Console.WriteLine($"设置色彩模式失败: {ex.Message}"); - // 发生异常时可以考虑恢复到之前的设置或记录错误 + return false; } } @@ -2483,12 +2989,120 @@ namespace JoyD.Windows.CS.Toprie private volatile bool _isConnecting = false; private void StartAutoReconnect() - { - // 停止现有的重连定时器 - StopAutoReconnect(); + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 开始执行"); - // 创建新的重连定时器 - 使用一次性定时器而非重复定时器 - _reconnectTimer = new System.Threading.Timer(ReconnectCallback, null, 0, Timeout.Infinite); + // 检查对象是否已释放 + if (_isDisposed) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 对象已释放,取消重连"); + return; + } + + // 如果已经在连接状态,不需要启动重连 + if (_connectionStatus == ConnectionStatus.Connected) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 设备已连接,无需启动重连"); + return; + } + + try + { + // 使用锁确保线程安全,避免创建多个重连定时器 + lock (_lockObject) + { + // 再次检查状态,防止在获取锁期间状态已变更 + if (_isDisposed || _connectionStatus == ConnectionStatus.Connected) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 状态已变更,取消重连启动"); + return; + } + + // 停止现有的重连定时器 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 停止现有重连定时器"); + StopAutoReconnectInternal(); // 使用内部方法避免重复获取锁 + + // 使用Interlocked.Exchange安全创建新的重连定时器 + System.Threading.Timer newTimer = new System.Threading.Timer( + ReconnectCallback, + null, + 1000, // 延迟1秒后启动,避免过于频繁 + Timeout.Infinite); + + // 确保原子性地替换定时器引用 + System.Threading.Timer oldTimer = Interlocked.Exchange(ref _reconnectTimer, newTimer); + + // 如果有旧定时器没有被StopAutoReconnectInternal释放,确保这里也释放它 + if (oldTimer != null && oldTimer != newTimer) + { + try + { + oldTimer.Change(Timeout.Infinite, Timeout.Infinite); + oldTimer.Dispose(); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 释放了未被清理的旧定时器"); + } + catch (Exception ex) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 清理旧定时器时发生异常: {ex.Message}"); + } + } + + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 成功创建并启动重连定时器"); + } + } + catch (Exception ex) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 创建重连定时器失败: {ex.Message},堆栈: {ex.StackTrace}"); + + // 触发连接异常事件 + try + { + if (!_isDisposed) + { + OnConnectionException(new ConnectionExceptionEventArgs(ex, "启动自动重连失败")); + } + } + catch (Exception ex2) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 触发异常事件失败: {ex2.Message}"); + } + } + + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 执行完成"); + } + + /// + /// 内部停止重连方法,不获取锁,用于已在锁内的调用 + /// + private void StopAutoReconnectInternal() + { + try + { + System.Threading.Timer timerToStop = Interlocked.Exchange(ref _reconnectTimer, null); + if (timerToStop != null) + { + try + { + timerToStop.Change(Timeout.Infinite, Timeout.Infinite); + timerToStop.Dispose(); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnectInternal() - 重连定时器已停止并释放"); + } + catch (ObjectDisposedException) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnectInternal() - 重连定时器已被释放"); + } + } + + // 重置重连尝试次数 + Interlocked.Exchange(ref _currentReconnectAttempt, 0); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnectInternal() - 重连尝试次数已重置"); + } + catch (Exception ex) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnectInternal() - 异常: {ex.Message}"); + // 确保即使异常,引用也被清空 + Interlocked.Exchange(ref _reconnectTimer, null); + } } /// @@ -2533,28 +3147,33 @@ namespace JoyD.Windows.CS.Toprie /// 停止自动重连 /// private void StopAutoReconnect() - { + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnect() - 开始执行"); + + if (_isDisposed) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnect() - 对象已释放,跳过操作"); + return; + } + try - { - // 使用局部变量暂存,避免多线程访问时的空引用问题 - System.Threading.Timer timerToDispose = Interlocked.Exchange(ref _reconnectTimer, null); - if (timerToDispose != null) - { - // 立即停止定时器,避免回调执行 - timerToDispose.Change(Timeout.Infinite, Timeout.Infinite); - // 安全释放定时器资源 - timerToDispose.Dispose(); - Console.WriteLine("自动重连已停止"); + { + // 使用锁确保线程安全 + lock (_lockObject) + { + // 调用内部方法执行实际停止操作 + StopAutoReconnectInternal(); } - - // 重置重连尝试次数 - _currentReconnectAttempt = 0; } catch (Exception ex) - { - Console.WriteLine($"停止自动重连异常: {ex.Message}"); + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnect() - 异常: {ex.Message}"); // 确保即使异常,引用也被清空 - _reconnectTimer = null; + Interlocked.Exchange(ref _reconnectTimer, null); + } + finally + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopAutoReconnect() - 执行完成"); } } @@ -2564,41 +3183,61 @@ namespace JoyD.Windows.CS.Toprie /// 定时器状态 private void ReconnectCallback(object state) { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 开始执行"); + // 使用Interlocked.Exchange实现原子操作检查,防止重入 if (Interlocked.Exchange(ref _isReconnecting, 1) != 0) { - Console.WriteLine("检测到重连回调正在执行,避免重入"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 检测到重连回调正在执行,避免重入"); return; } try { - // 检查是否已达到最大重连次数 - if (_currentReconnectAttempt >= _maxReconnectAttempts) + if (_isDisposed) { - Console.WriteLine($"已达到最大重连次数 ({_maxReconnectAttempts}),停止自动重连"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 对象已释放,跳过重连"); + return; + } + + // 检查是否已达到最大重连次数 + int currentAttempts = Interlocked.CompareExchange(ref _currentReconnectAttempt, 0, 0); + if (currentAttempts >= _maxReconnectAttempts) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 已达到最大重连次数 ({_maxReconnectAttempts}),停止自动重连"); StopAutoReconnect(); UpdateConnectionStatus(ConnectionStatus.Disconnected, "已达到最大重连次数,请手动检查设备状态"); return; } // 改进的重连间隔递增策略,避免指数增长过快 - int currentInterval = Math.Min(_reconnectInterval * (int)(1 + _currentReconnectAttempt * 0.5), 30000); // 最多30秒 + int currentInterval = Math.Min(_reconnectInterval * (int)(1 + currentAttempts * 0.5), 30000); // 最多30秒 - _currentReconnectAttempt++; - Console.WriteLine($"自动重连尝试 {_currentReconnectAttempt}/{_maxReconnectAttempts},当前间隔: {currentInterval}ms"); + // 线程安全地递增重连尝试次数 + Interlocked.Increment(ref _currentReconnectAttempt); + currentAttempts++; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 自动重连尝试 {currentAttempts}/{_maxReconnectAttempts},当前间隔: {currentInterval}ms"); // 在重试前先检查网络状态 if (!IsNetworkAvailable()) { - Console.WriteLine("网络不可用,推迟重连尝试"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 网络不可用,推迟重连尝试"); UpdateConnectionStatus(ConnectionStatus.Disconnected, "网络连接不可用,请检查网络设置"); // 网络不可用时,调整为重试间隔较长 int networkDownInterval = Math.Min(_reconnectInterval * 3, 15000); - if (_reconnectTimer != null) + // 安全地更新定时器 + System.Threading.Timer timerToUpdate = _reconnectTimer; + if (timerToUpdate != null) { - _reconnectTimer.Change(networkDownInterval, Timeout.Infinite); + try + { + timerToUpdate.Change(networkDownInterval, Timeout.Infinite); + } + catch (ObjectDisposedException) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 定时器已被释放,无法更新网络不可用状态的重连间隔"); + } } return; } @@ -2609,13 +3248,19 @@ namespace JoyD.Windows.CS.Toprie bool connectionSuccessful = false; // 仅用IP地址重连设备,这样更直接更快 - if (!string.IsNullOrEmpty(_deviceIp)) + string deviceIp = null; + lock (_lockObject) { - Console.WriteLine($"用IP地址 {_deviceIp} 重连设备"); + deviceIp = _deviceIp; + } + + if (!string.IsNullOrEmpty(deviceIp)) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 用IP地址 {deviceIp} 重连设备"); try { // 检查IP是否可达,使用改进的PingDevice方法 - if (PingDevice(_deviceIp)) + if (PingDevice(deviceIp)) { // 使用Interlocked.Exchange确保线程安全地释放旧实例 A8SDK oldSdk = Interlocked.Exchange(ref _a8Sdk, null); @@ -2623,18 +3268,18 @@ namespace JoyD.Windows.CS.Toprie { try { - Console.WriteLine("安全释放旧SDK实例资源"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 安全释放旧SDK实例资源"); // 注意:根据SDK文档,如果有destroy方法应调用 } catch (Exception ex) { - Console.WriteLine($"释放旧SDK实例资源时发生异常: {ex.Message}"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 释放旧SDK实例资源时发生异常: {ex.Message}"); } } // 创建新的SDK实例 - Console.WriteLine($"创建新的SDK实例,目标IP: {_deviceIp}"); - A8SDK newSdk = new A8SDK(_deviceIp); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 创建新的SDK实例,目标IP: {deviceIp}"); + A8SDK newSdk = new A8SDK(deviceIp); // 保存到字段前进行验证,避免无效实例 bool isConnected = false; @@ -2644,13 +3289,13 @@ namespace JoyD.Windows.CS.Toprie { try { - Console.WriteLine($"尝试建立连接并发送心跳包...(尝试 {retry + 1}/{maxHeartbeatRetries})"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 尝试建立连接并发送心跳包...(尝试 {retry + 1}/{maxHeartbeatRetries})"); // 添加延时,给SDK实例初始化一些时间 if (retry > 0) { int retryDelay = 500 + (retry - 1) * 300; // 递增延时 - Console.WriteLine($"等待{retryDelay}ms后重试..."); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 等待{retryDelay}ms后重试..."); Thread.Sleep(retryDelay); } @@ -2658,18 +3303,18 @@ namespace JoyD.Windows.CS.Toprie int heartbeatResult = newSdk.Heartbeat(); if (heartbeatResult > 0) { - Console.WriteLine("心跳检测成功!"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 心跳检测成功!"); isConnected = true; break; } else { - Console.WriteLine($"心跳检测失败,返回值: {heartbeatResult}"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 心跳检测失败,返回值: {heartbeatResult}"); } } catch (Exception ex) { - Console.WriteLine($"心跳检测异常: {ex.Message},堆栈: {ex.StackTrace}"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 心跳检测异常: {ex.Message},堆栈: {ex.StackTrace}"); } } @@ -2678,80 +3323,125 @@ namespace JoyD.Windows.CS.Toprie // 连接成功,进行额外验证 try { - Console.WriteLine("进行额外连接验证..."); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 进行额外连接验证..."); // 再次发送心跳包确保连接稳定 int finalResult = newSdk.Heartbeat(); if (finalResult > 0) { // 验证成功后,安全地更新SDK实例 Interlocked.Exchange(ref _a8Sdk, newSdk); - Console.WriteLine($"使用IP地址 {_deviceIp} 重连成功"); - _currentDeviceId = 1; // 临时ID,确保状态更新正确 - UpdateConnectionStatus(ConnectionStatus.Connected, $"设备 {_deviceIp} 连接成功"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 使用IP地址 {deviceIp} 重连成功"); + + // 线程安全地更新设备ID + lock (_lockObject) + { + _currentDeviceId = 1; // 临时ID,确保状态更新正确 + } + + UpdateConnectionStatus(ConnectionStatus.Connected, $"设备 {deviceIp} 连接成功"); StartConnectionCheck(); connectionSuccessful = true; } else { - Console.WriteLine($"最终验证失败,返回值: {finalResult}"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 最终验证失败,返回值: {finalResult}"); } } catch (Exception ex) { - Console.WriteLine($"连接验证异常: {ex.Message}"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 连接验证异常: {ex.Message}"); } } // 如果连接不成功,释放资源 if (!connectionSuccessful) { - Console.WriteLine($"使用IP地址 {_deviceIp} 重连失败,所有心跳尝试都未成功"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 使用IP地址 {deviceIp} 重连失败,所有心跳尝试都未成功"); // 确保资源被释放 try { newSdk = null; } catch { } } } else { - Console.WriteLine($"IP地址 {_deviceIp} 不可达,等待设备上线"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - IP地址 {deviceIp} 不可达,等待设备上线"); } } catch (Exception ex) { - Console.WriteLine($"使用IP地址重连异常: {ex.Message},堆栈: {ex.StackTrace}"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 使用IP地址重连异常: {ex.Message},堆栈: {ex.StackTrace}"); } } // 如果IP地址连接失败,再尝试使用设备ID连接(保持兼容性) - // 但如果已经在连接中,则跳过 - if (!connectionSuccessful && _currentDeviceId != -1 && !_isConnecting) + // 但如果已经在连接中,则跳过 + if (!connectionSuccessful) { - Console.WriteLine($"尝试使用设备ID {_currentDeviceId} 连接"); - ConnectDevice(_currentDeviceId); - if (_connectionStatus == ConnectionStatus.Connected) + int currentDeviceId = -1; + bool isConnecting = false; + lock (_lockObject) { - connectionSuccessful = true; + currentDeviceId = _currentDeviceId; + isConnecting = _isConnecting; + } + + if (currentDeviceId != -1 && !isConnecting) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 尝试使用设备ID {currentDeviceId} 连接"); + ConnectDevice(currentDeviceId); + + // 线程安全地检查连接状态 + ConnectionStatus status = ConnectionStatus.Disconnected; + lock (_lockObject) + { + status = _connectionStatus; + } + if (status == ConnectionStatus.Connected) + { + connectionSuccessful = true; + } } } // 如果没有保存的设备ID,但有搜索到的设备,尝试连接第一个 - // 但如果已经在连接中,则跳过 - if (!connectionSuccessful && _deviceIds != null && _deviceIds.Count > 0 && !_isConnecting) + // 但如果已经在连接中,则跳过 + if (!connectionSuccessful) { - Console.WriteLine($"尝试使用搜索到的设备列表中的第一个设备"); - if (int.TryParse(_deviceIds[0], out int deviceId)) + bool isConnecting = false; + List deviceIds = null; + lock (_lockObject) { - ConnectDevice(deviceId); + isConnecting = _isConnecting; + if (_deviceIds != null) + { + deviceIds = new List(_deviceIds); + } } - if (_connectionStatus == ConnectionStatus.Connected) + + if (deviceIds != null && deviceIds.Count > 0 && !isConnecting) { - connectionSuccessful = true; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 尝试使用搜索到的设备列表中的第一个设备"); + if (int.TryParse(deviceIds[0], out int deviceId)) + { + ConnectDevice(deviceId); + } + + // 线程安全地检查连接状态 + ConnectionStatus status = ConnectionStatus.Disconnected; + lock (_lockObject) + { + status = _connectionStatus; + } + if (status == ConnectionStatus.Connected) + { + connectionSuccessful = true; + } } } // 如果连接成功,停止重连定时器 if (connectionSuccessful) { - Console.WriteLine("设备连接成功,停止自动重连"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 设备连接成功,停止自动重连"); StopAutoReconnect(); return; } @@ -2760,8 +3450,8 @@ namespace JoyD.Windows.CS.Toprie UpdateConnectionStatus(ConnectionStatus.Disconnected, "所有连接尝试失败"); // 改进的间隔递增策略,避免频繁重连导致的网络压力 - int retryInterval = Math.Min(_reconnectInterval * (1 + _currentReconnectAttempt / 5), 20000); // 最大20秒 - Console.WriteLine($"调整重连间隔为 {retryInterval}ms"); + int retryInterval = Math.Min(_reconnectInterval * (1 + currentAttempts / 5), 20000); // 最大20秒 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 调整重连间隔为 {retryInterval}ms"); // 安全地更新定时器 System.Threading.Timer currentTimer = _reconnectTimer; @@ -2774,13 +3464,13 @@ namespace JoyD.Windows.CS.Toprie } catch (ObjectDisposedException) { - Console.WriteLine("定时器已被释放,无法更新"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 定时器已被释放,无法更新"); } } } catch (Exception ex) { - Console.WriteLine($"重连回调函数发生异常: {ex.Message},堆栈: {ex.StackTrace}"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 重连回调函数发生异常: {ex.Message},堆栈: {ex.StackTrace}"); // 确保定时器继续工作,防止重连机制中断 System.Threading.Timer currentTimer = _reconnectTimer; if (currentTimer != null) @@ -2792,14 +3482,15 @@ namespace JoyD.Windows.CS.Toprie } catch (ObjectDisposedException) { - Console.WriteLine("定时器已被释放,无法恢复"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 定时器已被释放,无法恢复"); } } } finally { // 无论如何都要重置重入标志,确保后续重连可以正常触发 - _isReconnecting = 0; + Interlocked.Exchange(ref _isReconnecting, 0); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 执行完成"); } } @@ -2809,44 +3500,78 @@ namespace JoyD.Windows.CS.Toprie /// private void StartHeartbeat() { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 开始执行"); + // 在设计模式下,跳过实际的心跳检测 if (IsDesignMode) { - Log("设计模式下跳过实际的心跳检测"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 设计模式下跳过实际的心跳检测"); + return; + } + + if (_isDisposed) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 对象已释放,无法启动心跳检测"); return; } - // 停止现有的心跳定时器 - StopHeartbeat(); - - // 创建新的心跳定时器 - _heartbeatTimer = new System.Threading.Timer(HeartbeatCallback, null, 0, _heartbeatInterval); + try + { + // 停止现有的心跳定时器 + StopHeartbeat(); + + // 创建新的心跳定时器 + System.Threading.Timer newTimer = new System.Threading.Timer(HeartbeatCallback, null, 0, _heartbeatInterval); + Interlocked.Exchange(ref _heartbeatTimer, newTimer); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 心跳检测已启动,间隔: {_heartbeatInterval}ms"); + } + catch (Exception ex) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 创建心跳定时器异常: {ex.Message},堆栈: {ex.StackTrace}"); + OnConnectionException(new ConnectionExceptionEventArgs(ex, "启动心跳检测失败")); + } + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartHeartbeat() - 执行完成"); } /// /// 停止心跳检测 /// private void StopHeartbeat() - { + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 开始执行"); + try - { + { // 使用Interlocked.Exchange确保线程安全 System.Threading.Timer timerToDispose = Interlocked.Exchange(ref _heartbeatTimer, null); if (timerToDispose != null) - { - // 立即停止定时器,避免回调执行 - timerToDispose.Change(Timeout.Infinite, Timeout.Infinite); - // 安全释放定时器资源 - timerToDispose.Dispose(); - Console.WriteLine("心跳检测已停止"); + { + try + { + // 立即停止定时器,避免回调执行 + timerToDispose.Change(Timeout.Infinite, Timeout.Infinite); + // 安全释放定时器资源 + timerToDispose.Dispose(); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 心跳检测已停止"); + } + catch (ObjectDisposedException) + { + // 如果定时器已被释放,无需再处理 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 定时器已被释放"); + } + } + else + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 心跳定时器不存在,无需停止"); } } catch (Exception ex) - { - Console.WriteLine($"停止心跳检测异常: {ex.Message}"); + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 停止心跳检测异常: {ex.Message},堆栈: {ex.StackTrace}"); // 确保即使异常,引用也被清空 - _heartbeatTimer = null; + Interlocked.Exchange(ref _heartbeatTimer, null); } + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopHeartbeat() - 执行完成"); } /// @@ -2854,7 +3579,16 @@ namespace JoyD.Windows.CS.Toprie /// /// 定时器状态 private void HeartbeatCallback(object state) - { + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 开始执行"); + + // 检查对象是否已释放 + if (_isDisposed) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 对象已释放,退出心跳检测"); + return; + } + try { // 检查设备是否可达,使用改进的PingDevice方法 @@ -2872,14 +3606,14 @@ namespace JoyD.Windows.CS.Toprie if (i < pingRetries) { - Console.WriteLine($"Ping设备失败,{300 * (i + 1)}ms后重试..."); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - Ping设备失败,{300 * (i + 1)}ms后重试..."); Thread.Sleep(300 * (i + 1)); // 递增延时 } } if (!deviceReachable) { - Console.WriteLine("心跳检测失败,设备不可达"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 心跳检测失败,设备不可达"); UpdateConnectionStatus(ConnectionStatus.Disconnected, "心跳检测失败,设备不可达"); // 如果启用了自动重连,开始重连 @@ -2890,12 +3624,33 @@ namespace JoyD.Windows.CS.Toprie return; } + // 检查是否最近收到过数据 + bool recentlyReceivedData = false; + if (_lastDataReceivedTime != DateTime.MinValue) + { + TimeSpan timeSinceLastData = DateTime.Now - _lastDataReceivedTime; + recentlyReceivedData = timeSinceLastData.TotalMilliseconds < DataReceivedTimeout; + + if (recentlyReceivedData) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 最近{timeSinceLastData.TotalMilliseconds:F0}ms内收到过数据,跳过心跳检测"); + // 更新连接状态为正常 + if (_connectionStatus != ConnectionStatus.Connected) + { + UpdateConnectionStatus(ConnectionStatus.Connected, "最近收到数据,连接正常"); + } + return; + } + } + + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 长时间未收到数据,执行心跳检测"); + // 使用SDK的Heartbeat方法进行心跳检测,添加重试机制 A8SDK currentSdk = _a8Sdk; if (currentSdk != null) { bool heartbeatSuccessful = false; - int heartbeatRetries = 1; + int heartbeatRetries = 2; // 修改为2次重试,总共3次尝试 for (int i = 0; i <= heartbeatRetries; i++) { @@ -2905,32 +3660,32 @@ namespace JoyD.Windows.CS.Toprie if (heartbeatResult > 0) { heartbeatSuccessful = true; - Console.WriteLine("心跳检测成功"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 心跳检测成功 (第{i+1}次尝试)"); // 定期更新连接状态,表明连接正常 if (_connectionStatus != ConnectionStatus.Connected) { UpdateConnectionStatus(ConnectionStatus.Connected, "设备连接已恢复"); } - break; + break; // 一次成功就立即返回,不再重试 } else { - Console.WriteLine($"SDK心跳检测失败,返回值: {heartbeatResult}"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - SDK心跳检测失败 (第{i+1}次尝试),返回值: {heartbeatResult}"); if (i < heartbeatRetries) { - Console.WriteLine("300ms后重试心跳检测..."); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 300ms后重试心跳检测..."); Thread.Sleep(300); } } } catch (Exception ex) { - Console.WriteLine($"心跳检测异常: {ex.Message}"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 心跳检测异常 (第{i+1}次尝试): {ex.Message},堆栈: {ex.StackTrace}"); if (i < heartbeatRetries) { - Console.WriteLine("300ms后重试心跳检测..."); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 300ms后重试心跳检测..."); Thread.Sleep(300); } } @@ -2938,7 +3693,7 @@ namespace JoyD.Windows.CS.Toprie if (!heartbeatSuccessful) { - Console.WriteLine("SDK心跳检测失败,连接可能已断开"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - SDK心跳检测失败,连接可能已断开"); UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK心跳检测失败,连接已断开"); // 如果启用了自动重连,开始重连 @@ -2950,7 +3705,7 @@ namespace JoyD.Windows.CS.Toprie } else { - Console.WriteLine("SDK实例不存在,尝试重连"); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - SDK实例不存在,尝试重连"); UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK实例不存在"); if (_isAutoReconnectEnabled) @@ -2960,14 +3715,34 @@ namespace JoyD.Windows.CS.Toprie } } catch (Exception ex) - { - Console.WriteLine($"心跳检测时发生异常: {ex.Message},堆栈: {ex.StackTrace}"); - OnConnectionException(new ConnectionExceptionEventArgs(ex, "心跳检测失败")); + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 心跳检测时发生异常: {ex.Message},堆栈: {ex.StackTrace}"); + + // 触发连接异常事件 + try + { + if (!_isDisposed) + { + OnConnectionException(new ConnectionExceptionEventArgs(ex, "心跳检测失败")); + } + } + catch (Exception ex2) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 触发异常事件失败: {ex2.Message}"); + } // 异常情况下也尝试重连 - if (_isAutoReconnectEnabled) - { - StartAutoReconnect(); + try + { + if (!_isDisposed && _isAutoReconnectEnabled) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 异常情况下触发自动重连"); + StartAutoReconnect(); + } + } + catch (Exception ex3) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 启动自动重连失败: {ex3.Message}"); } } } @@ -3237,57 +4012,101 @@ namespace JoyD.Windows.CS.Toprie /// /// 是否释放托管资源 protected virtual void Dispose(bool disposing) - { - if (!_disposed) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Dispose({disposing}) - 开始执行"); + + // 使用锁确保线程安全 + lock (_lockObject) { - if (disposing) + if (!_disposed) { - // 释放托管资源 - StopAutoReconnect(); - StopConnectionCheck(); - StopHeartbeat(); - StopAllImageReceivers(); - _stopRequested.Dispose(); - } + _disposed = true; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Dispose() - 开始清理资源"); - // 安全释放SDK实例,避免内存访问冲突 - try - { - if (_a8Sdk != null) - { - Console.WriteLine("释放SDK实例资源"); - // 先设置为null,避免在Dispose过程中的访问 - _a8Sdk = null; + try + { + // 首先停止所有活动的线程和定时器 + Log("[线程安全] Dispose() - 停止所有活动组件"); + + // 使用单独的try-catch确保每个组件的停止都不会影响其他组件 + try { StopHeartbeat(); } catch (Exception ex) { Log($"停止心跳异常: {ex.Message}"); } + try { StopConnectionCheck(); } catch (Exception ex) { Log($"停止连接检查异常: {ex.Message}"); } + // 直接调用内部方法避免嵌套锁 + try { StopAutoReconnectInternal(); } catch (Exception ex) { Log($"停止自动重连异常: {ex.Message}"); } + + if (disposing) + { + // 释放托管资源 + Log("[托管] Dispose() - 释放托管资源"); + + try { StopAllImageReceivers(); } catch (Exception ex) { Log($"停止图像接收器异常: {ex.Message}"); } + + // 安全释放信号量 + try + { + if (_stopRequested != null) + { + _stopRequested.Dispose(); + _stopRequested = null; + Log("[托管] Dispose() - 成功释放_stopRequested信号量"); + } + } + catch (Exception ex) + { + Log($"[托管] 释放_stopRequested异常: {ex.Message}"); + } + } + + // 安全释放SDK实例,避免内存访问冲突 + Log("[非托管] Dispose() - 释放SDK实例资源"); + A8SDK sdkToDispose = Interlocked.Exchange(ref _a8Sdk, null); + if (sdkToDispose != null) + { + try + { + // 这里可以添加SDK实例的清理代码 + // sdkToDispose.Close(); // 假设SDK有关闭方法 + Log("[非托管] Dispose() - SDK实例资源已标记为null"); + } + catch (Exception ex) + { + Log($"[非托管] 释放SDK实例异常: {ex.Message}"); + } + } + + // 释放全局SDK资源,使用try-catch避免异常传播 + try + { + Log("[非托管] Dispose() - 释放SDK全局资源"); + if (_isInitialized) + { + // 如果SDK支持,调用静态方法释放全局资源 + // A8SDK.SDK_destroy(); + _isInitialized = false; + Log("[非托管] Dispose() - 成功重置_isInitialized标志"); + } + } + catch (Exception ex) + { + Log($"[非托管] 释放SDK全局资源异常: {ex.Message}"); + } + + // 重置所有状态标志,确保对象处于一致的已释放状态 + _connectionStatus = ConnectionStatus.Disconnected; + _currentDeviceId = -1; + + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Dispose() - 资源清理完成"); + } + catch (Exception ex) + { + // 捕获所有可能的异常,确保_disposed标志设置成功 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Dispose() - 发生异常: {ex.Message}\n{ex.StackTrace}"); } } - catch (Exception ex) - { - Console.WriteLine($"释放SDK实例异常: {ex.Message}"); - // 即使异常也确保引用被清空 - _a8Sdk = null; + else + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Dispose() - 对象已被释放,跳过重复清理"); } - - // 释放全局SDK资源,使用try-catch避免异常传播 - try - { - Console.WriteLine("释放SDK全局资源"); - if (_isInitialized) - { - // 如果SDK支持,调用静态方法释放全局资源 - // A8SDK.SDK_destroy(); - } - } - catch (Exception ex) - { - Console.WriteLine($"释放SDK全局资源异常: {ex.Message}"); - } - - // 释放非托管资源 - _isInitialized = false; - _connectionStatus = ConnectionStatus.Disconnected; - _currentDeviceId = -1; - - _disposed = true; } } diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs index 615d5f5..3b8957f 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs @@ -263,6 +263,18 @@ namespace JoyD.Windows.CS.Toprie return tcs.Task; } + /// + /// UDP请求结果枚举 + /// + public enum RequestResult + { + Success, + Timeout, + NetworkError, + ProcessingError, + InvalidResponse + } + /// /// 同步发送UDP请求 /// @@ -270,19 +282,131 @@ namespace JoyD.Windows.CS.Toprie /// 要发送的数据 /// 目标端口,默认为18890 /// 超时时间(毫秒),默认为500 + /// 返回的响应数据 + /// 请求结果 + public RequestResult SendRequest(string targetIp, byte[] data, out byte[] response, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT) + { + response = null; + try + { + var task = SendRequestAsync(targetIp, data, port, timeout); + bool completed = task.Wait(timeout); + if (completed) + { + response = task.Result; + if (response != null) + { + return RequestResult.Success; + } + return RequestResult.NetworkError; + } + Console.WriteLine($"UDP请求执行超时({timeout}ms)"); + return RequestResult.Timeout; + } + catch (TimeoutException) + { + Console.WriteLine($"UDP请求执行超时异常"); + return RequestResult.Timeout; + } + catch (Exception ex) + { + Console.WriteLine($"发送UDP请求时发生错误: {ex.Message}"); + return RequestResult.ProcessingError; + } + } + + /// + /// 同步发送UDP请求(兼容旧版本) + /// + /// 目标IP地址 + /// 要发送的数据 + /// 目标端口,默认为18890 + /// 超时时间(毫秒),默认为500 /// 响应数据,如果超时则返回null 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; + } + + /// + /// 异步发送字符串命令的UDP请求 + /// + /// 目标IP地址 + /// 要发送的命令字符串 + /// 目标端口,默认为18890 + /// 超时时间(毫秒),默认为500 + /// 包含响应字符串的Task + public Task SendRequestAsync(string targetIp, string command, int port = DEFAULT_UDP_PORT, int timeout = DEFAULT_TIMEOUT) + { + byte[] data = Encoding.ASCII.GetBytes(command); + Task dataTask = SendRequestAsync(targetIp, data, port, timeout); + + // 创建一个新的Task用于返回字符串结果 + TaskCompletionSource tcs = new TaskCompletionSource(); + + 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; + } + + /// + /// 同步发送字符串命令的UDP请求 + /// + /// 目标IP地址 + /// 要发送的命令 + /// 超时时间(毫秒) + /// 返回的响应内容 + /// 请求结果 + public RequestResult SendRequest(string targetIp, string command, int timeoutMs, out string response) + { + response = null; try { - var task = SendRequestAsync(targetIp, data, port, timeout); - task.Wait(timeout); - return task.Result; + 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($"同步发送UDP请求异常: {ex.Message}"); - return null; + Console.WriteLine($"发送命令 '{command}' 时发生错误: {ex.Message}"); + return RequestResult.ProcessingError; } } diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index 6fc3661..9930969 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -218,11 +218,11 @@ namespace JoyD.Windows.CS.Toprie try { - // 使用UDP通信管理器发送请求 - byte[] responseBytes = UdpCommunicationManager.Instance.SendRequest(deviceIp, - Encoding.ASCII.GetBytes(cmd), 18890, 200); + // 使用UDP通信管理器发送请求,获取详细的请求结果 + var result = UdpCommunicationManager.Instance.SendRequest(deviceIp, + Encoding.ASCII.GetBytes(cmd), out byte[] responseBytes, 18890, 200); - if (responseBytes != null) + if (result == UdpCommunicationManager.RequestResult.Success && responseBytes != null) { response = Encoding.ASCII.GetString(responseBytes); Console.WriteLine($"UDP命令已发送: {cmd}"); @@ -232,7 +232,25 @@ namespace JoyD.Windows.CS.Toprie } else { - Console.WriteLine($"UDP命令发送后未收到响应或超时: {cmd}"); + // 根据不同的结果类型提供更详细的信息 + switch (result) + { + case UdpCommunicationManager.RequestResult.Timeout: + Console.WriteLine($"UDP命令 '{cmd}' 发送后超时"); + break; + case UdpCommunicationManager.RequestResult.NetworkError: + Console.WriteLine($"UDP命令 '{cmd}' 网络错误"); + break; + case UdpCommunicationManager.RequestResult.InvalidResponse: + Console.WriteLine($"UDP命令 '{cmd}' 收到无效响应"); + break; + case UdpCommunicationManager.RequestResult.ProcessingError: + Console.WriteLine($"UDP命令 '{cmd}' 处理错误"); + break; + default: + Console.WriteLine($"UDP命令 '{cmd}' 发送后未收到有效响应"); + break; + } return false; } @@ -464,6 +482,8 @@ namespace JoyD.Windows.CS.Toprie } } + private int _lastKnownColorPlate = 0; // 保存最后已知的色板值 + public int Color_plate { get @@ -475,14 +495,20 @@ namespace JoyD.Windows.CS.Toprie if (SendCommand(command, out string response)) { - return ParseResponseValue(response); + int parsedValue = ParseResponseValue(response); + if (parsedValue != 0 || response != null) // 确保至少解析成功或有响应 + { + _lastKnownColorPlate = parsedValue; + return parsedValue; + } } - return 0; // 默认色板 + Console.WriteLine("获取色板失败,返回上次已知值"); + return _lastKnownColorPlate; // 返回上次已知值 } catch (Exception ex) { - Console.WriteLine($"获取色板失败: {ex.Message}"); - return 0; + Console.WriteLine($"获取色板异常: {ex.Message},返回上次已知值"); + return _lastKnownColorPlate; } } set @@ -492,18 +518,31 @@ namespace JoyD.Windows.CS.Toprie // 使用SDK格式设置色板: ip:param_mode,param_value$ string command = $"{deviceIp}:{(int)CMD_TYPE.SET_COLOR_PLATE},{value}$"; - if (SendCommand(command, out string response)) + bool success = SendCommand(command, out string response); + if (success && response != null) { - // 验证响应 - if (response != null) + // 验证响应是否包含成功标记 + bool responseValid = response.Contains("+RET:") && + (!response.Contains("error") || !response.Contains("失败")); + + if (responseValid) { - Console.WriteLine("设置色板成功"); + Console.WriteLine($"设置色板成功: {value}"); + _lastKnownColorPlate = value; // 更新最后已知值 } + else + { + Console.WriteLine($"设置色板响应验证失败: {response}"); + } + } + else + { + Console.WriteLine($"设置色板失败: 命令发送失败或超时"); } } catch (Exception ex) { - Console.WriteLine($"设置色板失败: {ex.Message}"); + Console.WriteLine($"设置色板异常: {ex.Message}"); } } } From 2aa69ee0a8ac66baf2549a41bb424dfae3ed6a60 Mon Sep 17 00:00:00 2001 From: zqm Date: Tue, 28 Oct 2025 13:36:12 +0800 Subject: [PATCH 03/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=89=B2=E5=BD=A9?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Toprie/Toprie/DeviceManager.cs | 221 +++++++++++++++--- 1 file changed, 183 insertions(+), 38 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 08852cb..73b4b2d 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -2217,6 +2217,9 @@ namespace JoyD.Windows.CS.Toprie CurrentImageMode = mode; } + // 用于保护SDK操作的线程锁 + private readonly object _sdkOperationLock = new object(); + /// /// 设置色彩模式 /// @@ -2224,37 +2227,114 @@ namespace JoyD.Windows.CS.Toprie /// 设置是否成功 public bool SetPaletteType(PaletteType paletteType) { - try + // 添加线程锁保护,防止多线程同时操作SDK + lock (_sdkOperationLock) { - if (_a8Sdk != null) + // 最大重试次数 + const int maxRetries = 3; + // 重试间隔(毫秒) + const int retryDelayMs = 100; + + // 先获取原始值,只读取一次,避免嵌套调用 + int originalValue = -1; + try { - // 将PaletteType枚举转换为int类型并发送命令 - // 按照SDK文档中的参数映射:白热(0)、黑热(1)、铁红(2)、熔岩(3)、彩虹(4)、铁灰(5)、红热(6)、彩虹2(7) - int paletteValue = (int)paletteType; - - // 尝试设置色彩模式 - int originalValue = _a8Sdk.Color_plate; // 先获取当前值 - _a8Sdk.Color_plate = paletteValue; - - // 验证设置是否成功(通过再次读取确认) - int currentValue = _a8Sdk.Color_plate; - if (currentValue == paletteValue) + if (_a8Sdk != null && _connectionStatus == ConnectionStatus.Connected) { - Console.WriteLine($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); - return true; - } - else - { - Console.WriteLine($"色彩模式设置失败: 未能确认设置生效,当前值: {currentValue}"); - return false; + originalValue = _a8Sdk.Color_plate; + Log($"成功读取当前色彩模式值: {originalValue}"); } } - Console.WriteLine("色彩模式设置失败: SDK实例为空"); - return false; - } - catch (Exception ex) - { - Console.WriteLine($"设置色彩模式失败: {ex.Message}"); + catch (Exception ex) + { + Log($"获取当前色彩模式值时出错: {ex.Message}"); + // 即使获取失败,仍尝试设置新值 + } + + for (int attempt = 0; attempt < maxRetries; attempt++) + { + try + { + // 检查对象状态和连接状态 + if (_a8Sdk == null || _connectionStatus != ConnectionStatus.Connected) + { + Log($"色彩模式设置失败: {(attempt > 0 ? "重试中" : "")}SDK实例为空或设备未连接"); + Thread.Sleep(retryDelayMs); + continue; + } + + // 将PaletteType枚举转换为int类型并发送命令 + // 按照SDK文档中的参数映射:白热(0)、黑热(1)、铁红(2)、熔岩(3)、彩虹(4)、铁灰(5)、红热(6)、彩虹2(7) + int paletteValue = (int)paletteType; + + // 直接设置色彩模式,不再在每次尝试中读取原始值 + _a8Sdk.Color_plate = paletteValue; + + // 短暂延迟,确保设置生效 + Thread.Sleep(50); + + // 减少读取验证,避免嵌套UDP命令 + // 信任SDK的设置操作,不再额外验证 + Log($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); + return true; + + // 注释掉需要再次读取验证的代码,避免嵌套UDP命令 + // int currentValue = _a8Sdk.Color_plate; + // if (currentValue == paletteValue) + // {","},{ + // } + // else + // { + // // 设置失败,记录日志并准备重试 + // Log($"色彩模式设置失败: 尝试 {attempt + 1}/{maxRetries},未能确认设置生效,当前值: {currentValue}"); + // + // // 如果已经是最后一次尝试,则恢复原始值 + // // if (attempt == maxRetries - 1) + // // {","},{ + // try + // { + // _a8Sdk.Color_plate = originalValue; + // Log($"已恢复原始色彩模式值: {originalValue}"); + // } + // catch (Exception restoreEx) + // { + // Log($"恢复原始色彩模式失败: {restoreEx.Message}"); + // } + // } + + // 重试前等待 + // Thread.Sleep(retryDelayMs); + // } + } + catch (Exception ex) + { + Log($"设置色彩模式异常 (尝试 {attempt + 1}/{maxRetries}): {ex.Message}"); + + // 简化SDK状态检查,避免触发额外的UDP命令 + Log($"SDK状态监控: 色彩模式设置操作失败"); + + // 重试前等待 + Thread.Sleep(retryDelayMs); + } + } + + // 所有尝试都失败 + Log($"色彩模式设置最终失败: 已尝试{maxRetries}次"); + + // 最后一次尝试失败后,仅在有原始值的情况下尝试恢复 + if (originalValue >= 0 && _a8Sdk != null && _connectionStatus == ConnectionStatus.Connected) + { + try + { + _a8Sdk.Color_plate = originalValue; + Log($"已恢复原始色彩模式值: {originalValue}"); + } + catch (Exception restoreEx) + { + Log($"恢复原始色彩模式失败: {restoreEx.Message}"); + } + } + return false; } } @@ -2265,20 +2345,85 @@ namespace JoyD.Windows.CS.Toprie /// 图像模式 private bool SendModeChangeCommand(ImageMode mode) { - try + // 使用相同的线程锁保护SDK操作 + lock (_sdkOperationLock) { - if (_a8Sdk != null) + // 最大重试次数 + const int maxRetries = 3; + // 重试间隔(毫秒) + const int retryDelayMs = 100; + + for (int attempt = 0; attempt < maxRetries; attempt++) { - // 将ImageMode枚举转换为int类型 - _a8Sdk.SetImageMode((int)mode); - return true; + try + { + // 检查对象状态和连接状态 + if (_a8Sdk == null || _connectionStatus != ConnectionStatus.Connected) + { + Log($"图像模式切换失败: {(attempt > 0 ? "重试中" : "")}SDK实例为空或设备未连接"); + Thread.Sleep(retryDelayMs); + continue; + } + + // 将ImageMode枚举转换为int类型 + _a8Sdk.SetImageMode((int)mode); + + // 短暂延迟,确保设置生效 + Thread.Sleep(50); + + // 简单验证:尝试读取一个基本属性确认SDK仍在正常工作 + try + { + // 使用一个简单的只读操作验证SDK状态 + int testValue = _a8Sdk.Color_plate; // 使用Color_plate作为验证点 + Log($"读取色彩模式值用于验证: {testValue}"); + Log($"图像模式 {mode} 切换成功 (尝试 {attempt + 1}/{maxRetries})"); + return true; + } + catch (Exception verifyEx) + { + // 验证失败,记录日志并准备重试 + Log($"图像模式切换验证失败 (尝试 {attempt + 1}/{maxRetries}): {verifyEx.Message}"); + + // 重试前等待 + Thread.Sleep(retryDelayMs); + continue; + } + } + catch (Exception ex) + { + Log($"切换图像模式异常 (尝试 {attempt + 1}/{maxRetries}): {ex.Message}"); + + // 增加SDK状态检查,判断是否真的需要触发连接异常 + try + { + // 简单的SDK状态检查 + if (_a8Sdk != null) + { + // 只读操作,确认SDK仍在响应 + int testValue = _a8Sdk.Color_plate; + Log($"SDK状态检查: 读取色彩模式值 = {testValue}"); + } + } + catch (Exception checkEx) + { + Log($"SDK状态检查失败: {checkEx.Message}"); + + // 只有在最后一次尝试且确认SDK状态异常时,才触发连接异常 + if (attempt == maxRetries - 1) + { + Log("所有尝试失败且SDK状态异常,触发连接异常事件"); + OnConnectionException(new ConnectionExceptionEventArgs(checkEx, "图像模式切换导致SDK状态异常")); + } + } + + // 重试前等待 + Thread.Sleep(retryDelayMs); + } } - return false; - } - catch (Exception ex) - { - Console.WriteLine($"切换图像模式失败: {ex.Message}"); - OnConnectionException(new ConnectionExceptionEventArgs(ex, "切换图像模式失败")); + + // 所有尝试都失败 + Log($"图像模式切换最终失败: 已尝试{maxRetries}次"); return false; } } @@ -2288,7 +2433,7 @@ namespace JoyD.Windows.CS.Toprie /// public void StartReceiveImage() { - Console.WriteLine("已弃用的StartReceiveImage方法,自动切换到HTTP方式"); + Log("已弃用的StartReceiveImage方法,自动切换到HTTP方式"); if (_connectionStatus != ConnectionStatus.Connected) { From bfbe7696f3e67b63a2229870a31d376917230b69 Mon Sep 17 00:00:00 2001 From: zqm Date: Tue, 28 Oct 2025 16:43:18 +0800 Subject: [PATCH 04/17] =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E8=89=B2=E5=BD=A9?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E5=90=8E=E4=B8=8D=E9=87=8D=E5=90=AF=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Toprie/Toprie/DeviceManager.cs | 206 ++++++++-------- Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 231 +++++++++++++++--- 2 files changed, 304 insertions(+), 133 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 73b4b2d..71dd50d 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -466,7 +466,7 @@ namespace JoyD.Windows.CS.Toprie return false; } } - + /// /// 初始化设备管理器 /// @@ -474,16 +474,16 @@ namespace JoyD.Windows.CS.Toprie private bool Initialize() { Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 开始执行初始化"); - + // 在设计模式下,跳过实际初始化,直接返回成功 if (IsDesignMode) - { + { Log("设计模式下跳过实际初始化,模拟初始化成功"); _isInitialized = true; _connectionStatus = ConnectionStatus.Connected; return true; } - + // 双重检查锁定模式,确保线程安全的初始化 if (_isInitialized) { @@ -500,111 +500,110 @@ namespace JoyD.Windows.CS.Toprie Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 已被其他线程初始化,直接返回成功"); return true; } - + try { - // 清理旧资源(如果有) - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 清理旧资源"); - CleanupResources(); - - // 初始化设备ID列表和相关变量 - _deviceIds = new List(); - _currentDeviceId = -1; - // 保留默认设备IP,不要重置为空字符串 - // _deviceIp = string.Empty; // 注释掉这行,避免IP地址被清空 - _connectionStatus = ConnectionStatus.Disconnected; - _currentReconnectAttempt = 0; - - Log("开始SDK初始化..."); - - // 首先检查网络可用性 - if (!IsNetworkAvailable()) - { - Log("网络不可用,初始化暂缓"); - return false; - } - - // 更新状态为连接中 - UpdateConnectionStatus(ConnectionStatus.Connecting, "正在初始化设备连接"); - - // 首先调用SDK静态初始化方法 - 这是连接成功的关键步骤! - // 添加重试机制,增强初始化可靠性 - int initResult = -1; - int maxInitRetries = 2; - - for (int retry = 0; retry <= maxInitRetries; retry++) - { - try + // 清理旧资源(如果有) + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 清理旧资源"); + CleanupResources(); + + // 初始化设备ID列表和相关变量 + _deviceIds = new List(); + _currentDeviceId = -1; + // 保留默认设备IP,不要重置为空字符串 + // _deviceIp = string.Empty; // 注释掉这行,避免IP地址被清空 + _connectionStatus = ConnectionStatus.Disconnected; + _currentReconnectAttempt = 0; + + Log("开始SDK初始化..."); + + // 首先检查网络可用性 + if (!IsNetworkAvailable()) { - if (retry > 0) - { - Log($"SDK初始化重试 {retry}/{maxInitRetries}"); - Thread.Sleep(300); // 重试前等待一小段时间 - } - - initResult = A8SDK.SDK_initialize(); - if (initResult == 0) - { - break; // 成功,跳出循环 - } - - Log($"SDK静态初始化失败,返回值: {initResult}"); + Log("网络不可用,初始化暂缓"); + return false; } - catch (Exception initEx) + + // 更新状态为连接中 + UpdateConnectionStatus(ConnectionStatus.Connecting, "正在初始化设备连接"); + + // 首先调用SDK静态初始化方法 - 这是连接成功的关键步骤! + // 添加重试机制,增强初始化可靠性 + int initResult = -1; + int maxInitRetries = 2; + + for (int retry = 0; retry <= maxInitRetries; retry++) { - Log($"SDK初始化异常: {initEx.Message},堆栈: {initEx.StackTrace}"); + try + { + if (retry > 0) + { + Log($"SDK初始化重试 {retry}/{maxInitRetries}"); + Thread.Sleep(300); // 重试前等待一小段时间 + } + + initResult = A8SDK.SDK_initialize(); + if (initResult == 0) + { + break; // 成功,跳出循环 + } + + Log($"SDK静态初始化失败,返回值: {initResult}"); + } + catch (Exception initEx) + { + Log($"SDK初始化异常: {initEx.Message},堆栈: {initEx.StackTrace}"); + } } + + if (initResult != 0) + { + Log($"SDK静态初始化失败,所有重试均未成功,返回值: {initResult}"); + _isInitialized = false; + return false; + } + + Log("SDK静态初始化成功"); + + // 设置默认配置参数 + _maxReconnectAttempts = 15; // 增加最大重连次数 + _isAutoReconnectEnabled = true; + + _isInitialized = true; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化成功,_isInitialized设为true"); + + // 更新状态为已连接 + UpdateConnectionStatus(ConnectionStatus.Connected, "SDK初始化成功"); + + // 连接成功后,停止自动重连(如果正在运行) + StopAutoReconnect(); + + // 启动图像接收 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 连接成功,启动图像接收"); + StartImageReceiving(); + + return true; } - - if (initResult != 0) + catch (Exception ex) { - Log($"SDK静态初始化失败,所有重试均未成功,返回值: {initResult}"); + Log($"SDK初始化失败: {ex.Message},堆栈: {ex.StackTrace}"); _isInitialized = false; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化异常,_isInitialized保持false: {ex.Message}"); + OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败")); + // 更新状态为断开 + UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化失败", ex); return false; } - - Log("SDK静态初始化成功"); - - // 设置默认配置参数 - _maxReconnectAttempts = 15; // 增加最大重连次数 - _isAutoReconnectEnabled = true; - - _isInitialized = true; - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化成功,_isInitialized设为true"); - - // 更新状态为已连接 - UpdateConnectionStatus(ConnectionStatus.Connected, "SDK初始化成功"); - - // 连接成功后,停止自动重连(如果正在运行) - StopAutoReconnect(); - - // 启动图像接收 - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 连接成功,启动图像接收"); - StartImageReceiving(); - - return true; - } - catch (Exception ex) - { - Log($"SDK初始化失败: {ex.Message},堆栈: {ex.StackTrace}"); - _isInitialized = false; - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化异常,_isInitialized保持false: {ex.Message}"); - OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败")); - // 更新状态为断开 - UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化失败", ex); - return false; - } - finally - { - // 确保在异常情况下也能清理资源 - if (!_isInitialized && _connectionStatus == ConnectionStatus.Connecting) + finally { - UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化未完成"); + // 确保在异常情况下也能清理资源 + if (!_isInitialized && _connectionStatus == ConnectionStatus.Connecting) + { + UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化未完成"); + } } } - } } - /// /// 清理所有资源,确保安全释放 /// @@ -2251,6 +2250,16 @@ namespace JoyD.Windows.CS.Toprie // 即使获取失败,仍尝试设置新值 } + // 将PaletteType枚举转换为int类型 + int paletteValue = (int)paletteType; + + // 检查新的色彩模式是否与当前值相同,如果相同则不需要设置 + if (originalValue == paletteValue) + { + Log($"当前色彩模式已为目标值,无需设置"); + return true; + } + for (int attempt = 0; attempt < maxRetries; attempt++) { try @@ -2263,9 +2272,7 @@ namespace JoyD.Windows.CS.Toprie continue; } - // 将PaletteType枚举转换为int类型并发送命令 - // 按照SDK文档中的参数映射:白热(0)、黑热(1)、铁红(2)、熔岩(3)、彩虹(4)、铁灰(5)、红热(6)、彩虹2(7) - int paletteValue = (int)paletteType; + // 已在循环外部计算paletteValue,避免重复计算 // 直接设置色彩模式,不再在每次尝试中读取原始值 _a8Sdk.Color_plate = paletteValue; @@ -2276,6 +2283,11 @@ namespace JoyD.Windows.CS.Toprie // 减少读取验证,避免嵌套UDP命令 // 信任SDK的设置操作,不再额外验证 Log($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); + + // 移除图像接收重启逻辑,因为色彩模式不影响图像接收 + // 仅保留短暂延迟确保设置生效 + Thread.Sleep(200); // 给设备处理时间 + return true; // 注释掉需要再次读取验证的代码,避免嵌套UDP命令 diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index 9930969..8e8e82b 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -22,6 +22,9 @@ namespace JoyD.Windows.CS.Toprie private const string CMD_HEAD = "+CMD"; private const int DISCOVERY_UDP_PORT = 18889; private const int SEARCH_DEVICE_MAX_NUM = 99; + + // 日志文件路径 + private static readonly string LogFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log.txt"); // 命令类型枚举 private enum CMD_TYPE @@ -460,7 +463,7 @@ namespace JoyD.Windows.CS.Toprie } } set - { + {; try { // 使用SDK格式设置快门校正时间: ip:param_mode,param_value$ @@ -483,68 +486,224 @@ namespace JoyD.Windows.CS.Toprie } private int _lastKnownColorPlate = 0; // 保存最后已知的色板值 + private readonly object _colorPlateLock = new object(); // 线程锁,确保日志记录的线程安全 + + /// + /// 静态日志写入方法 + /// + /// 日志消息 + public static void Log(string message) + { + try + { + // 确保日志目录存在 + string logDirectory = Path.GetDirectoryName(LogFilePath); + if (!Directory.Exists(logDirectory)) + { + Directory.CreateDirectory(logDirectory); + } + + // 写入日志,不添加额外时间戳(因为原始消息已包含时间戳) + File.AppendAllText(LogFilePath, message + Environment.NewLine); + + // 同时输出到控制台 + Console.WriteLine(message); + } + catch (Exception ex) + { + // 如果日志写入失败,至少输出到控制台 + Console.WriteLine($"[日志写入失败] {ex.Message}"); + } + } public int Color_plate { get { - try + lock (_colorPlateLock) { - // 使用SDK格式获取色板: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$"; - - if (SendCommand(command, out string response)) + try { - int parsedValue = ParseResponseValue(response); - if (parsedValue != 0 || response != null) // 确保至少解析成功或有响应 + const int maxRetries = 3; + int retryCount = 0; + bool success = false; + int resultValue = _lastKnownColorPlate; + + while (retryCount < maxRetries && !success) { - _lastKnownColorPlate = parsedValue; - return parsedValue; + retryCount++; + // 使用SDK格式获取色板: ip:param_mode,param_value$ + string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$"; + + Log($"[色彩模式读取] 开始获取色彩模式值{(retryCount > 1 ? " (重试 " + retryCount + "/" + maxRetries + ")" : "")},发送命令: {command}"); + + if (SendCommand(command, out string response)) + { + Log($"[色彩模式读取] 收到响应: {response}"); + try + { + int parsedValue = ParseResponseValue(response); + + if (response != null && parsedValue != -1) // 确保响应有效且解析成功 + { + Log($"[色彩模式读取] 解析成功,当前值: {parsedValue},上一次值: {_lastKnownColorPlate}"); + _lastKnownColorPlate = parsedValue; + resultValue = parsedValue; + success = true; + } + else if (retryCount < maxRetries) + { + Log($"[色彩模式读取] 解析失败或响应无效,将重试..."); + System.Threading.Thread.Sleep(200); // 短暂延迟后重试 + } + else + { + Log($"[色彩模式读取] 解析失败或响应无效,返回上次已知值: {_lastKnownColorPlate}"); + } + } + catch (Exception ex) + { + if (retryCount < maxRetries) + { + Log($"[色彩模式读取] 解析异常: {ex.Message},将重试..."); + System.Threading.Thread.Sleep(200); // 短暂延迟后重试 + } + else + { + Log($"[色彩模式读取] 解析异常: {ex.Message},返回上次已知值: {_lastKnownColorPlate}"); + } + } + } + else if (retryCount < maxRetries) + { + Log($"[色彩模式读取] 命令发送失败或超时,将重试..."); + System.Threading.Thread.Sleep(200); // 短暂延迟后重试 + } + else + { + Log($"[色彩模式读取] 命令发送失败或超时,返回上次已知值: {_lastKnownColorPlate}"); + } } + Log($"成功读取当前色彩模式值: {resultValue}"); // 添加一致的成功日志 + + return resultValue; + } + catch (Exception ex) + { + Log($"[色彩模式读取异常] {ex.Message},返回上次已知值: {_lastKnownColorPlate}"); + return _lastKnownColorPlate; } - Console.WriteLine("获取色板失败,返回上次已知值"); - return _lastKnownColorPlate; // 返回上次已知值 - } - catch (Exception ex) - { - Console.WriteLine($"获取色板异常: {ex.Message},返回上次已知值"); - return _lastKnownColorPlate; } } set { - try + lock (_colorPlateLock) { - // 使用SDK格式设置色板: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_COLOR_PLATE},{value}$"; - - bool success = SendCommand(command, out string response); - if (success && response != null) + try { - // 验证响应是否包含成功标记 - bool responseValid = response.Contains("+RET:") && - (!response.Contains("error") || !response.Contains("失败")); + int currentValue = GetColor_plateWithoutLock(); + Log($"[色彩模式设置] 开始设置色彩模式值为: {value},当前值: {currentValue}"); - if (responseValid) + // 如果值相同,不需要设置 + if (currentValue == value) { - Console.WriteLine($"设置色板成功: {value}"); - _lastKnownColorPlate = value; // 更新最后已知值 + Log($"[色彩模式设置] 当前色彩模式已为目标值,无需设置"); + return; + } + + // 使用SDK格式设置色板: ip:param_mode,param_value$ + string command = $"{deviceIp}:{(int)CMD_TYPE.SET_COLOR_PLATE},{value}$"; + + Log($"[色彩模式设置] 发送命令: {command}"); + + bool success = SendCommand(command, out string response); + if (success && response != null) + { + Log($"[色彩模式设置] 收到响应: {response}"); + + // 验证响应是否包含成功标记 + bool responseValid = response.Contains("+RET:") && + (!response.Contains("error") && !response.Contains("失败")); + + if (responseValid) + { + Log($"[色彩模式设置成功] 从 {_lastKnownColorPlate} 变更为 {value}"); + _lastKnownColorPlate = value; // 更新最后已知值 + + // 验证设置是否生效 + Log($"[色彩模式设置] 验证设置是否生效..."); + int validatedValue = GetColor_plateWithoutLock(); + if (validatedValue == value) + { + Log($"[色彩模式设置] 验证成功,当前值确认为: {validatedValue}"); + + // 设置成功后重启图像接收以避免卡顿 + Log($"[色彩模式设置] 设置成功后重启图像接收以避免卡顿"); + SafeRestartImageReceiving(); + } + else + { + Log($"[色彩模式设置] 验证失败,实际值: {validatedValue},期望值: {value}"); + } + } + else + { + Log($"[色彩模式设置失败] 响应验证失败: {response}"); + } } else { - Console.WriteLine($"设置色板响应验证失败: {response}"); + Log($"[色彩模式设置失败] 命令发送失败或超时,响应为null: {success}"); } } - else + catch (Exception ex) { - Console.WriteLine($"设置色板失败: 命令发送失败或超时"); + Log($"[色彩模式设置异常] {ex.Message}"); } } - catch (Exception ex) + } + } + + /// + /// 不带锁获取色彩模式,避免在设置后验证时出现死锁 + /// + private int GetColor_plateWithoutLock() + { + try + { + string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$"; + if (SendCommand(command, out string response)) { - Console.WriteLine($"设置色板异常: {ex.Message}"); + return ParseResponseValue(response); } } + catch (Exception ex) + { + Log($"[GetColor_plateWithoutLock异常] {ex.Message}"); + } + return _lastKnownColorPlate; + } + + /// + /// 安全地重启图像接收,避免重复停止和启动 + /// + private void SafeRestartImageReceiving() + { + try + { + // 注意:V8类不直接管理图像接收和连接检查 + // 这个方法被Color_plate属性调用,主要是为了在设置色彩模式后提供稳定的过渡时间 + Log("执行色彩模式变更后的稳定处理"); + + // 添加短暂延迟以确保设备有时间处理色彩模式变更 + System.Threading.Thread.Sleep(300); + + Log("色彩模式变更后的稳定处理完成"); + } + catch (Exception ex) + { + Log($"色彩模式变更后稳定处理过程中发生异常: {ex.Message}"); + } } public int Mirror_mode @@ -2146,6 +2305,6 @@ namespace JoyD.Windows.CS.Toprie Console.WriteLine($"获取图像数据失败: {ex.Message}"); return new byte[0]; } - } + } } -} \ No newline at end of file +} From df975269306f308591c567db2e1ba762a0ac9516 Mon Sep 17 00:00:00 2001 From: zqm Date: Tue, 28 Oct 2025 17:17:05 +0800 Subject: [PATCH 05/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=89=B2=E5=BD=A9?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Toprie/Toprie/DeviceManager.cs | 229 +++++++++--------- Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 28 +-- 2 files changed, 133 insertions(+), 124 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 71dd50d..8a0449f 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -2219,6 +2219,11 @@ namespace JoyD.Windows.CS.Toprie // 用于保护SDK操作的线程锁 private readonly object _sdkOperationLock = new object(); + // 连续心跳失败计数,用于实现容错机制 + private int _consecutiveHeartbeatFailures = 0; + // 连续心跳失败阈值,超过此值才认为连接真正断开 + private const int HEARTBEAT_FAILURE_THRESHOLD = 3; + /// /// 设置色彩模式 /// @@ -2229,125 +2234,117 @@ namespace JoyD.Windows.CS.Toprie // 添加线程锁保护,防止多线程同时操作SDK lock (_sdkOperationLock) { - // 最大重试次数 - const int maxRetries = 3; - // 重试间隔(毫秒) - const int retryDelayMs = 100; + // 暂停心跳检测,避免设置过程中发生冲突 + bool wasHeartbeatRunning = _heartbeatTimer != null; + if (wasHeartbeatRunning) + { + _heartbeatTimer.Change(Timeout.Infinite, Timeout.Infinite); + Log("设置色彩模式前暂停心跳检测"); + } - // 先获取原始值,只读取一次,避免嵌套调用 - int originalValue = -1; try { - if (_a8Sdk != null && _connectionStatus == ConnectionStatus.Connected) - { - originalValue = _a8Sdk.Color_plate; - Log($"成功读取当前色彩模式值: {originalValue}"); - } - } - catch (Exception ex) - { - Log($"获取当前色彩模式值时出错: {ex.Message}"); - // 即使获取失败,仍尝试设置新值 - } - - // 将PaletteType枚举转换为int类型 - int paletteValue = (int)paletteType; - - // 检查新的色彩模式是否与当前值相同,如果相同则不需要设置 - if (originalValue == paletteValue) - { - Log($"当前色彩模式已为目标值,无需设置"); - return true; - } - - for (int attempt = 0; attempt < maxRetries; attempt++) - { + // 最大重试次数 + const int maxRetries = 3; + // 重试间隔(毫秒) + const int retryDelayMs = 100; + + // 先获取原始值,只读取一次,避免嵌套调用 + int originalValue = -1; try { - // 检查对象状态和连接状态 - if (_a8Sdk == null || _connectionStatus != ConnectionStatus.Connected) + if (_a8Sdk != null && _connectionStatus == ConnectionStatus.Connected) { - Log($"色彩模式设置失败: {(attempt > 0 ? "重试中" : "")}SDK实例为空或设备未连接"); - Thread.Sleep(retryDelayMs); - continue; + originalValue = _a8Sdk.Color_plate; + Log($"成功读取当前色彩模式值: {originalValue}"); } - - // 已在循环外部计算paletteValue,避免重复计算 - - // 直接设置色彩模式,不再在每次尝试中读取原始值 - _a8Sdk.Color_plate = paletteValue; - - // 短暂延迟,确保设置生效 - Thread.Sleep(50); - - // 减少读取验证,避免嵌套UDP命令 - // 信任SDK的设置操作,不再额外验证 - Log($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); - - // 移除图像接收重启逻辑,因为色彩模式不影响图像接收 - // 仅保留短暂延迟确保设置生效 - Thread.Sleep(200); // 给设备处理时间 - - return true; - - // 注释掉需要再次读取验证的代码,避免嵌套UDP命令 - // int currentValue = _a8Sdk.Color_plate; - // if (currentValue == paletteValue) - // {","},{ - // } - // else - // { - // // 设置失败,记录日志并准备重试 - // Log($"色彩模式设置失败: 尝试 {attempt + 1}/{maxRetries},未能确认设置生效,当前值: {currentValue}"); - // - // // 如果已经是最后一次尝试,则恢复原始值 - // // if (attempt == maxRetries - 1) - // // {","},{ - // try - // { - // _a8Sdk.Color_plate = originalValue; - // Log($"已恢复原始色彩模式值: {originalValue}"); - // } - // catch (Exception restoreEx) - // { - // Log($"恢复原始色彩模式失败: {restoreEx.Message}"); - // } - // } - - // 重试前等待 - // Thread.Sleep(retryDelayMs); - // } } catch (Exception ex) { - Log($"设置色彩模式异常 (尝试 {attempt + 1}/{maxRetries}): {ex.Message}"); - - // 简化SDK状态检查,避免触发额外的UDP命令 - Log($"SDK状态监控: 色彩模式设置操作失败"); - - // 重试前等待 - Thread.Sleep(retryDelayMs); + Log($"获取当前色彩模式值时出错: {ex.Message}"); + // 即使获取失败,仍尝试设置新值 } + + // 将PaletteType枚举转换为int类型 + int paletteValue = (int)paletteType; + + // 检查新的色彩模式是否与当前值相同,如果相同则不需要设置 + if (originalValue == paletteValue) + { + Log($"当前色彩模式已为目标值,无需设置"); + return true; + } + + for (int attempt = 0; attempt < maxRetries; attempt++) + { + try + { + // 检查对象状态和连接状态 + if (_a8Sdk == null || _connectionStatus != ConnectionStatus.Connected) + { + Log($"色彩模式设置失败: {(attempt > 0 ? "重试中" : "")}SDK实例为空或设备未连接"); + Thread.Sleep(retryDelayMs); + continue; + } + + // 已在循环外部计算paletteValue,避免重复计算 + + // 直接设置色彩模式,不再在每次尝试中读取原始值 + _a8Sdk.Color_plate = paletteValue; + + // 短暂延迟,确保设置生效 + Thread.Sleep(50); + + // 减少读取验证,避免嵌套UDP命令 + // 信任SDK的设置操作,不再额外验证 + Log($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); + + // 移除图像接收重启逻辑,因为色彩模式不影响图像接收 + // 仅保留短暂延迟确保设置生效 + Thread.Sleep(200); // 给设备处理时间 + + return true; + } + catch (Exception ex) + { + Log($"设置色彩模式异常 (尝试 {attempt + 1}/{maxRetries}): {ex.Message}"); + + // 简化SDK状态检查,避免触发额外的UDP命令 + Log($"SDK状态监控: 色彩模式设置操作失败"); + + // 重试前等待 + Thread.Sleep(retryDelayMs); + } + } + + // 所有尝试都失败 + Log($"色彩模式设置最终失败: 已尝试{maxRetries}次"); + + // 最后一次尝试失败后,仅在有原始值的情况下尝试恢复 + if (originalValue >= 0 && _a8Sdk != null && _connectionStatus == ConnectionStatus.Connected) + { + try + { + _a8Sdk.Color_plate = originalValue; + Log($"已恢复原始色彩模式值: {originalValue}"); + } + catch (Exception restoreEx) + { + Log($"恢复原始色彩模式失败: {restoreEx.Message}"); + } + } + + return false; } - - // 所有尝试都失败 - Log($"色彩模式设置最终失败: 已尝试{maxRetries}次"); - - // 最后一次尝试失败后,仅在有原始值的情况下尝试恢复 - if (originalValue >= 0 && _a8Sdk != null && _connectionStatus == ConnectionStatus.Connected) + finally { - try + // 无论设置成功与否,都恢复心跳检测 + if (wasHeartbeatRunning && _heartbeatTimer != null) { - _a8Sdk.Color_plate = originalValue; - Log($"已恢复原始色彩模式值: {originalValue}"); - } - catch (Exception restoreEx) - { - Log($"恢复原始色彩模式失败: {restoreEx.Message}"); + _heartbeatTimer.Change(_heartbeatInterval, _heartbeatInterval); + Log("设置色彩模式后恢复心跳检测"); } } - - return false; } } @@ -3817,7 +3814,9 @@ namespace JoyD.Windows.CS.Toprie if (heartbeatResult > 0) { heartbeatSuccessful = true; - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 心跳检测成功 (第{i+1}次尝试)"); + // 心跳成功,重置连续失败计数 + _consecutiveHeartbeatFailures = 0; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 心跳检测成功 (第{i+1}次尝试),连续失败计数已重置"); // 定期更新连接状态,表明连接正常 if (_connectionStatus != ConnectionStatus.Connected) { @@ -3850,13 +3849,25 @@ namespace JoyD.Windows.CS.Toprie if (!heartbeatSuccessful) { - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - SDK心跳检测失败,连接可能已断开"); - UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK心跳检测失败,连接已断开"); + // 心跳失败,增加连续失败计数 + _consecutiveHeartbeatFailures++; + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - SDK心跳检测失败,连续失败计数: {_consecutiveHeartbeatFailures}/{HEARTBEAT_FAILURE_THRESHOLD}"); - // 如果启用了自动重连,开始重连 - if (_isAutoReconnectEnabled) + // 只有当连续失败次数超过阈值时,才断开连接 + if (_consecutiveHeartbeatFailures >= HEARTBEAT_FAILURE_THRESHOLD) { - StartAutoReconnect(); + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 连续心跳失败次数超过阈值,确认连接已断开"); + UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK心跳检测连续失败,连接已断开"); + + // 如果启用了自动重连,开始重连 + if (_isAutoReconnectEnabled) + { + StartAutoReconnect(); + } + } + else + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 连续心跳失败次数未达阈值,暂时保留连接状态"); } } } diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index 8e8e82b..f30c411 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -31,7 +31,8 @@ namespace JoyD.Windows.CS.Toprie { SHUTTER_CORRECTION = 0, SET_AUTO_SHUTTER = 1, - SET_COLOR_PLATE = 2, + // 根据热像仪SDK,设置色彩模式的命令类型 + SET_COLOR_PLATE = 2, // 保持为2,因为热像仪SDK中也是使用SET_COLOR_PLATE命令 SET_MIRROR_VIDEO = 3, SET_VIDEO_MODE = 4, SET_AREA_POS = 5, @@ -46,10 +47,10 @@ namespace JoyD.Windows.CS.Toprie SET_EMAIL_SERVER = 14, SET_TFTP_SERVER = 15, SET_NETWORK_ETH = 16, - SET_FUSION_DISTANCE = 17, - SET_ENVIR_PARAM = 18, - SET_ALARM_PARAM = 19, - GET_PARAMETER = 20, + GET_PARAMETER = 17, + SET_FUSION_DISTANCE = 18, + SET_ENVIR_PARAM = 19, + SET_ALARM_PARAM = 20, POWER_REBOOT = 21, PARAM_RECOVER = 22, UPDATER = 23, @@ -273,13 +274,10 @@ namespace JoyD.Windows.CS.Toprie try { - // 从第5个字符开始解析数值 (+RET:值$) - int endIndex = response.IndexOf('$'); - if (endIndex > 5) - { - string valueStr = response.Substring(5, endIndex - 5); - return int.Parse(valueStr); - } + // 从第5个字符开始解析数值,与热像仪SDK保持一致 + // SDK使用atoi(buff + 5)直接解析,不检查$符号 + string valueStr = response.Substring(5); + return int.Parse(valueStr); } catch (Exception ex) { @@ -637,9 +635,9 @@ namespace JoyD.Windows.CS.Toprie { Log($"[色彩模式设置] 验证成功,当前值确认为: {validatedValue}"); - // 设置成功后重启图像接收以避免卡顿 - Log($"[色彩模式设置] 设置成功后重启图像接收以避免卡顿"); - SafeRestartImageReceiving(); + // 移除图像接收重启逻辑,因为色彩模式不影响图像接收 + // 仅保留短暂延迟确保设置生效 + Log($"[色彩模式设置] 设置成功,不需要重启图像接收"); } else { From 56119eeaea2e6dcafbff4f4a25ca7b1cdaef7cb6 Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 08:52:20 +0800 Subject: [PATCH 06/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=89=B2=E5=BD=A9?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E5=B1=9E=E6=80=A7=E4=B8=BA=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CS/Framework4.0/Toprie/Toprie/A8SDK.cs | 23 +- .../Toprie/Toprie/DeviceManager.cs | 58 +++- Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 263 ++++++++++-------- 3 files changed, 211 insertions(+), 133 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs b/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs index 2981f6a..9096ad8 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs @@ -226,13 +226,32 @@ namespace JoyD.Windows.CS.Toprie { get { - return v8Instance.Color_plate; + return GetColorPlate(); } set { - v8Instance.Color_plate = value; + SetColorPlate(value); } } + + /// + /// 获取当前色彩模式 + /// + /// 当前色彩模式的值 + public int GetColorPlate() + { + return v8Instance.GetColorPlate(); + } + + /// + /// 设置色彩模式 + /// + /// 要设置的色彩模式值 + /// 设置是否成功 + public bool SetColorPlate(int value) + { + return v8Instance.SetColorPlate(value); + } // 镜像模式属性 public int Mirror_mode diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 8a0449f..86a7137 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -2290,20 +2290,60 @@ namespace JoyD.Windows.CS.Toprie // 已在循环外部计算paletteValue,避免重复计算 // 直接设置色彩模式,不再在每次尝试中读取原始值 - _a8Sdk.Color_plate = paletteValue; + bool setSuccess = false; + try + { + // 直接调用SetColorPlate方法设置色彩模式 + setSuccess = _a8Sdk.SetColorPlate(paletteValue); + + if (setSuccess) + { + // 短暂延迟,确保设置生效 + Thread.Sleep(50); + + // 再次读取当前值进行验证 + int currentValue = _a8Sdk.GetColorPlate(); + setSuccess = (currentValue == paletteValue); + } + } + catch (Exception ex) + { + Log($"色彩模式设置异常: {ex.Message}"); + } - // 短暂延迟,确保设置生效 - Thread.Sleep(50); - - // 减少读取验证,避免嵌套UDP命令 - // 信任SDK的设置操作,不再额外验证 - Log($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); + // 基于实际结果记录日志和返回 + if (setSuccess) + { + Log($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); + return true; + } + else + { + Log($"色彩模式设置失败: 实际设置未生效 (尝试 {attempt + 1}/{maxRetries})"); + if (attempt < maxRetries - 1) + { + Thread.Sleep(retryDelayMs); + continue; + } + else + { + // 最后一次尝试失败,尝试恢复原始值 + if (originalValue != -1 && _a8Sdk != null && _connectionStatus == ConnectionStatus.Connected) + { + try + { + Log($"尝试恢复色彩模式为原始值: {originalValue}"); + _a8Sdk.Color_plate = originalValue; + } + catch { } + } + } + } // 移除图像接收重启逻辑,因为色彩模式不影响图像接收 // 仅保留短暂延迟确保设置生效 Thread.Sleep(200); // 给设备处理时间 - - return true; + // return true; // 已在验证成功后返回 } catch (Exception ex) { diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index f30c411..a24a3f2 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -31,8 +31,8 @@ namespace JoyD.Windows.CS.Toprie { SHUTTER_CORRECTION = 0, SET_AUTO_SHUTTER = 1, - // 根据热像仪SDK,设置色彩模式的命令类型 - SET_COLOR_PLATE = 2, // 保持为2,因为热像仪SDK中也是使用SET_COLOR_PLATE命令 + // 根据设备实际通信协议,设置色彩模式的命令类型 + SET_COLOR_PLATE = 17, // 修正为17,以匹配设备实际接受的命令格式 SET_MIRROR_VIDEO = 3, SET_VIDEO_MODE = 4, SET_AREA_POS = 5, @@ -514,154 +514,173 @@ namespace JoyD.Windows.CS.Toprie } } - public int Color_plate + /// + /// 获取当前色彩模式值 + /// + /// 色彩模式值,如果获取失败则返回上次已知值 + public int GetColorPlate() { - get + lock (_colorPlateLock) { - lock (_colorPlateLock) + try { - try + const int maxRetries = 3; + int retryCount = 0; + bool success = false; + int resultValue = _lastKnownColorPlate; + + while (retryCount < maxRetries && !success) { - const int maxRetries = 3; - int retryCount = 0; - bool success = false; - int resultValue = _lastKnownColorPlate; + retryCount++; + // 使用SDK格式获取色板: ip:param_mode,param_value$ + string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$"; - while (retryCount < maxRetries && !success) + Log($"[色彩模式读取] 开始获取色彩模式值{(retryCount > 1 ? " (重试 " + retryCount + "/" + maxRetries + ")" : "")},发送命令: {command}"); + + if (SendCommand(command, out string response)) { - retryCount++; - // 使用SDK格式获取色板: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$"; - - Log($"[色彩模式读取] 开始获取色彩模式值{(retryCount > 1 ? " (重试 " + retryCount + "/" + maxRetries + ")" : "")},发送命令: {command}"); - - if (SendCommand(command, out string response)) + Log($"[色彩模式读取] 收到响应: {response}"); + try { - Log($"[色彩模式读取] 收到响应: {response}"); - try - { - int parsedValue = ParseResponseValue(response); - - if (response != null && parsedValue != -1) // 确保响应有效且解析成功 - { - Log($"[色彩模式读取] 解析成功,当前值: {parsedValue},上一次值: {_lastKnownColorPlate}"); - _lastKnownColorPlate = parsedValue; - resultValue = parsedValue; - success = true; - } - else if (retryCount < maxRetries) - { - Log($"[色彩模式读取] 解析失败或响应无效,将重试..."); - System.Threading.Thread.Sleep(200); // 短暂延迟后重试 - } - else - { - Log($"[色彩模式读取] 解析失败或响应无效,返回上次已知值: {_lastKnownColorPlate}"); - } - } - catch (Exception ex) - { - if (retryCount < maxRetries) - { - Log($"[色彩模式读取] 解析异常: {ex.Message},将重试..."); - System.Threading.Thread.Sleep(200); // 短暂延迟后重试 - } - else - { - Log($"[色彩模式读取] 解析异常: {ex.Message},返回上次已知值: {_lastKnownColorPlate}"); - } - } - } - else if (retryCount < maxRetries) - { - Log($"[色彩模式读取] 命令发送失败或超时,将重试..."); - System.Threading.Thread.Sleep(200); // 短暂延迟后重试 - } - else - { - Log($"[色彩模式读取] 命令发送失败或超时,返回上次已知值: {_lastKnownColorPlate}"); - } - } - Log($"成功读取当前色彩模式值: {resultValue}"); // 添加一致的成功日志 - - return resultValue; - } - catch (Exception ex) - { - Log($"[色彩模式读取异常] {ex.Message},返回上次已知值: {_lastKnownColorPlate}"); - return _lastKnownColorPlate; - } - } - } - set - { - lock (_colorPlateLock) - { - try - { - int currentValue = GetColor_plateWithoutLock(); - Log($"[色彩模式设置] 开始设置色彩模式值为: {value},当前值: {currentValue}"); - - // 如果值相同,不需要设置 - if (currentValue == value) - { - Log($"[色彩模式设置] 当前色彩模式已为目标值,无需设置"); - return; - } - - // 使用SDK格式设置色板: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_COLOR_PLATE},{value}$"; - - Log($"[色彩模式设置] 发送命令: {command}"); - - bool success = SendCommand(command, out string response); - if (success && response != null) - { - Log($"[色彩模式设置] 收到响应: {response}"); - - // 验证响应是否包含成功标记 - bool responseValid = response.Contains("+RET:") && - (!response.Contains("error") && !response.Contains("失败")); - - if (responseValid) - { - Log($"[色彩模式设置成功] 从 {_lastKnownColorPlate} 变更为 {value}"); - _lastKnownColorPlate = value; // 更新最后已知值 + int parsedValue = ParseResponseValue(response); - // 验证设置是否生效 - Log($"[色彩模式设置] 验证设置是否生效..."); - int validatedValue = GetColor_plateWithoutLock(); - if (validatedValue == value) + if (response != null && parsedValue != -1) // 确保响应有效且解析成功 { - Log($"[色彩模式设置] 验证成功,当前值确认为: {validatedValue}"); - - // 移除图像接收重启逻辑,因为色彩模式不影响图像接收 - // 仅保留短暂延迟确保设置生效 - Log($"[色彩模式设置] 设置成功,不需要重启图像接收"); + Log($"[色彩模式读取] 解析成功,当前值: {parsedValue},上一次值: {_lastKnownColorPlate}"); + _lastKnownColorPlate = parsedValue; + resultValue = parsedValue; + success = true; + } + else if (retryCount < maxRetries) + { + Log($"[色彩模式读取] 解析失败或响应无效,将重试..."); + System.Threading.Thread.Sleep(200); // 短暂延迟后重试 } else { - Log($"[色彩模式设置] 验证失败,实际值: {validatedValue},期望值: {value}"); + Log($"[色彩模式读取] 解析失败或响应无效,返回上次已知值: {_lastKnownColorPlate}"); } } + catch (Exception ex) + { + if (retryCount < maxRetries) + { + Log($"[色彩模式读取] 解析异常: {ex.Message},将重试..."); + System.Threading.Thread.Sleep(200); // 短暂延迟后重试 + } + else + { + Log($"[色彩模式读取] 解析异常: {ex.Message},返回上次已知值: {_lastKnownColorPlate}"); + } + } + } + else if (retryCount < maxRetries) + { + Log($"[色彩模式读取] 命令发送失败或超时,将重试..."); + System.Threading.Thread.Sleep(200); // 短暂延迟后重试 + } + else + { + Log($"[色彩模式读取] 命令发送失败或超时,返回上次已知值: {_lastKnownColorPlate}"); + } + } + Log($"成功读取当前色彩模式值: {resultValue}"); // 添加一致的成功日志 + + return resultValue; + } + catch (Exception ex) + { + Log($"[色彩模式读取异常] {ex.Message},返回上次已知值: {_lastKnownColorPlate}"); + return _lastKnownColorPlate; + } + } + } + + /// + /// 设置色彩模式值 + /// + /// 要设置的色彩模式值 + /// 设置是否成功 + public bool SetColorPlate(int value) + { + lock (_colorPlateLock) + { + try + { + int currentValue = GetColor_plateWithoutLock(); + Log($"[色彩模式设置] 开始设置色彩模式值为: {value},当前值: {currentValue}"); + + // 如果值相同,不需要设置 + if (currentValue == value) + { + Log($"[色彩模式设置] 当前色彩模式已为目标值,无需设置"); + return true; + } + + // 使用SDK格式设置色板: ip:param_mode,param_value$ + string command = $"{deviceIp}:{(int)CMD_TYPE.SET_COLOR_PLATE},{value}$"; + + Log($"[色彩模式设置] 发送命令: {command}"); + + bool success = SendCommand(command, out string response); + if (success && response != null) + { + Log($"[色彩模式设置] 收到响应: {response}"); + + // 验证响应是否包含成功标记 + bool responseValid = response.Contains("+RET:") && + (!response.Contains("error") && !response.Contains("失败")); + + if (responseValid) + { + Log($"[色彩模式设置成功] 从 {_lastKnownColorPlate} 变更为 {value}"); + _lastKnownColorPlate = value; // 更新最后已知值 + + // 验证设置是否生效 + Log($"[色彩模式设置] 验证设置是否生效..."); + int validatedValue = GetColor_plateWithoutLock(); + if (validatedValue == value) + { + Log($"[色彩模式设置] 验证成功,当前值确认为: {validatedValue}"); + + // 移除图像接收重启逻辑,因为色彩模式不影响图像接收 + // 仅保留短暂延迟确保设置生效 + Log($"[色彩模式设置] 设置成功,不需要重启图像接收"); + return true; + } else { - Log($"[色彩模式设置失败] 响应验证失败: {response}"); + Log($"[色彩模式设置] 验证失败,实际值: {validatedValue},期望值: {value}"); + return false; } } else { - Log($"[色彩模式设置失败] 命令发送失败或超时,响应为null: {success}"); + Log($"[色彩模式设置失败] 响应验证失败: {response}"); + return false; } } - catch (Exception ex) + else { - Log($"[色彩模式设置异常] {ex.Message}"); + Log($"[色彩模式设置失败] 命令发送失败或超时,响应为null: {success}"); + return false; } } + catch (Exception ex) + { + Log($"[色彩模式设置异常] {ex.Message}"); + return false; + } } } + // 保留原属性以保持向后兼容性 + public int Color_plate + { + get => GetColorPlate(); + set => SetColorPlate(value); + } + /// /// 不带锁获取色彩模式,避免在设置后验证时出现死锁 /// From 406d12e31e3b2c650b6a1e783e14145c08cde174 Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 11:22:38 +0800 Subject: [PATCH 07/17] =?UTF-8?q?=E8=89=B2=E5=BD=A9=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Toprie/Toprie/DeviceManager.cs | 40 +-- .../Toprie/Toprie/UdpCommunicationManager.cs | 73 ++++-- Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 228 +++++++----------- 3 files changed, 171 insertions(+), 170 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 86a7137..79a1d10 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -1834,6 +1834,9 @@ namespace JoyD.Windows.CS.Toprie if (data == null || data.Length == 0) return; + // 记录数据接收时间 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [图像接收] 开始处理接收到的数据,大小: {data.Length}字节"); + // 更新最后接收数据时间戳 _lastDataReceivedTime = DateTime.Now; @@ -1889,20 +1892,26 @@ namespace JoyD.Windows.CS.Toprie if (imageData[endPos + 1] == 0xD9) { // 创建并触发事件 - Image jpegImage = Image.FromStream(ms); - OnImageReceived(new ImageReceivedEventArgs(imageData, _currentImageMode)); - return; + Image jpegImage = Image.FromStream(ms); + // 记录图像接收时间 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [图像接收] 成功接收到JPEG图像,大小: {imageData.Length}字节"); + OnImageReceived(new ImageReceivedEventArgs(imageData, _currentImageMode)); + return; } } } // 对于其他格式或无法确认完整度的情况,直接尝试创建 Image generalImage = Image.FromStream(ms); + // 记录图像接收时间 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [图像接收] 成功接收到图像,大小: {imageData.Length}字节"); OnImageReceived(new ImageReceivedEventArgs(imageData, _currentImageMode)); } } catch (Exception ex) { + // 记录异常日志 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [图像接收] 处理图像数据异常: {ex.Message}"); Console.WriteLine("处理图像数据异常: " + ex.Message); // 尝试查找有效的图像起始位置 int validStartPos = FindImageStartPosition(imageData); @@ -1918,6 +1927,8 @@ namespace JoyD.Windows.CS.Toprie using (MemoryStream ms = new MemoryStream(validImageData)) { Image validImage = Image.FromStream(ms); + // 记录图像接收时间 + Log($"[{DateTime.Now:HH:mm:ss.fff}] [图像接收] 成功接收到有效图像(二次尝试),大小: {validImageData.Length}字节"); OnImageReceived(new ImageReceivedEventArgs(validImageData, _currentImageMode)); } } @@ -3818,23 +3829,20 @@ namespace JoyD.Windows.CS.Toprie return; } - // 检查是否最近收到过数据 - bool recentlyReceivedData = false; - if (_lastDataReceivedTime != DateTime.MinValue) + // 检查是否在最近收到过数据 + bool recentlyReceivedData = _lastDataReceivedTime != DateTime.MinValue && + (DateTime.Now - _lastDataReceivedTime).TotalMilliseconds < DataReceivedTimeout; + + if (recentlyReceivedData) { TimeSpan timeSinceLastData = DateTime.Now - _lastDataReceivedTime; - recentlyReceivedData = timeSinceLastData.TotalMilliseconds < DataReceivedTimeout; - - if (recentlyReceivedData) + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 最近收到数据({timeSinceLastData.TotalMilliseconds:F0}ms前),不进行心跳检测"); + // 更新连接状态为正常 + if (_connectionStatus != ConnectionStatus.Connected) { - Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 最近{timeSinceLastData.TotalMilliseconds:F0}ms内收到过数据,跳过心跳检测"); - // 更新连接状态为正常 - if (_connectionStatus != ConnectionStatus.Connected) - { - UpdateConnectionStatus(ConnectionStatus.Connected, "最近收到数据,连接正常"); - } - return; + UpdateConnectionStatus(ConnectionStatus.Connected, "最近收到数据,连接正常"); } + return; } Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 长时间未收到数据,执行心跳检测"); diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs index 3b8957f..6f5bf00 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/UdpCommunicationManager.cs @@ -275,6 +275,52 @@ namespace JoyD.Windows.CS.Toprie InvalidResponse } + /// + /// 使用独立UDP客户端发送请求并接收响应(即用即销毁模式) + /// + /// 目标IP地址 + /// 要发送的数据 + /// 目标端口 + /// 超时时间(毫秒) + /// 响应数据,如果超时或出错则返回null + 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; + } + } + } + /// /// 同步发送UDP请求 /// @@ -289,28 +335,21 @@ namespace JoyD.Windows.CS.Toprie response = null; try { - var task = SendRequestAsync(targetIp, data, port, timeout); - bool completed = task.Wait(timeout); - if (completed) + // 使用新的即用即销毁UDP方法 + response = SendRequestWithDisposableUdp(targetIp, data, port, timeout); + + if (response != null) { - response = task.Result; - if (response != null) - { - return RequestResult.Success; - } - return RequestResult.NetworkError; + return RequestResult.Success; } - Console.WriteLine($"UDP请求执行超时({timeout}ms)"); - return RequestResult.Timeout; - } - catch (TimeoutException) - { - Console.WriteLine($"UDP请求执行超时异常"); - return RequestResult.Timeout; + + // 如果响应为null,可能是超时或网络错误 + // 在SendRequestWithDisposableUdp方法中已经记录了具体错误 + return RequestResult.NetworkError; } catch (Exception ex) { - Console.WriteLine($"发送UDP请求时发生错误: {ex.Message}"); + Console.WriteLine($"处理UDP请求结果时发生错误: {ex.Message}"); return RequestResult.ProcessingError; } } diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index a24a3f2..b89d72d 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -30,9 +30,9 @@ namespace JoyD.Windows.CS.Toprie private enum CMD_TYPE { SHUTTER_CORRECTION = 0, - SET_AUTO_SHUTTER = 1, - // 根据设备实际通信协议,设置色彩模式的命令类型 - SET_COLOR_PLATE = 17, // 修正为17,以匹配设备实际接受的命令格式 + // 根据设备实际通信协议调整枚举值,避免冲突 + SET_COLOR_PLATE = 2, // 修正为2,与设备实际接受的命令格式匹配 + SET_AUTO_SHUTTER = 1, // 保持为1,不要与其他命令冲突 SET_MIRROR_VIDEO = 3, SET_VIDEO_MODE = 4, SET_AREA_POS = 5, @@ -47,7 +47,7 @@ namespace JoyD.Windows.CS.Toprie SET_EMAIL_SERVER = 14, SET_TFTP_SERVER = 15, SET_NETWORK_ETH = 16, - GET_PARAMETER = 17, + GET_PARAMETER = 20, // 修改为20,与a8_sdk保持一致 SET_FUSION_DISTANCE = 18, SET_ENVIR_PARAM = 19, SET_ALARM_PARAM = 20, @@ -144,25 +144,7 @@ namespace JoyD.Windows.CS.Toprie Disconnect(); } - // 连接设备 - private bool Connect() - { - try - { - // 注意:设备使用UDP协议通信,不需要建立TCP连接 - // 直接设置连接状态为true,表示准备好进行UDP通信 - // 这里仍然保持isConnected标志,以兼容现有代码逻辑 - isConnected = true; - Console.WriteLine($"UDP通信准备就绪,目标设备 {deviceIp}:18890"); - return true; - } - catch (Exception ex) - { - Console.WriteLine($"初始化UDP通信失败: {ex.Message}"); - isConnected = false; - return false; - } - } + // 断开连接(UDP模式下) private void Disconnect() @@ -224,7 +206,7 @@ namespace JoyD.Windows.CS.Toprie { // 使用UDP通信管理器发送请求,获取详细的请求结果 var result = UdpCommunicationManager.Instance.SendRequest(deviceIp, - Encoding.ASCII.GetBytes(cmd), out byte[] responseBytes, 18890, 200); + Encoding.ASCII.GetBytes(cmd), out byte[] responseBytes, 18890, 500); if (result == UdpCommunicationManager.RequestResult.Success && responseBytes != null) { @@ -270,7 +252,7 @@ namespace JoyD.Windows.CS.Toprie private int ParseResponseValue(string response) { if (string.IsNullOrEmpty(response) || !response.StartsWith("+RET:")) - return 0; + return -1; // 返回-1表示解析失败 try { @@ -283,7 +265,7 @@ namespace JoyD.Windows.CS.Toprie { Console.WriteLine($"解析响应失败: {ex.Message}"); } - return 0; + return -1; // 返回-1表示解析失败 } // SDK核心功能实现 - 不使用DllImport @@ -533,7 +515,7 @@ namespace JoyD.Windows.CS.Toprie { retryCount++; // 使用SDK格式获取色板: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$"; Log($"[色彩模式读取] 开始获取色彩模式值{(retryCount > 1 ? " (重试 " + retryCount + "/" + maxRetries + ")" : "")},发送命令: {command}"); @@ -544,7 +526,7 @@ namespace JoyD.Windows.CS.Toprie { int parsedValue = ParseResponseValue(response); - if (response != null && parsedValue != -1) // 确保响应有效且解析成功 + if (response != null && parsedValue != -1) // 确保响应有效且解析成功(现在parsedValue为-1表示解析失败) { Log($"[色彩模式读取] 解析成功,当前值: {parsedValue},上一次值: {_lastKnownColorPlate}"); _lastKnownColorPlate = parsedValue; @@ -558,7 +540,8 @@ namespace JoyD.Windows.CS.Toprie } else { - Log($"[色彩模式读取] 解析失败或响应无效,返回上次已知值: {_lastKnownColorPlate}"); + Log($"[色彩模式读取] 解析失败或响应无效,返回-1表示获取失败"); + resultValue = -1; // 失败时返回-1而不是上次已知值 } } catch (Exception ex) @@ -570,7 +553,8 @@ namespace JoyD.Windows.CS.Toprie } else { - Log($"[色彩模式读取] 解析异常: {ex.Message},返回上次已知值: {_lastKnownColorPlate}"); + Log($"[色彩模式读取] 解析异常: {ex.Message},返回-1表示获取失败"); + resultValue = -1; // 失败时返回-1而不是上次已知值 } } } @@ -581,18 +565,26 @@ namespace JoyD.Windows.CS.Toprie } else { - Log($"[色彩模式读取] 命令发送失败或超时,返回上次已知值: {_lastKnownColorPlate}"); + Log($"[色彩模式读取] 命令发送失败或超时,返回-1表示获取失败"); + resultValue = -1; // 失败时返回-1而不是上次已知值 } } - Log($"成功读取当前色彩模式值: {resultValue}"); // 添加一致的成功日志 + // 根据是否成功获取来记录不同的日志 + if (resultValue != -1) + { + Log($"成功读取当前色彩模式值: {resultValue}"); + } + else + { + Log($"获取色彩模式值失败"); + } return resultValue; } catch (Exception ex) - { - Log($"[色彩模式读取异常] {ex.Message},返回上次已知值: {_lastKnownColorPlate}"); - return _lastKnownColorPlate; - } + { Log($"[色彩模式读取异常] {ex.Message},返回-1表示获取失败"); + return -1; // 异常时返回-1而不是上次已知值 + } } } @@ -607,18 +599,25 @@ namespace JoyD.Windows.CS.Toprie { try { + // 首先尝试获取当前值 int currentValue = GetColor_plateWithoutLock(); - Log($"[色彩模式设置] 开始设置色彩模式值为: {value},当前值: {currentValue}"); + Log($"[色彩模式设置] 开始设置色彩模式值为: {value},尝试获取当前值: {currentValue}"); - // 如果值相同,不需要设置 - if (currentValue == value) + // 只有在成功获取到当前值(currentValue != -1)且与目标值相同时才跳过设置 + // 如果获取失败(currentValue == -1),仍然执行设置操作,避免因获取失败而跳过设置 + if (currentValue != -1 && currentValue == value) { Log($"[色彩模式设置] 当前色彩模式已为目标值,无需设置"); return true; } + // 如果获取失败,记录日志说明将继续执行设置 + else if (currentValue == -1) + { + Log($"[色彩模式设置] 获取当前值失败,将执行设置操作"); + } - // 使用SDK格式设置色板: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_COLOR_PLATE},{value}$"; + // 使用SDK格式设置色板: +CMD:param_mode,param_value$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_COLOR_PLATE},{value}$"; Log($"[色彩模式设置] 发送命令: {command}"); @@ -688,40 +687,28 @@ namespace JoyD.Windows.CS.Toprie { try { - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$"; + // 使用SDK格式获取色彩模式: +CMD:param_mode,param_value$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$"; if (SendCommand(command, out string response)) { - return ParseResponseValue(response); + int result = ParseResponseValue(response); + // 如果解析结果不是特殊值-1,表示解析成功 + if (result != -1) + { + return result; + } } } catch (Exception ex) { Log($"[GetColor_plateWithoutLock异常] {ex.Message}"); } - return _lastKnownColorPlate; + // 返回特殊值-1表示获取失败,而不是返回_lastKnownColorPlate + // 这样SetColorPlate方法可以明确判断是否获取到了真实值 + return -1; } - /// - /// 安全地重启图像接收,避免重复停止和启动 - /// - private void SafeRestartImageReceiving() - { - try - { - // 注意:V8类不直接管理图像接收和连接检查 - // 这个方法被Color_plate属性调用,主要是为了在设置色彩模式后提供稳定的过渡时间 - Log("执行色彩模式变更后的稳定处理"); - - // 添加短暂延迟以确保设备有时间处理色彩模式变更 - System.Threading.Thread.Sleep(300); - - Log("色彩模式变更后的稳定处理完成"); - } - catch (Exception ex) - { - Log($"色彩模式变更后稳定处理过程中发生异常: {ex.Message}"); - } - } + public int Mirror_mode { @@ -729,8 +716,8 @@ namespace JoyD.Windows.CS.Toprie { try { - // 使用SDK格式获取镜像模式: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.MIRROR_MODE}$"; + // 使用SDK格式获取镜像模式: +CMD:param_mode,param_value$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.MIRROR_MODE}$"; if (SendCommand(command, out string response)) { @@ -748,8 +735,8 @@ namespace JoyD.Windows.CS.Toprie { try { - // 使用SDK格式设置镜像模式: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_MIRROR_VIDEO},{value}$"; + // 使用SDK格式设置镜像模式: +CMD:param_mode,param_value$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_MIRROR_VIDEO},{value}$"; if (SendCommand(command, out string response)) { @@ -773,8 +760,8 @@ namespace JoyD.Windows.CS.Toprie { try { - // 使用SDK格式获取视频模式: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.VIDEO_MODE}$"; + // 使用SDK格式获取视频模式: +CMD:param_mode,param_value$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.VIDEO_MODE}$"; if (SendCommand(command, out string response)) { @@ -792,8 +779,8 @@ namespace JoyD.Windows.CS.Toprie { try { - // 使用SDK格式设置视频模式: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_VIDEO_MODE},{value}$"; + // 使用SDK格式设置视频模式: +CMD:param_mode,param_value$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_VIDEO_MODE},{value}$"; if (SendCommand(command, out string response)) { @@ -915,8 +902,8 @@ namespace JoyD.Windows.CS.Toprie { try { - // 使用SDK格式设置线位置: ip:param_mode,enable,sta_x,sta_y,end_x,end_y$ - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_LINE_POS},{value.enable},{value.sta_x},{value.sta_y},{value.end_x},{value.end_y}$"; + // 使用SDK格式设置线位置: +CMD:param_mode,enable,sta_x,sta_y,end_x,end_y$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_LINE_POS},{value.enable},{value.sta_x},{value.sta_y},{value.end_x},{value.end_y}$"; SendCommand(command, out _); } @@ -929,8 +916,8 @@ namespace JoyD.Windows.CS.Toprie { try { - // 使用SDK格式获取线位置: ip:param_mode,0$ - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_LINE_POS},0$"; + // 使用SDK格式获取线位置: +CMD:param_mode,0$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_LINE_POS},0$"; if (SendCommand(command, out string response)) { @@ -1195,7 +1182,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用SDK格式获取Y缩放: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.ISP_Y_SCALE}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.ISP_Y_SCALE}$"; if (SendCommand(command, out string response)) { @@ -1217,8 +1204,8 @@ namespace JoyD.Windows.CS.Toprie { try { - // 使用SDK格式设置LED状态: ip:param_mode,param_value$ - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_LED},{value}$"; + // 使用SDK格式设置LED状态: +CMD:param_mode,param_value$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_LED},{value}$"; if (SendCommand(command, out string response)) { @@ -1360,9 +1347,9 @@ namespace JoyD.Windows.CS.Toprie { try { - // 使用SDK格式设置网络参数: ip:param_mode,param_value$ + // 使用SDK格式设置网络参数: +CMD:param_mode,param_value$ string paramsStr = $"{value.enable}"; - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_NETWORK_ETH},{paramsStr}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_NETWORK_ETH},{paramsStr}$"; if (SendCommand(command, out string response)) { @@ -1383,7 +1370,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用GET_PARAMETER命令获取网络参数 - string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER}:{(int)PARAM_TYPE.NETWORK_ETH_CONFIG}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.NETWORK_ETH_CONFIG}$"; if (SendCommand(command, out string response)) { @@ -1431,7 +1418,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用GET_PARAMETER命令获取融合距离 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.FUSION_DISTANCE}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.FUSION_DISTANCE}$"; if (SendCommand(command, out string response)) { @@ -1454,7 +1441,7 @@ namespace JoyD.Windows.CS.Toprie // 使用SET_ENVIR_PARAM命令设置环境参数 - SDK格式 // 构建参数字符串,包含所有环境参数 string paramsStr = $"{data.method},{data.num},{data.emissivity},{data.airTemp},{data.targetTemp},{data.atmosTrans},{data.distance},{data.infraredTemp},{data.infraredRadia}"; - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_ENVIR_PARAM},{paramsStr}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_ENVIR_PARAM},{paramsStr}$"; if (SendCommand(command, out string response)) { @@ -1476,7 +1463,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用GET_AREA_ENVIR_PARAM命令获取区域环境参数 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_AREA_ENVIR_PARAM},{index}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_AREA_ENVIR_PARAM},{index}$"; if (SendCommand(command, out string response)) { @@ -1500,7 +1487,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用GET_SPOT_ENVIR_PARAM命令获取点环境参数 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_SPOT_ENVIR_PARAM},{index}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_SPOT_ENVIR_PARAM},{index}$"; if (SendCommand(command, out string response)) { @@ -1524,7 +1511,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用GET_LINE_ENVIR_PARAM命令获取线环境参数 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_LINE_ENVIR_PARAM}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_LINE_ENVIR_PARAM}$"; if (SendCommand(command, out string response)) { @@ -1548,7 +1535,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用GET_GLOBAL_ENVIR_PARAM命令获取全局环境参数 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_GLOBAL_ENVIR_PARAM}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_GLOBAL_ENVIR_PARAM}$"; if (SendCommand(command, out string response)) { @@ -1572,7 +1559,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用SET_ALARM_PARAM命令设置报警参数 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_ALARM_PARAM},{data.method},{data.num},{data.active},{data.condition},{data.captrue},{data.disableCalib},{data.email},{data.digital},{data.ftp},{data.threshold},{data.hysteresis},{data.thresholeTime}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_ALARM_PARAM},{data.method},{data.num},{data.active},{data.condition},{data.captrue},{data.disableCalib},{data.email},{data.digital},{data.ftp},{data.threshold},{data.hysteresis},{data.thresholeTime}$"; if (SendCommand(command, out string response)) { @@ -1870,64 +1857,32 @@ namespace JoyD.Windows.CS.Toprie } } - // 使用UDP协议发送心跳命令,与SDK保持一致 + // 使用SendCommand方法发送心跳命令,与SDK保持一致 public int Heartbeat() { - // 使用UDP通信管理器进行心跳检测,防止异步数据相互影响 try { // 根据SDK实际实现,心跳命令格式是 +CMD:24$ string command = $"{CMD_HEAD}:{(int)CMD_TYPE.HEARTBEAT}$"; - byte[] commandBytes = Encoding.ASCII.GetBytes(command); // SDK实现中会尝试3次心跳,这里也采用相同的策略 for (int retry = 0; retry < 3; retry++) { Console.WriteLine($"心跳检测...(尝试 {retry + 1}/3)"); - Console.WriteLine($"心跳命令: {command}"); - Console.WriteLine($"目标IP: {deviceIp}:18890"); - try + if (SendCommand(command, out string response)) { - // 使用UDP通信管理器发送心跳请求,设置500ms超时 - byte[] responseBytes = UdpCommunicationManager.Instance.SendRequest(deviceIp, - commandBytes, 18890, 500); - - Console.WriteLine("UDP心跳命令已发送,等待响应..."); - - if (responseBytes != null) + // SDK要求响应中必须包含 ":ok" 字符串才算成功 + if (!string.IsNullOrEmpty(response) && response.Contains(":ok")) { - string response = Encoding.ASCII.GetString(responseBytes); - - Console.WriteLine($"收到UDP心跳响应: {response}"); - // 不使用LINQ的Select方法,避免缺少System.Linq命名空间的问题 - string[] asciiValues = new string[response.Length]; - for (int i = 0; i < response.Length; i++) - { - asciiValues[i] = ((int)response[i]).ToString(); - } - Console.WriteLine($"响应长度: {response.Length} 字节,响应ASCII码: {string.Join(",", asciiValues)}"); - - // SDK要求响应中必须包含 ":ok" 字符串才算成功 - if (!string.IsNullOrEmpty(response) && response.Contains(":ok")) - { - Console.WriteLine("心跳成功: 响应包含':ok'"); - return 1; // 返回1表示成功,与DeviceManager中的heartbeatResult > 0判断一致 - } - else - { - Console.WriteLine($"心跳响应不包含':ok',验证失败。收到的响应: '{response}'"); - } + Console.WriteLine("心跳成功: 响应包含':ok'"); + return 1; // 返回1表示成功,与DeviceManager中的heartbeatResult > 0判断一致 } else { - Console.WriteLine("UDP心跳未收到响应或超时"); + Console.WriteLine($"心跳响应不包含':ok',验证失败。收到的响应: '{response}'"); } } - catch (Exception ex) - { - Console.WriteLine($"UDP心跳异常: {ex.Message}"); - } // 如果不是最后一次尝试,短暂延迟后重试 if (retry < 2) @@ -1943,7 +1898,6 @@ namespace JoyD.Windows.CS.Toprie catch (Exception ex) { Console.WriteLine($"心跳检测异常: {ex.Message}"); - Console.WriteLine($"异常堆栈: {ex.StackTrace}"); return -1; } } @@ -1953,7 +1907,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用SET_UART命令设置串口参数 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_UART},{nSpeed},{nBits},{(int)nEvent},{nStop}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_UART},{nSpeed},{nBits},{(int)nEvent},{nStop}$"; if (SendCommand(command, out string response)) { @@ -2014,7 +1968,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用SET_DEVICE_NAME命令设置设备名称 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_DEVICE_NAME},{data}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_DEVICE_NAME},{data}$"; if (SendCommand(command, out string response)) { @@ -2036,7 +1990,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用GET_PARAMETER命令获取设备名称 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.DEVICE_NA}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.DEVICE_NA}$"; if (SendCommand(command, out string response)) { @@ -2105,7 +2059,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用SET_TEMP_FRAME命令设置温度帧 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_TEMP_FRAME},{(byte)value}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_TEMP_FRAME},{(byte)value}$"; SendCommand(command, out _); } @@ -2119,7 +2073,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用GET_TEMP_FRAME命令获取温度帧 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.GET_TEMP_FRAME}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_TEMP_FRAME}$"; if (SendCommand(command, out string response)) { @@ -2241,7 +2195,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用SET_TIME命令设置系统时间 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_TIME},{data.year},{data.month},{data.day},{data.hour},{data.minute},{data.second}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_TIME},{data.year},{data.month},{data.day},{data.hour},{data.minute},{data.second}$"; if (SendCommand(command, out string response)) { @@ -2264,7 +2218,7 @@ namespace JoyD.Windows.CS.Toprie try { // 使用SET_IMAGE_MODE命令设置图像模式 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.SET_IMAGE_MODE},{mode}$"; + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_IMAGE_MODE},{mode}$"; if (SendCommand(command, out string response)) { From 8f7585fd0fb81099af08134215b1984f92efff4c Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 12:55:14 +0800 Subject: [PATCH 08/17] =?UTF-8?q?=E8=8F=9C=E5=8D=95=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CS/Framework4.0/Toprie/Toprie/Camera.cs | 98 +++++++++++++++++-- .../Toprie/Toprie/DeviceManager.cs | 88 +++++++++++++++-- Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 18 +++- 3 files changed, 186 insertions(+), 18 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index a58df52..5fa9365 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -26,6 +26,9 @@ namespace JoyD.Windows.CS.Toprie { InitializeComponent(); + // 为右键菜单添加Opening事件,用于在菜单显示前更新色彩模式的选中状态 + this.contextMenuStrip1.Opening += ContextMenuStrip1_Opening; + // 将设计模式状态传递给DeviceManager DeviceManager.IsDesignMode = DesignMode; @@ -672,20 +675,30 @@ namespace JoyD.Windows.CS.Toprie // 清除错误信息 ShowError(string.Empty); - // 确保设置为热图模式 - try - { - _deviceManager.SetImageMode(ImageMode.Thermal); - Console.WriteLine("连接成功后确认热图模式"); + // 仅在首次连接时设置为热图模式,重连时保留之前的模式 + if (!_isReceivingImage) // 首次连接时_isReceivingImage为false + { + try + { + _deviceManager.SetImageMode(ImageMode.Thermal); + 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; @@ -956,6 +969,73 @@ namespace JoyD.Windows.CS.Toprie } } + /// + /// 右键菜单显示前的事件处理方法 + /// 用于更新色彩模式菜单项的选中状态 + /// + private void ContextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e) + { + try + { + // 首先清除所有菜单项的选中状态 + 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); + } + } + } + catch (Exception ex) + { + Console.WriteLine("更新右键菜单选中状态失败: " + ex.Message); + } + } + #region 色彩模式切换方法 /// diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 79a1d10..3bbbb09 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -161,6 +161,8 @@ namespace JoyD.Windows.CS.Toprie private bool _isDisposed = false; // 图像模式 private ImageMode _currentImageMode = ImageMode.Thermal; + // 当前色彩模式 + private PaletteType _currentPaletteType = PaletteType.WhiteHot; // 自动重连是否启用 private bool _autoReconnectEnabled = true; // 自动重连定时器 @@ -2235,6 +2237,53 @@ namespace JoyD.Windows.CS.Toprie // 连续心跳失败阈值,超过此值才认为连接真正断开 private const int HEARTBEAT_FAILURE_THRESHOLD = 3; + /// + /// 获取或设置当前色彩模式 + /// + public PaletteType CurrentPaletteType + { + get { return _currentPaletteType; } + set { _currentPaletteType = value; } + } + + /// + /// 从设备同步色彩模式到内部状态 + /// + public void SyncPaletteTypeFromDevice() + { + try + { + // 确保设备已连接且SDK实例有效 + if (_connectionStatus == ConnectionStatus.Connected && _a8Sdk != null) + { + // 获取当前设备的色彩模式值 + int currentValue = _a8Sdk.GetColorPlate(); + Log($"从设备读取的色彩模式值: {currentValue}"); + + // 尝试将读取到的值转换为PaletteType枚举并更新内部状态 + if (Enum.IsDefined(typeof(PaletteType), currentValue)) + { + PaletteType actualPalette = (PaletteType)currentValue; + _currentPaletteType = actualPalette; + Log($"已更新内部状态为设备实际值: {actualPalette}"); + } + else + { + Log($"警告:设备返回的色彩模式值 {currentValue} 不在枚举定义范围内,使用默认值"); + } + } + else + { + Log($"同步色彩模式失败:设备未连接或SDK未初始化"); + } + } + catch (Exception ex) + { + Log($"同步色彩模式时发生异常: {ex.Message}"); + throw; // 重新抛出异常,让调用方知道发生了错误 + } + } + /// /// 设置色彩模式 /// @@ -2306,15 +2355,21 @@ namespace JoyD.Windows.CS.Toprie { // 直接调用SetColorPlate方法设置色彩模式 setSuccess = _a8Sdk.SetColorPlate(paletteValue); + Log($"SetColorPlate返回结果: {setSuccess}"); + // 短暂延迟,确保设置生效 + Thread.Sleep(50); + + // 再次读取当前值进行验证,不依赖SetColorPlate的返回值 + int currentValue = _a8Sdk.GetColorPlate(); + Log($"验证读取到的当前色彩模式值: {currentValue},目标值: {paletteValue}"); + setSuccess = (currentValue == paletteValue); + + // 如果设置成功,更新内部状态 if (setSuccess) { - // 短暂延迟,确保设置生效 - Thread.Sleep(50); - - // 再次读取当前值进行验证 - int currentValue = _a8Sdk.GetColorPlate(); - setSuccess = (currentValue == paletteValue); + _currentPaletteType = paletteType; + Log($"内部状态_currentPaletteType已更新为: {paletteType}"); } } catch (Exception ex) @@ -2326,6 +2381,7 @@ namespace JoyD.Windows.CS.Toprie if (setSuccess) { Log($"色彩模式设置成功: {paletteType} (值: {paletteValue})"); + _currentPaletteType = paletteType; return true; } else @@ -3032,6 +3088,16 @@ namespace JoyD.Windows.CS.Toprie StartConnectionCheck(); UpdateConnectionStatus(ConnectionStatus.Connected, "设备连接成功"); + + // 连接成功后同步色彩模式 + try + { + SyncPaletteTypeFromDevice(); + } + catch (Exception ex) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 同步色彩模式失败: {ex.Message}"); + }; } else if (!token.IsCancellationRequested) { @@ -3544,6 +3610,16 @@ namespace JoyD.Windows.CS.Toprie } UpdateConnectionStatus(ConnectionStatus.Connected, $"设备 {deviceIp} 连接成功"); + + // 重连成功后同步色彩模式 + try + { + SyncPaletteTypeFromDevice(); + } + catch (Exception ex) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 重连后同步色彩模式失败: {ex.Message}"); + }; StartConnectionCheck(); connectionSuccessful = true; } diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index b89d72d..076db3b 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -251,14 +251,26 @@ namespace JoyD.Windows.CS.Toprie // 解析响应数据 private int ParseResponseValue(string response) { - if (string.IsNullOrEmpty(response) || !response.StartsWith("+RET:")) + if (string.IsNullOrEmpty(response)) return -1; // 返回-1表示解析失败 + // 支持两种响应格式: +RET: 和 +RSP: + int startIndex = -1; + if (response.StartsWith("+RET:")) + startIndex = 5; + else if (response.StartsWith("+RSP:")) + startIndex = 5; + + if (startIndex == -1) + return -1; + try { // 从第5个字符开始解析数值,与热像仪SDK保持一致 - // SDK使用atoi(buff + 5)直接解析,不检查$符号 - string valueStr = response.Substring(5); + string valueStr = response.Substring(startIndex); + // 移除结尾可能存在的$符号 + if (valueStr.EndsWith("$")) + valueStr = valueStr.Substring(0, valueStr.Length - 1); return int.Parse(valueStr); } catch (Exception ex) From 0c5d343940441b5b30316e3888a2fb0f2d4e0165 Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 13:13:31 +0800 Subject: [PATCH 09/17] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=BE=E5=83=8F?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CS/Framework4.0/Toprie/Toprie/A8SDK.cs | 12 ++ .../Toprie/Toprie/Camera.Designer.cs | 84 +++++++- .../CS/Framework4.0/Toprie/Toprie/Camera.cs | 189 +++++++++++++++++- .../Toprie/Toprie/DeviceManager.cs | 92 +++++++++ Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 28 +++ 5 files changed, 402 insertions(+), 3 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs b/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs index 9096ad8..df57999 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs @@ -652,6 +652,18 @@ namespace JoyD.Windows.CS.Toprie v8Instance.SetImageMode(mode); } + // 获取视频模式 + public int GetVideoMode() + { + return v8Instance.GetVideoMode(); + } + + // 设置视频模式 + public void SetVideoMode(int mode) + { + v8Instance.SetVideoMode(mode); + } + // 获取图像数据 public byte[] GetImageData() { diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs index 7cba02f..12526d9 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs @@ -1,4 +1,4 @@ -namespace JoyD.Windows.CS.Toprie +namespace JoyD.Windows.CS.Toprie { partial class Camera { @@ -18,6 +18,14 @@ 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(); @@ -45,9 +53,73 @@ // 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, 48); + this.contextMenuStrip1.Size = new System.Drawing.Size(161, 74); + // + // imageModeToolStripMenuItem + // + this.imageModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.thermalModeToolStripMenuItem, + this.visibleModeToolStripMenuItem, + this.fusionMode1ToolStripMenuItem, + this.fusionMode2ToolStripMenuItem, + this.fusionMode3ToolStripMenuItem, + this.fusionMode4ToolStripMenuItem, + this.fusionMode5ToolStripMenuItem}); + 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); + // + // fusionMode1ToolStripMenuItem + // + this.fusionMode1ToolStripMenuItem.Name = "fusionMode1ToolStripMenuItem"; + this.fusionMode1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.fusionMode1ToolStripMenuItem.Text = "融合模式1"; + this.fusionMode1ToolStripMenuItem.Click += new System.EventHandler(this.fusionMode1ToolStripMenuItem_Click); + // + // fusionMode2ToolStripMenuItem + // + this.fusionMode2ToolStripMenuItem.Name = "fusionMode2ToolStripMenuItem"; + this.fusionMode2ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.fusionMode2ToolStripMenuItem.Text = "融合模式2"; + this.fusionMode2ToolStripMenuItem.Click += new System.EventHandler(this.fusionMode2ToolStripMenuItem_Click); + // + // fusionMode3ToolStripMenuItem + // + this.fusionMode3ToolStripMenuItem.Name = "fusionMode3ToolStripMenuItem"; + this.fusionMode3ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.fusionMode3ToolStripMenuItem.Text = "融合模式3"; + this.fusionMode3ToolStripMenuItem.Click += new System.EventHandler(this.fusionMode3ToolStripMenuItem_Click); + // + // fusionMode4ToolStripMenuItem + // + this.fusionMode4ToolStripMenuItem.Name = "fusionMode4ToolStripMenuItem"; + this.fusionMode4ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.fusionMode4ToolStripMenuItem.Text = "融合模式4"; + this.fusionMode4ToolStripMenuItem.Click += new System.EventHandler(this.fusionMode4ToolStripMenuItem_Click); + // + // fusionMode5ToolStripMenuItem + // + this.fusionMode5ToolStripMenuItem.Name = "fusionMode5ToolStripMenuItem"; + this.fusionMode5ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.fusionMode5ToolStripMenuItem.Text = "融合模式5"; + this.fusionMode5ToolStripMenuItem.Click += new System.EventHandler(this.fusionMode5ToolStripMenuItem_Click); // // colorModeToolStripMenuItem // @@ -149,5 +221,13 @@ 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; } } diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index 5fa9365..635c09b 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -977,7 +977,16 @@ namespace JoyD.Windows.CS.Toprie { try { - // 首先清除所有菜单项的选中状态 + // 清除视频模式菜单项的选中状态 + 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; @@ -1028,6 +1037,40 @@ namespace JoyD.Windows.CS.Toprie { Console.WriteLine("获取当前色彩模式失败: " + ex.Message); } + + // 更新视频模式菜单项的选中状态 + try + { + int currentMode = _deviceManager.GetCurrentVideoMode(); + switch (currentMode) + { + case 0: + thermalModeToolStripMenuItem.Checked = true; + break; + case 1: + visibleModeToolStripMenuItem.Checked = true; + break; + case 2: + fusionMode1ToolStripMenuItem.Checked = true; + break; + case 3: + fusionMode2ToolStripMenuItem.Checked = true; + break; + case 4: + fusionMode3ToolStripMenuItem.Checked = true; + break; + case 5: + fusionMode4ToolStripMenuItem.Checked = true; + break; + case 6: + fusionMode5ToolStripMenuItem.Checked = true; + break; + } + } + catch (Exception ex) + { + Console.WriteLine("获取当前视频模式失败: " + ex.Message); + } } } catch (Exception ex) @@ -1192,6 +1235,150 @@ namespace JoyD.Windows.CS.Toprie #endregion + #region 视频模式切换方法 + + /// + /// 红外模式 + /// + private void thermalModeToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + if (_deviceManager != null) + { + Console.WriteLine("切换到红外模式"); + _deviceManager.SetVideoMode(0); + } + } + catch (Exception ex) + { + Console.WriteLine("切换到红外模式失败: " + ex.Message); + ShowError("切换到红外模式失败"); + } + } + + /// + /// 自然模式 + /// + private void visibleModeToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + if (_deviceManager != null) + { + Console.WriteLine("切换到自然模式"); + _deviceManager.SetVideoMode(1); + } + } + catch (Exception ex) + { + Console.WriteLine("切换到自然模式失败: " + ex.Message); + ShowError("切换到自然模式失败"); + } + } + + /// + /// 融合模式1 + /// + private void fusionMode1ToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + if (_deviceManager != null) + { + Console.WriteLine("切换到融合模式1"); + _deviceManager.SetVideoMode(2); + } + } + catch (Exception ex) + { + Console.WriteLine("切换到融合模式1失败: " + ex.Message); + ShowError("切换到融合模式1失败"); + } + } + + /// + /// 融合模式2 + /// + private void fusionMode2ToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + if (_deviceManager != null) + { + Console.WriteLine("切换到融合模式2"); + _deviceManager.SetVideoMode(3); + } + } + catch (Exception ex) + { + Console.WriteLine("切换到融合模式2失败: " + ex.Message); + ShowError("切换到融合模式2失败"); + } + } + + /// + /// 融合模式3 + /// + private void fusionMode3ToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + if (_deviceManager != null) + { + Console.WriteLine("切换到融合模式3"); + _deviceManager.SetVideoMode(4); + } + } + catch (Exception ex) + { + Console.WriteLine("切换到融合模式3失败: " + ex.Message); + ShowError("切换到融合模式3失败"); + } + } + + /// + /// 融合模式4 + /// + private void fusionMode4ToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + if (_deviceManager != null) + { + Console.WriteLine("切换到融合模式4"); + _deviceManager.SetVideoMode(5); + } + } + catch (Exception ex) + { + Console.WriteLine("切换到融合模式4失败: " + ex.Message); + ShowError("切换到融合模式4失败"); + } + } + + /// + /// 融合模式5 + /// + private void fusionMode5ToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + if (_deviceManager != null) + { + Console.WriteLine("切换到融合模式5"); + _deviceManager.SetVideoMode(6); + } + } + catch (Exception ex) + { + Console.WriteLine("切换到融合模式5失败: " + ex.Message); + ShowError("切换到融合模式5失败"); + } + } + + #endregion + /// /// 清理资源 /// diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 3bbbb09..fb40335 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -2229,6 +2229,98 @@ namespace JoyD.Windows.CS.Toprie CurrentImageMode = mode; } + /// + /// 设置视频模式 + /// + /// 视频模式(0:红外,1:自然,2-6:融合模式1-5) + public void SetVideoMode(int videoMode) + { + // 验证参数范围 + if (videoMode < 0 || videoMode > 6) + { + throw new ArgumentOutOfRangeException("videoMode", "视频模式必须在0-6范围内"); + } + + // 调用SDK设置视频模式 + SendVideoModeCommand(videoMode); + } + + /// + /// 获取当前视频模式 + /// + /// 当前视频模式值(0-6) + public int GetCurrentVideoMode() + { + lock (_sdkOperationLock) + { + // 验证连接状态 + if (_connectionStatus != ConnectionStatus.Connected || _a8Sdk == null) + { + throw new InvalidOperationException("设备未连接"); + } + + // 调用SDK获取视频模式 + return _a8Sdk.GetVideoMode(); + } + } + + /// + /// 发送视频模式变更命令到设备 + /// + /// 视频模式值 + private void SendVideoModeCommand(int videoMode) + { + lock (_sdkOperationLock) + { + const int maxRetries = 3; + const int retryDelayMs = 100; + + for (int attempt = 0; attempt < maxRetries; attempt++) + { + try + { + // 检查对象状态和连接状态 + if (_a8Sdk == null || _connectionStatus != ConnectionStatus.Connected) + { + Log($"视频模式切换失败: {(attempt > 0 ? "重试中" : "")}SDK实例为空或设备未连接"); + Thread.Sleep(retryDelayMs); + continue; + } + + // 调用SDK设置视频模式 + _a8Sdk.SetVideoMode(videoMode); + + // 短暂延迟,确保设置生效 + Thread.Sleep(50); + + // 简单验证:尝试读取视频模式确认设置成功 + int currentMode = _a8Sdk.GetVideoMode(); + if (currentMode == videoMode) + { + Log($"视频模式切换成功,当前模式: {videoMode}"); + return; + } + else + { + Log($"视频模式验证失败,期望: {videoMode},实际: {currentMode}"); + } + } + catch (Exception ex) + { + Log($"视频模式切换异常 (尝试 {attempt + 1}/{maxRetries}): {ex.Message}"); + } + + // 重试前短暂延迟 + if (attempt < maxRetries - 1) + { + Thread.Sleep(retryDelayMs); + } + } + + throw new Exception($"视频模式切换失败,已重试 {maxRetries} 次"); + } + } + // 用于保护SDK操作的线程锁 private readonly object _sdkOperationLock = new object(); diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index 076db3b..3d9a0e3 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -35,6 +35,7 @@ namespace JoyD.Windows.CS.Toprie SET_AUTO_SHUTTER = 1, // 保持为1,不要与其他命令冲突 SET_MIRROR_VIDEO = 3, SET_VIDEO_MODE = 4, + GET_VIDEO_MODE = 28, SET_AREA_POS = 5, SET_SPOT_POS = 6, SET_LINE_POS = 7, @@ -2247,6 +2248,33 @@ namespace JoyD.Windows.CS.Toprie } } + // 获取视频模式 + public int GetVideoMode() + { + try + { + return Video_mode; + } + catch (Exception ex) + { + Console.WriteLine($"获取视频模式失败: {ex.Message}"); + return 0; + } + } + + // 设置视频模式 + public void SetVideoMode(int mode) + { + try + { + Video_mode = mode; + } + catch (Exception ex) + { + Console.WriteLine($"设置视频模式失败: {ex.Message}"); + } + } + // 新增方法:获取图像数据 public byte[] GetImageData() { From f9fa743de0fffe83c045f038929fcf957afa3b72 Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 13:47:51 +0800 Subject: [PATCH 10/17] =?UTF-8?q?=E8=A7=86=E9=A2=91=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CS/Framework4.0/Toprie/Toprie/Camera.cs | 30 +-- .../Toprie/Toprie/DeviceManager.cs | 147 ++++++++++--- Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 208 +++++++++++++++--- 3 files changed, 310 insertions(+), 75 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index 635c09b..42cd53c 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -1041,28 +1041,28 @@ namespace JoyD.Windows.CS.Toprie // 更新视频模式菜单项的选中状态 try { - int currentMode = _deviceManager.GetCurrentVideoMode(); + var currentMode = _deviceManager.CurrentVideoMode; switch (currentMode) { - case 0: + case VideoMode.Infrared: thermalModeToolStripMenuItem.Checked = true; break; - case 1: + case VideoMode.VisibleLight: visibleModeToolStripMenuItem.Checked = true; break; - case 2: + case VideoMode.Fusion1: fusionMode1ToolStripMenuItem.Checked = true; break; - case 3: + case VideoMode.Fusion2: fusionMode2ToolStripMenuItem.Checked = true; break; - case 4: + case VideoMode.Fusion3: fusionMode3ToolStripMenuItem.Checked = true; break; - case 5: + case VideoMode.Fusion4: fusionMode4ToolStripMenuItem.Checked = true; break; - case 6: + case VideoMode.Fusion5: fusionMode5ToolStripMenuItem.Checked = true; break; } @@ -1247,7 +1247,7 @@ namespace JoyD.Windows.CS.Toprie if (_deviceManager != null) { Console.WriteLine("切换到红外模式"); - _deviceManager.SetVideoMode(0); + _deviceManager.SetVideoMode(VideoMode.Infrared); } } catch (Exception ex) @@ -1267,7 +1267,7 @@ namespace JoyD.Windows.CS.Toprie if (_deviceManager != null) { Console.WriteLine("切换到自然模式"); - _deviceManager.SetVideoMode(1); + _deviceManager.SetVideoMode(VideoMode.VisibleLight); } } catch (Exception ex) @@ -1287,7 +1287,7 @@ namespace JoyD.Windows.CS.Toprie if (_deviceManager != null) { Console.WriteLine("切换到融合模式1"); - _deviceManager.SetVideoMode(2); + _deviceManager.SetVideoMode(VideoMode.Fusion1); } } catch (Exception ex) @@ -1307,7 +1307,7 @@ namespace JoyD.Windows.CS.Toprie if (_deviceManager != null) { Console.WriteLine("切换到融合模式2"); - _deviceManager.SetVideoMode(3); + _deviceManager.SetVideoMode(VideoMode.Fusion2); } } catch (Exception ex) @@ -1327,7 +1327,7 @@ namespace JoyD.Windows.CS.Toprie if (_deviceManager != null) { Console.WriteLine("切换到融合模式3"); - _deviceManager.SetVideoMode(4); + _deviceManager.SetVideoMode(VideoMode.Fusion3); } } catch (Exception ex) @@ -1347,7 +1347,7 @@ namespace JoyD.Windows.CS.Toprie if (_deviceManager != null) { Console.WriteLine("切换到融合模式4"); - _deviceManager.SetVideoMode(5); + _deviceManager.SetVideoMode(VideoMode.Fusion4); } } catch (Exception ex) @@ -1367,7 +1367,7 @@ namespace JoyD.Windows.CS.Toprie if (_deviceManager != null) { Console.WriteLine("切换到融合模式5"); - _deviceManager.SetVideoMode(6); + _deviceManager.SetVideoMode(VideoMode.Fusion5); } } catch (Exception ex) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index fb40335..8cd3403 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -52,6 +52,21 @@ namespace JoyD.Windows.CS.Toprie RedHot, // 红热,对应SDK参数6 Rainbow2 // 彩虹2,对应SDK参数7 } + + /// + /// 视频模式枚举 + /// 对应SDK文档中的7种视频模式:红外、可见光、融合1~5 + /// + public enum VideoMode + { + Infrared, // 红外,对应SDK参数0 + VisibleLight, // 可见光,对应SDK参数1 + Fusion1, // 融合1,对应SDK参数2 + Fusion2, // 融合2,对应SDK参数3 + Fusion3, // 融合3,对应SDK参数4 + Fusion4, // 融合4,对应SDK参数5 + Fusion5 // 融合5,对应SDK参数6 + } /// /// 连接状态改变事件参数 @@ -163,6 +178,8 @@ namespace JoyD.Windows.CS.Toprie private ImageMode _currentImageMode = ImageMode.Thermal; // 当前色彩模式 private PaletteType _currentPaletteType = PaletteType.WhiteHot; + // 当前视频模式 + private VideoMode _currentVideoMode = VideoMode.Infrared; // 默认红外模式 // 自动重连是否启用 private bool _autoReconnectEnabled = true; // 自动重连定时器 @@ -2232,15 +2249,9 @@ namespace JoyD.Windows.CS.Toprie /// /// 设置视频模式 /// - /// 视频模式(0:红外,1:自然,2-6:融合模式1-5) - public void SetVideoMode(int videoMode) + /// 视频模式枚举 + public void SetVideoMode(VideoMode videoMode) { - // 验证参数范围 - if (videoMode < 0 || videoMode > 6) - { - throw new ArgumentOutOfRangeException("videoMode", "视频模式必须在0-6范围内"); - } - // 调用SDK设置视频模式 SendVideoModeCommand(videoMode); } @@ -2248,8 +2259,8 @@ namespace JoyD.Windows.CS.Toprie /// /// 获取当前视频模式 /// - /// 当前视频模式值(0-6) - public int GetCurrentVideoMode() + /// 当前视频模式枚举 + public VideoMode GetCurrentVideoMode() { lock (_sdkOperationLock) { @@ -2259,16 +2270,19 @@ namespace JoyD.Windows.CS.Toprie throw new InvalidOperationException("设备未连接"); } - // 调用SDK获取视频模式 - return _a8Sdk.GetVideoMode(); + // 从设备同步视频模式到内部状态 + SyncVideoModeFromDevice(); + + // 返回内部状态中的视频模式值 + return _currentVideoMode; } } /// /// 发送视频模式变更命令到设备 /// - /// 视频模式值 - private void SendVideoModeCommand(int videoMode) + /// 视频模式枚举值 + private void SendVideoModeCommand(VideoMode videoMode) { lock (_sdkOperationLock) { @@ -2287,22 +2301,32 @@ namespace JoyD.Windows.CS.Toprie continue; } - // 调用SDK设置视频模式 - _a8Sdk.SetVideoMode(videoMode); + // 调用SDK设置视频模式(转换为int) + _a8Sdk.SetVideoMode((int)videoMode); // 短暂延迟,确保设置生效 Thread.Sleep(50); // 简单验证:尝试读取视频模式确认设置成功 - int currentMode = _a8Sdk.GetVideoMode(); - if (currentMode == videoMode) + int currentModeInt = _a8Sdk.GetVideoMode(); + if (Enum.IsDefined(typeof(VideoMode), currentModeInt)) { - Log($"视频模式切换成功,当前模式: {videoMode}"); - return; + VideoMode currentMode = (VideoMode)currentModeInt; + if (currentMode == videoMode) + { + // 更新内部状态 + _currentVideoMode = videoMode; + Log($"视频模式切换成功,当前模式: {videoMode}"); + return; + } + else + { + Log($"视频模式验证失败,期望: {videoMode},实际: {currentMode}"); + } } else { - Log($"视频模式验证失败,期望: {videoMode},实际: {currentMode}"); + Log($"视频模式验证失败,获取到无效的视频模式值: {currentModeInt}"); } } catch (Exception ex) @@ -2337,7 +2361,54 @@ namespace JoyD.Windows.CS.Toprie get { return _currentPaletteType; } set { _currentPaletteType = value; } } + + /// + /// 获取或设置当前视频模式 + /// + public VideoMode CurrentVideoMode + { + get { return _currentVideoMode; } + set { _currentVideoMode = value; } + } + /// + /// 从设备同步视频模式到内部状态 + /// + public void SyncVideoModeFromDevice() + { + try + { + // 确保设备已连接且SDK实例有效 + if (_connectionStatus == ConnectionStatus.Connected && _a8Sdk != null) + { + // 获取当前设备的视频模式值 + int currentValue = _a8Sdk.GetVideoMode(); + Log($"从设备读取的视频模式值: {currentValue}"); + + // 验证视频模式值是否在有效范围内并转换为枚举 + if (Enum.IsDefined(typeof(VideoMode), currentValue)) + { + VideoMode actualMode = (VideoMode)currentValue; + _currentVideoMode = actualMode; + Log($"已更新内部状态为设备实际值: {actualMode} (值: {currentValue})"); + } + else + { + Log($"警告:设备返回的视频模式值 {currentValue} 不在有效范围内(0-6),使用默认值"); + } + } + else + { + Log($"同步视频模式失败:设备未连接或SDK未初始化"); + } + } + catch (Exception ex) + { + Log($"同步视频模式时发生异常: {ex.Message}"); + throw; // 重新抛出异常,让调用方知道发生了错误 + } + } + /// /// 从设备同步色彩模式到内部状态 /// @@ -2458,11 +2529,11 @@ namespace JoyD.Windows.CS.Toprie setSuccess = (currentValue == paletteValue); // 如果设置成功,更新内部状态 - if (setSuccess) - { - _currentPaletteType = paletteType; - Log($"内部状态_currentPaletteType已更新为: {paletteType}"); - } + if (setSuccess) + { + _currentPaletteType = paletteType; + Log($"内部状态_currentPaletteType已更新为: {paletteType}"); + } } catch (Exception ex) { @@ -3181,7 +3252,7 @@ namespace JoyD.Windows.CS.Toprie UpdateConnectionStatus(ConnectionStatus.Connected, "设备连接成功"); - // 连接成功后同步色彩模式 + // 连接成功后同步色彩模式和视频模式 try { SyncPaletteTypeFromDevice(); @@ -3190,6 +3261,16 @@ namespace JoyD.Windows.CS.Toprie { Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 同步色彩模式失败: {ex.Message}"); }; + + // 同步视频模式 + try + { + SyncVideoModeFromDevice(); + } + catch (Exception ex) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 同步视频模式失败: {ex.Message}"); + }; } else if (!token.IsCancellationRequested) { @@ -3703,7 +3784,7 @@ namespace JoyD.Windows.CS.Toprie UpdateConnectionStatus(ConnectionStatus.Connected, $"设备 {deviceIp} 连接成功"); - // 重连成功后同步色彩模式 + // 重连成功后同步色彩模式和视频模式 try { SyncPaletteTypeFromDevice(); @@ -3712,6 +3793,16 @@ namespace JoyD.Windows.CS.Toprie { Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 重连后同步色彩模式失败: {ex.Message}"); }; + + // 同步视频模式 + try + { + SyncVideoModeFromDevice(); + } + catch (Exception ex) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] 重连后同步视频模式失败: {ex.Message}"); + }; StartConnectionCheck(); connectionSuccessful = true; } diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index 3d9a0e3..3d50950 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -767,49 +767,208 @@ namespace JoyD.Windows.CS.Toprie } } - public int Video_mode + private int _lastKnownVideoMode = 0; // 保存最后已知的视频模式值 + private readonly object _videoModeLock = new object(); // 线程锁,确保线程安全 + + /// + /// 获取当前视频模式值(内部方法) + /// + /// 视频模式值,如果获取失败则返回上次已知值 + private int GetVideoModeInternal() { - get + lock (_videoModeLock) { try { - // 使用SDK格式获取视频模式: +CMD:param_mode,param_value$ - string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.VIDEO_MODE}$"; + const int maxRetries = 3; + int retryCount = 0; + bool success = false; + int resultValue = _lastKnownVideoMode; - if (SendCommand(command, out string response)) + while (retryCount < maxRetries && !success) { - return ParseResponseValue(response); + retryCount++; + // 使用SDK格式获取视频模式: +CMD:param_mode,param_value$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.VIDEO_MODE}$"; + + Log($"[视频模式读取] 开始获取视频模式值{(retryCount > 1 ? " (重试 " + retryCount + "/" + maxRetries + ")" : "")},发送命令: {command}"); + + if (SendCommand(command, out string response)) + { + Log($"[视频模式读取] 收到响应: {response}"); + try + { + int parsedValue = ParseResponseValue(response); + + if (response != null && parsedValue != -1) // 确保响应有效且解析成功 + { + Log($"[视频模式读取] 解析成功,当前值: {parsedValue},上一次值: {_lastKnownVideoMode}"); + _lastKnownVideoMode = parsedValue; + resultValue = parsedValue; + success = true; + } + else if (retryCount < maxRetries) + { + Log($"[视频模式读取] 解析失败或响应无效,将重试..."); + System.Threading.Thread.Sleep(200); // 短暂延迟后重试 + } + else + { + Log($"[视频模式读取] 解析失败或响应无效,返回-1表示获取失败"); + resultValue = -1; // 失败时返回-1而不是上次已知值 + } + } + catch (Exception ex) + { + if (retryCount < maxRetries) + { + Log($"[视频模式读取] 解析异常: {ex.Message},将重试..."); + System.Threading.Thread.Sleep(200); // 短暂延迟后重试 + } + else + { + Log($"[视频模式读取] 解析异常: {ex.Message},重试次数已达上限"); + resultValue = -1; + } + } + } + else if (retryCount < maxRetries) + { + Log($"[视频模式读取] 发送命令失败,将重试..."); + System.Threading.Thread.Sleep(200); // 短暂延迟后重试 + } + else + { + Log($"[视频模式读取] 发送命令失败,重试次数已达上限"); + resultValue = -1; + } } - return 0; // 默认模式 + + return resultValue; } catch (Exception ex) { - Console.WriteLine($"获取视频模式失败: {ex.Message}"); - return 0; + Log($"[视频模式读取] 发生异常: {ex.Message}"); + return -1; } } - set + } + + /// + /// 设置视频模式值(内部方法) + /// + /// 要设置的视频模式值 + /// 设置是否成功 + private bool SetVideoModeInternal(int value) + { + lock (_videoModeLock) { try { + // 首先尝试获取当前值 + int currentValue = GetVideoModeWithoutLock(); + Log($"[视频模式设置] 开始设置视频模式值为: {value},尝试获取当前值: {currentValue}"); + + // 只有在成功获取到当前值(currentValue != -1)且与目标值相同时才跳过设置 + // 如果获取失败(currentValue == -1),仍然执行设置操作,避免因获取失败而跳过设置 + if (currentValue != -1 && currentValue == value) + { + Log($"[视频模式设置] 当前视频模式已为目标值,无需设置"); + return true; + } + // 如果获取失败,记录日志说明将继续执行设置 + else if (currentValue == -1) + { + Log($"[视频模式设置] 获取当前值失败,将执行设置操作"); + } + // 使用SDK格式设置视频模式: +CMD:param_mode,param_value$ string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_VIDEO_MODE},{value}$"; - if (SendCommand(command, out string response)) + Log($"[视频模式设置] 发送命令: {command}"); + + bool success = SendCommand(command, out string response); + if (success && response != null) { - // 验证响应 - if (response != null) + Log($"[视频模式设置] 收到响应: {response}"); + + // 验证响应是否包含成功标记 + bool responseValid = response.Contains("+RET:") && + (!response.Contains("error") && !response.Contains("失败")); + + if (responseValid) { - Console.WriteLine("设置视频模式成功"); + Log($"[视频模式设置成功] 从 {_lastKnownVideoMode} 变更为 {value}"); + _lastKnownVideoMode = value; // 更新最后已知值 + + // 验证设置是否生效 + Log($"[视频模式设置] 验证设置是否生效..."); + int validatedValue = GetVideoModeWithoutLock(); + if (validatedValue == value) + { + Log($"[视频模式设置] 验证成功,当前值确认为: {validatedValue}"); + return true; + } + else + { + Log($"[视频模式设置] 验证失败,当前值: {validatedValue},目标值: {value}"); + return false; + } } + else + { + Log($"[视频模式设置] 响应无效或失败: {response}"); + return false; + } + } + else + { + Log($"[视频模式设置] 发送命令失败或未收到响应"); + return false; } } catch (Exception ex) { - Console.WriteLine($"设置视频模式失败: {ex.Message}"); + Log($"[视频模式设置] 发生异常: {ex.Message}"); + return false; } } } + + /// + /// 不带锁获取视频模式,避免在设置后验证时出现死锁 + /// + private int GetVideoModeWithoutLock() + { + try + { + // 使用SDK格式获取视频模式: +CMD:param_mode,param_value$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.VIDEO_MODE}$"; + if (SendCommand(command, out string response)) + { + int result = ParseResponseValue(response); + // 如果解析结果不是特殊值-1,表示解析成功 + if (result != -1) + { + return result; + } + } + } + catch (Exception ex) + { + Log($"[GetVideoModeWithoutLock异常] {ex.Message}"); + } + // 返回特殊值-1表示获取失败,而不是返回_lastKnownVideoMode + // 这样SetVideoModeInternal方法可以明确判断是否获取到了真实值 + return -1; + } + + // 保留原属性以保持向后兼容性 + public int Video_mode + { + get => GetVideoModeInternal(); + set => SetVideoModeInternal(value); + } public void Set_area_pos(int index, SharedStructures.AreaPos area_data) { @@ -2251,28 +2410,13 @@ namespace JoyD.Windows.CS.Toprie // 获取视频模式 public int GetVideoMode() { - try - { - return Video_mode; - } - catch (Exception ex) - { - Console.WriteLine($"获取视频模式失败: {ex.Message}"); - return 0; - } + return GetVideoModeInternal(); } // 设置视频模式 public void SetVideoMode(int mode) { - try - { - Video_mode = mode; - } - catch (Exception ex) - { - Console.WriteLine($"设置视频模式失败: {ex.Message}"); - } + SetVideoModeInternal(mode); } // 新增方法:获取图像数据 From 34961d325adbc5282dff8780aa9c1955fd993dcf Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 14:27:19 +0800 Subject: [PATCH 11/17] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=BE=E5=83=8F?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CS/Framework4.0/Toprie/Toprie/A8SDK.cs | 6 - .../CS/Framework4.0/Toprie/Toprie/Camera.cs | 14 +-- .../Toprie/Toprie/DeviceManager.cs | 103 +----------------- Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 24 ---- 4 files changed, 7 insertions(+), 140 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs b/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs index df57999..b1e718f 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/A8SDK.cs @@ -646,12 +646,6 @@ namespace JoyD.Windows.CS.Toprie v8Instance.Set_time(data); } - // 设置图像模式 - public void SetImageMode(int mode) - { - v8Instance.SetImageMode(mode); - } - // 获取视频模式 public int GetVideoMode() { diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index 42cd53c..1e6c7a2 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -131,16 +131,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; @@ -155,7 +145,7 @@ namespace JoyD.Windows.CS.Toprie { try { - _deviceManager.SetImageMode(ImageMode.Thermal); + _deviceManager.SetImageMode(ImageMode.Infrared); Console.WriteLine("连接后已设置热图模式"); } catch (Exception ex) @@ -680,7 +670,7 @@ namespace JoyD.Windows.CS.Toprie { try { - _deviceManager.SetImageMode(ImageMode.Thermal); + _deviceManager.SetImageMode(ImageMode.Infrared); Console.WriteLine("首次连接,设置热图模式"); } catch (Exception ex) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 8cd3403..0decf21 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -27,14 +27,15 @@ namespace JoyD.Windows.CS.Toprie Reconnecting } + + /// /// 图像模式枚举 /// public enum ImageMode { - Thermal, - Visible, - Fusion + Infrared, // 红外模式 + Natural // 自然模式 } /// @@ -175,7 +176,7 @@ namespace JoyD.Windows.CS.Toprie // 是否已释放 private bool _isDisposed = false; // 图像模式 - private ImageMode _currentImageMode = ImageMode.Thermal; + private ImageMode _currentImageMode = ImageMode.Infrared; // 当前色彩模式 private PaletteType _currentPaletteType = PaletteType.WhiteHot; // 当前视频模式 @@ -1425,11 +1426,6 @@ namespace JoyD.Windows.CS.Toprie if (_currentImageMode != value) { _currentImageMode = value; - // 如果已连接,发送模式变更命令 - if (_connectionStatus == ConnectionStatus.Connected) - { - SendModeChangeCommand(value); - } } } } @@ -2618,95 +2614,6 @@ namespace JoyD.Windows.CS.Toprie } } - /// - /// 发送模式切换命令 - /// - /// 图像模式 - private bool SendModeChangeCommand(ImageMode mode) - { - // 使用相同的线程锁保护SDK操作 - lock (_sdkOperationLock) - { - // 最大重试次数 - const int maxRetries = 3; - // 重试间隔(毫秒) - const int retryDelayMs = 100; - - for (int attempt = 0; attempt < maxRetries; attempt++) - { - try - { - // 检查对象状态和连接状态 - if (_a8Sdk == null || _connectionStatus != ConnectionStatus.Connected) - { - Log($"图像模式切换失败: {(attempt > 0 ? "重试中" : "")}SDK实例为空或设备未连接"); - Thread.Sleep(retryDelayMs); - continue; - } - - // 将ImageMode枚举转换为int类型 - _a8Sdk.SetImageMode((int)mode); - - // 短暂延迟,确保设置生效 - Thread.Sleep(50); - - // 简单验证:尝试读取一个基本属性确认SDK仍在正常工作 - try - { - // 使用一个简单的只读操作验证SDK状态 - int testValue = _a8Sdk.Color_plate; // 使用Color_plate作为验证点 - Log($"读取色彩模式值用于验证: {testValue}"); - Log($"图像模式 {mode} 切换成功 (尝试 {attempt + 1}/{maxRetries})"); - return true; - } - catch (Exception verifyEx) - { - // 验证失败,记录日志并准备重试 - Log($"图像模式切换验证失败 (尝试 {attempt + 1}/{maxRetries}): {verifyEx.Message}"); - - // 重试前等待 - Thread.Sleep(retryDelayMs); - continue; - } - } - catch (Exception ex) - { - Log($"切换图像模式异常 (尝试 {attempt + 1}/{maxRetries}): {ex.Message}"); - - // 增加SDK状态检查,判断是否真的需要触发连接异常 - try - { - // 简单的SDK状态检查 - if (_a8Sdk != null) - { - // 只读操作,确认SDK仍在响应 - int testValue = _a8Sdk.Color_plate; - Log($"SDK状态检查: 读取色彩模式值 = {testValue}"); - } - } - catch (Exception checkEx) - { - Log($"SDK状态检查失败: {checkEx.Message}"); - - // 只有在最后一次尝试且确认SDK状态异常时,才触发连接异常 - if (attempt == maxRetries - 1) - { - Log("所有尝试失败且SDK状态异常,触发连接异常事件"); - OnConnectionException(new ConnectionExceptionEventArgs(checkEx, "图像模式切换导致SDK状态异常")); - } - } - - // 重试前等待 - Thread.Sleep(retryDelayMs); - } - } - - // 所有尝试都失败 - Log($"图像模式切换最终失败: 已尝试{maxRetries}次"); - return false; - } - } - /// /// 开始接收图像(已弃用,使用HTTP方式替代) /// diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index 3d50950..01d0bdc 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -2383,30 +2383,6 @@ namespace JoyD.Windows.CS.Toprie Console.WriteLine($"设置系统时间失败: {ex.Message}"); } } - - // 新增方法:设置图像模式 - public void SetImageMode(int mode) - { - try - { - // 使用SET_IMAGE_MODE命令设置图像模式 - SDK格式 - string command = $"{CMD_HEAD}:{(int)CMD_TYPE.SET_IMAGE_MODE},{mode}$"; - - if (SendCommand(command, out string response)) - { - // 验证响应是否成功 - if (response != null) - { - Console.WriteLine($"设置图像模式为: {mode}"); - } - } - } - catch (Exception ex) - { - Console.WriteLine($"设置图像模式失败: {ex.Message}"); - } - } - // 获取视频模式 public int GetVideoMode() { From 129dc170365fa6417d1d35150c5bbd0d66962360 Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 14:47:34 +0800 Subject: [PATCH 12/17] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=8F=AF=E8=A7=81?= =?UTF-8?q?=E5=85=89=E5=9B=BE=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Toprie/Toprie/Camera.Designer.cs | 42 +---- .../CS/Framework4.0/Toprie/Toprie/Camera.cs | 146 +----------------- .../Toprie/Toprie/DeviceManager.cs | 5 +- 3 files changed, 11 insertions(+), 182 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs index 12526d9..0e05154 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.Designer.cs @@ -62,12 +62,7 @@ namespace JoyD.Windows.CS.Toprie // this.imageModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.thermalModeToolStripMenuItem, - this.visibleModeToolStripMenuItem, - this.fusionMode1ToolStripMenuItem, - this.fusionMode2ToolStripMenuItem, - this.fusionMode3ToolStripMenuItem, - this.fusionMode4ToolStripMenuItem, - this.fusionMode5ToolStripMenuItem}); + this.visibleModeToolStripMenuItem}); this.imageModeToolStripMenuItem.Name = "imageModeToolStripMenuItem"; this.imageModeToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.imageModeToolStripMenuItem.Text = "图像模式"; @@ -86,41 +81,6 @@ namespace JoyD.Windows.CS.Toprie this.visibleModeToolStripMenuItem.Text = "自然模式"; this.visibleModeToolStripMenuItem.Click += new System.EventHandler(this.visibleModeToolStripMenuItem_Click); // - // fusionMode1ToolStripMenuItem - // - this.fusionMode1ToolStripMenuItem.Name = "fusionMode1ToolStripMenuItem"; - this.fusionMode1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); - this.fusionMode1ToolStripMenuItem.Text = "融合模式1"; - this.fusionMode1ToolStripMenuItem.Click += new System.EventHandler(this.fusionMode1ToolStripMenuItem_Click); - // - // fusionMode2ToolStripMenuItem - // - this.fusionMode2ToolStripMenuItem.Name = "fusionMode2ToolStripMenuItem"; - this.fusionMode2ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); - this.fusionMode2ToolStripMenuItem.Text = "融合模式2"; - this.fusionMode2ToolStripMenuItem.Click += new System.EventHandler(this.fusionMode2ToolStripMenuItem_Click); - // - // fusionMode3ToolStripMenuItem - // - this.fusionMode3ToolStripMenuItem.Name = "fusionMode3ToolStripMenuItem"; - this.fusionMode3ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); - this.fusionMode3ToolStripMenuItem.Text = "融合模式3"; - this.fusionMode3ToolStripMenuItem.Click += new System.EventHandler(this.fusionMode3ToolStripMenuItem_Click); - // - // fusionMode4ToolStripMenuItem - // - this.fusionMode4ToolStripMenuItem.Name = "fusionMode4ToolStripMenuItem"; - this.fusionMode4ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); - this.fusionMode4ToolStripMenuItem.Text = "融合模式4"; - this.fusionMode4ToolStripMenuItem.Click += new System.EventHandler(this.fusionMode4ToolStripMenuItem_Click); - // - // fusionMode5ToolStripMenuItem - // - this.fusionMode5ToolStripMenuItem.Name = "fusionMode5ToolStripMenuItem"; - this.fusionMode5ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); - this.fusionMode5ToolStripMenuItem.Text = "融合模式5"; - this.fusionMode5ToolStripMenuItem.Click += new System.EventHandler(this.fusionMode5ToolStripMenuItem_Click); - // // colorModeToolStripMenuItem // this.colorModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index 1e6c7a2..aa49ab3 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -1031,35 +1031,14 @@ namespace JoyD.Windows.CS.Toprie // 更新视频模式菜单项的选中状态 try { - var currentMode = _deviceManager.CurrentVideoMode; - switch (currentMode) - { - case VideoMode.Infrared: - thermalModeToolStripMenuItem.Checked = true; - break; - case VideoMode.VisibleLight: - visibleModeToolStripMenuItem.Checked = true; - break; - case VideoMode.Fusion1: - fusionMode1ToolStripMenuItem.Checked = true; - break; - case VideoMode.Fusion2: - fusionMode2ToolStripMenuItem.Checked = true; - break; - case VideoMode.Fusion3: - fusionMode3ToolStripMenuItem.Checked = true; - break; - case VideoMode.Fusion4: - fusionMode4ToolStripMenuItem.Checked = true; - break; - case VideoMode.Fusion5: - fusionMode5ToolStripMenuItem.Checked = true; - break; - } + // 更改为使用ImageMode枚举 + var currentImageMode = _deviceManager.CurrentImageMode; + thermalModeToolStripMenuItem.Checked = currentImageMode == ImageMode.Infrared; + visibleModeToolStripMenuItem.Checked = currentImageMode == ImageMode.Natural; } catch (Exception ex) { - Console.WriteLine("获取当前视频模式失败: " + ex.Message); + Console.WriteLine("获取当前图像模式失败: " + ex.Message); } } } @@ -1234,11 +1213,7 @@ namespace JoyD.Windows.CS.Toprie { try { - if (_deviceManager != null) - { - Console.WriteLine("切换到红外模式"); - _deviceManager.SetVideoMode(VideoMode.Infrared); - } + _deviceManager.SetImageMode(ImageMode.Infrared); } catch (Exception ex) { @@ -1247,18 +1222,11 @@ namespace JoyD.Windows.CS.Toprie } } - /// - /// 自然模式 - /// private void visibleModeToolStripMenuItem_Click(object sender, EventArgs e) { try { - if (_deviceManager != null) - { - Console.WriteLine("切换到自然模式"); - _deviceManager.SetVideoMode(VideoMode.VisibleLight); - } + _deviceManager.SetImageMode(ImageMode.Natural); } catch (Exception ex) { @@ -1267,106 +1235,6 @@ namespace JoyD.Windows.CS.Toprie } } - /// - /// 融合模式1 - /// - private void fusionMode1ToolStripMenuItem_Click(object sender, EventArgs e) - { - try - { - if (_deviceManager != null) - { - Console.WriteLine("切换到融合模式1"); - _deviceManager.SetVideoMode(VideoMode.Fusion1); - } - } - catch (Exception ex) - { - Console.WriteLine("切换到融合模式1失败: " + ex.Message); - ShowError("切换到融合模式1失败"); - } - } - - /// - /// 融合模式2 - /// - private void fusionMode2ToolStripMenuItem_Click(object sender, EventArgs e) - { - try - { - if (_deviceManager != null) - { - Console.WriteLine("切换到融合模式2"); - _deviceManager.SetVideoMode(VideoMode.Fusion2); - } - } - catch (Exception ex) - { - Console.WriteLine("切换到融合模式2失败: " + ex.Message); - ShowError("切换到融合模式2失败"); - } - } - - /// - /// 融合模式3 - /// - private void fusionMode3ToolStripMenuItem_Click(object sender, EventArgs e) - { - try - { - if (_deviceManager != null) - { - Console.WriteLine("切换到融合模式3"); - _deviceManager.SetVideoMode(VideoMode.Fusion3); - } - } - catch (Exception ex) - { - Console.WriteLine("切换到融合模式3失败: " + ex.Message); - ShowError("切换到融合模式3失败"); - } - } - - /// - /// 融合模式4 - /// - private void fusionMode4ToolStripMenuItem_Click(object sender, EventArgs e) - { - try - { - if (_deviceManager != null) - { - Console.WriteLine("切换到融合模式4"); - _deviceManager.SetVideoMode(VideoMode.Fusion4); - } - } - catch (Exception ex) - { - Console.WriteLine("切换到融合模式4失败: " + ex.Message); - ShowError("切换到融合模式4失败"); - } - } - - /// - /// 融合模式5 - /// - private void fusionMode5ToolStripMenuItem_Click(object sender, EventArgs e) - { - try - { - if (_deviceManager != null) - { - Console.WriteLine("切换到融合模式5"); - _deviceManager.SetVideoMode(VideoMode.Fusion5); - } - } - catch (Exception ex) - { - Console.WriteLine("切换到融合模式5失败: " + ex.Message); - ShowError("切换到融合模式5失败"); - } - } - #endregion /// diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 0decf21..127a96e 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -262,7 +262,7 @@ namespace JoyD.Windows.CS.Toprie } } private TcpClient _imageTcpClient; - private bool _isInfraredMode = true; + // 使用CurrentImageMode代替_isInfraredMode private static readonly object _logLock = new object(); #region 私有方法 @@ -1768,7 +1768,6 @@ namespace JoyD.Windows.CS.Toprie Console.WriteLine($"图像接收线程启动: 使用HTTP请求获取图像数据"); try { - string url = string.Format("http://{0}:8080{1}", DeviceIp, _isInfraredMode ? "/video/infrared.jpg" : "/video/optical.jpg"); while (_isReceivingImages) { // 确保连接状态正常 @@ -1778,6 +1777,8 @@ namespace JoyD.Windows.CS.Toprie Thread.Sleep(500); continue; } + // 根据当前图像模式构建URL,确保模式更改能实时生效 + string url = string.Format("http://{0}:8080{1}", DeviceIp, CurrentImageMode == ImageMode.Infrared ? "/video/infrared.jpg" : "/video/optical.jpg"); // 添加时间戳避免缓存 string timestampUrl = url + "?t=" + DateTime.Now.Ticks; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(timestampUrl); From 13da804dd24c0d5cb07de79dcbe8fb45eb7c508b Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 14:51:26 +0800 Subject: [PATCH 13/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9Camera.cs=EF=BC=8C?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=87=AA=E7=84=B6=E6=A8=A1=E5=BC=8F=E4=B8=8B?= =?UTF-8?q?=E9=9A=90=E8=97=8F=E8=89=B2=E5=BD=A9=E6=A8=A1=E5=BC=8F=E8=8F=9C?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index aa49ab3..7958556 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -967,6 +967,12 @@ namespace JoyD.Windows.CS.Toprie { try { + // 根据当前图像模式控制色彩模式菜单的可见性 + if (_deviceManager != null) + { + colorModeToolStripMenuItem.Visible = _deviceManager.CurrentImageMode == ImageMode.Infrared; + } + // 清除视频模式菜单项的选中状态 thermalModeToolStripMenuItem.Checked = false; visibleModeToolStripMenuItem.Checked = false; From 5517ef2814a8697b49387b7ecb8388d00fc7d6d5 Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 15:12:44 +0800 Subject: [PATCH 14/17] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=E6=8E=A7=E4=BB=B6=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=9A=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0DesignMode=E6=9D=A1=E4=BB=B6=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=EF=BC=8C=E4=BB=85=E5=9C=A8=E9=9D=9E=E8=AE=BE=E8=AE=A1=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=E5=88=9D=E5=A7=8B=E5=8C=96=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=99=A8=E5=B9=B6=E8=BF=9E=E6=8E=A5=E8=AE=BE?= =?UTF-8?q?=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CS/Framework4.0/Toprie/Toprie/Camera.cs | 67 ++++++++++++++----- .../Toprie/Toprie/DeviceManager.cs | 15 ++++- 2 files changed, 64 insertions(+), 18 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index 7958556..55cdef5 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -19,6 +19,33 @@ namespace JoyD.Windows.CS.Toprie // 是否正在接收图像 private bool _isReceivingImage = false; + // 项目路径,用于数据文件的存取 + private string _projectPath = ""; + + /// + /// 获取或设置项目路径,控件所需的数据文件将在此目录中进行存取 + /// + [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; @@ -39,6 +66,12 @@ namespace JoyD.Windows.CS.Toprie try { 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); @@ -78,28 +111,28 @@ namespace JoyD.Windows.CS.Toprie /// 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; + } } /// diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 127a96e..929fb97 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -152,7 +152,10 @@ namespace JoyD.Windows.CS.Toprie { // 设计模式标志,用于在设计模式下跳过实际的设备连接和初始化 public static bool IsDesignMode { get; set; } = false; - + + // 项目路径,用于数据文件的存取 + private string _projectPath = ""; + // A8SDK实例 private A8SDK _a8Sdk; // 设备ID列表 @@ -243,6 +246,16 @@ namespace JoyD.Windows.CS.Toprie private Thread _imageReconnectThread; private Stream _imageStream; + /// + /// 项目路径属性 + /// 用于设置控件存取数据文件的目录 + /// + public string ProjectPath + { + get { return _projectPath; } + set { _projectPath = value; } + } + /// /// 检查网络是否可用 /// From c98b187312e031d380e364bf7538c116a01f6897 Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 15:16:41 +0800 Subject: [PATCH 15/17] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=E6=8E=A7=E4=BB=B6=E4=BB=8D=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E8=AE=BE=E5=A4=87=E5=B9=B6=E6=9B=B4=E6=96=B0=E5=9B=BE?= =?UTF-8?q?=E5=83=8F=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=9A=E4=BF=AE=E6=94=B9?= =?UTF-8?q?DeviceManager=E7=9A=84ConnectDevice=E5=92=8CInitialize=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E5=9C=A8=E8=AE=BE=E8=AE=A1=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E4=B8=8B=E4=B8=8D=E8=AE=BE=E7=BD=AE=E8=BF=9E=E6=8E=A5=E6=88=90?= =?UTF-8?q?=E5=8A=9F=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Toprie/Toprie/DeviceManager.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 929fb97..dfe3adc 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -508,13 +508,13 @@ namespace JoyD.Windows.CS.Toprie { Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 开始执行初始化"); - // 在设计模式下,跳过实际初始化,直接返回成功 + // 在设计模式下,跳过实际初始化,不设置连接成功状态 if (IsDesignMode) - { - Log("设计模式下跳过实际初始化,模拟初始化成功"); - _isInitialized = true; - _connectionStatus = ConnectionStatus.Connected; - return true; + { + Log("设计模式下跳过实际初始化"); + _isInitialized = true; // 仍然标记为已初始化,避免重复初始化 + _connectionStatus = ConnectionStatus.Disconnected; // 设置为断开状态,避免触发图像接收 + return false; // 返回false,避免Camera认为初始化成功 } // 双重检查锁定模式,确保线程安全的初始化 @@ -2965,14 +2965,15 @@ namespace JoyD.Windows.CS.Toprie { try { - // 在设计模式下,跳过实际连接,直接模拟连接成功 + // 在设计模式下,跳过实际连接,不设置真实的连接状态 if (IsDesignMode) { - Log("设计模式下跳过实际设备连接,模拟连接成功"); + Log("设计模式下跳过实际设备连接"); + // 不设置真实的连接状态,避免触发图像接收 _currentDeviceId = deviceId; - _isConnected = true; - _connectionStatus = ConnectionStatus.Connected; - UpdateConnectionStatus(ConnectionStatus.Connected, "设计模式:设备模拟连接成功"); + _isConnected = false; + _connectionStatus = ConnectionStatus.Disconnected; + UpdateConnectionStatus(ConnectionStatus.Disconnected, "设计模式:跳过设备连接"); return; } From fe3bd21124f0aac0bf74c6c40c302e8ee6e4a4ed Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 15:26:06 +0800 Subject: [PATCH 16/17] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=E6=8E=A7=E4=BB=B6=E4=BB=8D=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E8=AE=BE=E5=A4=87=E5=B9=B6=E6=9B=B4=E6=96=B0=E5=9B=BE?= =?UTF-8?q?=E5=83=8F=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=9A=E5=9C=A8DeviceMan?= =?UTF-8?q?ager=E4=B8=AD=E4=B8=BA=E6=89=80=E6=9C=89=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E5=99=A8=E5=92=8C=E7=BA=BF=E7=A8=8B=E6=96=B9=E6=B3=95=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=AE=BE=E8=AE=A1=E6=A8=A1=E5=BC=8F=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96Camera=E6=8E=A7=E4=BB=B6=E7=9A=84?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E6=A8=A1=E5=BC=8F=E7=8A=B6=E6=80=81=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CS/Framework4.0/Toprie/Toprie/Camera.cs | 11 +++++++- .../Toprie/Toprie/DeviceManager.cs | 27 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index 55cdef5..a8ed870 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -48,6 +48,15 @@ namespace JoyD.Windows.CS.Toprie // 显示错误的定时器 private System.Windows.Forms.Timer _errorDisplayTimer; + + /// + /// 更新设计模式状态到DeviceManager + /// + private void UpdateDesignModeStatus() + { + DeviceManager.IsDesignMode = DesignMode; + Console.WriteLine($"相机控件设计模式状态已更新: {DesignMode}"); + } public Camera() { @@ -57,7 +66,7 @@ namespace JoyD.Windows.CS.Toprie this.contextMenuStrip1.Opening += ContextMenuStrip1_Opening; // 将设计模式状态传递给DeviceManager - DeviceManager.IsDesignMode = DesignMode; + UpdateDesignModeStatus(); // 只有在非设计模式下才初始化设备管理器和错误定时器 if (!DesignMode) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index dfe3adc..2ec9601 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -878,6 +878,13 @@ namespace JoyD.Windows.CS.Toprie { try { + // 在设计模式下,跳过实际的连接检查 + if (IsDesignMode) + { + Log("设计模式下跳过实际的连接检查"); + return; + } + // 首先停止现有的连接检查,确保资源释放 StopConnectionCheck(); @@ -1975,6 +1982,12 @@ namespace JoyD.Windows.CS.Toprie /// private void StartImageReconnect() { + // 在设计模式下,跳过实际的图像重连 + if (IsDesignMode) + { + return; + } + // 避免重复创建重连线程 if (_imageReconnectThread != null && _imageReconnectThread.IsAlive) { @@ -3358,6 +3371,13 @@ namespace JoyD.Windows.CS.Toprie { Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartAutoReconnect() - 开始执行"); + // 在设计模式下,跳过实际的自动重连 + if (IsDesignMode) + { + Log("设计模式下跳过实际的自动重连"); + return; + } + // 检查对象是否已释放 if (_isDisposed) { @@ -3479,6 +3499,13 @@ namespace JoyD.Windows.CS.Toprie { try { + // 在设计模式下,跳过实际的定期连接检查 + if (IsDesignMode) + { + Log("设计模式下跳过实际的定期连接检查"); + return; + } + // 停止现有的检查 StopConnectionCheck(); From 1b51115e4e7fdf9259bb501c4b593b6c7818ef77 Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 29 Oct 2025 16:26:54 +0800 Subject: [PATCH 17/17] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=E6=8E=A7=E4=BB=B6=E4=BB=8D=E5=9C=A8?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E8=AE=BE=E5=A4=87=E5=B9=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=9B=BE=E5=83=8F=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BA=86=E8=AE=BE=E8=AE=A1=E6=A8=A1=E5=BC=8F=E6=A3=80?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CS/Framework4.0/Toprie/Toprie/Camera.cs | 16 ++++++++-- .../Toprie/Toprie/DeviceManager.cs | 32 ++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index a8ed870..cfe5736 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -158,6 +158,7 @@ namespace JoyD.Windows.CS.Toprie /// public void StartCamera() { + if (DesignMode) return; try { // 只有在没有接收图像时才启动 @@ -244,6 +245,7 @@ namespace JoyD.Windows.CS.Toprie /// private void StartReceiveImage() { + if (DesignMode) return; try { if (!_isReceivingImage && _deviceManager.ConnectionStatus == ConnectionStatus.Connected) @@ -265,6 +267,7 @@ namespace JoyD.Windows.CS.Toprie /// public void StopCamera() { + if (DesignMode) return; try { if (_isReceivingImage) @@ -284,6 +287,7 @@ namespace JoyD.Windows.CS.Toprie /// private void DeviceManager_ImageReceived(object sender, ImageReceivedEventArgs e) { + if (DesignMode) return; Image image = null; try { @@ -449,6 +453,7 @@ namespace JoyD.Windows.CS.Toprie /// private void UpdateImageOnUI(Image image) { + if (DesignMode) return; // 线程安全检查 - 确保在UI线程上执行 if (this.InvokeRequired) { @@ -605,7 +610,8 @@ namespace JoyD.Windows.CS.Toprie /// 设备管理器连接状态变更事件处理 /// private void DeviceManager_ConnectionStatusChanged(object sender, ConnectionStatusChangedEventArgs e) - { + { + if (DesignMode) return; // 参数验证 if (e == null) { @@ -680,6 +686,7 @@ namespace JoyD.Windows.CS.Toprie /// private void HandleConnectionStatusChanged(ConnectionStatusChangedEventArgs e) { + if (DesignMode) return; try { // 确保在UI线程上更新UI状态 @@ -787,6 +794,7 @@ namespace JoyD.Windows.CS.Toprie /// 是否已连接 private void UpdateUIState(bool isConnected) { + if (DesignMode) return; try { // 根据连接状态更新图像框状态 @@ -829,7 +837,8 @@ namespace JoyD.Windows.CS.Toprie /// 设备管理器连接异常事件处理 /// private void DeviceManager_ConnectionException(object sender, ConnectionExceptionEventArgs e) - { + { + if (DesignMode) return; // 参数验证 if (e == null) { @@ -924,6 +933,7 @@ namespace JoyD.Windows.CS.Toprie /// private void ShowError(string message) { + if (DesignMode) return; Console.WriteLine(message); // 可以根据需要添加UI上的错误显示 @@ -968,6 +978,7 @@ namespace JoyD.Windows.CS.Toprie /// private void ErrorDisplayTimer_Tick(object sender, EventArgs e) { + if (DesignMode) return; _errorDisplayTimer.Stop(); // 清除错误显示,恢复到等待图像状态 @@ -1007,6 +1018,7 @@ namespace JoyD.Windows.CS.Toprie /// private void ContextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e) { + if (DesignMode) return; try { // 根据当前图像模式控制色彩模式菜单的可见性 diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index 2ec9601..2f31099 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -151,7 +151,7 @@ namespace JoyD.Windows.CS.Toprie public class DeviceManager : IDisposable { // 设计模式标志,用于在设计模式下跳过实际的设备连接和初始化 - public static bool IsDesignMode { get; set; } = false; + public static bool IsDesignMode { get; set; } = true; // 项目路径,用于数据文件的存取 private string _projectPath = ""; @@ -1785,6 +1785,15 @@ namespace JoyD.Windows.CS.Toprie /// private void ReceiveImageDataWithHttpWebRequest() { + // 在设计模式下,跳过实际的图像接收 + if (IsDesignMode) + { + Log("设计模式下跳过实际的图像接收"); + // 设置为不接收状态,避免持续运行 + _isReceivingImages = false; + return; + } + Console.WriteLine($"图像接收线程启动: 使用HTTP请求获取图像数据"); try { @@ -2670,6 +2679,13 @@ namespace JoyD.Windows.CS.Toprie /// private void ReceiveImageThread() { + // 在设计模式下,跳过实际的图像接收 + if (IsDesignMode) + { + Log("设计模式下跳过实际的图像接收"); + return; + } + Console.WriteLine("警告: 已弃用的图像接收方式,使用HTTP方式替代"); try { @@ -3576,6 +3592,13 @@ namespace JoyD.Windows.CS.Toprie /// 定时器状态 private void ReconnectCallback(object state) { + // 在设计模式下,跳过实际的重连操作 + if (IsDesignMode) + { + Log("设计模式下跳过实际的重连操作"); + return; + } + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReconnectCallback() - 开始执行"); // 使用Interlocked.Exchange实现原子操作检查,防止重入 @@ -3993,6 +4016,13 @@ namespace JoyD.Windows.CS.Toprie /// 定时器状态 private void HeartbeatCallback(object state) { + // 在设计模式下,跳过实际的心跳检测 + if (IsDesignMode) + { + Log("设计模式下跳过实际的心跳检测"); + return; + } + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] HeartbeatCallback() - 开始执行"); // 检查对象是否已释放