Microsoft Robotics Studio - 触摸板服务






4.81/5 (10投票s)
2007年9月8日
3分钟阅读

56372

432
使用 CCR 和 DSS 实现触摸板服务。

引言
在本文中,您将看到如何创建一个 Microsoft Robotics (从现在开始称为 MR) 服务来使用游戏手柄。本文不是 CCR 或 DSS 的教程,对这些技术有深入的了解将非常有帮助。在本文中,我假设您知道消息、队列和服务是什么。
基本思想非常简单:服务将每 x
毫秒轮询一次游戏手柄设备。游戏手柄设备将使用托管的 DirectInput (.NET 版本) 进行查询,并返回 -1
到 1
之间的值。因此,例如,左上角将是 (-1,1)
,右下角将是 (1,-1)
。

不会使用计时器或线程来实现轮询。我将仅使用消息、队列和 CCR 类。
背景
关于硬件(手柄)的几句话。我使用的是 Logitech Dual Action Gamepad (USB)。这是一款价格便宜且适合游戏的设备,足够精确,可以控制爱好者的机器人。我从未接触过 DirectInput,因此由于我知识不足,被迫在代码中“硬编码”数值。我假设这些值与我的硬件严格相关,因此,如果您有不同的游戏手柄,请相应地更改它们。
此服务的下一个版本将支持使用 XML 文件进行配置。
要编译此解决方案,您必须拥有 Microsoft.DirectX.DirectInput.dll 文件。您可以在 Directx SDK 中找到它。
项目
要创建一个空服务,您有两种方式:从空白解决方案开始,或者使用 DssNewService.exe。我将向您展示简单的方法:打开 MR 命令行,然后 cd 到 samples 目录,接着启动 DssNewService.exe /s:Pad 这将创建一个新项目,其中包含拥有 DSS 服务和 VS2005 解决方案所需的所有文件。该项目还具有生成代理类的生成后操作。
创建的其他文件是 Manifest.xml 和 PadTypes.cs 文件。PadTypes 包含此服务将使用的所有消息。在那里,您可以定义您的服务将响应的“操作”。
打开解决方案并添加对 Microsoft.DirectX.DirectInput.dll 的引用。在 Pad.cs 中添加
using Microsoft.DirectX.DirectInput;
您现在可以使用 Managed Direct Input 了。
该服务分为三个逻辑部分
- DirectInput 初始化
- DirectInput 轮询
- 消息通知
DirectInput 初始化
//private class variable
private Device _gamepad = null;
...
//Init direct input
DeviceList devlist = Manager.GetDevices(DeviceClass.GameControl,
EnumDevicesFlags.AttachedOnly);
foreach (DeviceInstance inst in devlist)
{
Guid g = inst.InstanceGuid;
_gamepad = new Device(g);
_gamepad.SetCooperativeLevel(_form.Handle,
CooperativeLevelFlags.Background |
CooperativeLevelFlags.NonExclusive);
}
if (_gamepad != null)
{
_gamepad.SetDataFormat(DeviceDataFormat.Joystick);
_gamepad.Acquire();
}
现在我们可以使用 _gamepad
来查询手柄状态,以查找摇杆值或按钮状态。_form
仅用于获取一个“句柄”来初始化 DirectInput
。此表单可用于为手柄提供图形反馈。
注意! 如果我在 VS2005 中开始调试 (F5),我会收到“Loader Lock”警告。这是由 托管调试助手 (MDAs) 完成的。因此,您可以按文章所述禁用助手,或者运行后稍后附加到进程进行调试 (CTRL + ALT + P)。
DirectInput 轮询
手柄轮询分为两个“块”。第一个基于“计时器”:每次滴答都会执行一次游戏手柄读取,并将数据立即发送到队列。一个 Arbiter 被激活在队列上:一个匿名委托处理消息,并最终向主端口通知新的“事件”。
计时器是通过以下代码片段实现的(自由摘自 robotics examples)。
//the timer port
private Port<DateTime> _timerPort = null;
...
_timerPort = new Port<DateTime>();
...
_timerPort.Post(DateTime.Now);
Activate(Arbiter.Receive(true, _timerPort,
delegate(DateTime sign)
{
Activate(//every 75 ms ...
Arbiter.Receive(false, TimeoutPort(75),
delegate(DateTime time)
{
//poll the gamepad device and enqueue the data read
if (_gamepad != null)
{
// ... poll the pad
JoystickState jstate = default(JoystickState);
_gamepad.Poll();
jstate = _gamepad.CurrentJoystickState;
_joyReads.Post(jstate);
}
_timerPort.Post(time);
}
)
);
}
));
另一个 Arbiter 正在 _joyReads
上运行
Activate(
Arbiter.Receive<JoystickState>(true, _joyReads,
delegate(JoystickState jsta)
{
//jsta analysis in order to send external notifications
})
);
就是这样。手柄轮询几乎完成了。注释掉的代码读取当前摇杆 (L 和 R) 的值,并将其归一化到 (-1,1)
的范围内。在省略的代码中,进行的感兴趣的“服务”操作与此代码片段类似。
RightStickUpdate actR = new RightStickUpdate();
actR.Body.Stick.StickValueX = _state.RightStick.StickValueX;
actR.Body.Stick.StickValueY = _state.RightStick.StickValueY;
LogInfo("RightStickUpdate " + actR.Body.Stick.StickValueX,
actR.Body.Stick.StickValueY);
_mainPort.Post(actR);
RightStickUpdate
是在 PadType.cs 文件中定义的类。
public class RightStickUpdate :
Update<StickUpdateResponseRight, PortSet<DefaultUpdateResponseType, Fault>>
{
private StickStatus _stick;
[DataMember]
public StickStatus StickStatus
{
get { return _stick; }
set { _stick = value; }
}
public RightStickUpdate()
: base(new StickUpdateResponseRight())
{
_stick = base.Body.Stick;
}
}
最后一步是消息处理程序方法,例如
[ServiceHandler(ServiceHandlerBehavior.Exclusive)]
public IEnumerator<ITask> RightStickUpdateHandler(RightStickUpdate rsUpd)
{
rsUpd.ResponsePort.Post(DefaultUpdateResponseType.Instance);
//notify it
SendNotification(_submgrPort, rsUpd);
yield break;
}
在响应端口发布并发送通知。服务流程现已完成。解决方案中的其他消息用于完全处理摇杆和按钮。
GUI
很快将开发一个 GUI 来提供手柄状态的反馈。要将一个表单“显示”在服务“内部”,请使用此代码片段(实际上已在源文件中注释掉)。PadInfoForm
是完全空的。
using Microsoft.Ccr.Adapters.WinForms;
...
//Create Form
PadInfoForm _form = new PadInfoForm();
WinFormsServicePort.Post(new RunForm(
delegate()
{
return _form;
}
));
下一个改进将是一个 GUI。如果您有评论和建议,请发表!
历史
- 2007 年 9 月:首次发布