2017-07-11 10:15:17 +02:00
|
|
|
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
|
|
|
/// <reference path="../node_modules/pxt-core/localtypings/pxtarget.d.ts"/>
|
|
|
|
/// <reference path="../built/common-sim.d.ts"/>
|
|
|
|
|
|
|
|
namespace pxsim {
|
|
|
|
|
2017-12-18 22:04:17 +01:00
|
|
|
export class EV3Board extends CoreBoard {
|
2019-10-09 06:57:55 +02:00
|
|
|
viewHost: visuals.BoardHost;
|
2017-07-11 10:15:17 +02:00
|
|
|
view: SVGSVGElement;
|
|
|
|
|
2017-12-18 22:04:17 +01:00
|
|
|
outputState: EV3OutputState;
|
|
|
|
analogState: EV3AnalogState;
|
|
|
|
uartState: EV3UArtState;
|
|
|
|
motorState: EV3MotorState;
|
2018-02-27 00:16:17 +01:00
|
|
|
screenState: ScreenState;
|
2017-12-18 22:04:17 +01:00
|
|
|
audioState: AudioState;
|
2018-02-02 18:48:27 +01:00
|
|
|
remoteState: RemoteState;
|
2017-12-18 22:04:17 +01:00
|
|
|
|
|
|
|
inputNodes: SensorNode[] = [];
|
|
|
|
brickNode: BrickNode;
|
|
|
|
outputNodes: MotorNode[] = [];
|
|
|
|
|
2018-03-28 22:36:52 +02:00
|
|
|
highcontrastMode?: boolean;
|
|
|
|
lightMode?: boolean;
|
|
|
|
|
2018-01-05 06:57:01 +01:00
|
|
|
public motorMap: pxt.Map<number> = {
|
2017-12-18 22:04:17 +01:00
|
|
|
0x01: 0,
|
|
|
|
0x02: 1,
|
|
|
|
0x04: 2,
|
|
|
|
0x08: 3
|
|
|
|
}
|
|
|
|
|
2017-07-11 10:15:17 +02:00
|
|
|
constructor() {
|
|
|
|
super()
|
|
|
|
|
|
|
|
this.bus.setNotify(DAL.DEVICE_ID_NOTIFY, DAL.DEVICE_ID_NOTIFY_ONE);
|
|
|
|
|
2017-12-18 22:04:17 +01:00
|
|
|
this.brickNode = new BrickNode();
|
|
|
|
|
|
|
|
this.outputState = new EV3OutputState();
|
|
|
|
this.analogState = new EV3AnalogState();
|
|
|
|
this.uartState = new EV3UArtState();
|
|
|
|
this.motorState = new EV3MotorState();
|
2018-02-27 00:16:17 +01:00
|
|
|
this.screenState = new ScreenState(["#97b5a6", "#000000"], visuals.SCREEN_WIDTH, visuals.SCREEN_HEIGHT);
|
2017-12-18 22:04:17 +01:00
|
|
|
this.audioState = new AudioState();
|
2018-02-02 18:48:27 +01:00
|
|
|
this.remoteState = new RemoteState();
|
2017-07-11 10:15:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
receiveMessage(msg: SimulatorMessage) {
|
|
|
|
if (!runtime || runtime.dead) return;
|
|
|
|
|
|
|
|
switch (msg.type || "") {
|
|
|
|
case "eventbus": {
|
|
|
|
let ev = <SimulatorEventBusMessage>msg;
|
|
|
|
this.bus.queue(ev.id, ev.eventid, ev.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "serial": {
|
|
|
|
let data = (<SimulatorSerialMessage>msg).data || "";
|
|
|
|
// TODO
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
initAsync(msg: SimulatorRunMessage): Promise<void> {
|
|
|
|
super.initAsync(msg);
|
|
|
|
|
|
|
|
const options = (msg.options || {}) as pxt.RuntimeOptions;
|
|
|
|
|
|
|
|
const boardDef = msg.boardDefinition;
|
|
|
|
const cmpsList = msg.parts;
|
|
|
|
const cmpDefs = msg.partDefinitions || {};
|
|
|
|
const fnArgs = msg.fnArgs;
|
|
|
|
|
|
|
|
const opts: visuals.BoardHostOpts = {
|
|
|
|
state: this,
|
|
|
|
boardDef: boardDef,
|
|
|
|
partsList: cmpsList,
|
|
|
|
partDefs: cmpDefs,
|
|
|
|
fnArgs: fnArgs,
|
|
|
|
maxWidth: "100%",
|
|
|
|
maxHeight: "100%",
|
2018-03-28 22:36:52 +02:00
|
|
|
highContrast: msg.highContrast,
|
|
|
|
light: msg.light
|
2017-07-11 10:15:17 +02:00
|
|
|
};
|
2019-10-09 06:57:55 +02:00
|
|
|
this.viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
|
|
|
|
boardDef,
|
2018-03-28 22:36:52 +02:00
|
|
|
visual: boardDef.visual,
|
|
|
|
highContrast: msg.highContrast,
|
|
|
|
light: msg.light
|
2017-07-11 10:15:17 +02:00
|
|
|
}), opts);
|
|
|
|
|
|
|
|
document.body.innerHTML = ""; // clear children
|
2018-03-28 22:36:52 +02:00
|
|
|
document.body.className = msg.light ? "light" : "";
|
2019-10-09 06:57:55 +02:00
|
|
|
document.body.appendChild(this.view = this.viewHost.getView() as SVGSVGElement);
|
2017-07-11 10:15:17 +02:00
|
|
|
|
2017-12-22 23:00:23 +01:00
|
|
|
this.inputNodes = [];
|
|
|
|
this.outputNodes = [];
|
|
|
|
|
2018-03-28 22:36:52 +02:00
|
|
|
this.highcontrastMode = msg.highContrast;
|
|
|
|
this.lightMode = msg.light;
|
|
|
|
|
2017-07-11 10:15:17 +02:00
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
2019-10-09 06:57:55 +02:00
|
|
|
screenshotAsync(width?: number): Promise<ImageData> {
|
|
|
|
return this.viewHost.screenshotAsync(width);
|
2017-07-11 10:15:17 +02:00
|
|
|
}
|
|
|
|
|
2017-12-18 22:04:17 +01:00
|
|
|
getBrickNode() {
|
|
|
|
return this.brickNode;
|
|
|
|
}
|
|
|
|
|
2018-01-16 08:57:21 +01:00
|
|
|
motorUsed(ports: number, large: boolean): boolean {
|
2018-01-05 06:57:01 +01:00
|
|
|
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
|
2017-12-28 18:07:57 +01:00
|
|
|
const p = 1 << i;
|
2018-01-16 08:57:21 +01:00
|
|
|
if (ports & p) {
|
2018-01-05 06:57:01 +01:00
|
|
|
const motorPort = this.motorMap[p];
|
|
|
|
const outputNode = this.outputNodes[motorPort];
|
2018-01-16 08:57:21 +01:00
|
|
|
if (!outputNode) {
|
|
|
|
this.outputNodes[motorPort] = new MotorNode(motorPort, large);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (outputNode && outputNode.isLarge() != large)
|
|
|
|
return false;
|
2018-01-05 06:57:01 +01:00
|
|
|
}
|
|
|
|
}
|
2018-01-16 08:57:21 +01:00
|
|
|
return true;
|
2017-12-28 18:07:57 +01:00
|
|
|
}
|
|
|
|
|
2017-12-18 22:04:17 +01:00
|
|
|
getMotor(port: number, large?: boolean): MotorNode[] {
|
2017-12-28 18:07:57 +01:00
|
|
|
const r = [];
|
2018-01-05 06:57:01 +01:00
|
|
|
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
|
2017-12-28 18:07:57 +01:00
|
|
|
const p = 1 << i;
|
|
|
|
if (port & p) {
|
|
|
|
const motorPort = this.motorMap[p];
|
|
|
|
const outputNode = this.outputNodes[motorPort];
|
|
|
|
if (outputNode)
|
|
|
|
r.push(outputNode);
|
2018-01-05 06:57:01 +01:00
|
|
|
}
|
2017-12-28 18:07:57 +01:00
|
|
|
}
|
|
|
|
return r;
|
2017-12-18 22:04:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
getMotors() {
|
|
|
|
return this.outputNodes;
|
|
|
|
}
|
|
|
|
|
2018-01-05 06:57:01 +01:00
|
|
|
hasSensor(port: number) {
|
|
|
|
return !!this.inputNodes[port];
|
|
|
|
}
|
|
|
|
|
2023-05-09 16:27:05 +02:00
|
|
|
getSensor(port: number, type: number): SensorNode | SensorExtendedNode {
|
2017-12-28 02:05:15 +01:00
|
|
|
if (!this.inputNodes[port]) {
|
2017-12-18 22:04:17 +01:00
|
|
|
switch (type) {
|
|
|
|
case DAL.DEVICE_TYPE_GYRO: this.inputNodes[port] = new GyroSensorNode(port); break;
|
|
|
|
case DAL.DEVICE_TYPE_COLOR: this.inputNodes[port] = new ColorSensorNode(port); break;
|
|
|
|
case DAL.DEVICE_TYPE_TOUCH: this.inputNodes[port] = new TouchSensorNode(port); break;
|
|
|
|
case DAL.DEVICE_TYPE_ULTRASONIC: this.inputNodes[port] = new UltrasonicSensorNode(port); break;
|
2018-02-02 07:03:01 +01:00
|
|
|
case DAL.DEVICE_TYPE_IR: this.inputNodes[port] = new InfraredSensorNode(port); break;
|
2017-12-18 22:04:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.inputNodes[port];
|
|
|
|
}
|
|
|
|
|
|
|
|
getInputNodes() {
|
|
|
|
return this.inputNodes;
|
|
|
|
}
|
2017-07-11 10:15:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function initRuntimeWithDalBoard() {
|
|
|
|
U.assert(!runtime.board);
|
2017-12-18 22:04:17 +01:00
|
|
|
let b = new EV3Board();
|
2017-07-11 10:15:17 +02:00
|
|
|
runtime.board = b;
|
|
|
|
runtime.postError = (e) => {
|
|
|
|
// TODO
|
|
|
|
runtime.updateDisplay();
|
2018-01-05 06:57:01 +01:00
|
|
|
console.log('runtime error: ' + e);
|
2017-07-11 10:15:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-18 22:04:17 +01:00
|
|
|
export function ev3board(): EV3Board {
|
|
|
|
return runtime.board as EV3Board;
|
|
|
|
}
|
|
|
|
|
2018-03-28 22:36:52 +02:00
|
|
|
export function inLightMode(): boolean {
|
|
|
|
return /light=1/i.test(window.location.href) || ev3board().lightMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function inHighcontrastMode(): boolean {
|
2023-05-09 16:27:05 +02:00
|
|
|
///
|
2018-03-28 22:36:52 +02:00
|
|
|
return ev3board().highcontrastMode;
|
|
|
|
}
|
|
|
|
|
2017-07-11 10:15:17 +02:00
|
|
|
if (!pxsim.initCurrentRuntime) {
|
|
|
|
pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
|
|
|
|
}
|
|
|
|
}
|