6.2 公共代码配置API

1. 概述

扩展设计器的一大功能是能够以编程的方式设计积木块的功能。

积木块的在线模式:

  • 对于角色扩展,舞台上可用的积木块使用方式一致;
  • 对于设备扩展,使用时有以下几个步骤:
    • 1) 连接设备
    • 2) 点击进入在线模式(有些设备默认只有在线模式)
    • 3) 拖块编程
    • 4) 点击绿旗/点击积木

积木块的上传模式:

仅针对于硬件功能的积木,使用的步骤如下:

  • 1) 连接硬件
  • 2) 切换进入上传模式(有些设备默认只有上传模式)
  • 3) 拖块编程
  • 4) 上传代码(upload)

事件回调

扩展设计器提供了一些事件入口,诸如加载扩展,设备连接时,积木块运行时,会触发相应的事件。

API

扩展设计器提供了一些与mblock,设备相关的函数,你可以组合以达到所需功能。

2. 基础

设计器提供了与积木块的事件模型与可用的API(此功能入口在 公共代码配置>扩展处理函数),如下:

const ExtHandler = {
    // 事件,当扩展加载进慧编程
    // 参数,app 表示软件相关的API接口
    // target 则表示,与该扩展对应的角色/设备API接口,
    async onLoad(app, target) {
        // app.log为web ide调试窗口下打印的功能,同console.log,
        // target.id 则为该扩展的唯一标志
        app.log('I am loaded, get ID of '+ target.id);       
    }
// ......

设备是所有扩展的容器,你应当区别,主扩展类型和从扩展类型。

一个设备只能有主扩展,和多个从扩展,比如说 codey(id) 是一个主设备扩展,而 codey_neurons 则是 codey 的一个神经元从扩展。

3. 事件回调

事件回调分为两类:

1) block handle event 积木块事件:
包括积木块填加,移除,积木块运行,在下文以 block.* 表示

2) extension handle event 扩展事件:
包括加载,连接,切换状态等,在下文以 extension.* 表示;

block.onAdd

  • 功能: 当帽子头(hat block)被添加到工作区
  • 注意:
    不仅在编辑状态有效,在播放模式也有效,通常是作品加载时,virtual machine 已经加载了积木块,会触发此函数,另外当 device.resetEvents 时,也会重新触发此事件。

例子:

onAdd(app, device, block) {
    app.log("hat block of ${block.opcode} is add to workspace");
}

block.onRemove

  • 功能: 当帽子头(hat block)被移除工作区
  • 注意:
    不仅在编辑状态有效,在播放模式也有效,通常是作品加载时,virtual machine 已经加载了积木块,会触发此函数,另外当 device.resetEvents 时,也会重新触发此事件。

例子:

onRemove(app, device, block) {
    app.log("hat block of ${block.opcode} is remove from workspace");
}

block.onRun

  • 功能: 当积木块运行
  • 注意:根据积木块的类型,帽子头/命令块/值块

例子:

// example of hat block
async onRun(args, app, device, block) {
    app.log("block of ${block.opcode} is checked to trigger");
    if (args.ARGMENT1 > 100) {
        app.log("block of ${block.opcode} is condition satisfied, start this thread running");
        return true;
    } else {
        app.log("block of ${block.opcode} is not condition satisfied, prohabit this thread from running");
        return false;
    }
}

// example of command block
async onRun(args, app, device, block) {
    app.log("block of ${block.opcode} is running");
    // you can use promise to return laterly, or you can return immidiately
    return new Promise((resolve)=>{
        resolve();
    });
}

// example of value block
async onRun(args, app, device, block) {
    app.log("block of ${block.opcode} is running");
    // you can use promise to return laterly, or you can return immidiately
    return new Promise((resolve)=>{
        window.settimeout(()=>app.log("block of ${block.opcode} is stoped then, return value of 100"));
        resolve(100);
    });
}

extension.onLoad

  • 功能:当扩展加载
  • 注意:此函数在加载卸载时可以触发多次,在单次加载扩展时,也可能由于支持多个目标(角色),而每个角色上触发一次。

例子:

async onLoad(app, target) {
    // app.log为web ide调试窗口下打印的功能,同console.log,
    // target.id 则为该扩展的唯一标志
    app.log('I am loaded, get ID of '+ target.id);
}

extension.onUnload

  • 功能:当扩展卸载

例子:

onUnload(app, target) {
    app.log("I am unloading, bye, yours "+ target.id);
},

extension.onStopAll

  • 功能:当mblock里点击红色停止时,或主动调用 stop all 积木块,你可以用在此事件回调里处理设备停止。

例子:

onStopAll(app, device) {
    app.log("stop running");
}

extension.onConnect

  • 功能:当设备连接上
  • 注意:device的通信操作必须在此函数响应之后(你可以用 device.isConnected 判断是否有连接)。

例子:

onConnect(app, device) {
    app.log("${device.id} is connected, you can send data to ${device.id}");
    device.writeText("a text message");
}

extension.onDisconnect

  • 功能:当扩展断连
  • 注意:此时已无法和设备进行通信(你可以用 device.isConnected 判断是否有连接)。

例子:

onDisconnect(app, device) {
    app.log("${device.id} is not connected, you can not communicate to device any more");
}

extension.beforeChangeUploadMode

  • 功能:请求进入上传模式,在这里可以给设备发送数据切换到上传模式。
  • 注意:需和 afterChangeUploadMode 联合使用,当返回 true 时,才会触发 afterChangeUploadMode

例子:

async beforeChangeUploadMode(app, device) {
    // 异步等待,参考javascript es6语法,
    // 使用名为F3F4协议组一条协议
    await device.asyncWriteProtocol('f3f4', [0x0d, 0x00, 0x03]);
    // 在这里假设一定会成功
    return true;
}

extension.afterChangeUploadMode

  • 功能:成功进入上传模式
  • 注意:需和 beforeChangeUploadMode 联合使用,当 beforeChangeUploadMode 返回 true 时,才会导致触发。

例子:

afterChangeUploadMode(app, device) {
    app.log("${device.id} switch to upload mode successfully");
}

extension.beforeChangeDebugMode

  • 功能:请求进入调试(在线模式),在这里可以给设备发送数据切换到调试(在线模式)
  • 注意:需和 afterChangeUploadMode 联合使用,当返回 true 时,才会触发 afterChangeUploadMode

例子:

async beforeChangeDebugMode(app, device) {
    // 异步等待,参考javascript es6语法,
    // 使用名为F3F4协议组一条切换模式协议
    await device.asyncWriteProtocol('f3f4', [0x0d, 0x00, 0x00]);
    // 在这里假设一定会成功
    return true;
}

extension.afterChangeUploadMode

  • 功能:成功进入调试(在线模式)
  • 注意:需和 beforeChangeDebugMode 联合使用,当 beforeChangeDebugMode 返回 true 时,才会导致触发。

例子:

afterChangeUploadMode(app, device) {
    app.log("${device.id} switch to online mode successfully");
}

extension.onSelect

  • 功能:当角色/设备被选中,在多设备的一个项目里,点击切换角色(该角色/设备被编辑)时触发
  • 注意:当设备主扩展加载时,onSelect 会接着触发。

例子:

onSelect(app, device) {
    app.log("switch to ${device.id}");
}

extension.onUnselect

  • 功能:当角色/设备被选中,在多设备的一个项目里,点击切换角色(该角色/设备被编辑)时触发
  • 注意:当切换角色时,onSelect/onUnselect 发生顺序为,先 onUnselect 当前, 再 onSelect 之后。

例子:

onUnselect(app, device) {
    app.log("from ${device.id} switch to other target");
}

extension.onRead

  • 功能:当收到设备数据时
  • 注意:需要通过 device 拿到数据

例子:

onRead(app, device) {
    let anyText = device.readText(false);
    app.log("text receive from ${device.id}: ${anyText}");
}

extension.beforeCodeUpload

  • 功能:上传模式下,准备上传代码
  • 注意:此时还未将代码上传到设备

例子:

// 以下代码实现了,在代码上传前取得并修改代码;
beforeCodeUpload(app, device) {
   let {type, text} = device.getCode();
   app.log("upload code of ${type}: ${text}");
   return "# prepend before code\n" + text;
}

extension.afterCodeUpload

  • 功能:上传模式下,上传代码已完成
  • 注意:此时已将代码上传到设备,即完成上传代码

例子:

onUnselect(app, device) {
    app.log("congratulations! you've uploaded code to ${device.id}");
}

4. API

mblock扩展设计成所有的API都从 app、device、block三个对象中取得(此外你还可以使用除了DOM操作之外的任何 web API, 请查阅相关文档)。

  • app (application): 取得并操作慧编程APP,积木,数据等相关功能;
  • device:获取当前设备相关系统,并读写数据;
  • block: 获取在积木块运行时的信息;

API列表:

app.version

  • 功能:当前 api 的版本
  • 可用版本:≥ v1.0.0
  • 参数:无
  • 返回:string

例子:

app.log(app.version() );

app.log

  • 功能:打印信息到 web 调试窗口,同 console.log
  • 可用版本:≥ v1.0.0
  • 参数:msgs[] 多个输出字串
  • 返回:无

例子:

app.log("blah-blah","no one", "nothing");

app.warn

  • 功能:打印警告到 web 调试窗口,同 console.warn
  • 可用版本:≥ v1.0.0
  • 参数:msgs[] 多个输出字串
  • 返回:无

例子:

app.warn("something dangerous");

app.error

  • 功能:打印错误到 web 调试窗口,同 console.error
  • 可用版本:≥ v1.0.0
  • 参数:msgs[] 多个输出字串
  • 返回:无

例子:

app.warn("fatal is happending!");

app.workspace.broadcast

  • 功能:发起一个慧编程的消息
  • 可用版本:≥ v1.0.0
  • 参数:message: string 消息名
  • 返回:无

例子:

app.workspace.broadcast

app.workspace.getVariable

  • 功能:获取一个慧编程的变量值(只对全局变量名有效)
  • 可用版本:≥ v1.0.0
  • 参数:varName: string 变量名
  • 返回:value: string|number 变量值

例子:

app.log("var:'a', val: ${app.workspace.getVariable('a')}");

app.workspace.setVariable

  • 功能:设置一个慧编程的变量名和值(只对全局变量名有效)
  • 可用版本:≥ v1.0.0
  • 参数:
    varName: string 变量名;
    value: string|number 变量值
  • 返回:Promise<void>

例子:

await app.workspace.setVariable('a', 100);
app.log(app.workspace.getVariable('a'));

app.workspace.onBroadcast

  • 功能:当慧编程收到一个消息(再次设置 handle 将会取消之前一个 handle, 你也可以设置一个null(空)的 handle
  • 可用版本:≥ v1.0.0
  • 参数:
    message: string 消息名,可以是消息名的子串;
    onBroadcaseHandle: (sting)=>void 事件处理函数
  • 返回:无

例子:

app.workspace.onBroadcast('a', (message)=>{
    app.log('receive a message of ', message);
});

app.workspace.runBlocks

  • 功能:执行帽子块,注意,它不一定真正触发帽子块,在执行 <block_handle>.onRun 后,根据返回结果决定是否执行是否执行帽子头(hat block)事件触发
  • 可用版本:≥ v1.0.0
  • 参数:
    opcode: string 积木块ID;
    coldDown?: number 冷却时间 ms(可选参数,应用于触发很频繁时节流(throttle)的情况,默认值为 50ms)
  • 返回:Promise<void>

例子:

await app.workspace.runBlocks('your_extension_id.block_id');

app.workspace.runBlocksInQueue

  • 功能:放在队列(task queue)里执行帽子块,注意,它不一定真正触发帽子块,在执行 .onRun后,根据返回结果决定是否执行帽子头(hat block)事件触发
  • 可用版本:≥ v1.0.0
  • 参数:
    opcode: string 积木块ID;
    queueLength: number 队列长度,(用于节流情况,消息若是很频繁,超出队列长度,将被拒绝);
    onStart: () => void 推入队列时的触发;
    onStop: () => void 从队列取出(处理)的触发
  • 返回:无

例子:

app.workspace.runBlocksInQueue('your_extension_id.block_id', 200, ()=>{
    // aquest(); //you can acquire or suspend here
}, ()=>{
    // release(); // then release after block is triggered or processed
})

app.workspace.disableBlocks

  • 功能:禁用积木块,积木块将会置灰不可用
  • 可用版本:≥ v1.0.0
  • 参数:opcodes: string[] 积木块ID
  • 返回:无

例子:

app.workspace.disableBlocks('your_extension_id.block_1', 'your_extension_id.block_2', 'your_extension_id.block_3')
// you cannot use these blocks from now

app.workspace.enableBlocks

  • 功能:解禁积木块,积木块将会从置灰不可用状态到可用
  • 可用版本:≥ v1.0.0
  • 参数:opcodes: string[] 积木块ID
  • 返回:无

例子:

app.workspace.enableBlocks('your_extension_id.block_1', 'your_extension_id.block_2', 'your_extension_id.block_3')// you can use these blocks again

app.workspace.resetEvents

  • 功能:重置块事件,一般在线模式连接后需要使用,
  • 可用版本:≥ v1.0.0
  • 参数:无
  • 返回:无

例子:

function onConnect(app, device) {
  // sometimes device may disconnect by accident, then communicate state of mblock to device is messed up, you need to reset it.
  app.workspace.resetEvents();
}

app.getService

  • 功能:获取设备服务API,慧编程提供了在不同设备上(web、 PC、mobile)的不同类型API服务(service),这些 API service 以名字获取(TODO service规范)
  • 可用版本:≥ v1.0.0
  • 参数:name: string service名字
  • 返回:service: IService service 对象

例子:

function onConnect(app, device) {
    // ......
    // get account service from mblock, then check if user is login
    let service = app.getService('account');
    let isLogin = await service.isLogin();
}

app.registService

  • 功能:注册设备服务API,慧编程提供了在不同设备上(web, PC, mobile)的不同类型API服务(service),这些API service可以通过名字获取(TODO service规范)
  • 可用版本:≥ v1.0.0
  • 参数:service: IService service 对象
  • 返回:无

例子:

// one file ......
app.registService({id:'timer', reset:function(){this._start =  Date.now();}, last:function(){return (Date.now()-this._start);}  }
// other file .....
let timer = app.getService('timer');
timer.reset();
// .....
app.log("you've running abount ${timer.last()} millisecond");

app.sendMessage

  • 功能:跨不同扩展的事件发送(与 app.workspace.broadcast 不同,此消息不与mblock交互)
  • 可用版本:≥ v1.0.0
  • 参数:msgName: string 消息名;datas: any[] 消息数据
  • 返回:无

例子:

// in one file ......
app.sendMessage("echo", 1234);
// in other files ......
app.subscribeMessage ("echo", (...datas)=>{ app.sendMessage("echo back", ...datas);} )

app.subscribeMessage

  • 功能:跨不同扩展的事件接收,不可迭加,该次注册会把上次注册取消,也可用 app.subscribeMessage('msgName', null) 取消;
  • 可用版本:≥ v1.0.0
  • 参数:msgName: string 消息名;handle: function 事件响应函数
  • 返回:无

例子:

// in one file ......
app.sendMessage("echo", 1234);
// in other files ......
app.subscribeMessage ("echo", (...datas)=>{ app.sendMessage("echo back", ...datas);} )

app.receiveMessage

  • 功能:跨不同扩展的事件接收,异步等待(async/await)返回
  • 可用版本:≥ v1.0.0
  • 参数:msgName: string 消息名
  • 返回:Promise<any[]> 异步返回的数据

例子:

// in one file ......
app.sendMessage("echo", "balabala", 1234);
// in other files ......
let datas = app.receiveMessage("echo"));
app.sendMessage("echo back", ...datas);
app.log('received datas of ${datas}');

target.id / device.id

  • 功能:获取 target 名字,和 mblock 显示的角色名字一致
  • 可用版本:≥ v1.0.0
  • 参数:无
  • 返回:string 名字

例子:

app.log("current target name is ${target.id}");

device.isSelected

  • 功能:是否是当前选中/编辑的角色
  • 可用版本:≥ v1.0.0
  • 参数:无
  • 返回:string 名字

例子:

if (device.isSelected() ) {
    app.log("${device.name} is current target");
}

device.getCode

  • 功能:是否是当前选中/编辑的角色
  • 可用版本:≥ v1.0.0
  • 参数:无
  • 返回:ICode 包含 typetext 字段,见例子

例子:

let {type, text} = device.getCode();
app.log("code of ${type}: ${text}");

device.switchChannel

  • 功能:切换数据通道,因为mblock在运行时,可能有多处收发数据,可用此功能实现,实现不受其它数据干扰的数据传送,注意,不要长时间持有,使用完需要把 channel 恢复,默认的 channel为 ""
  • 可用版本:≥ v1.0.0
  • 参数:string 名字
  • 返回:无

例子:

device.switchChannel('upload');
// write raw datas to device without being interrupted
device.writeRaw(new Uint8Array([0xff, 0xff]), 'upload');
// you must restore channel after use
device.switchChannel('');

device.writeRaw

  • 功能:通过串口/蓝牙,向 device 写字节数据
  • 可用版本:≥ v1.0.0
  • 参数:
    data: Uint8Array 字节数据(byte array);
    channel?: string 通道名字,此为可选参数,默认为 ""
  • 返回:无

例子:

device.writeRaw(new Uint8Array([0xff, 0xff]) );

device.readRaw

  • 功能:通过串口/蓝牙,从缓冲中拿到字节数据
  • 可用版本:≥ v1.0.0
  • 参数:consume? : bool 是否从缓冲中清除,默认值是 true
  • 返回:U

例子:

let datas1 = device.readRaw(false);
for (let i = 0; i<datas1.length; ++i) {
    app.log("datas1[${i}]", datas1[i]);
}
// no consuming in getting data1, so data2 is the same to data1
let datas2 = device.readRaw(false);
for (let i = 0; i<datas2.length; ++i) {
    app.log("datas2[${i}]", datas2[i]);
}

device.setTextDecoder

  • 功能:设置文字解码类型
  • 可用版本:≥ v1.0.0
  • 参数:decoderType : 'utf8' | 'ascii' 解码类型
  • 返回:无

例子:

device.setTextDecoder('utf8');

device.writeText

  • 功能:向设备写文字数据
  • 可用版本:≥ v1.0.0
  • 参数:text: string 文字;channel?: string 通道名, 可选,默认为 ""
  • 返回:Uint8Array

例子:

device.setTextDecoder('utf8');
device.writeText('text文字');

device.readText

  • 功能:从缓冲读入文字数据,注意:立即返回,取得已经读入缓冲的数据
  • 可用版本:≥ v1.0.0
  • 参数:consume?: bool 是否从缓冲中取出并消耗, 可选,默认为 true
  • 返回:string

例子:

device.setTextDecoder('utf8');
let text = device.readText();
app.log('text received from device', text);

device.writeHex

  • 功能:向串口写字节数据,以十六进制字串表示
  • 可用版本:≥ v1.0.0
  • 参数:text: string 十六进制字串;channel?: string 通道名, 可选,默认为 ""
  • 返回:无

例子:

device.writeHex('eeff00');

device.readHex

  • 功能:从缓冲读入字节数据,以十六进制字串表示
  • 可用版本:≥ v1.0.0
  • 参数:consume?: bool 是否从缓冲中取出并消耗, 可选,默认为 true
  • 返回:string

例子:

let hexStr = device.readHex();
app.log('hex received from device', hexStr);

device.registProtocol

  • 功能:注册一种协议实现(参考如何自定义协议
  • 可用版本:≥ v1.0.0
  • 参数:protocol: IProtocol 协议实现(TODO)
  • 返回:string

device.asyncReadProtocol

  • 功能:异步读取协议,当满足协议格式时返回,(详细请参考如何进行协议通信
  • 可用版本:≥ v1.0.0
  • 参数:
    protocolName: string 协议名
    pattern: any[] 匹配格式
    timeout? : number 等待超时,单位 ms,可选,默认 3000ms
  • 返回:Promise<any[]>

例子:

let datas = await device.asyncReadProtocol('your_protocol', ["ff55", 'string', 'float']);
if(datas == null) {
    app.log('receive nothing or not a valid protocol');
} else {
    app.log('receive datas, string of ${datas[0]}, float of ${datas[1]}');
}

device.asyncWriteProtocol

  • 功能:写数据,并包装成实际协议,注意是异步函数,需要等待, (详细请参考如何进行协议通信
  • 可用版本:≥ v1.0.0
  • 参数:
    protocolStr: string 协议名
    pattern: any[] 格式
    channel?: string 通道,见 device.writeRaw
  • 返回:Promise<void>

例子:

await device.asyncWriteProtocol('your_protocol', ["ff55", ['string', 'some text'], ['float', 1.1]]);

device.subscribeReadProtocol

  • 功能:异步读取协议,当满足协议格式时的触发回调(详细请参考如何进行协议通信
  • 可用版本:≥ v1.0.0
  • 参数:
    protocolName: string 协议名
    pattern: any[] 匹配格式
    handler: (datas: any[]) => void 当满足协议格式时触发回调
  • 返回:()=>void 撤销注册

例子:

device.subscribeReadProtocol('your_protocol', ["ff55", 'string', 'float'], (datas)=>{
    app.log('receive datas, string of ${datas[0]}, float of ${datas[1]}');
});

device.isConnected

  • 功能:是否设备已连接
  • 可用版本:≥ v1.0.0
  • 参数:无
  • 返回:bool

例子:

onConnect(app, device) {
  if(!device.isConnected()){
    app.error('unreachable!');
  }
}

device.isUploadMode

  • 功能:是否上传模式,否则是在线模式
  • 可用版本:≥ v1.0.0
  • 参数:无
  • 返回:bool

例子:

afterChangeUploadMode (app, device) {
  if(!device.isUploadMode()){
    app.error('unreachable!');
  }
}

device.uploadFirmware

  • 功能:上传固件(TODO:见上传固件)
  • 可用版本:≥ v1.0.0
  • 参数:data:ArrayBuffer
  • 返回:promise<void>

例子:

// internal codes implement of uploading firmware in mblock
function handle(app, device, info) {
            var uploaderUI = app.getService('system.ui.firmware.uploader');
            if (uploaderUI) {
              uploaderUI.onOK(function (data) {
                device.uploadFirmware(data);
              });
              uploaderUI.onFail(function (err) {
                console.error(err);
              });
              uploaderUI.open(info);
            }
}

block.opcode

  • 功能:积木块的 opcode(类型)
  • 可用版本:≥ v1.0.0
  • 参数:无
  • 返回:string

例子:

onRun(args, app, device, block) {
    app.log('ready to run block type of ', block.opcode);
    // ......
}

block.id

  • 功能:积木块的ID
  • 可用版本:≥ v1.0.0
  • 参数:无
  • 返回:string

例子:

onRun(args, app, device, block) {
    app.log('ready to run block id of ', block.id);
    // ......
}

results matching ""

    No results matching ""