程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> Visual Basic語言 >> VB.NET >> 計算機監控系統仿真開發平台的軟件實現(上)

計算機監控系統仿真開發平台的軟件實現(上)

編輯:VB.NET

計算機監控系統是以監測控制計算機為主體,加上檢測裝置、執行機構與被監測控制的對象共同構成 的整體。在這個系統中,計算機直接參與被監控對象的檢測、監督和控制[1]。檢測主要是 通過傳感器和相應的輸入模塊來取得被監控對象的狀態數據,監督主要是對狀態數據進行分析後給操作 員提供手動操作的參考,控制則是手動或按照一定的策略自動地對被監控對象執行相應的操作。由此可 見,檢測與控制模塊是計算機監控系統直接跟被監控對象關聯的不可或缺的輸入輸出(I/O)模塊,學習 和研究這些模塊對計算機監控系統的輔助開發、測試與教學等都具有重要意義。

I/O模塊一般配 置有串行通信接口,本文用軟件仿真一個2路模擬量輸入和2路數字量(又稱開關量)輸出的基於 RS-232 接口的模塊(Analog Input / Digital Output Module, 簡稱 AI/DO-M),並開發了針對該仿真模塊的 主控程序;開發了 RS-232/RS-232 轉換器,可以將兩個不同通信協議和不同波特率的主控機或受控機連 接起來,並可以截取兩者之間的通信協議; 開發了RS-232/RJ-45協議轉換器軟件,可以將串行通信協議 轉換為 TCP 協議,而且,該軟件既可以工作在客戶機方式,又可以工作在服務器方式,借助該協議轉換 器,可以對傳統的基於 RS-232 接口的監控系統轉換為基於 Internet 的監控系統,從而進行遠程測試 ;通用串口設備測試工具可以自動生成多種校驗碼及添加多種結尾碼,既可以充當主控機對受控機進行 測試,也可以充當受控機對主控機進行測試,並可以記錄測試結果和通信協議。在這些仿真模塊與協議 轉換器的基礎之上,可以在高校的計算機房零成本(僅需要串口連接線和網線)搭建多種形式的計算機 監控系統的仿真開發平台,並可對其進行綜合測試,從而節約大量教學設備經費的投入及相關項目開發 費用的支出。

1 串行通信的主要技術

通信是計算機監控系統實現的關鍵。串行通信的基 本技術主要包括數據處理技術、數據的校驗技術以及串口操作技術,串口數據接收技術則是關鍵技術。 有了這些技術,就可以自動生成指定格式的數據包、校驗數據包,也可以非常便捷地通過串行接口發送 和接收數據。

1.1 數據處理技術

Visual Basic 2008 提供了豐富的字符串處理函數,利 用16進制字符串表示數據,可以較好地觀察數據,例如 0xFF 是一個不可見字符,如果以 "FF" 來顯示則比較清楚。BytesToHexChars 函數實現字節序列到16進制字符串的轉換,其中 ,通過 ByteToTwoHexChars 函數(其定義比較簡單,略),將一個字節轉換為標准的兩個16進制字符, 然後,對這些字符進行累加。

Public Function BytesToHexChars(ByRef byteArray As Byte()) As String

Dim I As Integer

Dim strData As String = ""

For I = 0 To byteArray.Length - 1

  strData &= ByteToTwoHexChars(byteArray(I))

Next I

Return strData

End Function

在發送字節序列時,又需要將16進制字符串轉換為字節序列,這可以通過 HexCharsToBytes 來實現 ,其基本原理是調用 TwoHexCharsToByte 函數(定義略)將兩個16進制字符轉換為一個字節。

Public Function HexCharsToBytes(ByVal strVal As String) As Byte()

Dim I As Integer

Dim nLength As Integer

Dim bTmp() As Byte

If strVal = "" Then Return Nothing

strVal = Trim(strVal)           '刪除尾部空格

nLength = Len(strVal) 2 - 1   '求得字節長度

ReDim bTmp(nLength)             '可變數組保存字節序列

For I = 0 To nLength

 '每兩個16進制字符轉換為一個字節,存入可變數組

 bTmp(I) = TwoHexCharsToByte((Mid(strVal, I * 2 + 1, 2)))

Next I

Return bTmp

End Function

在計算機監控系統中,經常用一個字節的8位來表示8個開關的狀態,0表示開關打開,1表示開關閉合 (反之也可)。這時,就需要對字節中的某位進行測試、置位(置1)和復位(置0)。字節的位測試通 過 And 運算實現,如果結果不為0,則返回 True,否則返回 False。

Public Function CheckByteBit(ByVal bData As Byte, ByVal nBit As Integer)  _

As Boolean

Dim bTmp As Byte

Dim bResult As Byte

If nBit > 7 Or nBit < 0 Then Return False

bTmp = 2 ^ nBit

bResult = bData And bTmp

If bResult <> 0 Then

 Return True

Else

 Return False

End If

End Function

字節的某位置位通過 Or 運算實現,其源代碼如下。

Public Function SetByteBit (ByVal bData As Byte, ByVal nBit As Integer) As Byte

Dim bTmp As Byte

If nBit > 7 Or nBit < 0 Then

 Return bData

End If

bTmp = 2 ^ nBit

Return bData Or bTmp

End Function

字節中某位的復位通過 Xor 和 And 運算共同完成。首先,通過 Xor 運算使得臨時變量 bTmp 的該 位為0,其它位為1,再通過 And 運算進行復位。

Public Function ResetByteBit(ByVal bData As Byte, ByVal nBit As Integer) As Byte

Dim bTmp As Byte

If nBit > 7 Or nBit < 0 Then

 Return bData

End If

bTmp = (2 ^ nBit) Xor &HFF

Return bData And bTmp

End Function

1.2 數據的校驗技術

計算機監控系統的通信協議一般包括前導字符、地址、讀寫功能標志碼 、校驗碼與結尾碼。相同類型的模塊一般采用相同的前導字符,而地址主要用來區分模塊,因為一台計 算機可能要連接多個模塊,讀功能碼主要用來讀取模塊的狀態或數據,寫功能碼主要用來設置模塊的工 作方式或控制輸出開關,校驗碼(可選)主要用來對當前數據塊進行校驗,一般有異或(Xor)、累加和 (Add)和循環冗余(CRC)校驗碼等。相同類型的設備一般也有相同的結尾碼(可選),一般取CR (0x0d)或CRLF(0x0d0a)作為結尾碼。為了節省篇幅,這裡僅以 Xor 校驗碼為例進行說明。

首先,定義枚舉類型 CheckMode 表示數據包的校驗方式,0表示沒有校驗,1表示 Xor,2表示 Add,3表 示 CRC,4表示BCS(TCP 協議中的累加求補校驗),其中,Xor 與 Add 生成一個字節的校驗碼,CRC 與 BCS 生成兩個字節的校驗碼。

Public Enum CheckMode

Chk_None = 0

Chk_Xor = 1

Chk_Add = 2

Chk_CRC = 3

Chk_BCS = 4

End Enum

然後,定義枚舉類型 EndMark 表示數據包的結尾標志,0表示沒有結尾碼,1表示以回車符作為結尾 碼,2表示以回車換行作為結尾碼(這種結尾碼可用於 POP3、SMTP等網絡協議)。

Public Enum EndMark

Add_None = 0

Add_CR = 1

Add_CRLF = 2

End Enum

Xor 校驗碼通過函數 xorStrValue 進行,輸入16進制字符串(如"2F1F"),輸出兩個16 進制字符(如"30")。首先,將輸入的16進制字符串 strHexData 轉換成字節數組 byteBuffer,然後,以初始值0逐個字節異或,最後的字節通過 ByteToTwoHexChars 轉換為兩個16進制 字符返回。

Public Function xorStrValue(ByVal strHexData As String) As String

Dim I As Integer

Dim xorTmp As Byte

Dim byteBuffer As Byte()

byteBuffer = HexCharsToBytes(strHexData)

xorTmp = 0

For I = 0 To byteBuffer.Length - 1

 xorTmp = xorTmp Xor byteBuffer(I)

Next I

Return ByteToTwoHexChars(xorTmp)

End Function

如果一個字節序列的最後一個字節是前面所有字節的異或校驗碼,那麼,整個字節序列的異或校驗碼 將為0。例如,字節 30 是 字節序列 2F 1F 的異或校驗碼,則字節序列 2F 1F 30 的異或校驗碼應該為 0。CheckXorStrValue 函數正是根據這個原理,如果計算所得的校驗碼為 "00",則表示數據 包校驗正確,返回 True,否則,返回 False。

Public Function CheckXorStrValue (ByVal strHexData As String) As Boolean

If xorStrValue(strHexData) = "00" Then

 Return True

Else

 Return False

End If

End Function

添加結尾碼函數 AddEndMark 函數比較簡單,只要根據參數添加 "0D" 或 "0D0A" 或什麼都不添加,直接返回原來的16進制字符串即可。對應的 CheckEnd 函數,也只 是根據參數檢查結尾碼,不涉及到計算,只需要通過字符串處理函數完成。

CheckParity 函數檢 查數據包的各種校驗是否正確,正確則返回 True,錯誤,則返回 False。

Public Function CheckParity(ByVal strHexData As String, ByVal nCheckMode _

As CheckMode) As Boolean

Select Case nCheckMode

 Case CheckMode.Chk_Xor

   Return CheckXorStrValue(strHexData)

 Case CheckMode.Chk_Add

   Return CheckAddStrValue(strHexData)

 Case CheckMode.Chk_CRC

   Return CheckCrcStrValue(strHexData)

 Case CheckMode.Chk_BCS

   Return CheckAddStrBCSValue(strHexData)

End Select

Return True  'CheckMode.Chk_None

End Function

GetFullPackage 函數生成綜合數據包,自動添加校驗碼和結尾碼。GetEvenUCaseString 函數將輸入 的16進制字符串轉換成大寫,並刪除最後的奇數字符(定義略)。

Public Function GetFullPackage(ByVal strHexData As String, ByVal nParity _

As CheckMode, ByVal nEndMark As EndMark) As String

Dim strTmp As String

Dim strPackage As String = ""

strTmp = GetEvenUCaseString(strHexData)

Select Case nParity

  Case CheckMode.Chk_None

    strPackage = strTmp

  Case CheckMode.Chk_Xor

    strPackage = strTmp & xorStrValue(strTmp)

  Case CheckMode.Chk_Add

    strPackage = strTmp & addStrValue(strTmp)

  Case CheckMode.Chk_CRC

    strPackage = strTmp & crcStrValue(strTmp)

  Case CheckMode.Chk_BCS

    strPackage = strTmp & addStrBCSValue(strTmp)

End Select

Return AddEndMark(strPackage, nEndMark)

End Function

CheckPackage函數對包含校驗碼和結尾碼的數據包進行校驗,正確則返回 True,錯誤則返回 False 。首先調用 CheckEnd 函數檢查結尾碼,如果錯誤,則直接返回 False,程序結束,否則,數據包刪除 結尾碼後調用 CheckParity 檢查校驗碼。

Public Function CheckPackage(ByVal strHexData As String, ByVal nParity _

As CheckMode, ByVal nEndMark As EndMark) As Boolean

Dim strTmp As String

strTmp = UCase(strHexData)

If CheckEnd(strTmp, nEndMark) = False Then Return False

Select Case nEndMark

  Case EndMark.Add_CR

    strTmp = Mid(strTmp, 1, Len(strTmp) - 2)

  Case EndMark.Add_CRLF

    strTmp = Mid(strTmp, 1, Len(strTmp) - 4)

End Select

Return CheckParity(strTmp, nParity)

End Function

1.3 串口操作技術

串口一般通過下拉框進行選擇,可以使用 My 功能添加串口名稱,確保每 個串口都是本機存在的端口。

Public Sub AddComboBoxPorts(ByRef comb As ComboBox)

comb.Items.Clear()

For Each strPort As String In My.Computer.Ports.SerialPortNames

  comb.Items.Add(strPort)

Next

End Sub

在打開串口時,一般需要對串口狀態進行判斷,而且,軟件打開串口後,硬件一般有一定的延遲。根 據實際的工程項目經驗,一般軟件打開串口50毫秒後,可以確保硬件工作。打開串口通過函數 OpenPort 進行,其中,comPort 參數通過傳址傳遞,延遲 nDelay 是一個可選參數,缺省為 50 毫秒。

Public Function OpenPort(ByRef comPort As SerialPort, Optional ByVal nDelay _

As Integer = 50) As Integer

'打開串口,並根據需要延遲 nDelay 毫秒

If comPort.IsOpen = False Then

 Try

   comPort.Open()

   Return 0

 Catch ex As Exception

   Return -1

 End Try

 Threading.Thread.Sleep(nDelay)

End If

End Function

關閉串口比較簡單,通過ClosePort函數實現。

Public Sub ClosePort(ByRef comPort As SerialPort)

'關閉串口

If comPort.IsOpen = True Then comPort.Close()

End Sub

通過串口發送數據由 SendData 函數來完成。首先調用 GetFullPackage 函數生成16進制字符串形式 的綜合數據包,然後,通過 HexCharsToBytes 函數轉換為對應的16進制字節序列,最後,調用 SerialPort 類的 Write 方法發送這些字節序列,並返回16進制字符串。

Public Function SendData(ByRef comPort As SerialPort, _

ByVal strHexData As String, _

ByVal nParity As CheckMode, _

ByVal nEnd As EndMark) As String

Dim strTmp As String

With comPort

  If .IsOpen = False Then Return Nothing

  strTmp = GetFullPackage(strHexData, nParity, nEnd)

  Dim outBytes As Byte() = HexCharsToBytes(strTmp)

  .Write(outBytes, 0, outBytes.Length)

End With

Return strTmp

End Function

當串口有數據時,就調用 ReadData 函數讀取,該函數以 SerialPort 對象為參數。調用 SerialPort 對象的 Read 方法,根據字節數 BytesToRead 屬性,讀取相應的字節,然後,轉換為16進 制字符串返回。

Public Function ReadData(ByRef comPort As SerialPort) As String

Dim nLength As Integer

Dim nInBuffer As Byte()

With comPort

  nLength = .BytesToRead

  If nLength < 1 Then Return ""

  ReDim nInBuffer(nLength - 1)

  .Read(nInBuffer, 0, nLength)

End With

Return BytesToHexChars(nInBuffer)

End Function

1.4 數據接收技術

ReadData 函數可以方便地讀取串口數據,但是,什麼時候讀取,獲得數據 以後又如何進一步處理?這裡依然有好多工作要做,這也是串行通信的重點和難點。

一般在 DataReceived 事件處理程序中讀取數據,但是,卻不能將這些數據放到主界面的文本框中,因為 “線程間操作無效”。這可以通過代理來進行,即在事件處理程序中調用代理子程序,在代 理子程序中可以操作主界面中的控件。

串行通信發送和接收的數據並不是非常流暢的,中間一般 有時間間隔,因而,需要設置一個間隔時間,比如50毫秒,凡是間隔時間在50毫秒之內的數據,都當作 一批數據,不斷進行匯總,當數據到達的間隔時間超過50毫秒時,再提交這一批數據。

在主窗體 的類中作如下聲明,strReceiveHex 保存接收到的16進制字符串。bStart 為 True 表示串口正在接收數 據,為 False 表示串口處於空閒狀態。

Dim strReceiveHex As String = ""

Dim bStart As Boolean = False

再在窗體類中定義如下代理及其實體函數,每次 DataReceived 事件都調用一次 GeneralCom 函數, Timer_Port 是一個定時器,用來規定串行通信的數據間隔時間。當有數據到達時,即復位定時器,接收 完數據後,重新開始計時。在該函數中,每次接收到的數據都要匯總到 strReceiveHex 變量中。

Public Delegate Sub DelegateCom()

Dim dCom As DelegateCom = New DelegateCom(AddressOf GeneralCom)

Private Sub GeneralCom()

Dim strTmpHex As String = ""   ‘保存一次接收的數據

Timer_Port.Enabled = False  ‘定時器復位,開始處理數據

If bStart = False Then

 strReceiveHex = ""     ‘清空,准備保存數據

 bStart = True         ‘現在串口處於接收數據狀態

End If

strTmpHex = ReadData(comPort)  ‘讀取數據

strReceiveHex &= strTmpHex     ‘匯總數據

Timer_Port.Enabled = True   ‘本次數據接收完畢,開始計時

End Sub

在 SerialPort 對象的DataReceived 事件處理程序中,只要書寫一條語句即可。Me.Invoke(dCom)類 似地,處理 SerialPort 對象的PinChanged 和ErrorReceived 事件要簡單得多,這裡不再贅述。如果在 規定的時間間隔內沒有數據到達,則認為一批數據已經接收完畢。這時,可以在 Timer_Port 的 Tick 事件處理程序中提交數據,或者進行進一步的處理。首先,禁止定時器工作,然後,將 bStart 變量設 置為 False,表示數據接收完畢,現在串口處於空閒狀態,最後,將匯總後的16進制字符串 strReceiveHex 放入文本框中顯示(或做其它處理)。

Timer_Port.Enabled = False

bStart = False

txtReceive.Text = strReceiveHex

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved