如何开发一个 CodeLab Adapter 插件
文章目录
前言
本文讨论如何开发一个 CodeLab Adapter 插件(基于 Python)。
CodeLab Adapter 自带一个消息系统,理论上,任何语言都可以与之通信,任何有开放接口的事物都可以接入其中。
本文仅讨论如何在 Scratch 上构建客户端(Scratch Extension,基于 JavaScript),使其与 CodeLab Adapter 通信(收发消息)来扩展 Scratch 的能力。当然,你也可以在任何语言中做这件事。
思路
一个 Adapter 插件(plugin)被视为 Adapter 系统的一个节点(Node), 通过这些节点去适配不同的外部硬件/软件,进而将其接入到系统中。
在系统中,流动的一切都是消息,所以由这些插件连接的事物(软件/硬件)可以彼此沟通(talk),系统得以持续生长。
希望使用 Adapter 某个插件的能力时(如在 Scratch 中),只需要发送消息与 Adapter 对话即可。
开始
案例(Tello)
本文采用案例式教学。
近期我们重写了 Adapter Tello 插件,本文将以此为例,介绍 开发一个 CodeLab Adapter 插件 的典型流程。
该流程是完全通用的。
如何交互?
首先考虑第一个问题:你想接入什么东西?与之交互的方式是什么?(Adapter 是一个利用消息不停交互的系统)
如果你想接入的东西是硬件(如 Tello),那么与之通信的方式可能是调用它们的开放 SDK 。
如果你想接入的东西是软件(如 Teachable Machine),那么与之通信的方式可能是基于某些标准协议(如 http/websocket).
如果你想接入的东西是一门编程语言的内核(如 Python),那么与之通信的方式可能是 eval
寻找 SDK
在本文中,我们 想接入什么东西 是 Tello。 我们在 Github 上找到与之通信的 Python SDK: DJITelloPy
与 Tello 通信的方式是利用 socket, DJITelloPy封装了细节,使我们可以以面向对象的风格与之交互, 我们来一撇 SDK 的使用方式。
|
|
语义清晰,非常简易。
构建 Adapter 插件
一个 Adapter 插件不是一个孤岛,它试图与其他事物交谈(talk), 对外部的请求做出回应。
实现这件事的方式很多,软件工程有大量工作围绕这块: 对请求作出回应,提供服务,我们会想到 RESTful API、RPC…
Adapter 如果完成以上目标? 我们采取的策略是: 收发消息。我们把一切看作消息, 并且倾向于晚绑定(late binding)
回到正题。我们先来快速浏览一下 Tello 插件的代码(不必弄懂它,稍后会讲解),连同注释和模版代码,一共才 64 行。
|
|
你可以使用 Adapter 内置的 JupyterLab 浏览/修改 这些插件源码, 保存并重启插件之后,即刻生效(不需要重启 Adapter)
我们来看看 tello 插件各部分代码的含义和功能是什么。
什么?这就结束了?
是的这就结束了。
可是,都没有见到跟 Tello 有关的业务逻辑啊?
是的,这正是我们想法的核心部分: 晚绑定(late binding)
, 将功能描述不断后推,交给 client(甚至是用户)。
Adapter Tello 插件看起来颇像一个 REPL,它解释(run_python_code)收到的消息(副作用是 tello 飞行器的行为), Tello 的行为将由输入的消息决定,消息携带语义。我们贪图便利,直接将 Python 代码视为消息(因其能很好携带语义)
客户端
接下来我们来构建一个客户端来使用 Adapter Tello 插件。
前头提到,我们计划在 Scratch 里构建一个客户端,它是一个 Scratch Extension。
Scratch Extension
如果你对构建 Scratch Extension 不熟悉,请参考: 创建你的第一个 Scratch3.0 Extension
我们已经将 Scratch Tello 插件开放在这儿: scratch3_tello2
如何交互(talk)?
前头提到:
一个 Adapter 插件不是一个孤岛,它试图与其他事物交谈(talk), 对外部的请求做出回应。
Scratch Tello 插件(JavaScript)是如何与 Adapter 插件(Python)交互的呢?
它们通过 websocket(socketio)沟通, 但你不需要在意和弄懂它们沟通的细节,我们已经构建了一个 Adapter js client,抽象掉了 talk 的细节,让你可以基于它轻松在 js 里与 Adapter 交互。 (注意:你的开发环境里,需要有scratch3_eim)
源码解读
接下来,一起深入到源码里看看。
我们通过阐述这两块积木,来看看引擎盖后发生的事情。
首先看看,当我们 起飞 积木运行的时候发生了什么:
实际上,当 Scratch 中, 起飞 积木运行时,消息 tello.takeoff()
将发送到 Adapter Tello 插件,插件将解释这则消息– eval(执行)这段 Python 代码。
接着我们来看看 设置速度 积木(带有参数)运行的时候发生了什么:
可以看出,我们试图将参数拼凑到 Python 代码里。
this.client.emit_with_messageid
是与 Adapter 通信的关键,这部分也很简单,只是发送消息,如果你兴趣不大,不需要弄懂它, 将其视为模版代码,跟着既有的插件(我们开放了插件)填空即可。
需要注意的是,消息并不一定是 Python 代码,它只要携带语义就行。
发布 Adapter 插件
如果你构建了新的 Adapter 插件,欢迎提交到插件市场
调试
为了方便开发 Adapter 插件,一些调试技巧可能对你有用
进阶 && 进一步阅读
- scratch3_python_kernel
- scratch3_usb_microbit
- scratch3_microbit_radio
- Python 对象的连接器:EIM 插件
- scratch3_cozmo
- Scratch 拓展最佳实践 – 以 Cozmo 为例
FAQ
如何以 Scratch 的风格连接硬件?
这部分主要是通过与 Scratch 的 runtime 交互实现的,更多细节参考以下两个Scratch 插件的 scan
函数
参考:
如何刷入自定义固件
Adatper 内置了哪些第三方库
如何引入新的 Python 第三方库
Adapter 允许再分发, 把需要的第三方库放在相应目录下,再分发即可
放在目录下即可,再分发
更多 FAQ
参考
文章作者 种瓜
上次更新 2020-11-18