diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs index b4dfd0d..bd40c51 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Camera.cs @@ -506,7 +506,23 @@ namespace JoyD.Windows.CS.Toprie Console.WriteLine("Camera开始使用HTTP方式接收图像"); // 直接调用HTTP方式的图像接收 _deviceManager.StartImageReceiving(); - _deviceManager.StartTemperatureDataReceiving(); + + // 在调用StartTemperatureDataReceiving前先调用StopTemperatureDataReceiving,保持与ResumeDetection方法一致的模式 + // 并添加时间间隔检查,避免短时间内频繁调用 + lock (_temperatureCallLock) + { + TimeSpan elapsed = DateTime.Now - _lastTemperatureStartCall; + if (elapsed.TotalMilliseconds < 500) // 至少间隔500毫秒 + { + Console.WriteLine("避免短时间内重复启动温度数据接收,跳过本次调用"); + } + else + { + _deviceManager.StopTemperatureDataReceiving(); + _deviceManager.StartTemperatureDataReceiving(); + _lastTemperatureStartCall = DateTime.Now; + } + } _isReceivingImage = true; } } @@ -539,6 +555,9 @@ namespace JoyD.Windows.CS.Toprie } private bool _isPaused = false; // 暂停状态标志 + // 记录上次启动温度数据接收的时间,用于防止短时间内重复调用 + private DateTime _lastTemperatureStartCall = DateTime.MinValue; + private readonly object _temperatureCallLock = new object(); // Ping相关字段 private System.Threading.Timer _pingTimer; diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index f23430e..81bbdb9 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -267,7 +267,7 @@ namespace JoyD.Windows.CS.Toprie private NetworkStream _temperatureStream; private bool _isReceivingTemperatureData = false; private ManualResetEvent _stopTemperatureEvent; - private const int TEMPERATURE_TCP_PORT = 7682; // 温度数据TCP端口 + private const int TEMPERATURE_TCP_PORT = 8081; // 温度数据TCP端口 - 修正为热像仪SDK文档中指定的端口 /// /// 项目路径属性 @@ -888,6 +888,13 @@ namespace JoyD.Windows.CS.Toprie Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartTemperatureDataReceiving() - 连接未建立,无法开始温度数据接收"); return; } + + // 添加额外的状态检查,避免不必要的重复启动 + if (_isReceivingTemperatureData && _temperatureReceiveThread != null && _temperatureReceiveThread.IsAlive) + { + Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartTemperatureDataReceiving() - 温度数据接收已经在进行中,避免重复启动"); + return; + } } Log("开始使用TCP方式接收温度数据"); @@ -896,25 +903,37 @@ namespace JoyD.Windows.CS.Toprie // 确保之前的连接已关闭 StopTemperatureDataReceiving(); - // 重置停止事件 - _stopTemperatureEvent = new ManualResetEvent(false); + // 在锁内执行所有关键状态更新,确保原子性 lock (_lockObject) { + // 重置停止事件 + _stopTemperatureEvent?.Dispose(); + _stopTemperatureEvent = new ManualResetEvent(false); _isReceivingTemperatureData = true; } // 创建并启动温度数据接收线程 - _temperatureReceiveThread = new Thread(ReceiveTemperatureDataWithTcp) + Thread newThread = new Thread(ReceiveTemperatureDataWithTcp) { IsBackground = true, Name = "TemperatureReceiveThread" }; - _temperatureReceiveThread.Start(); + + // 在锁内更新线程引用 + lock (_lockObject) + { + _temperatureReceiveThread = newThread; + } + + // 启动线程 + newThread.Start(); } catch (Exception ex) { // 记录异常 Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StartTemperatureDataReceiving() - 异常: {ex.Message}"); + + // 在异常情况下确保状态正确重置 lock (_lockObject) { _isReceivingTemperatureData = false; @@ -943,26 +962,36 @@ namespace JoyD.Windows.CS.Toprie return; } + // 使用变量保存需要释放的资源,减少锁的持有时间 + ManualResetEvent stopEventToDispose = null; + Thread threadToJoin = null; + NetworkStream streamToClose = null; + TcpClient clientToClose = null; + try { - // 线程安全地更新状态 + // 在一个锁内收集所有需要清理的资源和更新状态 lock (_lockObject) { _isReceivingTemperatureData = false; + stopEventToDispose = _stopTemperatureEvent; + threadToJoin = _temperatureReceiveThread; + streamToClose = _temperatureStream; + clientToClose = _temperatureTcpClient; + + // 立即重置引用,避免资源被重新使用 + _temperatureStream = null; + _temperatureTcpClient = null; + _temperatureReceiveThread = null; + _stopTemperatureEvent = null; } - // 通知线程停止 - ManualResetEvent stopEvent = null; - lock (_lockObject) - { - stopEvent = _stopTemperatureEvent; - } - - if (stopEvent != null) + // 在锁外通知线程停止,避免死锁 + if (stopEventToDispose != null) { try { - stopEvent.Set(); + stopEventToDispose.Set(); } catch (ObjectDisposedException) { @@ -970,54 +999,75 @@ namespace JoyD.Windows.CS.Toprie } } - // 等待线程结束 - Thread temperatureThread = null; - lock (_lockObject) + // 在锁外等待线程结束,避免死锁 + if (threadToJoin != null && threadToJoin.IsAlive) { - temperatureThread = _temperatureReceiveThread; - } - - if (temperatureThread != null && temperatureThread.IsAlive) - { - temperatureThread.Join(1000); // 等待最多1秒 - Log("温度接收线程已停止"); + Log("等待温度接收线程结束..."); + // 增加等待时间,确保线程有足够时间完成清理 + if (threadToJoin.Join(2000)) // 等待最多2秒 + { + Log("温度接收线程已正常停止"); + } + else + { + Log("警告:温度接收线程可能未正常停止,已超时"); + } } - // 关闭TCP客户端和流 - NetworkStream stream = null; - TcpClient client = null; - - lock (_lockObject) - { - stream = _temperatureStream; - client = _temperatureTcpClient; - _temperatureStream = null; - _temperatureTcpClient = null; - } - - if (stream != null) + // 在锁外关闭TCP客户端和流,避免死锁 + if (streamToClose != null) { try { - stream.Close(); + streamToClose.Close(); + } + catch (Exception ex) + { + Log($"关闭NetworkStream时出现异常: {ex.Message}"); } - catch (Exception) - {} } - if (client != null) + if (clientToClose != null) { try { - client.Close(); + clientToClose.Close(); + } + catch (Exception ex) + { + Log($"关闭TcpClient时出现异常: {ex.Message}"); + } + } + + // 释放停止事件 + if (stopEventToDispose != null) + { + try + { + stopEventToDispose.Dispose(); + } + catch (Exception ex) + { + Log($"释放ManualResetEvent时出现异常: {ex.Message}"); } - catch (Exception) - {} } } catch (Exception ex) { Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] StopTemperatureDataReceiving() - 异常: {ex.Message}"); + + // 在异常情况下,再次尝试重置状态 + try + { + lock (_lockObject) + { + _isReceivingTemperatureData = false; + _temperatureStream = null; + _temperatureTcpClient = null; + _temperatureReceiveThread = null; + } + } + catch {} } finally { @@ -1032,63 +1082,131 @@ namespace JoyD.Windows.CS.Toprie { Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] ReceiveTemperatureDataWithTcp() - 开始执行"); + // 使用局部变量存储资源,避免在finally中访问可能已被释放的字段 + TcpClient localTcpClient = null; + NetworkStream localStream = null; + List temperatureDataAccumulator = new List(); + byte[] buffer = new byte[8192]; // 温度数据缓冲区 + try { // 创建TCP客户端并连接到设备的温度数据端口 - _temperatureTcpClient = new TcpClient + localTcpClient = new TcpClient { ReceiveTimeout = 5000, SendTimeout = 5000 }; - IAsyncResult result = _temperatureTcpClient.BeginConnect(_deviceIp, TEMPERATURE_TCP_PORT, null, null); + Log($"正在连接到温度数据端口 {TEMPERATURE_TCP_PORT}..."); + IAsyncResult result = localTcpClient.BeginConnect(_deviceIp, TEMPERATURE_TCP_PORT, null, null); bool connected = result.AsyncWaitHandle.WaitOne(3000, true); - if (!connected || !_temperatureTcpClient.Connected) + if (!connected || !localTcpClient.Connected) { Log("温度数据TCP连接失败,超时"); + localTcpClient.Close(); return; } - _temperatureTcpClient.EndConnect(result); + localTcpClient.EndConnect(result); Log("温度数据TCP连接成功"); // 获取网络流 - _temperatureStream = _temperatureTcpClient.GetStream(); - _temperatureStream.ReadTimeout = 5000; + localStream = localTcpClient.GetStream(); + localStream.ReadTimeout = 5000; - byte[] buffer = new byte[8192]; // 温度数据缓冲区 - List temperatureDataAccumulator = new List(); + // 更新类成员变量,在锁内进行 + lock (_lockObject) + { + _temperatureTcpClient = localTcpClient; + _temperatureStream = localStream; + } // 发送开始温度数据传输的命令 byte[] startCommand = Encoding.ASCII.GetBytes("start_temp_transfer\r\n"); - _temperatureStream.Write(startCommand, 0, startCommand.Length); - _temperatureStream.Flush(); + localStream.Write(startCommand, 0, startCommand.Length); + localStream.Flush(); Log("已发送开始温度数据传输命令,开始接收温度数据"); - while (!_stopTemperatureEvent.WaitOne(0)) + // 使用局部变量引用停止事件,避免在循环中重复加锁 + ManualResetEvent currentStopEvent = null; + lock (_lockObject) { + currentStopEvent = _stopTemperatureEvent; + } + + // 循环读取数据,使用更安全的退出机制 + bool shouldContinue = true; + while (shouldContinue) + { + // 检查停止信号 + if (currentStopEvent != null && currentStopEvent.WaitOne(0)) + { + Log("接收到停止信号,准备退出温度数据接收循环"); + shouldContinue = false; + break; + } + + // 检查是否仍在接收状态 + lock (_lockObject) + { + if (!_isReceivingTemperatureData) + { + Log("接收状态已更改,准备退出温度数据接收循环"); + shouldContinue = false; + break; + } + } + try { // 检查连接状态 - if (!_temperatureTcpClient.Connected) + if (localTcpClient == null || !localTcpClient.Connected) { Log("温度数据TCP连接已断开"); + shouldContinue = false; break; } - // 读取数据 - int bytesRead = _temperatureStream.Read(buffer, 0, buffer.Length); - if (bytesRead > 0) + // 检查流是否可读 + if (localStream == null || !localStream.CanRead) { - // 将读取的数据添加到累积器 - byte[] receivedBytes = new byte[bytesRead]; - Array.Copy(buffer, receivedBytes, bytesRead); - temperatureDataAccumulator.AddRange(receivedBytes); - - // 处理累积的数据 - ProcessReceivedTemperatureData(temperatureDataAccumulator); + Log("网络流不可读,停止接收"); + shouldContinue = false; + break; + } + + // 检查是否有数据可读,避免阻塞 + if (localStream.DataAvailable) + { + // 读取数据 + int bytesRead = localStream.Read(buffer, 0, buffer.Length); + if (bytesRead > 0) + { + // 将读取的数据添加到累积器 + byte[] receivedBytes = new byte[bytesRead]; + Array.Copy(buffer, receivedBytes, bytesRead); + + // 线程安全地更新累积器或直接处理 + lock (temperatureDataAccumulator) + { + temperatureDataAccumulator.AddRange(receivedBytes); + ProcessReceivedTemperatureData(temperatureDataAccumulator); + } + } + else + { + // 读取到0字节表示连接已关闭 + Log("远程主机关闭了连接"); + shouldContinue = false; + break; + } + } + else + { + // 如果没有数据可读,短暂休眠避免CPU占用过高 + Thread.Sleep(10); } } catch (TimeoutException) @@ -1099,12 +1217,14 @@ namespace JoyD.Windows.CS.Toprie catch (IOException ex) { Log($"温度数据接收IO异常: {ex.Message}"); + shouldContinue = false; break; } catch (Exception ex) { Log($"温度数据接收异常: {ex.Message}"); - // 非致命异常,继续尝试 + // 非致命异常,短暂休眠后继续尝试 + Thread.Sleep(100); } } } @@ -1120,10 +1240,28 @@ namespace JoyD.Windows.CS.Toprie lock (_lockObject) { _isReceivingTemperatureData = false; + // 不在这里调用StopTemperatureDataReceiving,避免递归调用和潜在死锁 + // 由调用者负责调用StopTemperatureDataReceiving } - // 清理资源 - StopTemperatureDataReceiving(); + // 清理局部资源 + try + { + if (localStream != null) + { + localStream.Close(); + } + } + catch {} + + try + { + if (localTcpClient != null) + { + localTcpClient.Close(); + } + } + catch {} } } diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/README.md b/Windows/CS/Framework4.0/Toprie/Toprie/README.md index 44edfae..017784d 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/README.md +++ b/Windows/CS/Framework4.0/Toprie/Toprie/README.md @@ -8,10 +8,12 @@ 1. 暂停或恢复时,设置暂停状态,调用更新Info 2. 断开或连接时,设置连接状态,调用更新Info 3. Ping通状态变化时,修改Ping状态,调用更新Info - 4. 图像更新时, 保存LastImage, 调用更新到UI + 4. 温度数据更新时,调用更新Info + 5. 图像更新时, 保存LastImage, 调用更新到UI ### 更新Info: - 1. 如果暂停,显示暂停信息,否则如果Ping不通或断开,显示重连信息,否则清空InfoImage - 2. 最后调用更新UI + 1. 如果暂停,显示暂停信息,否则如果Ping不通或断开,显示重连信息, + 2. 如果有温度数据,显示最高温度,否则清空Info + 3. 最后调用更新UI ### 更新UI: 1. 先将LastImage绘制到全局缓冲 2. 再将InfoImage绘制到缓冲