Add simulator
This commit is contained in:
381
sim/libmbit.ts
Normal file
381
sim/libmbit.ts
Normal file
@ -0,0 +1,381 @@
|
||||
/// <reference path="../node_modules/kindscript/typings/bluebird/bluebird.d.ts"/>
|
||||
/// <reference path="../node_modules/kindscript/built/kindsim.d.ts"/>
|
||||
|
||||
namespace ks.rt.micro_bit {
|
||||
export function initCurrentRuntime() {
|
||||
initBoard();
|
||||
}
|
||||
|
||||
ks.rt.initCurrentRuntime = initCurrentRuntime;
|
||||
|
||||
function initBoard() {
|
||||
U.assert(!runtime.board)
|
||||
runtime.board = new Board()
|
||||
}
|
||||
|
||||
export function board() {
|
||||
return runtime.board as Board
|
||||
}
|
||||
|
||||
export function enums() {
|
||||
return runtime.enums as any as Enums
|
||||
}
|
||||
|
||||
export interface AnimationOptions {
|
||||
interval: number;
|
||||
// false means last frame
|
||||
frame: () => boolean;
|
||||
whenDone?: (cancelled: boolean) => void;
|
||||
}
|
||||
|
||||
export class AnimationQueue {
|
||||
private queue: AnimationOptions[] = [];
|
||||
private process: () => void;
|
||||
|
||||
constructor(private runtime: Runtime) {
|
||||
this.process = () => {
|
||||
let top = this.queue[0]
|
||||
if (!top) return
|
||||
if (this.runtime.dead) return
|
||||
runtime = this.runtime
|
||||
let res = top.frame()
|
||||
runtime.queueDisplayUpdate()
|
||||
runtime.maybeUpdateDisplay()
|
||||
if (res === false) {
|
||||
this.queue.shift();
|
||||
// if there is already something in the queue, start processing
|
||||
if (this.queue[0])
|
||||
setTimeout(this.process, this.queue[0].interval)
|
||||
// this may push additional stuff
|
||||
top.whenDone(false);
|
||||
} else {
|
||||
setTimeout(this.process, top.interval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public cancelAll() {
|
||||
let q = this.queue
|
||||
this.queue = []
|
||||
for (let a of q) {
|
||||
a.whenDone(true)
|
||||
}
|
||||
}
|
||||
|
||||
public cancelCurrent() {
|
||||
let top = this.queue[0]
|
||||
if (top) {
|
||||
this.queue.shift();
|
||||
top.whenDone(true);
|
||||
}
|
||||
}
|
||||
|
||||
public enqueue(anim: AnimationOptions) {
|
||||
if (!anim.whenDone) anim.whenDone = () => { };
|
||||
this.queue.push(anim)
|
||||
// we start processing when the queue goes from 0 to 1
|
||||
if (this.queue.length == 1)
|
||||
this.process()
|
||||
}
|
||||
|
||||
public executeAsync(anim: AnimationOptions) {
|
||||
U.assert(!anim.whenDone)
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
anim.whenDone = resolve
|
||||
this.enqueue(anim)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function panic(code: number) {
|
||||
console.log("PANIC:", code)
|
||||
throw new Error("PANIC " + code)
|
||||
}
|
||||
|
||||
/* basic */
|
||||
export function showDigit(v: number) {
|
||||
if (!quiet)
|
||||
console.log("DIGIT:", v)
|
||||
}
|
||||
|
||||
export function clearScreen() {
|
||||
board().image.clear();
|
||||
runtime.queueDisplayUpdate()
|
||||
}
|
||||
|
||||
export function showLeds(leds: micro_bit.Image, delay: number): void {
|
||||
showAnimation(leds, delay);
|
||||
}
|
||||
|
||||
function scrollImage(leds: micro_bit.Image, interval: number, stride: number): void {
|
||||
let cb = getResume()
|
||||
let off = stride > 0 ? 0 : leds.width - 1;
|
||||
|
||||
board().animationQ.enqueue({
|
||||
interval: interval,
|
||||
frame: () => {
|
||||
if (off >= leds.width || off < 0)
|
||||
return false;
|
||||
let c = Math.min(5, leds.width - off);
|
||||
leds.copyTo(off, 5, board().image, 0)
|
||||
off += stride;
|
||||
return true;
|
||||
},
|
||||
whenDone: cb
|
||||
})
|
||||
}
|
||||
|
||||
export function showAnimation(leds: micro_bit.Image, interval: number = 400): void {
|
||||
scrollImage(leds, interval, 5);
|
||||
}
|
||||
|
||||
export function scrollNumber(x: number, interval: number) {
|
||||
if (interval < 0) return;
|
||||
|
||||
let leds = createImageFromString(x.toString());
|
||||
if (x < 0 || x >= 10) scrollImage(leds, interval, 5);
|
||||
else showLeds(leds, interval * 5);
|
||||
}
|
||||
|
||||
export function scrollString(s: string, interval: number) {
|
||||
if (interval < 0) return;
|
||||
if (s.length == 0) {
|
||||
clearScreen();
|
||||
pause(interval * 5);
|
||||
} else {
|
||||
let leds = createImageFromString(s);
|
||||
if (s.length == 1) showLeds(leds, interval * 5)
|
||||
else scrollImage(leds, interval, 1);
|
||||
}
|
||||
}
|
||||
|
||||
export function forever(a: RefAction) {
|
||||
function loop() {
|
||||
runtime.runFiberAsync(a)
|
||||
.then(() => Promise.delay(20))
|
||||
.then(loop)
|
||||
.done()
|
||||
}
|
||||
incr(a)
|
||||
loop()
|
||||
}
|
||||
|
||||
export var pause = thread.pause;
|
||||
|
||||
/* leds */
|
||||
export function plot(x: number, y: number) {
|
||||
board().image.set(x, y, 255);
|
||||
runtime.queueDisplayUpdate()
|
||||
}
|
||||
|
||||
export function unPlot(x: number, y: number) {
|
||||
board().image.set(x, y, 0);
|
||||
runtime.queueDisplayUpdate()
|
||||
}
|
||||
|
||||
export function point(x: number, y: number): boolean {
|
||||
return !!board().image.get(x, y);
|
||||
}
|
||||
|
||||
export function brightness(): number {
|
||||
return board().brigthness;
|
||||
}
|
||||
|
||||
export function setBrightness(value: number): void {
|
||||
board().brigthness = value;
|
||||
runtime.queueDisplayUpdate()
|
||||
}
|
||||
|
||||
export function stopAnimation(): void {
|
||||
board().animationQ.cancelAll();
|
||||
}
|
||||
|
||||
export function plotLeds(leds: micro_bit.Image): void {
|
||||
leds.copyTo(0, 5, board().image, 0)
|
||||
runtime.queueDisplayUpdate()
|
||||
}
|
||||
|
||||
export function setDisplayMode(mode: DisplayMode): void {
|
||||
board().displayMode = mode;
|
||||
runtime.queueDisplayUpdate()
|
||||
}
|
||||
|
||||
/* control */
|
||||
export var runInBackground = thread.runInBackground;
|
||||
|
||||
/* serial */
|
||||
export function serialSendString(s: string) {
|
||||
board().writeSerial(s);
|
||||
}
|
||||
|
||||
export function serialReadString() : string {
|
||||
return board().readSerial();
|
||||
}
|
||||
|
||||
/* input */
|
||||
export function onButtonPressed(button : number, handler: RefAction) : void {
|
||||
let ens = enums();
|
||||
board().bus.listen(button, ens.MICROBIT_BUTTON_EVT_CLICK, handler);
|
||||
}
|
||||
|
||||
export function isButtonPressed(button: number): boolean {
|
||||
var ens = enums();
|
||||
if (button == ens.MICROBIT_ID_BUTTON_AB && !board().usesButtonAB) {
|
||||
board().usesButtonAB = true;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
var bts = board().buttons;
|
||||
if (button == ens.MICROBIT_ID_BUTTON_A) return bts[0].pressed;
|
||||
if (button == ens.MICROBIT_ID_BUTTON_B) return bts[1].pressed;
|
||||
return bts[2].pressed || (bts[0].pressed && bts[1].pressed);
|
||||
}
|
||||
|
||||
export function onPinPressed(pin: Pin, handler: RefAction) {
|
||||
pin.isTouched();
|
||||
onButtonPressed(pin.id, handler);
|
||||
}
|
||||
|
||||
export function ioP0() { return board().pins[0]; }
|
||||
export function ioP1() { return board().pins[1]; }
|
||||
export function ioP2() { return board().pins[2]; }
|
||||
export function ioP3() { return board().pins[3]; }
|
||||
export function ioP4() { return board().pins[4]; }
|
||||
export function ioP5() { return board().pins[5]; }
|
||||
export function ioP6() { return board().pins[6]; }
|
||||
export function ioP7() { return board().pins[7]; }
|
||||
export function ioP8() { return board().pins[8]; }
|
||||
export function ioP9() { return board().pins[9]; }
|
||||
export function ioP10() { return board().pins[10]; }
|
||||
export function ioP11() { return board().pins[11]; }
|
||||
export function ioP12() { return board().pins[12]; }
|
||||
export function ioP13() { return board().pins[13]; }
|
||||
export function ioP14() { return board().pins[14]; }
|
||||
export function ioP15() { return board().pins[15]; }
|
||||
export function ioP16() { return board().pins[16]; }
|
||||
export function ioP19() { return board().pins[19]; }
|
||||
export function ioP20() { return board().pins[20]; }
|
||||
|
||||
export function isPinTouched(pin: Pin): boolean {
|
||||
return pin.isTouched();
|
||||
}
|
||||
|
||||
export function compassHeading(): number {
|
||||
var b = board();
|
||||
if (!b.usesHeading) {
|
||||
b.usesHeading = true;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
return b.heading;
|
||||
}
|
||||
|
||||
export function getAcceleration(dimension: number): number {
|
||||
var b = board();
|
||||
if (!b.usesAcceleration) {
|
||||
b.usesAcceleration = true;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
var acc = b.acceleration;
|
||||
switch (dimension) {
|
||||
case 0: return acc[0];
|
||||
case 1: return acc[1];
|
||||
case 2: return acc[2];
|
||||
default: return Math.sqrt(acc[0] * acc[0] + acc[1] * acc[1] + acc[2] * acc[2]);
|
||||
}
|
||||
}
|
||||
|
||||
export function lightLevel(): number {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getMagneticForce(): number {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getCurrentTime(): number {
|
||||
return runtime.runningTime();
|
||||
}
|
||||
|
||||
/* pins */
|
||||
export function digitalReadPin(pin : Pin) : number {
|
||||
pin.mode = PinMode.Digital | PinMode.Input;
|
||||
return pin.value > 100 ? 1 : 0;
|
||||
}
|
||||
|
||||
export function digitalWritePin(pin : Pin, value: number) {
|
||||
pin.mode = PinMode.Digital | PinMode.Output;
|
||||
pin.value = value > 0 ? 1023 : 0;
|
||||
board().updateView();
|
||||
}
|
||||
|
||||
export function analogReadPin(pin : Pin) : number {
|
||||
pin.mode = PinMode.Analog | PinMode.Input;
|
||||
return pin.value || 0;
|
||||
}
|
||||
|
||||
export function analogWritePin(pin : Pin, value: number) {
|
||||
pin.mode = PinMode.Analog | PinMode.Output;
|
||||
pin.value = value ? 1 : 0;
|
||||
board().updateView();
|
||||
}
|
||||
|
||||
export function setAnalogPeriodUs(pin: Pin, micros:number) {
|
||||
pin.mode = PinMode.Analog | PinMode.Output;
|
||||
}
|
||||
|
||||
export function servoWritePin(pin: Pin, value: number) {
|
||||
setAnalogPeriodUs(pin, 20000);
|
||||
// TODO
|
||||
}
|
||||
|
||||
export function servoSetPulse(pin: Pin, micros:number) {
|
||||
}
|
||||
|
||||
export function enablePitch(pin: Pin) {
|
||||
pin.mode = PinMode.Analog | PinMode.Output;
|
||||
}
|
||||
|
||||
export function pitch(frequency: number, ms: number) {
|
||||
let cb = getResume();
|
||||
setTimeout(() => { cb() }, ms);
|
||||
}
|
||||
|
||||
|
||||
/* radio */
|
||||
export function broadcastMessage(msg: number) : void {
|
||||
board().radio.broadcast(msg);
|
||||
}
|
||||
|
||||
export function onBroadcastMessageReceived(msg: number, handler: RefAction) : void {
|
||||
let ens = enums()
|
||||
board().bus.listen(ens.MES_BROADCAST_GENERAL_ID, msg, handler);
|
||||
}
|
||||
|
||||
export function setGroup(id : number) : void {
|
||||
board().radio.setGroup(id);
|
||||
}
|
||||
|
||||
|
||||
export function datagramSendNumbers(value0 : number, value1: number, value2: number, value3: number) : void {
|
||||
board().radio.datagram.send([value0, value1, value2, value3]);
|
||||
}
|
||||
|
||||
export function datagramReceiveNumber() : number {
|
||||
return board().radio.datagram.recv().data[0];
|
||||
}
|
||||
|
||||
export function datagramGetNumber(index : number) : number {
|
||||
return board().radio.datagram.lastReceived.data[index] || 0;
|
||||
}
|
||||
|
||||
export function datagramGetRSSI() : number {
|
||||
return board().radio.datagram.lastReceived.rssi;
|
||||
}
|
||||
|
||||
export function onDatagramReceived(handler: RefAction) : void {
|
||||
let ens = enums();
|
||||
board().bus.listen(ens.MICROBIT_ID_RADIO, ens.MICROBIT_RADIO_EVT_DATAGRAM, handler);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user