Skip to content
wwj718 edited this page Jun 27, 2019 · 11 revisions

设计原则

Codelab Adapter的设计追随Smalltalk的设计原则everything is message.

基础结构

消息的基础结构为

{ "topic": "TOPIC", "payload": "MESSAGE" }

注意: 此结构与MQTT消息结构保持一致。

插件消息

插件消息的topic由插件作者自定义,一般而言,由插件名决定。 举几个例子:

extension_eim.py
{"topic": "eim", "payload": "YOUR_MESSAGE"}
extension_python_kernel
{"topic": "eim/python", "payload": "YOUR_MESSAGE"}

以下两种情况,建议 topic直接用eim:

  • 测试阶段
  • 不想构建独立Scratch extension

同步消息插件

基于消息的系统一般都是异步非阻塞的。前头的两个插件都是异步的。

如果你希望基于消息构建同步插件(按顺序、阻塞式地逐个积木执行),可以参考这篇文章,核心思路是在消息结构里加上消息id(Scratch Link也是这么做的)

看几个例子

extension_req_rep.py
{"topic": "eim/reqRep", "payload": "YOUR_MESSAGE"}
extension_cozmo.py
// MESSAGE_ID来自前端
{"topic": "eim/cozmo", "payload": "YOUR_MESSAGE", "messageID": "MESSAGE_ID"}

系统消息

消息是Codelab Adapter的核心机制,Codelab Adapter使用系统消息来构建部分系统功能(有别于插件消息)。

随着功能需求的增长,这类消息会陆续增多。

目前有两个系统消息

  • CONTROL_TOPIC = "__control"
  • WORMHOLE = "__wormhole"

CONTROL_TOPIC用于控制插件的启停, WORMHOLE用于classroom功能(暂未发布,可以忽视)。

CONTROL_TOPIC

CONTROL_TOPIC用于以下几个功能

终止插件

Codelab Adapter 的extension由线程启动。

Python的线程无法像进程和协程一样被管理,目前使用python3-cookbook的建议来管理线程。

但有一个问题,试看extension_eim.py的代码

while self._running:
            read_message = self.read() # 不能有多个read
            topic = read_message.get("topic")
            if topic == self.TOPIC:
                data = read_message.get("payload")
                self.logger.info("eim message:%s", data)

由于self.read()是阻塞的,所以即便在Codelab Adapter里取消勾选停止了插件(self._running变为False),但依然需要scratch里多发一条消息,才能结束self.read()的阻塞,退出while。

所以这种情况下,当用户在Codelab Adapter暂停插件时,会触发一条消息:

{"topic":CONTROL_TOPIC, "payload":"terminate","type":"adapter"}

打破self.read()阻塞。

开启插件

从前端开启插件的消息为:

{
  "topic": CONTROL_TOPIC,
  "type": "web/extension_control",
  "payload": { "action": "turn_on", "extension_name": "extension_eim" }
}

CodeLab Adapter将定时汇报插件状态(这样可以支持多个前端,类似linux GUI与server分离)

{
  "message": {
    "topic": CONTROL_TOPIC,
    "payload": {
      "extension_iot": false,
      "extension_eim": true,
      "extension_vector": false,
      "extension_eim_monitor": false,
      "extension_tello": false,
      "extension_blender": false,
      "extension_mpfshell": false,
      "extension_eim_script": false,
      "extension_python_kernel": false,
      "extension_wechat": false,
      "extension_cozmo": false,
      "extension_usb_microbit": false
    },
    "type": "event/extensions_statu"
  }
}

todo

连接硬件

实现类似Scratch Link的交互:在前端选择硬件。

思路:分析Scratch micro:bit extension与micro:bit通信的细节,模仿它。

分析

在Scratch中选择microbit extension,前端发送发现硬件消息:

{"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":[61445]}]},"id":0}

Scratch Link不断回复:

{"params":{"name":"BBC micro:bit [gazat]","rssi":-32,"peripheralId":"89FC6234-54E4-4E68-8EE0-C947ECF9A292"},"method":"didDiscoverPeripheral","jsonrpc":"2.0"}

点击连接,前端发送消息:

{"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"89FC6234-54E4-4E68-8EE0-C947ECF9A292"},"id":1}

Scratch Link回复:

{"id":1,"result":null,"jsonrpc":"2.0"}

前端发送消息:

{"jsonrpc":"2.0","method":"read","params":{"serviceId":61445,"characteristicId":"5261da01-fa7e-42ab-850b-7c80220097cc","startNotifications":true},"id":2}

之后Scratch Link回复

{"id":2,"result":{"encoding":"base64","message":""},"jsonrpc":"2.0"}
{"params":{"encoding":"base64","message":""},"method":"characteristicDidChange","jsonrpc":"2.0"}
{"params":{"encoding":"base64","message":"\/8oCygAAAAAAAAAAAAAAAAAAAAA="},"method":"characteristicDidChange","jsonrpc":"2.0"}```

...

---


# 参考
*  [分析scratch3.0与micro:bit的通信](https://blog.just4fun.site/scratch3-microbit-analysis.html)
Clone this wiki locally