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

演练:制作 SetInMotion 设备插件

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2014年6月12日

CPOL

7分钟阅读

viewsIcon

8870

为 SetInMotion 制作一个插件,用于与设备通信,然后从整个 SetInMotion 生态系统中控制它!

引言

世界上有无数新颖、出色的设备和服务。当您想用它们做有趣的事情时,通常需要大量的开发工作来添加用户界面、连接性等。如果您可以编写最少量的必要代码来使您的设备或服务工作,然后立即以有趣的方式(甚至是在云端)开始使用它呢?如果这引起了您的兴趣,请继续阅读...

背景

SetInMotion 是一个 Windows .NET 应用程序和云服务,它充当实时连接事物的“粘合剂”。有两个可用的 API:一个用于插件(适用于 Windows 应用程序),一个用于云。本文讨论了如何使用插件 API。

SetInMotion 软件可在独立(非云)模式下免费使用,网址为 http://sim.x9tech.com。最终用户示例和教程在 SetInMotion 博客上重点介绍。

通道哲学

SetInMotion 强大的地方在于“通道”系统。SetInMotion 中的每个设备都必须通过其通道属性报告其状态。让我们来看一下在具有大部分输出的更复杂的设备(Xbox 控制器)和一个具有单一输入的简单设备(灯泡)中,这可能是什么样子。

Xbox 控制器和灯泡都是设备。它们都通过其通道属性报告其状态。通道属性值可以是

  • 开/关值,适用于按钮、继电器、LED 或任何只有开/关或真/假状态的设备。
  • 百分比值(例如 51.2),适用于调光器、伺服器的位置、速度等。
  • 文本字符串(假设您创建了一个“记分牌”设备,并且其中一个通道属性代表团队名称)。

当设备添加到系统时,用户需要定义通道名称。然后,设备会将其属性和值(以绿色突出显示)通知 SetInMotion,并在这些值发生更改时通知 SetInMotion。同样,当其他东西更改了设备的属性时(例如设置灯泡的“强度”属性),SetInMotion 会通知设备。设备的通道属性可以被视为输入、输出或两者兼有!

由于每个设备的输入和输出都符合通道格式,因此 SetInMotion 用户可以创建 **通道操作**,当通道属性更改时,这些操作会触发以执行有趣的操作。在上例中,当 Xbox 控制器的 **ButtonA** 等于 **On** 时,用户可以选择将灯泡的 **Intensity** 属性设置为 **100**,从而在按下 Xbox 控制器上的 A 按钮时打开灯泡。

当 SetInMotion 连接到云端时,通道的威力呈指数级增长,其他互联网连接的应用程序可以实时查看和控制这些通道。

创建类库,并添加 SetInMotion API NuGet 包

SetInMotion 插件是类库。在 Visual Studio 中,创建一个以 .NET 4.0 或 .NET 4.5 为目标的新类库项目。

然后,将名为 **SetInMotionApi** 的 NuGet 包添加到您的项目中。

(作为参考,NuGet 包位于 此处。)

安装此包后,您将在类库的引用中看到对 **SIM.IO.API** 的引用,并在对象浏览器中看到 SetInMotion API 命名空间。

您会注意到有各种可用的接口。

实现 IDevice 接口

SetInMotion 中的任何设备都通过实现 **IDevice** 接口的公共类公开。

我们将创建一个设备,将当前的 **DateTime.Now.Second** 引入 SetInMotion 通道。(这是最简单的了。)

您可以复制代码以开始。每个必需的方法中都添加了注释。

using SIM.IO.API;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PluginDemo
{
    public class MyPlugin : IDevice
    {
        // The InputHostConnector should be implemented as a public property.
        // It is the object that the device will use to send channel value updates to SetInMotion.
        // SetInMotion will set this property just before Initialize() is called.
        public SIM.IO.API.HostConnectors.IHostConnector InputHostConnector { get; set; }

        public void DoBackgroundProcess()
        {
            // Anything the device needs to do at a regular interval - for example, polling/reading input values
            // and notifying SetInMotion when its channel property values change.
            InputHostConnector.SetChannelValue("CurrentTimeSeconds", DateTime.Now.Second.ToString());
        }

        public TimeSpan? GetBackgroundProcessInterval()
        {
            // Return a TimeSpan indicating how often SetInMotion should call DoBackgroundProcess().
            // As a best practice, use the built-in DoBackgroundProcess() methods provided, instead
            // of spawning new threads within the device.

            // If the device does not require any background processes, simply return null.
            return TimeSpan.FromSeconds(1);
        }

        public DeviceFaultStatus GetFaultStatus()
        {
            // If there's something wrong with the device (for example, the user unplugs it from the machine),
            // return something similar to the following:
            // return new DeviceFaultStatus(true, "Device is unplugged");
            // When a fault status is indicated, the device will appear in a red color within SetInMotion.

            // But in this case, everything is okay, so we'll return a non-faulted status:
            return new DeviceFaultStatus(false);
        }

        public string GetFriendlyName()
        {
            // The friendly name of the device, that will be displayed to the user.
            return "Current Seconds Clock";
        }

        private bool _initialized = false; // variable to keep track of initialization status
        public bool IsInitialized
        {
            get
            {
                // SetInMotion will check this property to determine if the device needs to be initialized.
                // If this property returns False, then SetInMotion will attempt to call Initialize()
                // at regular intervals until this property returns true.

                return _initialized;
            }
        }

        public void Initialize()
        {
            // Do whatever is necessary to initialize the device.
            // In this case, not much.

            _initialized = true;
        }

        public void Shutdown()
        {
            // When SetInMotion needs to shut down or remove the device, this method will be called.
            // Shut down the device and clean up any left-over resources.

            _initialized = false;
        }

        public void OnChannelValueChange(string channelId, string property, string value)
        {
            // SetInMotion notifies this device when any of its channel properties change.
            // channelId is the user-defined name of your device.
            //
            // This is where the device should process any channel changes considered to be "inputs"
            // of this device.
            //
            // If this device controlled motors, perhaps 'property' could be 'Speed' and the value
            // would indicate the motor speed.

            // In this case, this device doesn't have any outputs, so nothing needs to be done.
            return;
        }

        public void ExecuteCommand(DeviceCommand command)
        {
            // Sequences can send commands to devices. If the device should support commands,
            // they will arrive here. Parameters are parsed in advance and available via the
            // command.CommandSegments property as a string[].

            // This device supports no commands, so we will do nothing.
            return;
        }
    }
}

重要的设备概念

在设计和实现任何设备插件时,有 3 件特别重要的事情需要牢记:

InputHostConnector - 通知 SetInMotion 何时发生更改。

SetInMotion 在初始化过程之前将此对象分配给您的设备。每当您的设备有任何更改时,都应通过调用

InputHostConnector.SetChannelValue("MyProperty", "MyNewValue");

您不必担心检查值自上次调用以来是否已更改 - SetInMotion 会为您处理。

SetInMotion 也设计得运行速度很快。尝试设计您的设备,使其能够快速检测到更改。这样,当用户按下按钮或与您的设备插件进行交互时,他们就能看到几乎即时的结果。

初始化、关机和故障

请仔细注意正确处理这些区域。当用户将您的设备添加到 SetInMotion 时,将执行以下工作流:

  1. SetInMotion 将尝试为您的设备分配一个 InputHostConnector。
  2. SetInMotion 将定期检查您的设备是否已初始化,通过您的 **IsInitialized** 属性。
  3. 如果您的 **IsInitialized** 属性返回 **false**,SetInMotion 将尝试使用 **Initialize()** 方法初始化您的设备。
  4. 当用户关闭或移除您的设备时,SetInMotion 将调用 **Shutdown()** 方法。

还会定期检查一个故障状态方法 - **GetFaultStatus()**。这是设备有机会告诉 SetInMotion(和用户)出了问题。它应尽一切努力报告设备运行中的任何问题。

设备应能从用户断开连接和重新连接中自动恢复。

后台进程

在与硬件交互时,很可能您需要在定期轮询或在后台执行操作。通常,在独立应用程序中,您会生成并管理一个线程。**不要这样做!** 崩溃的额外线程可能会导致整个 SetInMotion 应用程序崩溃,让用户感到沮丧。

SetInMotion 的后台进程设计用于简化此过程,并自动处理线程调度和错误处理。

只需通过 **GetBackgroundProcessesInterval()** 方法告诉 SetInMotion 您的后台进程需要运行的频率(无论是毫秒还是分钟),并在 **DoBackgroundProcess()** 方法中执行您的工作。

测试您的插件

编写测试应用程序来测试您的插件很麻烦。所以,我们为您代劳。从 http://sim.x9tech.com/developer/sandbox 获取 Developer Sandbox,并在编译类库后启动它。

点击 **Browse...** 并选择您的类库的 DLL。

现在您应该会在下拉列表中看到您的设备。

您可以在此处测试设备的初始化和关机,以及实时查看设备报告的通道。

当您的设备初始化时,Developer Sandbox 将分配一个随机的通道 ID,以模拟最终用户提供通道名称。

如果通道是输入,Developer Sandbox 允许您模拟将通道设置为您选择的值,或一些更常见的值(0、100、On 或 Off)。

调试技巧

如果您想在使用 Developer Sandbox 时逐步调试设备的处理过程,请在 Visual Studio 中,点击 **Debug > Attach to Process...**。

附加到 **SIM.DeveloperSandbox.exe**,您就可以在插件的代码中设置断点。

发布您的插件

测试完设备后,就可以打包和发布了!您可以将插件发布到本地计算机,或发布到 SetInMotion Cloud 以供他人使用。

在 Developer Sandbox 中,点击 **Package / Publish...** 并填写字段。

Package/Publish 对话框将自动从程序集的清单中提取建议的名称。

插件显示名称 - 这是安装和使用插件时 SetInMotion 用户将看到的名称。

插件系统名称 - 这是 SetInMotion 和 SetInMotion Store 引用您的插件的方式。您的插件的系统名称必须是唯一的 - 您不能上传一个系统名称已被他人账户发布的插件。

版本 - 此信息从您的插件程序集的清单中提取。初始版本号无关紧要,但当您重新发布插件时,类库清单(**AssemblyInfo.cs**)中的版本号必须大于先前发布的版本。

部署到我的机器

此选项将绕过 SetInMotion Store,并将插件打包/部署到您的本地计算机,非常适合使用实际 SetInMotion 软件进行测试。

SetInMotion 插件包存储在 %ProgramData%\SetInMotion\Plugins 中,并在运行时解压缩。虽然您可以随时部署,但您需要重新启动 SetInMotion 才能检测到新插件。

发布到 SetInMotion Store

发布到 SetInMotion Store 允许其他用户查看、安装、更新和使用您的插件。插件发布需要 SetInMotion 账户。

就是这样!

恭喜!此时您应该拥有一个可用的 SetInMotion 设备插件。如果您的设备需要设置支持,您还可以实现 **IDeviceWithSettings** 接口,但这将留待另一篇演练。

历史

2014 年 6 月 11 日 - 初始文章

© . All rights reserved.