优化温度数据接收逻辑,修复端口号并增强线程安全性
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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文档中指定的端口
|
||||
|
||||
/// <summary>
|
||||
/// 项目路径属性
|
||||
@@ -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<byte> temperatureDataAccumulator = new List<byte>();
|
||||
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<byte> temperatureDataAccumulator = new List<byte>();
|
||||
// 更新类成员变量,在锁内进行
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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绘制到缓冲
|
||||
|
||||
Reference in New Issue
Block a user