如何在服务应用环境中使用串口不断监听数据

how to use serial port in a service application environment constantly listening for data

我已经编写了一个服务应用程序来侦听端口以获取可能通过的任何通信,我们的实验室将 运行 进行某项测试,该测试将每隔几个小时左右发送一次串行数据。该服务 运行s 可以在几个小时内正常获取数据,然后神秘地停止。系统事件日志显示服务意外终止。在应用程序事件日志中,它有一个更具描述性的 .NET 错误,

Application: BondTestService.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.ObjectDisposedException at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean ByRef) at System.StubHelpers.StubHelpers.SafeHandleAddRef(System.Runtime.InteropServices.SafeHandle, Boolean ByRef) at Microsoft.Win32.UnsafeNativeMethods.GetOverlappedResult(Microsoft.Win32.SafeHandles.SafeFileHandle, System.Threading.NativeOverlapped*, Int32 ByRef, Boolean) at System.IO.Ports.SerialStream+EventLoopRunner.WaitForCommEvent() at System.Threading.ThreadHelper.ThreadStart_Context(System.Object) at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) at System.Threading.ThreadHelper.ThreadStart()

我正在阅读服务的行为方式和串行端口的行为方式,所以如果我错了,请纠正我,如果在测试之间有 2 小时左右的间隔,该服务将假定它不是 运行ning 和停止自己?

我还在从串行端口读取缓冲区后读取,我附加到如下所示的字符串生成器对象并对字符串执行我需要的操作,然后串行端口发生了什么,它只是保持打开状态等待下一个值还是我必须关闭它并重新打开它才能刷新它?

不确定如何处理此问题,因为它需要打开以等待实验室测试人员在任何给定时间发送他的数据。

Imports System
Imports System.Data.SqlClient
Imports System.IO.Ports
Imports System.Net.Mime
Imports Microsoft.Win32
Imports System.IO
Imports System.Text.RegularExpressions
Imports BondTestService.PI
Imports PCA.Core.Configuration

Public Class Bond
Dim WithEvents serialPort As New IO.Ports.SerialPort
Public Delegate Sub myDelegate()
Public RawString As New System.Text.StringBuilder
Public value As String
Public BondTest As Integer = 10

 #Region "Commport Traffic and Configuration Validations"
 Public Sub StartListening()

    If serialPort.IsOpen Then
        serialPort.Close()
        ErrorLog2(Now.ToString & "Port Closed because StartListening method started over")
    End If

    Try
        With serialPort
            .PortName = Registry.LocalMachine.OpenSubKey("SOFTWARE\Wow6432Node\AUTOLABDEVICESERVICE\bondtest", True).GetValue("commport")
            .BaudRate = CInt(Registry.LocalMachine.OpenSubKey("SOFTWARE\Wow6432Node\AUTOLABDEVICESERVICE\bondtest", True).GetValue("baudrate"))
            If Registry.LocalMachine.OpenSubKey("SOFTWARE\Wow6432Node\AUTOLABDEVICESERVICE\bondtest", True).GetValue("parity") = 0 Then
                .Parity = Parity.None
           End If
            If Registry.LocalMachine.OpenSubKey("SOFTWARE\Wow6432Node\AUTOLABDEVICESERVICE\bondtest", True).GetValue("stopbits") = 1 Then
                .StopBits = StopBits.One
            End If
            .DataBits = CInt(Registry.LocalMachine.OpenSubKey("SOFTWARE\Wow6432Node\AUTOLABDEVICESERVICE\bondtest", True).GetValue("bytesize"))
            .Handshake = Handshake.None
            If Registry.LocalMachine.OpenSubKey("SOFTWARE\Wow6432Node\AUTOLABDEVICESERVICE\bondtest", True).GetValue("RtsControl") = 1 Then
                .RtsEnable = True
            Else
                .RtsEnable = False
           End If
        End With
        serialPort.Open()

        'debug
        'ErrorLog2("Listening to COM 19, SerialPort has been Opened")

    Catch ex As Exception
         ErrorLog2(Now.ToString & ex.tostring)
    End Try
End Sub

Public Function Filelocator() As String

    ' Dim filePath As String = IO.Path.Combine(Application.StartupPath, "bondtest.bat")
    Dim filePath As String = IO.Path.Combine("C:\Program Files (x86)\PIPC\Interfaces\Lab", "BondTest.bat")
    'Dim reader As New System.IO.StreamReader(filePath)
    Dim LineNumber = 4
    Using file As New StreamReader(filePath)
        ' Skip all preceding lines: '
        For i As Integer = 1 To LineNumber - 1
            If file.ReadLine() Is Nothing Then
                ErrorLog2("LineNumber")
            End If
        Next
        ' Attempt to read the line you're interested in: '
        Dim line As String = file.ReadLine()
        If line Is Nothing Then
            ErrorLog2("LineNumber")
        End If
        Return line
    End Using
End Function

Private Sub serialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort.DataReceived
    Try
        If GetBondInterfaceStatus = 1 Then
            UPdateVariable()
        Else
            exit Sub
        End If
    Catch ex As Exception
        Errorlog2(Ex.Tostring)
    End Try
End Sub



#End Region

#Region "String Handling"
Public Sub UPdateVariable()
    With RawString
        .Append(serialPort.ReadLine())
    End With
    try
        ErrorLog2(now.ToString & RawString.ToString)
        InsertTestDataDEBUG(GetRecordID, BondTest, BondTestType.ToUpper.ToString, GetBondPosition(), StringParser(RawString.ToString()), RawString.tostring)
        InsertTestData(GetRecordID, BondTest, BondTestType.ToUpper.ToString, GetBondPosition(), StringParser(RawString.ToString()))
        RawString.Clear()
    Catch ex As Exception
        ErrorLog2(ex.ToString())
    End Try
End Sub

Public Function StringParser(RawString As String)As Double ()
    Dim Moisture = RawString
    Dim pattern As String = "[0-9],"
    Dim regex As New Regex(pattern)
    Dim Counter As Integer = 0
    Dim dblValues(1) As Double
    Dim values As String() = Moisture.Split(New Char() {" "c})


    for i = 0 to values.Count - 1
        if regex.IsMatch(values(i)) Then
            dblValues(Counter) = CDbl(values(i).Substring(0,1))
            Counter = Counter + 1
        Elseif values(i) = "" Then
            continue for
        else
            if Double.TryParse(values(i), dblValues(Counter)) Then
                Counter = Counter + 1 
            End If
        End If

    Next
    Return dblValues
End Function

#End Region

#Region "SQL Statements"
Private Sub InsertTestData(RecordID As Integer, BondTest As Integer, TestType As String, TestPos As Integer, dataArray() As Double)
    Dim InsertQuery As String = ""
    Dim conn As New BondSQLConnection("PaperTests")
   ' Dim TestPos = StartingTestPos + (CInt(dataArray(0)) - 1)

    conn("@RecordID") = RecordID
    conn("@Test") = BondTest
    conn("@TestType") = TestType
    conn("@TestPos") = TestPos
    conn("@TestData") = dataArray(1)
    conn("@TestDateTime") = now.tostring

    InsertQuery = "INSERT INTO PaperTests.dbo.PaperTestValues(ReelRecordID, Test, TestLocation, TestPosition, TestValue, TestTimeStamp) VALUES (@RecordID, @Test, @TestType, @TestPos, @TestData, @TestDateTime)"
    Try
        conn.ExecuteNonQuery(InsertQuery)
        IncrementTestPosition
    Catch ex As Exception
        ErrorLog2(ex.ToString())
    End Try

End Sub

Private Sub InsertTestDataDEBUG(RecordID As Integer, BondTest As Integer, TestType As String, TestPos As Integer, dataArray() As Double, rawString As String)
    Dim InsertQuery As String = ""
    Dim conn As New BondSQLConnection("PaperTests")

    conn("@RecordID") = RecordID
    conn("@Test") = BondTest
    conn("@TestType") = TestType
    conn("@TestPos") = TestPos
    conn("@TestData") = dataArray(1)
    conn("@RawString") = rawString
    conn("@TestDateTime") = now.tostring

    InsertQuery = "INSERT INTO PaperTests.dbo.InterfaceTesting(ReelRecordID, Test, TestLocation, TestPosition, TestValue, TestTimeStamp, RawValue) VALUES (@RecordID, @Test, @TestType, @TestPos, @TestData, @TestDateTime, @RawString)"
    Try
        conn.ExecuteNonQuery(InsertQuery)
    Catch ex As Exception
        ErrorLog2(ex.ToString())
    End Try

End Sub

Private Sub IncrementTestPosition()
    Dim tempPosition As Integer = GetBondPosition()
    Dim FrontOriginalMax = 5
    Dim CenterOriginalMax = 15
    Dim BackOriginalMax = 25
    Dim FrontRetestOrWinderMax = 10
    Dim CenterRetestOrWinderMax = 20
    Dim BackRetestOrWinderMax = 30

    If tempPosition = FrontOriginalMax Then
        tempPosition = 11
    else if tempPosition = CenterOriginalMax Then
        tempPosition = 21
    else if tempPosition = BackOriginalMax Then
        tempPosition = 1
    Else If tempPosition = FrontRetestOrWinderMax then
        tempPosition = 1
    Else If tempPosition = CenterRetestOrWinderMax then
        tempPosition = 1
    Else If tempPosition = BackRetestOrWinderMax then
        tempPosition = 1
    else 
        tempPosition = tempPosition + 1
    End If
    SetBondPosition(tempPosition.tostring)
End Sub

#End Region

#Region "Get PiValues"
Private Function GetRecordID() As Int64
    Dim RecordID As Int32 = 0
    Try
        Dim piserver As New PIServer("valpi", "piadmin", "fatb0y",True)
        RecordID = piserver.GetCurrentValue("PAPERLAB:PaperLabReelSelected")
    Catch ex As Exception
        ErrorLog2(ex.ToString())
    End Try
    Return RecordID
End Function

Private Function GetBondPosition() As Int64
    Dim BondPos As Int32 = 0
    Try
        Dim piserver As New PIServer("valpi", "piadmin", "fatb0y",True)
        BondPos = CInt(piserver.GetCurrentValue("PAPERLAB:SBOND.POS"))
    Catch ex As Exception
        ErrorLog2(ex.ToString())
    End Try
    Return BondPos
End Function
Private Sub SetBondPosition(pos As String) 
    Try
        Dim piserver As New PIServer("valpi", "piadmin", "fatb0y",True)
       piserver.WriteValue("PAPERLAB:SBOND.POS", pos)
    Catch ex As Exception
        ErrorLog2(ex.ToString())
    End Try
End Sub

Private Function BondTestType() As String
    Dim TestType As String = ""
    Try
        Dim piserver As New PIServer("valpi", "piadmin", "fatb0y",True)
        TestType = piserver.GetCurrentValue("M1:BOND.TYPE")
    Catch ex As Exception
        ErrorLog2(ex.ToString())
    End Try
    Return TestType
End Function

Private Function BondReelLoc() As String
    Dim ReelLoc As String = ""
    Try
        Dim piserver As New PIServer("valpi", "piadmin", "fatb0y",True)
        ReelLoc = piserver.GetCurrentValue("M1:BOND.ReelLoc")
    Catch ex As Exception
        ErrorLog2(ex.ToString())
    End Try
    Return ReelLoc
End Function

Private Function GetBondInterfaceStatus() As Integer
    Dim Status As Integer = 0
    Try
        Dim piserver As New PIServer("valpi", "piadmin", "fatb0y",True)
        Status = CInt(piserver.GetCurrentValue("PAPERLAB:BOND_INTERFACE.S"))
    Catch ex As Exception
        ErrorLog2(ex.ToString())
    End Try
    Return Status
End Function

#End Region

#Region "Debug"

    Private Sub ErrorLog(RecordID As Int32, BondTest As Integer, ReelLoc As String, TestType As String, StartingTestPos As Integer, dataArray() As Double)
    Dim SavePath As String = "C:\Program Files (x86)\PIPC\Interfaces\Lab"
    Dim NameOfFile As String = "BondTest Debug File"
    Dim TestPos = StartingTestPos + (CInt(dataArray(0)) - 1)

    If System.IO.File.Exists(SavePath & "\" & NameOfFile & ".txt") Then
        Using sw As StreamWriter =  New StreamWriter(SavePath & "\" & NameOfFile & ".txt", True)

          '  For i = 0 To dataArray.Count -1 
                sw.WriteLine(" ")
                sw.WriteLine(RecordID & "   " & BondTest & "    " & ReelLoc & "    " & TestType  & "   " & TestPos & "   " & dataArray(1).ToString)
              '  TestPos = TestPos + 1
          '  Next
        End Using
    else
        File.Create(SavePath & "\" & NameOfFile & ".txt").Dispose()
        Using sw As StreamWriter = File.CreateText(SavePath & "\" & NameOfFile & ".txt")
            'For i = 0 To dataArray.Count -1 
                sw.WriteLine(" ")
                sw.WriteLine(RecordID & "   " & BondTest & "    " & ReelLoc & "    " & TestType  & "   " & TestPos & "   " & dataArray(1).ToString)
              '  TestPos = TestPos + 1
            'Next
        End Using
    End If
End Sub

Private Sub ErrorLog2(dataArray as string)
    Dim SavePath As String = "C:\Program Files (x86)\PIPC\Interfaces\Lab"
    Dim NameOfFile As String = "BondTest Debug File"
   ' Dim TestPos = StartingTestPos

    If System.IO.File.Exists(SavePath & "\" & NameOfFile & ".txt") Then
        Using sw As StreamWriter = New StreamWriter(SavePath & "\" & NameOfFile & ".txt", True)
                sw.WriteLine(" ")
                sw.WriteLine(dataArray)
        End Using
    else
        File.Create(SavePath & "\" & NameOfFile & ".txt").Dispose()
        Using sw As StreamWriter = File.CreateText(SavePath & "\" & NameOfFile & ".txt")
                sw.WriteLine(" ")
                sw.WriteLine(dataArray)
        End Using
    End If
End Sub

#End Region

这是错误的截图:

提前致谢

通常情况下,在 .NET 中打开串行端口后,它会在任意时间内保持打开状态。我写了几个 .NET 应用程序,串行端口使用了数月或数年而无需重新启动应用程序或计算机,并且它们运行良好。

根据您发布的异常信息,该串口似乎已被处理掉。有几个可能的原因。

  1. 使用错误的驱动程序或硬件,断开您的串行端口。我一直在使用许多 USB 到 RS232 转换器,其中一些有错误的驱动程序,因此有时端口会随机断开连接并抛出 ObjectDisposedException。在早期的 Windows 版本 (XP) 中 OS 甚至 'blue-screened'。 Here 是关于抛出 ObjectDisposedException 这种情况的更多信息。

This is a known problem with SerialPort. Device removal causes an uncatchable exception in a background thread it uses (WaitForCommEvent). The only solutions are to not use SerialPort or create a .config file that puts unhandled exception trapping mode back to .NET 1.1 behavior.

  1. 您的RS232转换器的USB线被手动断开。如果这样做,大多数驱动程序通常会断开与串行端口的所有句柄,并且 .NET 会抛出 ObjectDisposedException.

  2. 如果使用 USB 到 RS232 转换器,还要检查 USB 端口上的电源管理设置。尝试在连接转换器的 USB 设备上取消选中此选项。

  3. 您的代码中存在 SW 错误。

总是建议(特别是如果使用转换器)尝试更多类型的转换器,以确保硬件没有问题 device/driver。

更新:正如 Timmy 所说,垃圾回收正在处理连接。所以我在 class

中将该对象声明为共享变量
Shared Dim WithEvents serialPort as IO.Ports.SerialPort

在 OnStart 方法中,我将其启动为一个新的串行端口并继续运行。没有抛出任何错误,因为垃圾收集不会处理它。希望这对遇到类似问题的人有所帮助。