程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Robotics:編寫和測試支持串行通信的VPL服務

Robotics:編寫和測試支持串行通信的VPL服務

編輯:關於.NET

正如您所想的,Microsoft Robotics Developer Studio (RDS) 是一個用於進行機器人編程的平台。 RDS 最早在 2006 年發布,最新的版本 RDS 2008 R2 發布於 2009 年 6 月。

RDS 主要由四個組件組成:並發和協調運行時 (CCR)、分散軟件服務 (DSS)、可視化編程語言 (VPL) 和可視化模擬環境 (VSE)。

不過,本文主要介紹 VPL。VPL 是一種數據流語言,這意味著您是通過在屏幕上繪制圖表來創建程序 的。在運行時,消息從圖表中的一個塊流向另一個塊,這種數據流就是程序的執行路徑。由於 VPL 基於 CCR 和 DSS 構建,因此數據流可以異步發生(作為服務通知的結果),也可以並行運行。盡管 VPL 適用 於程序員新手,但是有經驗的程序員會發現它對於原型設計也很有用。

本文概要介紹一個簡單的 RDS 服務,該服務使用串行端口(也稱為 COM 端口)來發送和接收數據。 示例代碼演示了編寫可重用的 RDS 服務時涉及的一些關鍵概念。

RDS 服務

RDS 服務使用 CCR 和 DSS 構建。它們在概念上與 Web 服務相似,因為 RDS 擁有一種面向服務的體 系結構 (SOA),該體系結構基於具象狀態傳輸 (REST) 模型,使用分散軟件服務協議 (DSSP) 在服務之間 進行通信。仔細推敲這句話,您會發現它意味著您並非直接與 RDS 服務進行通信。實際上,您是向一個 代理發送消息,而該代理擔當了服務的外部接口(Web 開發人員熟悉的一種方法)。這也意味著服務可以 分布在網絡上的任意位置。

使用代理會產生兩個效果。首先,在服務之間發送的消息會在傳輸之前序列化,並在另一端反序列化 。XML 是用來傳輸數據的一種有用格式。其次,代理定義了一個約定 - 實際上是向其他服務公開的一組 API。

每個服務都有一個內部狀態,您可以通過對該服務進行操作來檢索或修改狀態。這就涉及了發送請求 消息以及等待響應消息,其過程類似於大多數 Web 服務所使用的過程。

當服務的狀態改變時,它可以向訂閱者發送通知。這種“發布和訂閱”方法使 RDS 服務有別於傳統的 Web 服務,因為通知消息是異步發送給訂閱者的。

當您構建一個新服務時,該服務會在 VPL 中自動變為可見,因此您可以立即開始使用它。這是 RDS 的一項關鍵特性,它使測試和原型設計變得非常簡單 - 您不需要用 C# 來編寫測試工具,因為您可以使 用 VPL。

遠程控制機器人

許多簡單的教學機器人擁有 8 或 16 位微控制器作為其板載大腦。但由於 RDS 運行在 Windows 上的 .NET Framework 下,它不會生成能直接在這些機器人上運行的代碼。因此,必須通過通信鏈接對它們進 行遠程控制。(替代方法是使用板載 PC,例如 MobileRobots Pioneer 3DX。)

由於大部分微控制器都支持串行端口,串行連接是一個顯而易見的解決方案。但是,使用電纜來提供 鏈接並不是理想選擇,因為它會限制機器人的移動范圍。

另一種方法是通過在機器人上安裝串行到藍牙轉換設備,使用藍牙來創建無線連接。有些機器人(例 如 LEGO NXT)本身就內置了藍牙;而另一些(例如 RoboticsConnection Stinger)擁有可選的藍牙模塊 。藍牙是一種不錯的選擇,因為藍牙現已成為大多數便攜式計算機的標准配備。即使您的 PC 沒有藍牙功 能,您也可以找到價格便宜的現成 USB 藍牙設備。

一個好消息是您不需要了解藍牙編程,因為與機器人的連接會顯示為虛擬串行端口。您使用的代碼與 通過物理電纜提供串行連接時基本相同。唯一的差別是您必須在 PC 與機器人上的藍牙設備之間建立配對 ,這樣才能創建虛擬 COM 端口。

有些機器人控制器板卡擁有固件,能夠使用簡單的命令語言來接受命令。例如,使用來自 RoboticsConnection 的 Serializer(因使用了串行協議而得名),您可以通過 HyperTerminal 等終端 程序向控制器輸入命令。命令都是您可以直接看懂的,並且您通過按 Enter 來結束每個命令。

如果您在為機器人設計您自己的協議,則需要做出一些選擇。首先,您必須決定是否要發送二進制數 據。將二進制值轉換為十六進制或十進制用於傳輸,需要更多帶寬,並且會增加板載 CPU 的處理開銷。 而另一方面,它又使得閱讀消息非常容易,您不會由於誤解了控制字符而導致任何奇怪的行為。

下一個問題是,您是希望使用固定長度的數據包,還是希望使用更加靈活的可變長度格式。固定長度 更容易解析,最適合十六進制。

當然,您還應該考慮您是否要使用校驗和。對於計算機之間的通信,計算校驗位非常容易。但是,如 果您希望通過手動輸入命令來測試機器人,計算校驗位就會變得非常棘手。使用校驗和時,通常接收方會 根據命令是否成功到達,回發確認 (ACK) 或否定確認 (NAK)。您對是否使用校驗和的決定歸根結底取決 於連接的可靠性。

SerialPort 服務

到目前為止,串行端口服務非常有用,應該是顯而易見的。但是,盡管許多機器人樣品都使用了串行 通信鏈接,RDS 軟件包還是沒有包含這樣的服務。如果您浏覽 RDS 示例代碼,會發現每個示例處理串行 端口的方式都不一樣。本文簡要介紹一種串行端口使用方法。但它不是唯一的方法,也不一定是最好的方 法。

在開始之前,請確保您下載並安裝了 RDS。本文的下載包含 SerialPort 服務的源代碼。將其解壓到 RDS 安裝目錄(也稱為裝入點)下的某個文件夾中。請注意,您應該將您的代碼與 RDS 附帶的示例文件 夾分開,避免弄混您的代碼和 Microsoft 的代碼。此外,我建議您將自己的代碼放在 RDS 裝入點之下, 而不是放在硬盤驅動器上的其他位置,因為這樣可以簡化開發。我擁有一個 Projects 文件夾,在其子文 件夾中保存了我自己的所有代碼,因此它很容易備份。

SerialPort 源代碼中主要的文件是 SerialPort.cs、SerialPortTypes.cs 和 SerialPort.manifest.xml。

SerialPort.cs 包含該服務的實現或行為。它包含服務類本身(該類包含操作處理程序)以及所有必 要的支持方法和變量。

SerialPortTypes.cs 包含服務狀態、操作類型和消息類型的定義。事實上,它描述了服務的接口,並 且不應該包含任何可執行代碼。這是服務約定的要點 - 它僅僅是數據定義。

SerialPort.manifest.xml 稱為“清單”,描述了如何組合或合成服務來運行應用程序。此文件用作 DssHost 工具的輸入,該工具會創建用來運行服務的 DSS 節點。在本例中,清單僅運行 SerialPort 服 務,因此沒什麼用處。有用的清單還應該指定依靠 SerialPort 服務進行連接的其他服務。

在使用 SerialPort 服務之前,您必須先運行 DssProjectMigration 工具,然後再重新編譯該服務。 打開 DSS 命令提示符(在“開始”菜單的“RDS”下),確保設置了您的路徑,使您能夠執行 \bin 文件 夾中的文件。然後轉到解壓代碼的目錄,並輸入以下命令:

Dssprojectmigration /b- .

/b- 選項表示不創建備份副本,而句點 (.)表示當前目錄。

您可以在 Visual Studio 中編譯此服務,也可以通過在命令行中輸入以下命令來編譯此服務:

Msbuild serialport.sln

編譯此服務會生成此服務 DLL、一個代理 DLL 和一個轉換 DLL(用於在服務 DLL 與代理之間轉換數 據類型)。這些文件將放在 RDS 裝入點下的 \bin 文件夾中。您也應該將清單和配置文件復制到 \samples\config 文件夾中,將它們放在一起(但這不是必需的)。

服務約定

每一個服務都有一個唯一的約定標識符,該標識符在 Types.cs 文件中聲明,其形式類似於 URL。但 是請注意,它在 Web 上沒有任何意義。使用 URL 式的標識符,僅僅是通過允許組織創建自己的命名空間 來確保唯一性的一種簡便方法。(對於您的項目,您將需要使用除 microsoft.com/robotics 以外的命名 空間。)SerialPortTypes.cs 包含以下定義:

public sealed class Contract {
  [DataMember]
  public const string Identifier =  "http://www.promrds.com/contracts/2009/12/serialport.html";
}

服務約定還包含狀態和操作,用於定義其他服務可以操作的屬性以及操作方法。SerialPortState(請 參見圖 1)包含串行端口配置、一些超時參數以及異步操作時收到的最後一個字節。

圖 1 SerialPortState

[DataContract]
public class SerialPortState {
  private bool _openOnStart;
  private bool _asynchronous;
  private SerialPortConfig _config;
  private byte _lastByteReceived;
  private bool _isOpen;

  // Open the COM port when the service starts
  // Must be set in config file
  [DataMember]
  public bool OpenOnStart {
   get { return _openOnStart; }
   set { _openOnStart = value; }
  }

  // Operate in Asynchronous mode
  [DataMember]
  public bool Asynchronous {
   get { return _asynchronous; }
   set { _asynchronous = value; }
  }

  // Configuration parameters for the serial port
  [DataMember]
  public SerialPortConfig Config {
   get { return _config; }
   set { _config = value; }
  }

  // Last byte received from the serial port
  [DataMember]
  public byte LastByteReceived {
   get { return _lastByteReceived; }
    set { _lastByteReceived = value; }
  }

  // Indicates if the port is currently open
  [DataMember]
  public bool IsOpen {
   get { return _isOpen; }
   set { _isOpen = value; }
  }
}

您可以定義自己的數據類型,以便用在狀態和消息類型中。在此服務中,有一個 SerialPortConfig 類。要使其在代理中可見,此類必需是公共的,並且標有 [DataContract] 特性。此類中的每個屬性都必 須聲明為公共的,還必須用 [DataMember] 特性進行標記。如果不這麼做,就不能訪問此屬性。

您也可以公開公共枚舉 - 這樣,其他程序員就不必在使用您的服務的代碼中直接使用令人迷惑的數 字了。這也是一種良好的編程慣例,因為它可以實現數據類型檢查。

當您設計服務時,還必須決定其他服務如何與此服務進行交互。此 SerialPort 服務支持以下操作( 按邏輯分組):

Get、Subscribe

ReceiveByte

SetConfig、Open、Close、ClearBuffers

ReadByte、ReadByteArray、ReadString

WriteByte、WriteByteArray、WriteString

請注意,ReceiveByte 操作僅供服務本身內部使用,而不應由其他服務使用。稍後將對此進行詳細介 紹。

Read 和 Write 操作都是同步的 - 在操作完成之前,服務不會做出響應。如果您在異步模式下打開 串行端口(如下所述),服務將會在收到每個字節後向您發送 ReceiveByte 通知,並且您不應使用 Read 操作。而 Write 操作始終是同步的。

每個操作都具有一個請求消息類型和一個響應消息類型。其中有些類型可能擁有屬性,而另一些類型 是空的 - 數據類型本身就足以傳遞適當的消息。

服務行為

如上所述,此服務的可執行代碼包含在 SerialPort.cs 中。所有服務均從 DsspServiceBase 派生, 而 DsspServiceBase 提供了許多幫助器方法和屬性。在啟動服務時,將調用其 Start 方法:

protected override void Start() {
  InitializeState();
  base.Start();
  if (_state.OpenOnStart)
   OpenPort();
}

Start 方法負責初始化狀態(如有必要),然後如果在配置文件中指定了 OpenOnStart(如下所述) ,則自動打開串行端口。

服務所支持的每個操作都必須有一個服務處理程序。但是有些操作(例如 Drop 和 Get)由基礎結構 處理(除非您希望重寫默認行為)。

OpenHandler 演示了一個非常簡單的處理程序:

[ServiceHandler]
public void OpenHandler(Open open) {
  // Remember the Asynchronous flag
  _state.Asynchronous = open.Body.Asynchronous;
  if (OpenPort()) {
   open.ResponsePort.Post(
    DefaultSubmitResponseType.Instance);
  }
  else {
   throw (new Exception(“Open Failed”));
  }
}

此處理程序調用內部方法 OpenPort。如果成功,將回發一個響應。因為不需要返回任何信息,所以這 僅僅是 DSS 提供的默認響應。

如果打開失敗,則引發異常。DSS 會捕獲此異常,並將其轉換為故障,然後將其作為響應回發。盡管 不是很明顯,但是如果 OpenPort 中出現異常,它也會浮現出來並返回一個故障。

不需要全面解釋 OpenPort 方法,但是有一點非常重要 -您可以打開串行端口,以便進行同步或異步 操作。在同步模式下,必須完成所有讀和寫請求,才會返回響應。但是,如果您在異步模式下打開,則每 收到一個字符都會發送一個通知。為了實現此目的,代碼中設置了一個方便的事件處理程序:

if (_state.Asynchronous) {
  // Set up an Event Handler for received characters
  sp.ReceivedBytesThreshold = 1;
  sp.DataReceived += new SerialDataReceivedEventHandler(
   DataReceivedHandler);
}

事件處理程序如圖 2 所示。此處理程序運行於 .NET 線程上。但為了與 DSS 互操作,它必須切換到 CCR 線程上,因此代碼向服務自己的主要操作端口發出一個 ReceiveByte 請求。在發出請求後,代碼應 該會收到響應,否則就會出現內存洩露。這是 Arbiter.Choice 的目的,該方法使用 C# 簡化表示法來表 示用於處理兩種可能的響應的匿名委托。必須使用 Activate 將 Choice 接收方放到活動隊列中。在本例 中,只需要對故障進行處理,而成功不會導致任何操作。

圖 2 事件處理程序

void DataReceivedHandler(object sender,
  SerialDataReceivedEventArgs e) {

  int data;
  while (sp.BytesToRead > 0) {
   // Read a byte - this will return immediately because
   // we know that there is data available
   data = sp.ReadByte();
   // Post the byte to our own main port so that
   // notifications will be sent out
   ReceiveByte rb = new ReceiveByte();
   rb.Body.Data = (byte)data;
   _mainPort.Post(rb);
   Activate(Arbiter.Choice(rb.ResponsePort,
    success => { },
    fault => { LogError("ReceiveByte failed"); }
    ));
  }
}

接下來,將執行 ReceiveByte 處理程序來處理新字符:

[ServiceHandler]
public void ReceiveByteHandler(ReceiveByte recv) {
  _state.LastByteReceived = recv.Body.Data;
  // Send a notification, but only if in asynch mode
  if (_state.Asynchronous)
   SendNotification(_submgrPort, recv);
  recv.ResponsePort.Post(DefaultUpdateResponseType.Instance);
}

[ServiceHandler] 特性使 DSS 能夠在服務初始化過程中將處理程序掛鉤到操作端口。此處理程序向 訂閱者發送一個通知,然後回發一個響應,聲明操作已完成。(希望接收通知的服務必須向 SerialPort 服務發送一個 Subscribe 操作。)

其他讀寫操作的服務處理程序都非常容易理解。但是,WriteStringHandler(請參見圖 3)包含一個 小技巧 -它在發送字符的操作之間插入一小段延時。在全速發送數據時,有些速度較慢的微處理器可能 跟不上速度,因此這種設計很有幫助,尤其是對於像 BasicStamp 一樣執行位觸發且沒有硬件通用異步接 收器/轉換器 (UART) 而在軟件中執行串行 I/O 的設備。

圖 3 WriteStringHandler

[ServiceHandler]
public virtual IEnumerator<ITask> WriteStringHandler(
  WriteString write) {

  if (!_state.IsOpen) {
   throw (new Exception("Port not open"));
  }

  // Check the parameters - An empty string is valid, but not null 
  if (write.Body.DataString == null)
   throw (new Exception("Invalid Parameters"));

  // NOTE: This might hang forever if the comms link is broken
  // and you have not set a timeout. On the other hand, if there
  // is a timeout then an exception will be raised which is
  // handled automatically by DSS and returned as a Fault. 
  if (_state.Config.InterCharacterDelay > 0) {
   byte[] data = new byte[1];
   for (int i = 0; i < write.Body.DataString.Length; i++) {
    data[0] = (byte)write.Body.DataString[i];
    sp.Write(data, 0, 1);
    yield return Timeout(_state.Config.InterCharacterDelay);
   }
   sp.WriteLine("");
  }
  else
   sp.WriteLine(write.Body.DataString);

  // Send back an acknowledgement now that the data is sent
  write.ResponsePort.Post(DefaultSubmitResponseType.Instance);
  yield break;
}

此處理程序的另一個要點是它是一個迭代器。請注意,此方法被聲明為 IEnumerator<ITask>, 而且它使用了 yield return 和 yield break。C# 語言的這項特性使得 CCR 能夠掛起任務,而不會阻塞 線程。在執行 yield return 時,執行此方法的線程會返回到池中。一旦超時結束,它會回發一條消息, 使此方法恢復執行,但可能是在另一個線程上執行。

配置服務

配置文件是服務狀態的一個 XML 序列化版本,可在服務初始化過程中加載。支持配置文件是一個好主 意,因為這使您能夠更改服務的行為,而無需重新編譯。這對於設置 COM 端口號來說尤其重要。

在您聲明狀態實例時(在 SerialPort.cs 中),可以在服務中指定此文件的名稱:

[ServiceState]
// Add an initial state partner to read the config file
[InitialStatePartner(Optional = true,
  ServiceUri = "SerialPort.Config.xml")]
SerialPortState _state = new SerialPortState();

在本例中,我們將配置文件聲明為可選的;如果不這麼做,當配置文件丟失時,此服務就不能啟動。 另一方面,這意味著您必須檢查服務的 Start 方法的狀態,根據需要將其初始化為一些合理的默認值。

由於沒有為 ServiceUri 指定任何路徑,因此 DSS 假設此文件與用來啟動服務的清單位於同一文件夾 中。圖 4 顯示了典型配置文件的內容。

圖 4 SerialPortState 的配置文件

<?xml version="1.0" encoding="utf-8"?>
<SerialPortState
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns=http://www.promrds.com/contracts/2009/12/serialport.html 
  >
  <OpenOnStart>false</OpenOnStart>
  <Asynchronous>false</Asynchronous>
  <Config>
   <PortNumber>1</PortNumber>
   <BaudRate>57600</BaudRate>
   <Parity>None</Parity>
   <DataBits>8</DataBits>
   <StopBits>One</StopBits>
   <ReadTimeout>0</ReadTimeout>
   <WriteTimeout>0</WriteTimeout>
   <InterCharacterDelay>0</InterCharacterDelay>
  </Config>
  <LastByteReceived>0</LastByteReceived>
  <IsOpen>false</IsOpen>
</SerialPortState>

請注意,此配置文件不要求服務自動打開 COM 端口,因此您必須發送一個 Open 請求。

如果您希望自己的服務顯得更專業一些,您需要注意服務描述和圖標等細節。即使您自己不打算使用 VPL,最好也要在 VPL 中測試您的服務,使其對 VPL 友好,從而使其他人能夠方便地使用您的服務。

您可以使用兩個用來描述服務的特性對您的服務類進行修飾:

[DisplayName("Serial Port Service")]
[Description("SerialPort service provides access to a Serial (COM) Port")]

第一個特性在 VPL 服務列表中顯示為此服務的名稱(請參見圖 5),第二個特性在鼠標停留在列表中 的服務名稱上時顯示為工具提示。

圖 5 VPL 服務列表

如果您擁有一個網站,並且希望在線描述您的服務,則可以向服務類中附加另一個特性,用於提供超 鏈接:

[DssServiceDescription("http://www.promrds.com/SerialPort.htm")]

當 VPL 看到此特性時,它會在服務列表中的服務旁邊添加一個小信息圖標(藍色碟片上加一個白色“ i”)。

如果您為自己的服務添加圖標(很容易做到這一點),它們將顯示在 VPL 圖表和服務列表中。請注意 圖 5 中的服務名稱旁邊的小圖標(稱為“縮略圖”)。圖 6 顯示了較大版本的圖標,此圖標顯示在 VPL 圖表中所顯示的塊內。

圖 6 服務塊

當您將這些圖像添加到項目中時,請確保更改文件屬性,將“生成操作”設置為“嵌入的資源”。PNG 是首選的文件格式,因為它支持 Alpha 通道。通過將背景顏色的 Alpha 值設置為 0,可以創建具有透明 背景的圖標。

服務圖標圖像的大小應該是 32 x 32 像素,縮略圖大小應該是 16 x 16 像素。圖像的文件名必須以 服務的類名開頭,在本例中為 SerialPortService。因此在我的示例中,文件名為 SerialPortService.Image.png 和 SerialPortService.Thumbnail.png。

使用服務

此服務極其靈活。您可以在配置文件中指定串行端口配置(這是最常見的方法),也可以通過發送 SetConfig 請求來指定串行端口配置。當您設置了配置之後,就可以調用 Open 操作。為了方便起見,配 置文件中有一個標記會使服務在啟動時自動打開端口。如果端口已經打開,Open 調用將先關閉它,然後 重新打開它。

您需要決定您希望如何使用此服務:同步還是異步。在同步操作時,每個讀或寫請求都會等待,直到 操作完成之後,才會回發響應。對於 VPL,這是一種簡單方法,因為消息流會在圖表中的 SerialPort 塊 處暫停。請注意,異步操作不是完全異步的 -寫操作仍然是同步發生的。但收到的每個新字節都會作為 通知發送給訂閱者。

理論上,對服務執行的每個狀態修改操作都應該會導致發送一個通知,使訂閱者能夠自行保留緩存的 服務狀態版本。這意味著基於 DSSP Replace、Update、Insert、Delete 和 Upsert 操作的所有操作都應 該發送相應的通知。但是,開發人員經常會加快速度並對這項要求放低標准。

為了簡化起見,在異步模式下時,SerialPort 服務僅使用 ReceiveByte 操作類型來發送通知。Open 、Close 和 SetConfig 操作也會導致發送通知。

由於 Read 和 Write 操作不會修改狀態,因此它們是 DSSP Submit 操作的子類。當然,它們有一項 副作用,即通過串行鏈接來接收和發送數據。

使用 VPL 進行測試

本文的下載包含兩個示例 VPL 程序:EchoAsynch 和 EchoSynch。這兩個示例程序演示如何在異步模 式(通過通知)和同步模式下使用此服務。VPL 示例使用配置文件來設置 COM 端口的初始參數,包括端 口號(在配置文件中設置為 21,必須按照您的計算機的 COM 端口地址進行更改)。

請注意,要測試此服務,您需要一條空調制解調器電纜,以及兩台有串行端口的計算機或一台有兩個 串行端口的計算機。USB 到串行轉換設備很容易獲得,因此一台 PC 上有多個串行端口也是可行的。當您 將自己的 COM 端口相互連接起來後,運行一個終端模擬器(例如 HyperTerminal)並連接到其中一個 COM 端口。在另一個串行端口上運行 EchoAsynch VPL 程序,以便開始測試。(您可以在“開始”菜單的 “RDS”下找到 VPL。)當您在終端模擬器窗口中輸入信息時,應該會看到回顯字符。

您可以使用 VPL 來創建配置文件。單擊圖表中的 SerialPort 服務塊,並查看“屬性”面板。您會看 到類似圖 7 所示的內容。確保為您的 PC 正確設置了 PortNumber。(這必須是另一個串行端口,而不是 您在終端模擬器中打開的那個串行端口。)您會注意到,Parity 和 StopBits 是下拉列表。這些列表中 的項直接來自 SerialPortTypes.cs 中定義的枚舉。

圖 7 串行端口服務配置

在這裡,XML 文檔注釋就派上用場了。當鼠標光標懸停在某個配置參數上時,將彈出工具提示,顯示 每個狀態成員的相應注釋。

當您運行 EchoAsynch VPL 程序時,圖 8 所示的程序的第一部分將在異步模式下打開串行端口。

圖 8 在異步模式下打開串行端口

如果您在配置中輸入了無效值(例如錯誤的波特率),Open 操作將失敗。(如果 COM 端口被占用、 端口不存在或者您沒有適當的權限,也可能會失敗。)這就是程序檢查並顯示故障的原因。

程序的其余部分(請參見圖 9)只是回顯收到的每個字符。它通過從 ReceiveByte 通知獲取字符並使 用 WriteByte 將它們回發來實現此目的。

為了使輸出更容易閱讀,每個回車符(ASCII 13,十進制)後會附加一個換行符 (10),使光標移到終 端模擬器窗口中的下一行。請注意,在圖 9 的圖表中,所有 SerialPortService 塊指的都是同一個服務 實例。

圖 9 EchoAsynch 程序

EchoSynch VPL 程序(請參見圖 10)使用同步操作模式,因此不使用通知。(事實上,在同步模式下 ,從來不會發送通知。)

圖 10 EchoSynch 程序

與前一個程序不同,這個程序使用 ReadString 和 WriteString 來回顯數據。這些操作執行的功能類 似於 ReadByteArray 和 WriteByteArray,但是在 VPL 中,字符串比字節數組更容易處理。

字符串操作使用換行符(或新行符)作為行結束標記。因此 ReadString 會在您按換行 (Ctrl-J) 而 不是按 Enter 時結束。當您第一次使用終端模擬器進行測試時,這可能有點令人困惑,因為您可能想知 道為何沒有回顯任何內容。WriteString 向輸出中添加一個回車符和換行符,使每個字符串都顯示在單獨 一行中。

請注意,EchoSynch 的配置文件定義了一個 500ms 的 InterCharacterDelay。這使得字符串發送得非 常慢。您可以嘗試更改此值。

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