[Robotics Studio] 以 DSS 改寫自走型機器車當中的 Activity -- Day9

  • 6287
  • 0
  • 2009-02-17

[Robotics Studio] 以 DSS 改寫自走型機器車當中的 Activity -- Day9

還記得 Day7 當中的 CalculateSpeed Activity 嗎:

image

現在我們已經會用 Visual Studio 2008 開發 DSS Service, 讓我們寫一個一樣功能的 DSS Service 吧.

首先, 當然是用 VS2008 開啟新的一個 DSS Service 專案, 就取名叫 CalculateSpeedService , 產生完畢後,

我們先定義輸出以及輸入的資料型別, 在 CalculateSpeedServiceTypes.cs 當中加入以下的 code:


[DataContract]
public class CalculateSpeedInput
{
    [DataMember]
    public double WishLength { get; set; }

    [DataMember]
    public double StageRunned { get; set; }
}

[DataContract]
public class CalculateSpeedOutput
{
    [DataMember]
    public double Speed { get; set; }
}


我們分別定義了 CalculateSpeedInput 以及 CalculateSpeedOutput, 相信當中的意義應該很清楚才是,

接著我們定義一個 CalculateSpeed , 繼承自 Submit<CalculateSpeedInput, PortSet<CalculateSpeedOutput, Fault>> , Submit class 是眾多的 DSS Operations 之一, 通常用於不改變狀態 (State) , 而且不會產生通知的一種訊息處理模式, 此處的 TBody (CalculateSpeedInput) 就是說要輸入這個 CalculateSpeedInput 類別的物件 (訊息) , 而輸出會有 CalculateSpeedOutput 或是 Fault 這兩種. Code 如下:


[DisplayName("CalculateSpeedToRun")]
[Description("Calculate Speed to run")]
public class CalculateSpeed : Submit<CalculateSpeedInput, PortSet<CalculateSpeedOutput, Fault>>
{

}

然後我們就可以把這個 Class 放到 CalculateSpeedServiceOperations 的定義後面, 將程式碼:

public class CalculateSpeedServiceOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Subscribe>

改為

public class CalculateSpeedServiceOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Subscribe, CalculateSpeed>

到此, 也許你對 PortSet 是啥玩意有點小疑問, 這個 PortSet 是 CCR 當中定義資料交換的基本元素, Port 是單一簡單訊息交換, PortSet 則是可以接受多個訊息 (任選其一). 所以上面的程式碼現在的意義是, CalculateSpeedServiceOperations (CalculateSpeedService 可以接受的操作指令) 有 DssDefaultLookup, DssDefaultDrop, Get, Subscribe, CalculateSpeed 這幾種.

我們定義完畢輸出入資料型別, 也定義了操作的介面, 現在我們要寫操作的程式碼內容了.

現在打開 CalculateSpeedService.cs , 編輯 CalculateSpeedService 這個類別的內容, 加上下面的函式:


[ServiceHandler(ServiceHandlerBehavior.Concurrent)]
public IEnumerator<ITask> CalculateSpeedHandler(CalculateSpeed cs)
{
    double lengthleft = cs.Body.WishLength - cs.Body.StageRunned;

    double calculatedspeed = 0;

    if (lengthleft > 1)
        calculatedspeed = 1;
    else if (lengthleft > 0.5)
        calculatedspeed = 0.5;
    else if (lengthleft > 0)
        calculatedspeed = 0.3;
    else if (lengthleft <= 0)
        calculatedspeed = 0;

    cs.ResponsePort.Post(new CalculateSpeedOutput() { Speed = calculatedspeed });
    yield break;
}

我們定義了一個 CalculateSpeedHandler (一般習慣是 Handler 結尾, 但函式名稱可以自己定義, DSS 會自動尋找) , 並且給予  ServiceHandlerBehavior.Concurrent 的屬性 (表示不改變狀態), 這個函式輸出一定要是 IEnumerator<ITask> , 而輸入的物件一定要是 CalculateSpeed 類別, 但變數名稱當然可以自己定義.

中間的程式碼就跟我們在 VPL 中自己做的 CalculateSpeed Activity 內容幾乎相同.

最後, 我們對該操作指令的輸出Port 丟一個 CalculateSpeedOutput 的物件, 值為我們剛剛算出來的速度.

簡單解釋, yield 會由 C# Compiler 產生一個 class , 這個 class 會繼承 IEnumerator , 然後根據你的 yield return 或是 yield break 來實作 Finate State Machine , 之後傳回該 class 所產生的一個物件 (Instance) , 一旦這個 Instance 被呼叫 MoveNext() , 則會程式執行會跳回你的 yield return 之後, 繼續執行下去, 直到下一個 yield. 至於你的 yield return 後面的值就是該物件 (Instance) 的 Current 內容. (以上是我盡量對不了解的人所提的解釋 )

學習 .NET 3.5 提出的 Linq 對於 IEnumerator 的概念有很大的幫助.

在這裡, 因為我們沒有後續的動作要處理, 也沒有需要回傳的ITask 物件, 所以直接 yield break;

現在你可以將原本的 CalculateSpeed Activity 改用我們自己寫的 DSS Service - CalculateSpeedService, 效果是一模一樣的, 像這樣:

image

所以你知道 VPL (圖形式程式) 是可以跟 VS 2008 (Coding) 互通的囉... (那到底是 VPL寫 Activity 簡單還是用 VS 2008 寫 DSS 比較簡單?)