parent
6ea8a59f58
commit
5384ec567d
@ -2,36 +2,10 @@
|
|||||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||||
|
|
||||||
import { deployCoreAsync, initAsync } from "./deploy";
|
import { deployCoreAsync, initAsync } from "./deploy";
|
||||||
import { FieldPorts } from "./field_ports";
|
|
||||||
import { FieldMotors } from "./field_motors";
|
|
||||||
import { FieldSpeed } from "./field_speed";
|
|
||||||
import { FieldBrickButtons } from "./field_brickbuttons";
|
|
||||||
import { FieldTurnRatio } from "./field_turnratio";
|
|
||||||
import { FieldColorEnum } from "./field_color";
|
|
||||||
|
|
||||||
pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
|
pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
|
||||||
pxt.debug('loading pxt-ev3 target extensions...')
|
pxt.debug('loading pxt-ev3 target extensions...')
|
||||||
updateBlocklyShape();
|
|
||||||
const res: pxt.editor.ExtensionResult = {
|
const res: pxt.editor.ExtensionResult = {
|
||||||
fieldEditors: [{
|
|
||||||
selector: "ports",
|
|
||||||
editor: FieldPorts
|
|
||||||
}, {
|
|
||||||
selector: "motors",
|
|
||||||
editor: FieldMotors
|
|
||||||
}, {
|
|
||||||
selector: "speed",
|
|
||||||
editor: FieldSpeed
|
|
||||||
}, {
|
|
||||||
selector: "brickbuttons",
|
|
||||||
editor: FieldBrickButtons
|
|
||||||
}, {
|
|
||||||
selector: "turnratio",
|
|
||||||
editor: FieldTurnRatio
|
|
||||||
}, {
|
|
||||||
selector: "colorenum",
|
|
||||||
editor: FieldColorEnum
|
|
||||||
}],
|
|
||||||
deployCoreAsync,
|
deployCoreAsync,
|
||||||
showUploadInstructionsAsync: (fn: string, url: string, confirmAsync: (options: any) => Promise<number>) => {
|
showUploadInstructionsAsync: (fn: string, url: string, confirmAsync: (options: any) => Promise<number>) => {
|
||||||
let resolve: (thenableOrResult?: void | PromiseLike<void>) => void;
|
let resolve: (thenableOrResult?: void | PromiseLike<void>) => void;
|
||||||
@ -115,109 +89,6 @@ pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): P
|
|||||||
return Promise.resolve<pxt.editor.ExtensionResult>(res);
|
return Promise.resolve<pxt.editor.ExtensionResult>(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the shape of Blockly blocks with square corners
|
|
||||||
*/
|
|
||||||
function updateBlocklyShape() {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rounded corner radius.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).CORNER_RADIUS = 0 * (Blockly.BlockSvg as any).GRID_UNIT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inner space between edge of statement input and notch.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).STATEMENT_INPUT_INNER_SPACE = 3 * (Blockly.BlockSvg as any).GRID_UNIT;
|
|
||||||
/**
|
|
||||||
* SVG path for drawing next/previous notch from left to right.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).NOTCH_PATH_LEFT = (
|
|
||||||
'l 8,8 ' +
|
|
||||||
'h 16 ' +
|
|
||||||
'l 8,-8 '
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SVG path for drawing next/previous notch from right to left.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).NOTCH_PATH_RIGHT = (
|
|
||||||
'l -8,8 ' +
|
|
||||||
'h -16 ' +
|
|
||||||
'l -8,-8 '
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SVG start point for drawing the top-left corner.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).TOP_LEFT_CORNER_START =
|
|
||||||
'm 0,' + 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SVG path for drawing the rounded top-left corner.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).TOP_LEFT_CORNER =
|
|
||||||
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0 ';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SVG path for drawing the rounded top-right corner.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).TOP_RIGHT_CORNER =
|
|
||||||
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SVG path for drawing the rounded bottom-right corner.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).BOTTOM_RIGHT_CORNER =
|
|
||||||
'l 0,' + (Blockly.BlockSvg as any).CORNER_RADIUS;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SVG path for drawing the rounded bottom-left corner.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).BOTTOM_LEFT_CORNER =
|
|
||||||
'l -' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SVG path for drawing the top-left corner of a statement input.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).INNER_TOP_LEFT_CORNER =
|
|
||||||
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',-' + 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SVG path for drawing the bottom-left corner of a statement input.
|
|
||||||
* Includes the rounded inside corner.
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly.BlockSvg as any).INNER_BOTTOM_LEFT_CORNER =
|
|
||||||
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS * 2 +
|
|
||||||
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',' + 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Corner radius of the flyout background.
|
|
||||||
* @type {number}
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly as any).Flyout.prototype.CORNER_RADIUS = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Margin around the edges of the blocks in the flyout.
|
|
||||||
* @type {number}
|
|
||||||
* @const
|
|
||||||
*/
|
|
||||||
(Blockly as any).Flyout.prototype.MARGIN = 8;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// When require()d from node, bind the global pxt namespace
|
// When require()d from node, bind the global pxt namespace
|
||||||
// namespace pxt {
|
// namespace pxt {
|
||||||
// export const dummyExport = 1;
|
// export const dummyExport = 1;
|
||||||
|
145
fieldeditors/extension.ts
Normal file
145
fieldeditors/extension.ts
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts"/>
|
||||||
|
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||||
|
|
||||||
|
import { FieldPorts } from "./field_ports";
|
||||||
|
import { FieldMotors } from "./field_motors";
|
||||||
|
import { FieldSpeed } from "./field_speed";
|
||||||
|
import { FieldBrickButtons } from "./field_brickbuttons";
|
||||||
|
import { FieldTurnRatio } from "./field_turnratio";
|
||||||
|
import { FieldColorEnum } from "./field_color";
|
||||||
|
|
||||||
|
pxt.editor.initFieldExtensionsAsync = function (opts: pxt.editor.FieldExtensionOptions): Promise<pxt.editor.FieldExtensionResult> {
|
||||||
|
pxt.debug('loading pxt-ev3 target extensions...')
|
||||||
|
updateBlocklyShape();
|
||||||
|
const res: pxt.editor.FieldExtensionResult = {
|
||||||
|
fieldEditors: [{
|
||||||
|
selector: "ports",
|
||||||
|
editor: FieldPorts
|
||||||
|
}, {
|
||||||
|
selector: "motors",
|
||||||
|
editor: FieldMotors
|
||||||
|
}, {
|
||||||
|
selector: "speed",
|
||||||
|
editor: FieldSpeed
|
||||||
|
}, {
|
||||||
|
selector: "brickbuttons",
|
||||||
|
editor: FieldBrickButtons
|
||||||
|
}, {
|
||||||
|
selector: "turnratio",
|
||||||
|
editor: FieldTurnRatio
|
||||||
|
}, {
|
||||||
|
selector: "colorenum",
|
||||||
|
editor: FieldColorEnum
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
return Promise.resolve<pxt.editor.FieldExtensionResult>(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the shape of Blockly blocks with square corners
|
||||||
|
*/
|
||||||
|
function updateBlocklyShape() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounded corner radius.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).CORNER_RADIUS = 0 * (Blockly.BlockSvg as any).GRID_UNIT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner space between edge of statement input and notch.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).STATEMENT_INPUT_INNER_SPACE = 3 * (Blockly.BlockSvg as any).GRID_UNIT;
|
||||||
|
/**
|
||||||
|
* SVG path for drawing next/previous notch from left to right.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).NOTCH_PATH_LEFT = (
|
||||||
|
'l 8,8 ' +
|
||||||
|
'h 16 ' +
|
||||||
|
'l 8,-8 '
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SVG path for drawing next/previous notch from right to left.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).NOTCH_PATH_RIGHT = (
|
||||||
|
'l -8,8 ' +
|
||||||
|
'h -16 ' +
|
||||||
|
'l -8,-8 '
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SVG start point for drawing the top-left corner.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).TOP_LEFT_CORNER_START =
|
||||||
|
'm 0,' + 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SVG path for drawing the rounded top-left corner.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).TOP_LEFT_CORNER =
|
||||||
|
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0 ';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SVG path for drawing the rounded top-right corner.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).TOP_RIGHT_CORNER =
|
||||||
|
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SVG path for drawing the rounded bottom-right corner.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).BOTTOM_RIGHT_CORNER =
|
||||||
|
'l 0,' + (Blockly.BlockSvg as any).CORNER_RADIUS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SVG path for drawing the rounded bottom-left corner.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).BOTTOM_LEFT_CORNER =
|
||||||
|
'l -' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SVG path for drawing the top-left corner of a statement input.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).INNER_TOP_LEFT_CORNER =
|
||||||
|
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',-' + 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SVG path for drawing the bottom-left corner of a statement input.
|
||||||
|
* Includes the rounded inside corner.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly.BlockSvg as any).INNER_BOTTOM_LEFT_CORNER =
|
||||||
|
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS * 2 +
|
||||||
|
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',' + 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Corner radius of the flyout background.
|
||||||
|
* @type {number}
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly as any).Flyout.prototype.CORNER_RADIUS = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Margin around the edges of the blocks in the flyout.
|
||||||
|
* @type {number}
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
(Blockly as any).Flyout.prototype.MARGIN = 8;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// When require()d from node, bind the global pxt namespace
|
||||||
|
// namespace pxt {
|
||||||
|
// export const dummyExport = 1;
|
||||||
|
// }
|
||||||
|
// eval("if (typeof process === 'object' && process + '' === '[object process]') pxt = global.pxt")
|
@ -30,7 +30,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
|
|||||||
this.itemWidth_ = 75;
|
this.itemWidth_ = 75;
|
||||||
this.backgroundColour_ = pxtblockly.parseColour(options.colour);
|
this.backgroundColour_ = pxtblockly.parseColour(options.colour);
|
||||||
this.itemColour_ = "rgba(255, 255, 255, 0.6)";
|
this.itemColour_ = "rgba(255, 255, 255, 0.6)";
|
||||||
this.borderColour_ = Blockly.PXTUtils.fadeColour(this.backgroundColour_, 0.4, false);
|
this.borderColour_ = pxt.toolbox.fadeColor(this.backgroundColour_, 0.4, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
14
fieldeditors/tsconfig.json
Normal file
14
fieldeditors/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "../built/fieldeditors",
|
||||||
|
"rootDir": ".",
|
||||||
|
"newLine": "LF",
|
||||||
|
"sourceMap": false,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"declaration": true
|
||||||
|
}
|
||||||
|
}
|
285
fieldeditors/wrap.ts
Normal file
285
fieldeditors/wrap.ts
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
namespace pxt.editor {
|
||||||
|
import HF2 = pxt.HF2
|
||||||
|
import U = pxt.U
|
||||||
|
|
||||||
|
function log(msg: string) {
|
||||||
|
pxt.log("EWRAP: " + msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirEntry {
|
||||||
|
name: string;
|
||||||
|
md5?: string;
|
||||||
|
size?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const runTemplate = "C00882010084XX0060640301606400"
|
||||||
|
const usbMagic = 0x3d3f
|
||||||
|
|
||||||
|
export class Ev3Wrapper {
|
||||||
|
msgs = new U.PromiseBuffer<Uint8Array>()
|
||||||
|
private cmdSeq = U.randomUint32() & 0xffff;
|
||||||
|
private lock = new U.PromiseQueue();
|
||||||
|
isStreaming = false;
|
||||||
|
dataDump = false;
|
||||||
|
|
||||||
|
constructor(public io: pxt.HF2.PacketIO) {
|
||||||
|
io.onData = buf => {
|
||||||
|
buf = buf.slice(0, HF2.read16(buf, 0) + 2)
|
||||||
|
if (HF2.read16(buf, 4) == usbMagic) {
|
||||||
|
let code = HF2.read16(buf, 6)
|
||||||
|
let payload = buf.slice(8)
|
||||||
|
if (code == 1) {
|
||||||
|
let str = U.uint8ArrayToString(payload)
|
||||||
|
if (Util.isNodeJS)
|
||||||
|
console.log("SERIAL: " + str.replace(/\n+$/, ""))
|
||||||
|
else
|
||||||
|
window.postMessage({
|
||||||
|
type: 'serial',
|
||||||
|
id: 'n/a', // TODO?
|
||||||
|
data: str
|
||||||
|
}, "*")
|
||||||
|
} else
|
||||||
|
console.log("Magic: " + code + ": " + U.toHex(payload))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.dataDump)
|
||||||
|
log("RECV: " + U.toHex(buf))
|
||||||
|
this.msgs.push(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private allocCore(addSize: number, replyType: number) {
|
||||||
|
let len = 5 + addSize
|
||||||
|
let buf = new Uint8Array(len)
|
||||||
|
HF2.write16(buf, 0, len - 2) // pktLen
|
||||||
|
HF2.write16(buf, 2, this.cmdSeq++) // msgCount
|
||||||
|
buf[4] = replyType
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
private allocSystem(addSize: number, cmd: number, replyType = 1) {
|
||||||
|
let buf = this.allocCore(addSize + 1, replyType)
|
||||||
|
buf[5] = cmd
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
private allocCustom(code: number, addSize = 0) {
|
||||||
|
let buf = this.allocCore(1 + 2 + addSize, 0)
|
||||||
|
HF2.write16(buf, 4, usbMagic)
|
||||||
|
HF2.write16(buf, 6, code)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
stopAsync() {
|
||||||
|
return this.isVmAsync()
|
||||||
|
.then(vm => {
|
||||||
|
if (vm) return Promise.resolve();
|
||||||
|
log(`stopping PXT app`)
|
||||||
|
let buf = this.allocCustom(2)
|
||||||
|
return this.justSendAsync(buf)
|
||||||
|
.then(() => Promise.delay(500))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
dmesgAsync() {
|
||||||
|
log(`asking for DMESG buffer over serial`)
|
||||||
|
let buf = this.allocCustom(3)
|
||||||
|
return this.justSendAsync(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
runAsync(path: string) {
|
||||||
|
let codeHex = runTemplate.replace("XX", U.toHex(U.stringToUint8Array(path)))
|
||||||
|
let code = U.fromHex(codeHex)
|
||||||
|
let pkt = this.allocCore(2 + code.length, 0)
|
||||||
|
HF2.write16(pkt, 5, 0x0800)
|
||||||
|
U.memcpy(pkt, 7, code)
|
||||||
|
log(`run ${path}`)
|
||||||
|
return this.justSendAsync(pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
justSendAsync(buf: Uint8Array) {
|
||||||
|
return this.lock.enqueue("talk", () => {
|
||||||
|
this.msgs.drain()
|
||||||
|
if (this.dataDump)
|
||||||
|
log("SEND: " + U.toHex(buf))
|
||||||
|
return this.io.sendPacketAsync(buf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
talkAsync(buf: Uint8Array, altResponse = 0) {
|
||||||
|
return this.lock.enqueue("talk", () => {
|
||||||
|
this.msgs.drain()
|
||||||
|
if (this.dataDump)
|
||||||
|
log("TALK: " + U.toHex(buf))
|
||||||
|
return this.io.sendPacketAsync(buf)
|
||||||
|
.then(() => this.msgs.shiftAsync(1000))
|
||||||
|
.then(resp => {
|
||||||
|
if (resp[2] != buf[2] || resp[3] != buf[3])
|
||||||
|
U.userError("msg count de-sync")
|
||||||
|
if (buf[4] == 1) {
|
||||||
|
if (altResponse != -1 && resp[5] != buf[5])
|
||||||
|
U.userError("cmd de-sync")
|
||||||
|
if (altResponse != -1 && resp[6] != 0 && resp[6] != altResponse)
|
||||||
|
U.userError("cmd error: " + resp[6])
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
flashAsync(path: string, file: Uint8Array) {
|
||||||
|
log(`write ${file.length} bytes to ${path}`)
|
||||||
|
|
||||||
|
let handle = -1
|
||||||
|
|
||||||
|
let loopAsync = (pos: number): Promise<void> => {
|
||||||
|
if (pos >= file.length) return Promise.resolve()
|
||||||
|
let size = file.length - pos
|
||||||
|
if (size > 1000) size = 1000
|
||||||
|
let upl = this.allocSystem(1 + size, 0x93, 0x1)
|
||||||
|
upl[6] = handle
|
||||||
|
U.memcpy(upl, 6 + 1, file, pos, size)
|
||||||
|
return this.talkAsync(upl, 8) // 8=EOF
|
||||||
|
.then(() => loopAsync(pos + size))
|
||||||
|
}
|
||||||
|
|
||||||
|
let begin = this.allocSystem(4 + path.length + 1, 0x92)
|
||||||
|
HF2.write32(begin, 6, file.length) // fileSize
|
||||||
|
U.memcpy(begin, 10, U.stringToUint8Array(path))
|
||||||
|
return this.lock.enqueue("file", () =>
|
||||||
|
this.talkAsync(begin)
|
||||||
|
.then(resp => {
|
||||||
|
handle = resp[7]
|
||||||
|
return loopAsync(0)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
lsAsync(path: string): Promise<DirEntry[]> {
|
||||||
|
let lsReq = this.allocSystem(2 + path.length + 1, 0x99)
|
||||||
|
HF2.write16(lsReq, 6, 1024) // maxRead
|
||||||
|
U.memcpy(lsReq, 8, U.stringToUint8Array(path))
|
||||||
|
|
||||||
|
return this.talkAsync(lsReq, 8)
|
||||||
|
.then(resp =>
|
||||||
|
U.uint8ArrayToString(resp.slice(12)).split(/\n/).map(s => {
|
||||||
|
if (!s) return null as DirEntry
|
||||||
|
let m = /^([A-F0-9]+) ([A-F0-9]+) ([^\/]*)$/.exec(s)
|
||||||
|
if (m)
|
||||||
|
return {
|
||||||
|
md5: m[1],
|
||||||
|
size: parseInt(m[2], 16),
|
||||||
|
name: m[3]
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
name: s.replace(/\/$/, "")
|
||||||
|
}
|
||||||
|
}).filter(v => !!v))
|
||||||
|
}
|
||||||
|
|
||||||
|
rmAsync(path: string): Promise<void> {
|
||||||
|
log(`rm ${path}`)
|
||||||
|
let rmReq = this.allocSystem(path.length + 1, 0x9c)
|
||||||
|
U.memcpy(rmReq, 6, U.stringToUint8Array(path))
|
||||||
|
|
||||||
|
return this.talkAsync(rmReq, 5)
|
||||||
|
.then(resp => { })
|
||||||
|
}
|
||||||
|
|
||||||
|
isVmAsync(): Promise<boolean> {
|
||||||
|
let path = "/no/such/dir"
|
||||||
|
let mkdirReq = this.allocSystem(path.length + 1, 0x9b)
|
||||||
|
U.memcpy(mkdirReq, 6, U.stringToUint8Array(path))
|
||||||
|
return this.talkAsync(mkdirReq, -1)
|
||||||
|
.then(resp => {
|
||||||
|
let isVM = resp[6] == 0x05
|
||||||
|
log(`${isVM ? "PXT app" : "VM"} running`)
|
||||||
|
return isVM
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private streamFileOnceAsync(path: string, cb: (d: Uint8Array) => void) {
|
||||||
|
let fileSize = 0
|
||||||
|
let filePtr = 0
|
||||||
|
let handle = -1
|
||||||
|
let resp = (buf: Uint8Array): Promise<void> => {
|
||||||
|
if (buf[6] == 2) {
|
||||||
|
// handle not ready - file is missing
|
||||||
|
this.isStreaming = false
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf[6] != 0 && buf[6] != 8)
|
||||||
|
U.userError("bad response when streaming file: " + buf[6] + " " + U.toHex(buf))
|
||||||
|
|
||||||
|
this.isStreaming = true
|
||||||
|
fileSize = HF2.read32(buf, 7)
|
||||||
|
if (handle == -1) {
|
||||||
|
handle = buf[11]
|
||||||
|
log(`stream on, handle=${handle}`)
|
||||||
|
}
|
||||||
|
let data = buf.slice(12)
|
||||||
|
filePtr += data.length
|
||||||
|
if (data.length > 0)
|
||||||
|
cb(data)
|
||||||
|
|
||||||
|
if (buf[6] == 8) {
|
||||||
|
// end of file
|
||||||
|
this.isStreaming = false
|
||||||
|
return this.rmAsync(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
let contFileReq = this.allocSystem(1 + 2, 0x97)
|
||||||
|
HF2.write16(contFileReq, 7, 1000) // maxRead
|
||||||
|
contFileReq[6] = handle
|
||||||
|
return Promise.delay(data.length > 0 ? 0 : 500)
|
||||||
|
.then(() => this.talkAsync(contFileReq, -1))
|
||||||
|
.then(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
let getFileReq = this.allocSystem(2 + path.length + 1, 0x96)
|
||||||
|
HF2.write16(getFileReq, 6, 1000) // maxRead
|
||||||
|
U.memcpy(getFileReq, 8, U.stringToUint8Array(path))
|
||||||
|
return this.talkAsync(getFileReq, -1).then(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
streamFileAsync(path: string, cb: (d: Uint8Array) => void) {
|
||||||
|
let loop = (): Promise<void> =>
|
||||||
|
this.lock.enqueue("file", () =>
|
||||||
|
this.streamFileOnceAsync(path, cb))
|
||||||
|
.then(() => Promise.delay(500))
|
||||||
|
.then(loop)
|
||||||
|
return loop()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
downloadFileAsync(path: string, cb: (d: Uint8Array) => void) {
|
||||||
|
return this.lock.enqueue("file", () =>
|
||||||
|
this.streamFileOnceAsync(path, cb))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private initAsync() {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
private resetState() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
reconnectAsync(first = false): Promise<void> {
|
||||||
|
this.resetState()
|
||||||
|
if (first) return this.initAsync()
|
||||||
|
log(`reconnect`);
|
||||||
|
return this.io.reconnectAsync()
|
||||||
|
.then(() => this.initAsync())
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectAsync() {
|
||||||
|
log(`disconnect`);
|
||||||
|
return this.io.disconnectAsync()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
50
package-lock.json
generated
50
package-lock.json
generated
@ -1977,15 +1977,6 @@
|
|||||||
"verror": "1.10.0"
|
"verror": "1.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"keytar": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/keytar/-/keytar-3.0.2.tgz",
|
|
||||||
"integrity": "sha1-TcFd01I/4wYx+dOIV4pAFRpgWG8=",
|
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
|
||||||
"nan": "2.3.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"kind-of": {
|
"kind-of": {
|
||||||
"version": "3.2.2",
|
"version": "3.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
||||||
@ -2437,7 +2428,8 @@
|
|||||||
"nan": {
|
"nan": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.3.2.tgz",
|
||||||
"integrity": "sha1-TU7PF+HaTpie+08nPY0AIBytCH4="
|
"integrity": "sha1-TU7PF+HaTpie+08nPY0AIBytCH4=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"neatequal": {
|
"neatequal": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@ -2748,6 +2740,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||||
},
|
},
|
||||||
|
"pngjs": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-bVNd3LMXRzdo6s4ehr4XW2wFMu9cb40nPgHEjSSppm8/++Xc+g0b2QQb+SeDesgfANXbjydOr1or9YQ+pcCZPQ=="
|
||||||
|
},
|
||||||
"pointer-symbol": {
|
"pointer-symbol": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pointer-symbol/-/pointer-symbol-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pointer-symbol/-/pointer-symbol-1.0.0.tgz",
|
||||||
@ -3274,19 +3271,19 @@
|
|||||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||||
},
|
},
|
||||||
"pxt-common-packages": {
|
"pxt-common-packages": {
|
||||||
"version": "0.20.14",
|
"version": "0.20.38",
|
||||||
"resolved": "https://registry.npmjs.org/pxt-common-packages/-/pxt-common-packages-0.20.14.tgz",
|
"resolved": "https://registry.npmjs.org/pxt-common-packages/-/pxt-common-packages-0.20.38.tgz",
|
||||||
"integrity": "sha512-DlZIDfDSH5jGq5K4k+9QtALzcedT+lYBNnxUWrbAK/GOxkqXZxhwGGvi1O2p6eFOfCWBuH0kjBlui3dzxeF0LA==",
|
"integrity": "sha512-jtJrd9XwX9T/bOC/9uZyPB4SdvDfhxbnl8c8Gzyy+HiGbW1SMcABgTevJFpLli/ifszl+R+9ZLspFGle84jXkA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"autoprefixer": "6.7.7",
|
"autoprefixer": "6.7.7",
|
||||||
"pxt-core": "3.5.11",
|
"pxt-core": "3.8.13",
|
||||||
"rtlcss": "2.2.1"
|
"rtlcss": "2.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pxt-core": {
|
"pxt-core": {
|
||||||
"version": "3.5.11",
|
"version": "3.8.13",
|
||||||
"resolved": "https://registry.npmjs.org/pxt-core/-/pxt-core-3.5.11.tgz",
|
"resolved": "https://registry.npmjs.org/pxt-core/-/pxt-core-3.8.13.tgz",
|
||||||
"integrity": "sha512-niFvx2PbvWqNPikB0uyR22Pnsc7ipOfWsB656KvnenK4lRNUMEFcxUg4B+65+NZZQypEHk5OxsD3nbE62749EA==",
|
"integrity": "sha512-F7+P5X/TB6SXtXIkEujHccAxcbMCAKyUqT/VgaQBi2vRo3YdH1IswsMSgXDKG0H12sWOVL297TLdQSiBOuIvXA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bluebird": "3.5.1",
|
"bluebird": "3.5.1",
|
||||||
"browserify": "13.3.0",
|
"browserify": "13.3.0",
|
||||||
@ -3294,10 +3291,11 @@
|
|||||||
"faye-websocket": "0.11.1",
|
"faye-websocket": "0.11.1",
|
||||||
"fuse.js": "2.6.1",
|
"fuse.js": "2.6.1",
|
||||||
"highlight.js": "9.12.0",
|
"highlight.js": "9.12.0",
|
||||||
"keytar": "3.0.2",
|
"keytar": "4.2.1",
|
||||||
"lzma": "2.3.2",
|
"lzma": "2.3.2",
|
||||||
"marked": "0.3.12",
|
"marked": "0.3.12",
|
||||||
"node-hid": "0.5.7",
|
"node-hid": "0.5.7",
|
||||||
|
"pngjs": "3.3.2",
|
||||||
"postcss": "6.0.21",
|
"postcss": "6.0.21",
|
||||||
"request": "2.83.0",
|
"request": "2.83.0",
|
||||||
"rimraf": "2.5.4",
|
"rimraf": "2.5.4",
|
||||||
@ -3329,6 +3327,22 @@
|
|||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||||
},
|
},
|
||||||
|
"keytar": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/keytar/-/keytar-4.2.1.tgz",
|
||||||
|
"integrity": "sha1-igamV3/fY3PgqmsRInfmPex3/RI=",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"nan": "2.8.0",
|
||||||
|
"prebuild-install": "2.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nan": {
|
||||||
|
"version": "2.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz",
|
||||||
|
"integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"postcss": {
|
"postcss": {
|
||||||
"version": "6.0.21",
|
"version": "6.0.21",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.21.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.21.tgz",
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pxt-common-packages": "0.20.38",
|
"pxt-common-packages": "0.20.38",
|
||||||
"pxt-core": "3.8.15"
|
"pxt-core": "3.9.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node node_modules/pxt-core/built/pxt.js travis"
|
"test": "node node_modules/pxt-core/built/pxt.js travis"
|
||||||
|
@ -136,6 +136,7 @@
|
|||||||
"hasAudio": true,
|
"hasAudio": true,
|
||||||
"usbHelp": [],
|
"usbHelp": [],
|
||||||
"extendEditor": true,
|
"extendEditor": true,
|
||||||
|
"extendFieldEditors": true,
|
||||||
"disableBlockIcons": true,
|
"disableBlockIcons": true,
|
||||||
"blocklyOptions": {
|
"blocklyOptions": {
|
||||||
"grid": {
|
"grid": {
|
||||||
|
Loading…
Reference in New Issue
Block a user