# 内置硬件协议

# 概要

前面大概介绍了硬件设备接口

为了减轻开发者的负担,我们默认内置了几种常见硬件连接协议,主要用途是插件配置所需的设备搜索、连接的参数,向 uCode 注册,并获取连接后的实例,与设备实现通信。

如果内置的几种硬件协议无法满足需求,可以自定义硬件协议。

在插件中,需要注册插件所需的硬件协议,如:

// index.js
import { spRegister } from "./devices/sp-device";
import { WebsocketRegister } from "./devices/websocket-device";

const register = {
  DeviceRegister: [
    spRegister, // 串口连接协议注册方法
    WebsocketRegister, // Websocket连接协议注册方法
  ],
  BlockRegister: ExampleDeviceExtension, // 积木块注册方法
};

self.UCode.extensions.register(register); // 插件注册方法

对于硬件连接协议注册列表DeviceRegister,其类型是 DeviceRegisterType (点击查看 API 文档)。 它可以是以下三个主要的注册方法之一:

# 内置协议类型

以下是内置的几种硬件协议,如果符合需求的话,可以直接从 SDK 中导入使用:

内置协议 是否依赖 uCodeLink 说明
SerialPort 串口协议 目前基于 node-serialport 实现。我们会计划增加 WebSerial (opens new window)
BLE 蓝牙协议(4.0 以上) 目前 WebBle 暂未开放,后续会增加
WebSocket (未完成) 原生的 WebSocket 接口,注意在 Web 端的跨域和加密混用 等安全限制
WebTransport (未完成) WebTransport 是基于 UDP 但是是更上层的协议。 了解 WebTransport (opens new window)
MDns 苹果的 开源 Bonjour 协议,目前这个只是用于发现协议
UDP/TCP 经典 Socket 通信 -- UDP/TCP 协议

# 串口协议

以串口为例,内置的硬件协议已经内置到 ucode-extension-common-sdk 里面,使用起来非常简单

import { CommonProtocols } from "@ubtech/ucode-extension-common-sdk";
const { SerialPortProtocol, getSerialPortDeviceRegister } = CommonProtocols.SerialPort;
  • SerialPortProtocol 是我们封装好的 Protocol 类,直接继承该类的话,就可以获取所有串口的方法,具体的可以参照 uCode Common SDK 硬件协议的部分

在代码里面,可以看到我们直接调用了 this.send 的方法,这个其实继承于 SerialPortProtocolsend 方法

  say(data) {
    this.send(Buffer.from(data));
  }
  • getSerialPortDeviceRegister 则是我们封装好的,直接提供一个开箱即用的串口注册器

里面有很多可选参数,例如:

  • 串口的波特率
  • 串口 Buffer 缓冲 Size
  • 是否要开启队列
  • 发现设备的时候,是否要根据 vid 和 pid 过滤串口设备
  • 自定义串口的名称
 {
    openOptions: {
      baudRate: 115200, // 串口打开的波特率
      bufferSize: 12 * 1024 * 1024, // 缓冲区大小
    },
    queueOptions: {
      enable: true, // 数据发送是否启用队列
      interval: 70, // 启用队列时数据发送的间隔
    },
    // 发现设备时过滤用的vid和pid,配置后将只显示和配置id一致的串口设备
    filter: {
      vid: "0403",
      pid: "6001",
    },
    // 自定义显示串口设备名
    customDeviceName: (data) => `myRobot_${data?.comName}`,
  },

下面是完整的代码展示,你可以在脚手架或者内置插件模板,都可以找到

import { CommonProtocols } from "@ubtech/ucode-extension-common-sdk";
const { SerialPortProtocol, getSerialPortDeviceRegister } = CommonProtocols.SerialPort;

class DemoSerialPortProtocol extends SerialPortProtocol {
  say(data) {
    this.send(Buffer.from(data));
  }
}

export const spRegister = getSerialPortDeviceRegister({
  Protocol: DemoSerialPortProtocol,
  // 以下配置均为可选配置
  Options: {
    openOptions: {
      baudRate: 115200, // 串口打开的波特率
      bufferSize: 12 * 1024 * 1024, // 缓冲区大小
    },
    queueOptions: {
      enable: true, // 数据发送是否启用队列
      interval: 70, // 启用队列时数据发送的间隔
    },
    // 发现设备时过滤用的vid和pid,配置后将只显示和配置id一致的串口设备
    filter: {
      vid: "0403",
      pid: "6001",
    },
    // 自定义显示串口设备名
    customDeviceName: (data) => `myRobot_${data?.comName}`,
  },
});

# 完整的 串口 API 文档

最新的以 API 文档为主 SerialPort 串口 API 文档

# 蓝牙协议 (待完成)

# WebSocket 协议 (待完成)

# MDNS 协议 (待完成)

# UDP/TCP 协议

# 概要

uCode 内部封装了 UDP/TCP 协议的操作方法,并适配了 PC、Mobile 平台。API 参考 Node.js 的 UDP 写法。

我们内置了两个 UDP/TCP 设备“搜索+连接”注册方法,让插件可以向 uCode 注册需要用到的“搜索+连接”的方式及其参数:

  • getUDPDeviceRegister(),基于 UDP 发现设备、基于 UDP 与设备通信。
  • getTCPDeviceRegister(),基于 UDP 发现设备、基于 TCP 与设备通信。

你可以根据需要使用相应的注册方法,也可以自己实现不同的“搜索+连接”方案的注册方法。

下面就前面提到的两个注册方法进行详细描述。

# 导入与参数说明

  • getUDPDeviceRegister():适用于 UDP 搜索 + UDP 通信 的设备

    内置的 UDP 硬件协议注册方法,是基于 UDP 搜索与 UDP 连接通信类组合的。

    import { CommonProtocols } from "@ubtech/ucode-extension-common-sdk";
    const { getUDPDeviceRegister } = CommonProtocols.UDP;
    
    /**
     * UDP 搜索 + UDP 通信
     */
    export const udpRegister = getUDPDeviceRegister({
      DeviceConnection: xx,
      Options: xxxx,
      // Discover: xxx,
    });
    

    getUDPDeviceRegister 已经对部分参数封装,入参只需要设置DeviceConnectionOptions,或设置Discover替换内置的 UDP discover 类。

    返回值类型为 DiscoverDeviceRegister (点击查看 API 文档) ,包含以下字段:

    • type: 'discover' 属于“设备搜索”类型
    • DeviceType 设置搜索类型、描述、搜索超时时长、当只有一个设备时自动连接等参数。当有多个硬件协议注册时,可以在界面中看到描述。
    • DeviceConnection 设备连接类,用于建立硬件设备连接、收发消息、协议解析等
    • Discover 设备搜索类,用于处理“搜索设备”的逻辑
    • Options 设备搜索类、设备连接类所需的参数

    DeviceConnection参数可以直接使用或继承UDPDeviceConnection,覆写父类方法实现你所需的业务逻辑。

    如果内置的注册方法与连接通信类都无法满足业务需求,可以进行自定义

    查看 UDPRegister 详情

    UDPDeviceConnection如下:

    import { CommonProtocols } from "@ubtech/ucode-extension-common-sdk";
    
    const { UDPDeviceConnection } = CommonProtocols.UDP;
    class TestUDPDeviceConnection extends UDPDeviceConnection {}
    

    Options如下:

    type UDPRegisterOptions = {
      /**
       * UDP创建socket时构造函数的参数
       */
      udpConstructorOptions: UDPConstructorOptions | UDPSocketType;
      /**
       * 发消息来触发设备回应?
       */
      sendMsgForDiscover?: UDPMsg;
      /**
       * 收到消息后,发送一条固定的消息回应?
       */
      sendMsgForResponse?: UDPMsg;
      /**
       * 监听消息时,绑定的端口
       */
      bindPort?: number;
      /**
       * 监听消息时,绑定的地址
       */
      bindAddress?: string;
      /**
       * 发送消息时,发送数据的端口
       */
      sendPort?: number;
      /**
       * 发送消息时,发送数据的地址
       */
      sendAddress?: string;
      /**
       * 设置广播?
       */
      isBroadcast?: boolean;
      /**
       * 设置 IP_TTL 选项
       */
      ttl?: number;
      /**
       * 设置或取消 IP_MULTICAST_LOOP 选项
       */
      multicastLoopback?: boolean;
      /**
       * 将套接字的默认传出多播接口设置为所选接口或返回系统接口选择
       */
      multicastInterface?: string;
      /**
       * 自定义设备名,可以从socket数据中加工出新名字
       */
      customDeviceName?: (deviceData: DiscoverDeviceData) => string;
      /**
       * 设置 IP_MULTICAST_TTL 选项
       */
      multicastTTL?: number;
      /**
       * 设置接收消息的缓存大小
       */
      recvBufferSize?: number;
      /**
       * 设置发送消息的缓存大小
       */
      sendBufferSize?: number;
      /**
       * 设置多播地址,在 addMembership 或 dropMembership 使用
       */
      multicastAddress?: string;
      /**
       * 扫描超时
       */
      scanTime?: number;
      /**
       * 队列参数,可以设置 队列 发送的 间隔 或者 数量
       */
      queueOptions?: QueueConstructorType;
    };
    
  • getTCPDeviceRegister(): 适用于 UDP 搜索 + TCP 通信 的设备

    getTCPDeviceRegistergetUDPDeviceRegister参数大体上相同:

    • Discover 基于 UDP,这里用的也是 UDPDiscover。
    • DeviceConnection 可以用内置的,也可以继承,对某些方法进行覆写,或自定义,当然 Discover 也可以用别的,如串口、蓝牙等。
    • Options 两者的主要差异之处。将 Discover 和 DeviceConnection 的 options 分开设置。
    import { CommonProtocols } from "@ubtech/ucode-extension-common-sdk";
    
    const { getTCPDeviceRegister } = CommonProtocols.TCP;
    const { UDPDiscover, getUDPDeviceType } = CommonProtocols.UDP;
    
    export const tcpRegister = getTCPDeviceRegister({
      Discover: UDPDiscover, // 设置搜索器
      DeviceConnection: xxx, // 设置通信器
      Options: {
        // 设置搜索器、连接器用到的配置参数
        discoverDeviceType: getUDPDeviceType({ scanTime: scanTimeTimeout }), // 搜索器类型(搜索、输入ID/IP、自行处理)
        discoverOptions: xxx, // UDP搜索类参数
        connectionOptions: xxx, // TCP搜索类参数
      },
    });
    
    查看 Connection 详情

    discoverOptions见上文的UDPRegisterOptionsconnectionOptions如下:

    type ConnectionOptionsType = {
      /**
       * 创建TCP socket时,传给构造函数的参数
       */
      tcpConstructorOptions?: SocketConstructorOpts;
      /**
       * 队列参数,可以设置 队列 发送的 间隔 或者 数量
       */
      queueOptions?: QueueConstructorType;
      /**
       * 设置数据编码,也可以在write中设置
       */
      encoding?: BufferEncoding;
      /**
       * 是否开启KeepAlive功能
       */
      keepAlive?: {
        enable: boolean;
        initialDelay?: number;
      };
      /**
       * 是否开启拥塞控制算法
       */
      noDelay?: boolean;
      /**
       * 设置inactive超时时长
       */
      timeout?: number;
      /**
       * 连接超时
       */
      connectTimeout?: number;
    };
    

    部分类型说明见 API 文档

    如果使用内置的TCPDeviceConnection,需要实现抽象类一个方法getDeviceInfo

    这个方法参数来自于 Discover 类:当 Discover 搜索到设备时,会创建“设备对象”(不同的硬件设备,协议不同,“设备对象”也可能不同)。当用户点击“连接”按钮时,会将该“设备对象”传给 Connection 的 getDeviceInfo 方法。因此子类需要实现这个方法,解析“设备对象”中的端口和地址信息,属于具体的业务部分。

    import { CommonProtocols } from "@ubtech/ucode-extension-common-sdk";
    
    const { TCPClientConnection } = CommonProtocols.TCP;
    export class MyTCPClientConnection extends TCPClientConnection {
      /**
       * 实现抽象类获取设备信息的方法
       * @param { {buffer: Buffer, port: number, address: string} } device discover传过来的设备对象
       * @return Promise<SocketConnectOpts = {
       * port: number;
       * host?: string;
       * localAddress?: string;
       * localPort?: number;
       * hints?: number;
       * family?: number;
       * lookup?: LookupFunction;
       * onread?: OnReadOpts;
       * path?: string;
       * } TCP连接时用的设备信息
       */
      getDeviceInfo(device) {
        // 示例:device.buffer对象中包含port和address属性
        const { buffer } = device;
        const data = JSON.parse(Buffer.from(buffer).toString());
        console.log(data.port, data.address);
        return Promise.resolve({
          port: data.port,
          host: data.address,
        });
      }
    }
    

点击查阅 UDP API 文档

点击查阅 TCP API 文档

# 自定义

如果你想自定义 Discover 类,请遵循并实现接口IDeviceDiscover

IDeviceDiscover API 文档

如果你想自定义 Connection 类,请遵循并实现接口DeviceConnectionInterface

DeviceConnectionInterface API 文档

点击进一步了解 自定义 硬件协议

uCode v4.0 是基于 web 的,但是 web 天生有很多限制,因此为了提供一些额外的访问能力,我们增加了 uCodeLink 硬件连接助手,通过它可以访问 串口 或者 蓝牙等设备