6.2 Common Code Settings

1. Overview

In mBlock Extension Builder, the function of each block can be defined through programming.

Live mode:

  • Sprites extension blocks work the same as other default blocks
  • Device extension blocks require the following steps:
    • 1) Connect device
    • 2) Toggle on "Live" mode (some device has "Live" mode as the default mode)
    • 3) Program (drag and drop blocks to the Script Area)
    • 4) Click the green flag or blocks

Upload mode:

For device blocks, the following steps are required:

  • 1) Connect device
  • 2) Toggle on "Upload" mode (some device has "Live" mode as the default mode)
  • 3) Program (drag and drop blocks to the Script Area)
  • 4) Upload the program to the device

Events callback

The mBlock Extension Builder provides some events entries to trigger corresponding events in circumstances such as extension being loaded, device being connected, scripts being run, etc.

API

The mBlock Extension Builder provides a number of functions about mBlock or devices. You can choose and combine different functions to realize your goals.

2. Basics

The mBlock Extension Builder provides useful API about events related blocks (under Common code settings > Extension handler function).

Following is an example:

const ExtHandler = {
    // Event, when extension is being loaded
    // Parameter, app refers to related API
    // Target, the corresponding device/sprite of the extension
    async onLoad(app, target) {
        // app.log works as the print fucntion in web ide, the same as console.log,
        // target.id, ID of the extension
        app.log('I am loaded, get ID of '+ target.id);       
    }
// ......

Device is the holder of extensions. There are two type of extensions: main extension, and sub extension.

A device can only have one main extension, yet multiple sub extensions. Take Codey Rocky as an example: codey(id) is the main extension, yet codey_neurons is a sub extension of codey.

3. Events callback

Events callback includes two types:

1) block handle event:
concerns the adding, deleting, or running of blocks, displayed as block. in the following sections.

2) extension handle event:

Concerns loading, connection, or mode switching, displayed as extension. in the following sections.

block.onAdd

  • Function: when a Hat block is added to workspace
  • Note:
    Valid during editing or in "Live" mode. Usually, the function is triggered when a project is being loaded, during which process virtual machine has already loaded blocks, or when device.resetEvents is triggered.

Example:

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

block.onRemove

  • Function: when a Hat block is removed from workspace
  • Note:
    Valid during editing or in "Live" mode. Usually, the function is triggered when a project is being loaded, during which process virtual machine has already loaded blocks, or when device.resetEvents is triggered.

Example:

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

block.onRun

  • Function: when blocks being run
  • Note: based on types of blocks (hat/command/value)

Example:

// 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

  • Function: when the extension is being loaded
  • Note: the function can be triggered multiple times during loading; a single loading including multiple targets also lead to multiple times of triggering

Example:

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

extension.onUnload

  • Function: when the extension is being unloaded

Example:

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

extension.onStopAll

  • Function: stops all scripts when the stop sign under the stage is clicked or the stop all block is called

Example:

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

extension.onConnect

  • Function: when the device is connected
  • Note: communication with the device is available when this function works (use device.isConnected to confirm successful connection)

Example:

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

extension.onDisconnect

  • Function: when the device is disconnected
  • Note: communication with the device is unavailable (use device.isConnected to detect any established connection)

Example:

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

extension.beforeChangeUploadMode

  • Function: request of switching to upload mde
  • Note: this function is used with afterChangeUploadMode; when the returned value is true, afterChangeUploadMode is then triggered

Example:

async beforeChangeUploadMode(app, device) {
    // Asynchronous waiting, referring to JavaScript es6 for more information
    // Use a piece of F3F4 protocol
    await device.asyncWriteProtocol('f3f4', [0x0d, 0x00, 0x03]);
    // Presumed success
    return true;
}

extension.afterChangeUploadMode

  • Function: successfully switch to upload mode
  • Note: this function should be used with beforeChangeUploadMode, when beforeChangeUploadMode returns true, the function is then triggered

Example:

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

extension.beforeChangeDebugMode

  • Function: request of switching to live mode
  • Note: this function is used with afterChangeUploadMode, when the returned value is true, afterChangeUploadMode is then triggered

Example:

async beforeChangeDebugMode(app, device) {
    // Asynchronous waiting, referring to JavaScript es6 for more information
    // Use a piece of F3F4 protocol
    await device.asyncWriteProtocol('f3f4', [0x0d, 0x00, 0x00]);
    // Presumed success
    return true;
}

extension.afterChangeUploadMode

  • Function: successfully switch to upload mode
  • Note: this function is used with beforeChangeDebugMode; when beforeChangeDebugMode returns true, the function is then triggered

Example:

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

extension.onSelect

  • Function: triggered when selection of device or sprite is done when a project involves multiple devices/sprites
  • Note: when the main extension is triggered, onSelect will also be triggered

Example:

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

extension.onUnselect

  • Function: triggered when selection of device or sprite is done when a project involves multiple devices/sprites
  • Note: during the selection, the oder of onSelect/onUnselect is onUnselect first and then onSelect

Example:

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

extension.onRead

  • Function: when receiving data from the device
  • Note: data need to be collected through device

Example:

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

extension.beforeCodeUpload

  • Function: under upload mode, when program is ready to upload
  • Note: the program has not yet uploaded to the device

Example:

// The following code fetches and edits the program before uploading 
beforeCodeUpload(app, device) {
   let {type, text} = device.getCode();
   app.log("upload code of ${type}: ${text}");
   return "# prepend before code\n" + text;
}

extension.afterCodeUpload

  • Function: program uploaded successfully under "Upload" mode
  • Note: program has been uploaded to the device

Example:

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

4. API

All API can be called from three objects: app, device, and block.

You might also use other Web API (DOM operation excluded).

  • app (application): functions about app, block, or data of mBlock 5
  • device: functions about current device, to read/write data
  • block: functions about running blocks

API List:

app.version

  • Function: current api version
  • Available verion: ≥ v1.0.0
  • Parameter: none
  • Return: string

Example:

app.log(app.version() );

app.log

  • Function: prints data to Web Debugging window, the same as console.log
  • Available verion: ≥ v1.0.0
  • Parameter: msgs[] multiple output strings
  • Return: none

Example:

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

app.warn

  • Function: print warning message to Web Debugging window, the same as console.warn
  • Available verion: ≥ v1.0.0
  • Parameter: msgs[] multiple output strings
  • Return: none

Example:

app.warn("something dangerous");

app.error

  • Function: print error message to Web Debugging window, the same as console.error
  • Available verion: ≥ v1.0.0
  • Parameter: msgs[] multiple output strings
  • Return: none

Example:

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

app.workspace.broadcast

  • Function: broadcasts a piece of message in mBlock 5
  • Available verion: ≥ v1.0.0
  • Parameter: message: string message name
  • Return: none

Example:

app.workspace.broadcast

app.workspace.getVariable

  • Function: obtains the value of a certain variable (valid for global variable only)
  • Available verion: ≥ v1.0.0
  • Parameter: varName: string variable name
  • Return: value: string|number variable value

Example:

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

app.workspace.setVariable

  • Function: sets the name and value of a variable (valid for global variable only)
  • Available verion: ≥ v1.0.0
  • Parameter:
    varName: string variable name
    value: string|number variable value
  • Return: Promise<void>

Example:

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

app.workspace.onBroadcast

  • Function: when a message broadcast is received (a new handle will overwrite previous handle, a nul handle also plausible)
  • Available verion: ≥ v1.0.0
  • Parameter:
    message: string message name or the sub string of message
    onBroadcaseHandle: (sting)=>void events handling function
  • Return: none

Example:

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

app.workspace.runBlocks

  • Function: executes a hat block (note: this functions runs according to the return of <block_handle>.onRun)
  • Available verion: ≥ v1.0.0
  • Parameter:
    opcode: string block ID
    coldDown?: number cooldown time ms (optional parameter, used in frequent throttle, with a default value of 50ms)
  • Return: Promise<void>

Example:

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

app.workspace.runBlocksInQueue

  • Function: executes a hat block in a task queue (note: this functions runs according to the return of <block_handle>.onRun)
  • Available verion: ≥ v1.0.0
  • Parameter:
    opcode: string block ID
    queueLength: number length of the queue (for throttling, frequent messaging beyond queue length will be rejected)
    onStart: () => void triggered when in the queue
    onStop: () => void triggered when out of the queue
  • Return: none

Example:

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

  • Function: disables a block (displays as grey color and not selectable in the block palette)
  • Available verion: ≥ v1.0.0
  • Parameter: opcodes: string[] block ID
  • Return: none

Example:

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

  • Function: enables a block (previous disabled)
  • Available verion: ≥ v1.0.0
  • Parameter: opcodes: string[] block ID
  • Return: none

Example:

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

  • Function: resets the event (usually used in "Live" mode)
  • Available verion: ≥ v1.0.0
  • Parameter: none
  • Return: none

Example:

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

  • Function: calls device service API (web, pc, mobile), available as different names
  • Available verion: ≥ v1.0.0
  • Parameter: name: string service name
  • Return: service: IService service target

Example:

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

  • Function: calls device (registered) service API (web, pc, mobile), available as different names
  • Available verion: ≥ v1.0.0
  • Parameter: service: IService service target
  • Return: none

Example:

// 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 about ${timer.last()} millisecond");

app.sendMessage

  • Function: events across multiple extensions (different from app.workspace.broadcast, no communication with mBlock)
  • Available verion: ≥ v1.0.0
  • Parameter: msgName: string message name;datas: any[] message value
  • Return: none

Example:

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

app.subscribeMessage

  • Function: message receiving across multiple extension, add-on not available (new registration always overwrites previous one; use app.subscribeMessage('msgName', null) to cancel a registration)
  • Available verion: ≥ v1.0.0
  • Parameter: msgName: string message name; handle: function message respond
  • Return: none

Example:

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

app.receiveMessage

  • Function: events receiving across multiple extension, async/await return
  • Available verion: ≥ v1.0.0
  • Parameter: msgName: string message name
  • Return: Promise<any[]> data of async/await return

Example:

// 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

  • Function: obtains target name, the same name displayed in mBlock
  • Available verion: ≥ v1.0.0
  • Parameter: none
  • Return: string name

Example:

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

device.isSelected

  • Function: whether a sprite is currently selected or being edited
  • Available verion: ≥ v1.0.0
  • Parameter: none
  • Return: string name

Example:

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

device.getCode

  • Function: whether a sprite is currently selected or being edited
  • Available verion: ≥ v1.0.0
  • Parameter: none
  • Return: ICode includes type and text (see example)

Example:

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

device.switchChannel

  • Function: switches data channel (during running, mBlock involves multiple tasks of data receiving or sending; manually reset to default channel "" when done)
  • Available verion: ≥ v1.0.0
  • Parameter: string name
  • Return: none

Example:

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

  • Function: write byte data to device via serial port or bluetooth
  • Available verion: ≥ v1.0.0
  • Parameter:
    data: Uint8Array byte array)
    channel?: string channel name, customizable, default ""
  • Return: none

Example:

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

device.readRaw

  • Function: reads byte data from the buffer via serial port or bluetooth
  • Available verion: ≥ v1.0.0
  • Parameter: consume? : bool whether to delete from the buffer, default true
  • Return: U

Example:

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

  • Function: sets text decode standard
  • Available verion: ≥ v1.0.0
  • Parameter: decoderType : 'utf8' | 'ascii' decoder type
  • Return: none

Example:

device.setTextDecoder('utf8');

device.writeText

  • Function: writes text data to device
  • Available verion: ≥ v1.0.0
  • Parameter: text: string text; channel?: string channel name, customizable, default ""
  • Return: Uint8Array

Example:

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

device.readText

  • Function: reads text from the buffer (immediate reading)
  • Available verion: ≥ v1.0.0
  • Parameter: consume?: bool whether to delete from the buffer, customizable, default true
  • Return: string

Example:

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

device.writeHex

  • Function: writes hex data to serial port
  • Available verion: ≥ v1.0.0
  • Parameter: text: string hex strings; channel?: string channel name, customizable, default ""
  • Return: none

Example:

device.writeHex('eeff00');

device.readHex

  • Function: reads hex data from the buffer
  • Available verion: ≥ v1.0.0
  • Parameter: consume?: bool whether to delete from the buffer, customizable, default true
  • Return: string

Example:

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

device.registProtocol

  • Function: register a protocol (refer to Customize protocol)
  • Available verion: ≥ v1.0.0
  • Parameter: protocol: IProtocol TODO
  • Return: string

device.asyncReadProtocol

  • Function: asynce/await protocol, returns when it is true (refer to Communication protocol)
  • Available verion: ≥ v1.0.0
  • Parameter:
    protocolName: string protocol name
    pattern: any[] matching pattern
    timeout? : number timeout, measured in ms customizable, default 3000ms
  • Return: Promise<any[]>

Example:

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

  • Function: writes data and packs into actual protocol (async/await functions, require waiting; refer to Communication protocol)
  • Available verion: ≥ v1.0.0
  • Parameter:
    protocolStr: string protocol name
    pattern: any[] matching patterns
    channel?: string channel, refer to device.writeRaw
  • Return: Promise<void>

Example:

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

device.subscribeReadProtocol

  • Function: reads protocol asynchronously, callback when patterns match (refer to Communication protocol)
  • Available verion: ≥ v1.0.0
  • Parameter:
    protocolName: string protocol name
    pattern: any[] matching patterns
    handler: (datas: any[]) => void triggers callback when patterns matched
  • Return: ()=>void cancel registration

Example:

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

device.isConnected

  • Function: whether the device is connected
  • Available verion: ≥ v1.0.0
  • Parameter: none
  • Return: bool

Example:

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

device.isUploadMode

  • Function: whether it is "Upload" mode
  • Available verion: ≥ v1.0.0
  • Parameter: none
  • Return: bool

Example:

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

device.uploadFirmware

  • Function: uploads firmware
  • Available verion: ≥ v1.0.0
  • Parameter: data:ArrayBuffer
  • Return: promise<void>

Example:

// 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

  • Function: opcode of the block
  • Available verion: ≥ v1.0.0
  • Parameter: none
  • Return: string

Example:

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

block.id

  • Function: block ID
  • Available verion: ≥ v1.0.0
  • Parameter: none
  • Return: string

Example:

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

results matching ""

    No results matching ""