65.9K
CodeProject 正在变化。 阅读更多。
Home

Microsoft Robotics Studio - 触摸板服务

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (10投票s)

2007年9月8日

3分钟阅读

viewsIcon

56372

downloadIcon

432

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

Screenshot - screen_shot.jpg

引言

在本文中,您将看到如何创建一个 Microsoft Robotics (从现在开始称为 MR) 服务来使用游戏手柄。本文不是 CCRDSS 的教程,对这些技术有深入的了解将非常有帮助。在本文中,我假设您知道消息、队列和服务是什么。

基本思想非常简单:服务将每 x 毫秒轮询一次游戏手柄设备。游戏手柄设备将使用托管的 DirectInput (.NET 版本) 进行查询,并返回 -11 之间的值。因此,例如,左上角将是 (-1,1),右下角将是 (1,-1)

Screenshot - pad_axis.jpg

不会使用计时器或线程来实现轮询。我将仅使用消息、队列和 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.xmlPadTypes.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 月:首次发布
© . All rights reserved.