我参与了一个项目,经过多次测试,我和我的同事们对我们的编码技能和方法没有什么失望。问题如下:
应用程序必须从通过一个或多个串行端口连接在单一总线上的某种类型的 RS-485 设备读取数据。但是,这些设备来自不同的制造商,因此它们具有不同的连接设置、不同的命令集并返回不同的数据。此外,每个串行端口可以同时处理一个、一个或多个设备。
我们的每个自定义 COMPort 对象都包含一个设备列表,每个设备都包含推荐的类型和设备设置。因此,在加载所有设置并构建所有对象后,应用程序开始轮询每个 COMPort 对象列表中的每个设备。每次,在写入每个命令之前,串行端口缓冲区都会被清空,并且其设置会更改(波特率、奇偶校验、握手),而无需关闭和重新打开端口。在每个命令之后,aThreading.Thread.Sleep(delay)
会以可变延迟执行,具体取决于每个设备的响应时间(延迟从 150 毫秒到 800 毫秒不等)。Sleep() 结束后,应用程序读取串口并获取预期数据。之后,如果BytestoRead
> 0 或BytestoRead
= 0 或发生异常(返回错误数据、校验和错误等),此函数返回并且应用程序继续读取列表中的下一个设备。
因此,应用程序在无限循环中执行,从头到尾读取所有设备,直到用户从 UI 停止它(当项目是 Windows 窗体时)或服务停止时(当项目是 Windows 服务时)它最终结束. 应用程序不使用 DataReceived 事件,因为我们遇到了很多麻烦,因此手动完成读取过程。
最后,应用程序有一个全局日志文件,可以从所有对象访问并保存所有常见和不寻常的事件,每个 COMPort 对象都有自己的日志文件,所有有效的读取数据都写入其中。此外,每个 COMPort 都有一个计时器对象,该对象每秒检查白天的变化,每天午夜所有日志文件关闭并打开新文件。
主要问题是所有这一切似乎都很好,但不幸的是,应用程序在 1-2 小时后停止写入日志(大部分时间在 1:58 分钟后恰好在一台客户端计算机上),然后在几分钟后崩溃,有时会出现发送或不发送窗口,有时无声。CPU 负载从不超过 1%,私有和虚拟内存似乎没有泄漏,因为它没有增长。
我不得不提到,这个应用程序在 3 台机器(Windows XP、Windows 2003)上进行了测试,在以下情况下:混合类型的设备、一种类型的设备、没有连接设备(在一台开发机器上使用空调制解调器电缆。)甚至在最后一种情况下,应用程序静默崩溃。这让我想到这个问题与读取过程无关,因为在“没有连接设备”的情况下,没有要读取的数据。此外,该项目是使用 .Net Framework 2.0 使用 VB.Net 2008 构建的。
编辑: 下面是读、写和轮询过程的小总结。我把它写在 Notepad++ 上作为一个例子,因为我现在无法访问真正的代码,所以请原谅任何语法错误。这样做的目的是更好地解释我上面描述的情况,仅此而已。
Private deviceIndex as Integer = -1
Private deviceList as List(Of Devices)
Private serialPort as SerialPort()
Private Sub PollSensor()
Try
If (deviceIndex <> -1) And deviceIndex > deviceList.Count then
deviceIndex = 0
End If
With serialPort
If .IsOpen then
.DiscardInBuffer()
.DiscardOutBuffer()
' SerialPort attributes must change during runtime to suit device requirements.
' Some devices require for example 4800 BaudRate and some other 9600.
.BaudRate = deviceList(deviceIndex).BaudRate
.Parity = deviceList(deviceIndex).Parity
.Handshake = deviceList(deviceIndex).HandShake
' Write command to device, passing device specific response time.
' Some devices have for example 150ms response time and some other 800ms.
WritetoDevice(new Byte[] {01, 01}, deviceList(deviceIndex).ResponseTime)
End If
End With
Catch ex As Exception
' Log
End Try
End Sub
Private Sub WritetoDevice(ByVal command As Byte[], ByVal responseTime As Integer)
Try
serialPort.Write(command, 0, command.Length)
' Cause application to pause for time equal with the device response time.
Threading.Thread.Sleep(responseTime)
' If device response is regular, data may have been available in serial port.
If ReadDevice() = 0 then
' Poll next device
deviceIndex += 1
PollSensor()
End If
Catch ex As Exception
' Log
End Try
End Sub
Private Function ReadDevice() As Integer
Dim data as String = ""
Try
With serialPort
If .IsOpen then
If .BytesToRead() > 0 Then
Do
data += .ReadExisting()
Until .BytesToRead() = 0
' Do something with data
End If
End If
End With
Catch ex As Exception
' Log
Finally
' Continue with the next device in deviceList, ignoring the current device Read() result
' This happens because devices return faulty data too often and the polling must continue
Return 0
End Try
End Function
我们曾尝试使用 Debug Diagnostics Tool 1.2 x86 但返回结果,这无助于我们了解问题的根源。一些导出的报告包含以下内容,尽管所有异常都在 Try..Catch 块中处理:
- The following threads in ... are waiting in a WaitOne
- 6,25% of threads blocked
- System.ExecutionEngineException
- System.StackOverflowException
- System.OutOfMemoryException
- System.FormatException
- System.Threading.ThreadAbortException
有没有人对上述情况如何工作有更好的了解?
随意批评上述任何方法,因为这将帮助我们变得更好。