2017-07-06 18:34:31 +02:00
|
|
|
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts" />
|
|
|
|
|
|
|
|
// 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")
|
|
|
|
|
|
|
|
namespace pxt.editor {
|
2017-07-25 12:48:02 +02:00
|
|
|
import UF2 = pxtc.UF2;
|
|
|
|
|
2017-11-17 01:28:10 +01:00
|
|
|
export let ev3: Ev3Wrapper
|
|
|
|
|
|
|
|
export function debug() {
|
|
|
|
return initAsync()
|
|
|
|
.then(w => w.downloadFileAsync("/tmp/dmesg.txt", v => console.log(pxt.Util.uint8ArrayToString(v))))
|
|
|
|
}
|
|
|
|
|
2017-07-06 18:34:31 +02:00
|
|
|
// this comes from aux/pxt.lms
|
2017-07-07 14:08:36 +02:00
|
|
|
const rbfTemplate = `
|
2017-07-08 22:18:05 +02:00
|
|
|
4c45474f580000006d000100000000001c000000000000000e000000821b038405018130813e8053
|
|
|
|
74617274696e672e2e2e0084006080XX00448581644886488405018130813e80427965210084000a
|
2017-07-07 14:08:36 +02:00
|
|
|
`
|
2017-07-06 18:34:31 +02:00
|
|
|
|
|
|
|
function hf2Async() {
|
|
|
|
return pxt.HF2.mkPacketIOAsync()
|
|
|
|
.then(h => {
|
|
|
|
let w = new Ev3Wrapper(h)
|
2017-11-17 01:28:10 +01:00
|
|
|
ev3 = w
|
2017-07-06 18:34:31 +02:00
|
|
|
return w.reconnectAsync(true)
|
|
|
|
.then(() => w)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-07-25 12:48:02 +02:00
|
|
|
let noHID = false
|
|
|
|
|
2017-07-06 18:34:31 +02:00
|
|
|
let initPromise: Promise<Ev3Wrapper>
|
|
|
|
function initAsync() {
|
2017-08-08 11:47:10 +02:00
|
|
|
if (initPromise)
|
|
|
|
return initPromise
|
|
|
|
|
|
|
|
let canHID = false
|
|
|
|
if (U.isNodeJS) {
|
|
|
|
canHID = true
|
|
|
|
} else {
|
|
|
|
const forceHexDownload = /forceHexDownload/i.test(window.location.href);
|
|
|
|
if (Cloud.isLocalHost() && Cloud.localToken && !forceHexDownload)
|
|
|
|
canHID = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if (noHID)
|
|
|
|
canHID = false
|
|
|
|
|
|
|
|
if (canHID) {
|
|
|
|
initPromise = hf2Async()
|
|
|
|
.catch(err => {
|
|
|
|
initPromise = null
|
|
|
|
noHID = true
|
|
|
|
return Promise.reject(err)
|
|
|
|
})
|
2017-08-08 02:39:37 +02:00
|
|
|
} else {
|
2017-08-07 10:56:21 +02:00
|
|
|
noHID = true
|
2017-08-08 11:47:10 +02:00
|
|
|
initPromise = Promise.reject(new Error("no HID"))
|
2017-08-07 10:56:21 +02:00
|
|
|
}
|
2017-08-08 11:47:10 +02:00
|
|
|
|
2017-07-06 18:34:31 +02:00
|
|
|
return initPromise
|
|
|
|
}
|
|
|
|
|
|
|
|
export function deployCoreAsync(resp: pxtc.CompileResult, isCli = false) {
|
|
|
|
let w: Ev3Wrapper
|
2017-07-25 12:48:02 +02:00
|
|
|
|
2017-07-27 22:10:52 +02:00
|
|
|
let filename = resp.downloadFileBaseName || "pxt"
|
|
|
|
filename = filename.replace(/^lego-/, "")
|
2017-07-25 12:48:02 +02:00
|
|
|
|
|
|
|
let fspath = "../prjs/BrkProg_SAVE/"
|
|
|
|
|
|
|
|
let elfPath = fspath + filename + ".elf"
|
|
|
|
let rbfPath = fspath + filename + ".rbf"
|
|
|
|
|
|
|
|
let rbfHex = rbfTemplate
|
|
|
|
.replace(/\s+/g, "")
|
|
|
|
.replace("XX", U.toHex(U.stringToUint8Array(elfPath)))
|
|
|
|
let rbfBIN = U.fromHex(rbfHex)
|
|
|
|
HF2.write16(rbfBIN, 4, rbfBIN.length)
|
|
|
|
|
|
|
|
let origElfUF2 = UF2.parseFile(U.stringToUint8Array(atob(resp.outfiles[pxt.outputName()])))
|
|
|
|
|
|
|
|
let mkFile = (ext: string, data: Uint8Array = null) => {
|
|
|
|
let f = UF2.newBlockFile()
|
|
|
|
f.filename = "Projects/" + filename + ext
|
|
|
|
if (data)
|
|
|
|
UF2.writeBytes(f, 0, data)
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
|
|
|
let elfUF2 = mkFile(".elf")
|
|
|
|
for (let b of origElfUF2) {
|
|
|
|
UF2.writeBytes(elfUF2, b.targetAddr, b.data)
|
|
|
|
}
|
|
|
|
|
|
|
|
let r = UF2.concatFiles([elfUF2, mkFile(".rbf", rbfBIN)])
|
|
|
|
let data = UF2.serializeFile(r)
|
|
|
|
|
2017-07-27 13:50:12 +02:00
|
|
|
resp.outfiles[pxtc.BINARY_UF2] = btoa(data)
|
2017-07-25 12:48:02 +02:00
|
|
|
|
|
|
|
let saveUF2Async = () => {
|
|
|
|
if (isCli || !pxt.commands.saveOnlyAsync) {
|
|
|
|
return Promise.resolve()
|
|
|
|
} else {
|
|
|
|
return pxt.commands.saveOnlyAsync(resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (noHID) return saveUF2Async()
|
|
|
|
|
2017-07-06 18:34:31 +02:00
|
|
|
return initAsync()
|
|
|
|
.then(w_ => {
|
|
|
|
w = w_
|
2017-07-07 14:41:21 +02:00
|
|
|
if (w.isStreaming)
|
|
|
|
U.userError("please stop the program first")
|
2017-07-09 11:19:14 +02:00
|
|
|
return w.stopAsync()
|
2017-07-25 12:48:02 +02:00
|
|
|
})
|
|
|
|
.then(() => w.rmAsync(elfPath))
|
|
|
|
.then(() => w.flashAsync(elfPath, UF2.readBytes(origElfUF2, 0, origElfUF2.length * 256)))
|
|
|
|
.then(() => w.flashAsync(rbfPath, rbfBIN))
|
|
|
|
.then(() => w.runAsync(rbfPath))
|
|
|
|
.then(() => {
|
2017-07-06 18:34:31 +02:00
|
|
|
if (isCli)
|
|
|
|
return w.disconnectAsync()
|
|
|
|
else
|
|
|
|
return Promise.resolve()
|
2017-07-25 12:48:02 +02:00
|
|
|
//return Promise.delay(1000).then(() => w.dmesgAsync())
|
|
|
|
}).catch(e => {
|
|
|
|
// if we failed to initalize, retry
|
|
|
|
if (noHID)
|
|
|
|
return saveUF2Async()
|
|
|
|
else
|
|
|
|
return Promise.reject(e)
|
2017-07-06 18:34:31 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-01-02 22:32:07 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-07-06 18:34:31 +02:00
|
|
|
initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
|
2017-07-07 14:08:36 +02:00
|
|
|
pxt.debug('loading pxt-ev3 target extensions...')
|
2018-01-02 22:32:07 +01:00
|
|
|
updateBlocklyShape();
|
2017-07-06 18:34:31 +02:00
|
|
|
const res: pxt.editor.ExtensionResult = {
|
|
|
|
deployCoreAsync,
|
|
|
|
};
|
2017-08-07 11:12:52 +02:00
|
|
|
initAsync().catch(e => {
|
|
|
|
// probably no HID - we'll try this again upon deployment
|
|
|
|
})
|
2017-07-06 18:34:31 +02:00
|
|
|
return Promise.resolve<pxt.editor.ExtensionResult>(res);
|
|
|
|
}
|
2017-07-06 18:58:15 +02:00
|
|
|
}
|