继之前分享的一篇《Robotics Studio学习教程:第八天——Hello World Again! (DSS Service version)》, 我们将继续分享下一篇《Robotics Studio学习教程:第九天——用DSS 改写自行走小车的 Activity》,让我继续开始我们学习Visual Programming Language,以及使用Robotics Studio学习开发机器人应用的道路吧。
[Robotics Studio] 以 DSS 改写自走型机器车当中的 Activity -- Day9
还记得 Day7 当中的 CalculateSpeed Activity 吗:
现在我们已经会用 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, 效果是一模一样的, 像这样:
所以你知道 VPL (图形式程序) 是可以跟 VS 2008 (Coding) 互通的啰... (那到底是 VPL写 Activity 简单还是用 VS 2008 写 DSS 比较简单?)
让我们继续一下章教程:
《Robotics Studio学习教程:第十天——利用 DSS 建立虚拟环境(1)》