pxt-ev3
Version:
LEGO MINDSTORMS EV3 for Microsoft MakeCode
1,221 lines (1,220 loc) • 288 kB
JavaScript
/// <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"/>
var pxsim;
(function (pxsim) {
class EV3Board extends pxsim.CoreBoard {
constructor() {
super();
this.inputNodes = [];
this.outputNodes = [];
this.motorMap = {
0x01: 0,
0x02: 1,
0x04: 2,
0x08: 3
};
this.bus.setNotify(10000 /* DAL.DEVICE_ID_NOTIFY */, 10001 /* DAL.DEVICE_ID_NOTIFY_ONE */);
this.brickNode = new pxsim.BrickNode();
this.outputState = new pxsim.EV3OutputState();
this.analogState = new pxsim.EV3AnalogState();
this.uartState = new pxsim.EV3UArtState();
this.motorState = new pxsim.EV3MotorState();
this.screenState = new pxsim.ScreenState(["#97b5a6", "#000000"], pxsim.visuals.SCREEN_WIDTH, pxsim.visuals.SCREEN_HEIGHT);
this.audioState = new pxsim.AudioState();
this.remoteState = new pxsim.RemoteState();
}
receiveMessage(msg) {
if (!pxsim.runtime || pxsim.runtime.dead)
return;
switch (msg.type || "") {
case "eventbus": {
let ev = msg;
this.bus.queue(ev.id, ev.eventid, ev.value);
break;
}
case "serial": {
let data = msg.data || "";
// TODO
break;
}
}
}
initAsync(msg) {
super.initAsync(msg);
const options = (msg.options || {});
const boardDef = msg.boardDefinition;
const cmpsList = msg.parts;
const cmpDefs = msg.partDefinitions || {};
const fnArgs = msg.fnArgs;
const opts = {
state: this,
boardDef: boardDef,
partsList: cmpsList,
partDefs: cmpDefs,
fnArgs: fnArgs,
maxWidth: "100%",
maxHeight: "100%",
highContrast: msg.highContrast,
light: msg.light
};
this.viewHost = new pxsim.visuals.BoardHost(pxsim.visuals.mkBoardView({
boardDef,
visual: boardDef.visual,
highContrast: msg.highContrast,
light: msg.light
}), opts);
document.body.innerHTML = ""; // clear children
document.body.className = msg.light ? "light" : "";
document.body.appendChild(this.view = this.viewHost.getView());
this.inputNodes = [];
this.outputNodes = [];
this.highcontrastMode = msg.highContrast;
this.lightMode = msg.light;
return Promise.resolve();
}
screenshotAsync(width) {
return this.viewHost.screenshotAsync(width);
}
getBrickNode() {
return this.brickNode;
}
motorUsed(ports, large) {
for (let i = 0; i < 4 /* DAL.NUM_OUTPUTS */; ++i) {
const p = 1 << i;
if (ports & p) {
const motorPort = this.motorMap[p];
const outputNode = this.outputNodes[motorPort];
if (!outputNode) {
this.outputNodes[motorPort] = new pxsim.MotorNode(motorPort, large);
continue;
}
if (outputNode && outputNode.isLarge() != large)
return false;
}
}
return true;
}
getMotor(port, large) {
const r = [];
for (let i = 0; i < 4 /* DAL.NUM_OUTPUTS */; ++i) {
const p = 1 << i;
if (port & p) {
const motorPort = this.motorMap[p];
const outputNode = this.outputNodes[motorPort];
if (outputNode)
r.push(outputNode);
}
}
return r;
}
getMotors() {
return this.outputNodes;
}
hasSensor(port) {
return !!this.inputNodes[port];
}
getSensor(port, type) {
if (!this.inputNodes[port]) {
switch (type) {
case 32 /* DAL.DEVICE_TYPE_GYRO */:
this.inputNodes[port] = new pxsim.GyroSensorNode(port);
break;
case 29 /* DAL.DEVICE_TYPE_COLOR */:
this.inputNodes[port] = new pxsim.ColorSensorNode(port);
break;
case 16 /* DAL.DEVICE_TYPE_TOUCH */:
this.inputNodes[port] = new pxsim.TouchSensorNode(port);
break;
case 30 /* DAL.DEVICE_TYPE_ULTRASONIC */:
this.inputNodes[port] = new pxsim.UltrasonicSensorNode(port);
break;
case 33 /* DAL.DEVICE_TYPE_IR */:
this.inputNodes[port] = new pxsim.InfraredSensorNode(port);
break;
case 2 /* DAL.DEVICE_TYPE_NXT_LIGHT */:
this.inputNodes[port] = new pxsim.NXTLightSensorNode(port);
break;
}
}
return this.inputNodes[port];
}
getInputNodes() {
return this.inputNodes;
}
}
pxsim.EV3Board = EV3Board;
function initRuntimeWithDalBoard() {
pxsim.U.assert(!pxsim.runtime.board);
let b = new EV3Board();
pxsim.runtime.board = b;
pxsim.runtime.postError = (e) => {
// TODO
pxsim.runtime.updateDisplay();
console.log('runtime error: ' + e);
};
}
pxsim.initRuntimeWithDalBoard = initRuntimeWithDalBoard;
function ev3board() {
return pxsim.runtime.board;
}
pxsim.ev3board = ev3board;
function inLightMode() {
return /light=1/i.test(window.location.href) || ev3board().lightMode;
}
pxsim.inLightMode = inLightMode;
function inHighcontrastMode() {
return ev3board().highcontrastMode;
}
pxsim.inHighcontrastMode = inHighcontrastMode;
if (!pxsim.initCurrentRuntime) {
pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
}
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
let AnalogOff;
(function (AnalogOff) {
AnalogOff[AnalogOff["InPin1"] = 0] = "InPin1";
AnalogOff[AnalogOff["InPin6"] = 8] = "InPin6";
AnalogOff[AnalogOff["OutPin5"] = 16] = "OutPin5";
AnalogOff[AnalogOff["BatteryTemp"] = 24] = "BatteryTemp";
AnalogOff[AnalogOff["MotorCurrent"] = 26] = "MotorCurrent";
AnalogOff[AnalogOff["BatteryCurrent"] = 28] = "BatteryCurrent";
AnalogOff[AnalogOff["Cell123456"] = 30] = "Cell123456";
AnalogOff[AnalogOff["Pin1"] = 32] = "Pin1";
AnalogOff[AnalogOff["Pin6"] = 2432] = "Pin6";
AnalogOff[AnalogOff["Actual"] = 4832] = "Actual";
AnalogOff[AnalogOff["LogIn"] = 4840] = "LogIn";
AnalogOff[AnalogOff["LogOut"] = 4848] = "LogOut";
AnalogOff[AnalogOff["NxtCol"] = 4856] = "NxtCol";
AnalogOff[AnalogOff["OutPin5Low"] = 5144] = "OutPin5Low";
AnalogOff[AnalogOff["Updated"] = 5152] = "Updated";
AnalogOff[AnalogOff["InDcm"] = 5156] = "InDcm";
AnalogOff[AnalogOff["InConn"] = 5160] = "InConn";
AnalogOff[AnalogOff["OutDcm"] = 5164] = "OutDcm";
AnalogOff[AnalogOff["OutConn"] = 5168] = "OutConn";
AnalogOff[AnalogOff["Size"] = 5172] = "Size";
})(AnalogOff = pxsim.AnalogOff || (pxsim.AnalogOff = {}));
class EV3AnalogState {
constructor() {
let data = new Uint8Array(5172);
pxsim.MMapMethods.register("/dev/lms_analog", {
data,
beforeMemRead: () => {
//console.log("analog before read");
pxsim.util.map16Bit(data, AnalogOff.BatteryTemp, 21);
pxsim.util.map16Bit(data, AnalogOff.BatteryCurrent, 900);
const inputNodes = pxsim.ev3board().getInputNodes();
for (let port = 0; port < 4 /* DAL.NUM_INPUTS */; port++) {
const node = inputNodes[port];
if (node) {
if (node.isAnalog())
data[AnalogOff.InDcm + port] = node.getDeviceType();
data[AnalogOff.InConn + port] = node.isUart() ? 122 /* DAL.CONN_INPUT_UART */ : (!node.isNXT() ? 121 /* DAL.CONN_INPUT_DUMB */ : 119 /* DAL.CONN_NXT_DUMB */);
if (node.isAnalog() && node.hasData()) {
//data[AnalogOff.InPin6 + 2 * port] = node.getValue();
pxsim.util.map16Bit(data, node.getAnalogReadPin() + 2 * port, Math.floor(node.getValue()));
}
}
}
},
read: buf => {
let v = "vSIM";
for (let i = 0; i < buf.data.length; ++i)
buf.data[i] = v.charCodeAt(i) || 0;
return buf.data.length;
},
write: buf => {
return 2;
},
ioctl: (id, buf) => {
//console.log("ioctl: " + id);
for (let port = 0; port < 4 /* DAL.NUM_INPUTS */; port++) {
const connection = buf.data[pxsim.DevConOff.Connection + port];
const type = buf.data[pxsim.DevConOff.Type + port];
const mode = buf.data[pxsim.DevConOff.Mode + port];
//console.log(`${port}, mode: ${mode}`);
const node = pxsim.ev3board().getInputNodes()[port];
if (node)
node.setMode(mode);
}
return 2;
}
});
}
}
pxsim.EV3AnalogState = EV3AnalogState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
let NodeType;
(function (NodeType) {
NodeType[NodeType["Port"] = 0] = "Port";
NodeType[NodeType["Brick"] = 1] = "Brick";
NodeType[NodeType["TouchSensor"] = 2] = "TouchSensor";
NodeType[NodeType["MediumMotor"] = 3] = "MediumMotor";
NodeType[NodeType["LargeMotor"] = 4] = "LargeMotor";
NodeType[NodeType["GyroSensor"] = 5] = "GyroSensor";
NodeType[NodeType["ColorSensor"] = 6] = "ColorSensor";
NodeType[NodeType["UltrasonicSensor"] = 7] = "UltrasonicSensor";
NodeType[NodeType["InfraredSensor"] = 8] = "InfraredSensor";
NodeType[NodeType["NXTLightSensor"] = 9] = "NXTLightSensor";
})(NodeType = pxsim.NodeType || (pxsim.NodeType = {}));
class BaseNode {
constructor(port) {
this.isOutput = false;
this.used = false;
this.changed = true;
this.port = port;
}
didChange() {
const res = this.changed;
this.changed = false;
return res;
}
setChangedState() {
this.changed = true;
}
/**
* Updates any internal state according to the elapsed time since the last call to `updateState`
* @param elapsed
*/
updateState(elapsed) {
}
}
pxsim.BaseNode = BaseNode;
})(pxsim || (pxsim = {}));
/// <reference path="./nodeTypes.ts"/>
var pxsim;
(function (pxsim) {
class PortNode extends pxsim.BaseNode {
constructor(port) {
super(port);
this.id = pxsim.NodeType.Port;
}
}
pxsim.PortNode = PortNode;
class BrickNode extends pxsim.BaseNode {
constructor() {
super(-1);
this.id = pxsim.NodeType.Brick;
this.buttonState = new pxsim.EV3ButtonState();
this.lightState = new pxsim.EV3LightState();
}
}
pxsim.BrickNode = BrickNode;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
class EV3ButtonState extends pxsim.CommonButtonState {
constructor() {
super();
this.buttons = [
new pxsim.CommonButton(1 /* DAL.BUTTON_ID_UP */),
new pxsim.CommonButton(2 /* DAL.BUTTON_ID_ENTER */),
new pxsim.CommonButton(4 /* DAL.BUTTON_ID_DOWN */),
new pxsim.CommonButton(8 /* DAL.BUTTON_ID_RIGHT */),
new pxsim.CommonButton(16 /* DAL.BUTTON_ID_LEFT */),
new pxsim.CommonButton(32 /* DAL.BUTTON_ID_ESCAPE */)
];
let data = new Uint8Array(this.buttons.length);
pxsim.MMapMethods.register("/dev/lms_ui", {
data,
beforeMemRead: () => {
for (let i = 0; i < this.buttons.length; ++i)
data[i] = this.buttons[i].isPressed() ? 1 : 0;
},
read: buf => {
let v = "vSIM";
for (let i = 0; i < buf.data.length; ++i)
buf.data[i] = v.charCodeAt(i) || 0;
return buf.data.length;
},
write: buf => {
pxsim.output.setLights(buf.data[0] - 48);
return 2;
}
});
}
}
pxsim.EV3ButtonState = EV3ButtonState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
class SensorNode extends pxsim.BaseNode {
constructor(port) {
super(port);
}
isUart() {
return true;
}
isAnalog() {
return false;
}
isNXT() {
return false;
}
isModeReturnArr() {
return this.modeReturnArr;
}
getValue() {
return 0;
}
getValues() {
return [0];
}
getAnalogReadPin() {
return pxsim.AnalogOff.InPin6; // Defl for ev3 sensor
}
setMode(mode) {
this.mode = mode;
this.changed = true;
this.modeChanged = true;
this.modeReturnArr = false;
}
getMode() {
return this.mode;
}
getDeviceType() {
return 126 /* DAL.DEVICE_TYPE_NONE */;
}
hasData() {
return true;
}
valueChange() {
const res = this.valueChanged;
this.valueChanged = false;
return res;
}
modeChange() {
const res = this.modeChanged;
this.modeChanged = false;
return res;
}
setChangedState() {
this.changed = true;
this.valueChanged = false;
}
}
pxsim.SensorNode = SensorNode;
class AnalogSensorNode extends SensorNode {
constructor(port) {
super(port);
}
isUart() {
return false;
}
isAnalog() {
return true;
}
}
pxsim.AnalogSensorNode = AnalogSensorNode;
class UartSensorNode extends SensorNode {
constructor(port) {
super(port);
}
hasChanged() {
return this.changed;
}
}
pxsim.UartSensorNode = UartSensorNode;
})(pxsim || (pxsim = {}));
/// <reference path="./sensor.ts"/>
var pxsim;
(function (pxsim) {
let ColorSensorMode;
(function (ColorSensorMode) {
ColorSensorMode[ColorSensorMode["None"] = -1] = "None";
ColorSensorMode[ColorSensorMode["Reflected"] = 0] = "Reflected";
ColorSensorMode[ColorSensorMode["Ambient"] = 1] = "Ambient";
ColorSensorMode[ColorSensorMode["Colors"] = 2] = "Colors";
ColorSensorMode[ColorSensorMode["RefRaw"] = 3] = "RefRaw";
ColorSensorMode[ColorSensorMode["RgbRaw"] = 4] = "RgbRaw";
ColorSensorMode[ColorSensorMode["ColorCal"] = 5] = "ColorCal";
})(ColorSensorMode = pxsim.ColorSensorMode || (pxsim.ColorSensorMode = {}));
let ThresholdState;
(function (ThresholdState) {
ThresholdState[ThresholdState["Normal"] = 1] = "Normal";
ThresholdState[ThresholdState["High"] = 2] = "High";
ThresholdState[ThresholdState["Low"] = 3] = "Low";
})(ThresholdState = pxsim.ThresholdState || (pxsim.ThresholdState = {}));
class ColorSensorNode extends pxsim.UartSensorNode {
constructor(port) {
super(port);
this.id = pxsim.NodeType.ColorSensor;
this.color = 0;
this.colors = [0, 0, 0];
this.mode = -1;
this.modeReturnArr = false;
}
getDeviceType() {
return 29 /* DAL.DEVICE_TYPE_COLOR */;
}
isModeReturnArr() {
return this.modeReturnArr;
}
setColors(colors) {
this.colors = colors;
this.setChangedState();
}
setColor(color) {
this.color = color;
this.setChangedState();
}
getValue() {
return this.color;
}
getValues() {
return this.colors;
}
setMode(mode) {
this.mode = mode;
if (this.mode == ColorSensorMode.RefRaw) {
this.color = 512;
this.colors = [0, 0, 0];
this.modeReturnArr = false;
}
else if (this.mode == ColorSensorMode.RgbRaw) {
this.color = 0;
this.colors = [128, 128, 128];
this.modeReturnArr = true;
}
else if (this.mode == ColorSensorMode.Colors) {
this.color = 0; // None defl color
this.colors = [0, 0, 0];
this.modeReturnArr = false;
}
else { // Reflection or ambiend light
this.color = 50;
this.colors = [0, 0, 0];
this.modeReturnArr = false;
}
this.changed = true;
this.modeChanged = true;
}
}
pxsim.ColorSensorNode = ColorSensorNode;
})(pxsim || (pxsim = {}));
/// <reference path="../../libs/core/enums.d.ts"/>
var pxsim;
(function (pxsim) {
var MMapMethods;
(function (MMapMethods) {
var BM = pxsim.BufferMethods;
class MMap extends pxsim.RefObject {
constructor(impl, len) {
super();
this.impl = impl;
this.len = len;
if (!impl.data)
impl.data = new Uint8Array(this.len);
if (!impl.afterMemWrite)
impl.afterMemWrite = () => { };
if (!impl.beforeMemRead)
impl.beforeMemRead = () => { };
if (!impl.read)
impl.read = () => 0;
if (!impl.write)
impl.write = () => 0;
if (!impl.ioctl)
impl.ioctl = () => -1;
if (!impl.lseek)
impl.lseek = (offset, whence) => -1;
}
destroy() {
}
buf() {
return { data: this.impl.data };
}
}
MMapMethods.MMap = MMap;
MMapMethods.mmapRegistry = {};
function register(filename, impl) {
MMapMethods.mmapRegistry[filename] = impl;
}
MMapMethods.register = register;
function setNumber(m, format, offset, value) {
BM.setNumber(m.buf(), format, offset, value);
m.impl.afterMemWrite();
}
MMapMethods.setNumber = setNumber;
function getNumber(m, format, offset) {
m.impl.beforeMemRead();
return BM.getNumber(m.buf(), format, offset);
}
MMapMethods.getNumber = getNumber;
function slice(m, offset, length) {
m.impl.beforeMemRead();
return BM.slice(m.buf(), offset, length);
}
MMapMethods.slice = slice;
function length(m) {
m.impl.beforeMemRead();
return m.buf().data.length;
}
MMapMethods.length = length;
function ioctl(m, id, data) {
return m.impl.ioctl(id, data);
}
MMapMethods.ioctl = ioctl;
function write(m, data) {
return m.impl.write(data);
}
MMapMethods.write = write;
function read(m, data) {
return m.impl.read(data);
}
MMapMethods.read = read;
function lseek(m, offset, whence) {
return m.impl.lseek(offset, whence);
}
MMapMethods.lseek = lseek;
})(MMapMethods = pxsim.MMapMethods || (pxsim.MMapMethods = {}));
})(pxsim || (pxsim = {}));
(function (pxsim) {
var control;
(function (control) {
function mmap(filename, size, offset) {
let impl = pxsim.MMapMethods.mmapRegistry[filename];
if (!impl)
impl = {};
return new pxsim.MMapMethods.MMap(impl, size);
}
control.mmap = mmap;
})(control = pxsim.control || (pxsim.control = {}));
})(pxsim || (pxsim = {}));
(function (pxsim) {
var output;
(function (output) {
function createBuffer(size) {
return pxsim.BufferMethods.createBuffer(size);
}
output.createBuffer = createBuffer;
})(output = pxsim.output || (pxsim.output = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
class GyroSensorNode extends pxsim.UartSensorNode {
constructor(port) {
super(port);
this.id = pxsim.NodeType.GyroSensor;
this.rate = 0;
}
getDeviceType() {
return 32 /* DAL.DEVICE_TYPE_GYRO */;
}
setRate(rate) {
rate = rate | 0;
if (this.rate != rate) {
this.rate = rate;
this.setChangedState();
}
}
getRate() {
return this.rate;
}
getValue() {
return this.getRate();
}
}
pxsim.GyroSensorNode = GyroSensorNode;
})(pxsim || (pxsim = {}));
/// <reference path="./sensor.ts"/>
var pxsim;
(function (pxsim) {
let InfraredRemoteButton;
(function (InfraredRemoteButton) {
//% block="center beacon"
InfraredRemoteButton[InfraredRemoteButton["CenterBeacon"] = 1] = "CenterBeacon";
//% block="top left"
InfraredRemoteButton[InfraredRemoteButton["TopLeft"] = 2] = "TopLeft";
//% block="bottom left"
InfraredRemoteButton[InfraredRemoteButton["BottomLeft"] = 4] = "BottomLeft";
//% block="top right"
InfraredRemoteButton[InfraredRemoteButton["TopRight"] = 8] = "TopRight";
//% block="bottom right"
InfraredRemoteButton[InfraredRemoteButton["BottomRight"] = 16] = "BottomRight";
})(InfraredRemoteButton = pxsim.InfraredRemoteButton || (pxsim.InfraredRemoteButton = {}));
class RemoteState {
constructor() {
this.state = 0;
}
unmapButtons() {
switch (this.state) {
case InfraredRemoteButton.TopLeft: return 1;
case InfraredRemoteButton.BottomLeft: return 2;
case InfraredRemoteButton.TopRight: return 3;
case InfraredRemoteButton.BottomRight: return 4;
case InfraredRemoteButton.TopLeft | InfraredRemoteButton.TopRight: return 5;
case InfraredRemoteButton.TopLeft | InfraredRemoteButton.BottomRight: return 6;
case InfraredRemoteButton.BottomLeft | InfraredRemoteButton.TopRight: return 7;
case InfraredRemoteButton.BottomLeft | InfraredRemoteButton.BottomRight: return 8;
case InfraredRemoteButton.CenterBeacon: return 9;
case InfraredRemoteButton.BottomLeft | InfraredRemoteButton.TopLeft: return 10;
case InfraredRemoteButton.TopRight | InfraredRemoteButton.BottomRight: return 11;
default: return 0;
}
}
setPressed(btns, down) {
if (down)
this.state = this.state | btns;
else
this.state = ~(~this.state | btns);
}
}
pxsim.RemoteState = RemoteState;
let InfraredSensorMode;
(function (InfraredSensorMode) {
InfraredSensorMode[InfraredSensorMode["None"] = -1] = "None";
InfraredSensorMode[InfraredSensorMode["Proximity"] = 0] = "Proximity";
InfraredSensorMode[InfraredSensorMode["Seek"] = 1] = "Seek";
InfraredSensorMode[InfraredSensorMode["RemoteControl"] = 2] = "RemoteControl";
})(InfraredSensorMode = pxsim.InfraredSensorMode || (pxsim.InfraredSensorMode = {}));
class InfraredSensorNode extends pxsim.UartSensorNode {
constructor(port) {
super(port);
this.id = pxsim.NodeType.InfraredSensor;
this.proximity = 50; // [0..100]
}
getDeviceType() {
return 33 /* DAL.DEVICE_TYPE_IR */;
}
setPromixity(proximity) {
if (this.proximity != proximity) {
this.proximity = proximity;
this.setChangedState();
}
}
getValue() {
switch (this.mode) {
case InfraredSensorMode.Proximity: return this.proximity;
case InfraredSensorMode.RemoteControl: return pxsim.ev3board().remoteState.unmapButtons();
default: return 0;
}
}
}
pxsim.InfraredSensorNode = InfraredSensorNode;
})(pxsim || (pxsim = {}));
var lf = pxsim.localization.lf;
var pxsim;
(function (pxsim) {
var motors;
(function (motors) {
function portsToString(out) {
let r = "";
for (let i = 0; i < 4 /* DAL.NUM_OUTPUTS */; ++i) {
if (out & (1 << i)) {
if (r.length > 0)
r += "+";
r += "ABCD"[i];
}
}
return r;
}
function __motorUsed(ports, large) {
//console.log("MOTOR INIT " + port);
if (pxsim.ev3board().motorUsed(ports, large))
pxsim.runtime.queueDisplayUpdate();
else
pxsim.U.userError(`${lf("Multiple motors are connected to Port")} ${portsToString(ports)}`);
}
motors.__motorUsed = __motorUsed;
})(motors = pxsim.motors || (pxsim.motors = {}));
})(pxsim || (pxsim = {}));
(function (pxsim) {
var sensors;
(function (sensors) {
function __sensorUsed(port, type) {
//console.log("SENSOR INIT " + port + ", type: " + type);
if (type == 100 /* DAL.DEVICE_TYPE_IIC_UNKNOWN */)
return; // Ignore IIC
if (!pxsim.ev3board().hasSensor(port)) {
const sensor = pxsim.ev3board().getSensor(port, type);
pxsim.runtime.queueDisplayUpdate();
}
else {
pxsim.U.userError(`${lf("Multiple sensors are connected to Port")} ${port + 1}`);
}
}
sensors.__sensorUsed = __sensorUsed;
})(sensors = pxsim.sensors || (pxsim.sensors = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
class EV3LightState {
constructor() {
this.lightPattern = 0;
}
}
pxsim.EV3LightState = EV3LightState;
})(pxsim || (pxsim = {}));
(function (pxsim) {
var output;
(function (output) {
function setLights(pattern) {
const brickState = pxsim.ev3board().getBrickNode();
const lightState = brickState.lightState;
if (lightState.lightPattern != pattern) {
lightState.lightPattern = pattern;
brickState.setChangedState();
}
}
output.setLights = setLights;
})(output = pxsim.output || (pxsim.output = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
let MotorDataOff;
(function (MotorDataOff) {
MotorDataOff[MotorDataOff["TachoCounts"] = 0] = "TachoCounts";
MotorDataOff[MotorDataOff["Speed"] = 4] = "Speed";
MotorDataOff[MotorDataOff["Padding"] = 5] = "Padding";
MotorDataOff[MotorDataOff["TachoSensor"] = 8] = "TachoSensor";
MotorDataOff[MotorDataOff["Size"] = 12] = "Size";
})(MotorDataOff || (MotorDataOff = {}));
class EV3MotorState {
constructor() {
let data = new Uint8Array(12 * 4 /* DAL.NUM_OUTPUTS */);
pxsim.MMapMethods.register("/dev/lms_motor", {
data,
beforeMemRead: () => {
const outputs = pxsim.ev3board().outputNodes;
// console.log("motor before read");
for (let port = 0; port < 4 /* DAL.NUM_OUTPUTS */; ++port) {
const output = outputs[port];
const speed = output ? outputs[port].getSpeed() : 0;
const angle = output ? outputs[port].getAngle() : 0;
const tci = MotorDataOff.TachoCounts + port * MotorDataOff.Size;
const tsi = MotorDataOff.TachoSensor + port * MotorDataOff.Size;
data[tci] = data[tci + 1] = data[tci + 2] = data[tci + 3] = 0; // Tacho count
data[MotorDataOff.Speed + port * MotorDataOff.Size] = speed; // Speed
data[tsi] = angle & 0xff; // Count
data[tsi + 1] = (angle >> 8) & 0xff; // Count
data[tsi + 2] = (angle >> 16) & 0xff; // Count
data[tsi + 3] = (angle >> 24); // Count
}
},
read: buf => {
let v = "vSIM";
for (let i = 0; i < buf.data.length; ++i)
buf.data[i] = v.charCodeAt(i) || 0;
return buf.data.length;
},
write: buf => {
if (buf.data.length == 0)
return 2;
const cmd = buf.data[0];
return 2;
},
ioctl: (id, buf) => {
return 2;
}
});
}
}
pxsim.EV3MotorState = EV3MotorState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
const MIN_RAMP_SPEED = 3;
class MotorNode extends pxsim.BaseNode {
constructor(port, large) {
super(port);
this.isOutput = true;
// current state
this.angle = 0;
this.tacho = 0;
this.speed = 0;
this.manualReferenceAngle = undefined;
this.manualAngle = undefined;
this.setLarge(large);
}
isReady() {
return !this.speedCmd;
}
getSpeed() {
return Math.round(this.speed);
}
getAngle() {
return Math.round(this.angle);
}
// returns the secondary motor if any
getSynchedMotor() {
return this._synchedMotor;
}
setSpeedCmd(cmd, values) {
if (this.speedCmd != cmd ||
JSON.stringify(this.speedCmdValues) != JSON.stringify(values))
this.setChangedState();
// new command TODO: values
this.speedCmd = cmd;
this.speedCmdValues = values;
this.speedCmdTacho = this.tacho;
this.speedCmdTime = pxsim.U.now();
delete this._synchedMotor;
}
setSyncCmd(motor, cmd, values) {
this.setSpeedCmd(cmd, values);
this._synchedMotor = motor;
}
clearSpeedCmd() {
delete this.speedCmd;
delete this.speedCmdValues;
delete this._synchedMotor;
this.setChangedState();
}
clearSyncCmd() {
if (this._synchedMotor)
this.clearSpeedCmd();
}
setLarge(large) {
this.id = large ? pxsim.NodeType.LargeMotor : pxsim.NodeType.MediumMotor;
// large 170 rpm (https://education.lego.com/en-us/products/ev3-large-servo-motor/45502)
this.rotationsPerMilliSecond = (large ? 170 : 250) / 60000;
}
isLarge() {
return this.id == pxsim.NodeType.LargeMotor;
}
reset() {
// not sure what reset does...
}
clearCount() {
this.tacho = 0;
this.angle = 0;
}
stop() {
this.started = false;
this.clearSpeedCmd();
}
start() {
this.started = true;
}
manualMotorDown() {
this.manualReferenceAngle = this.angle;
this.manualAngle = 0;
}
// position: 0, 360
manualMotorAngle(angle) {
this.manualAngle = angle;
}
manualMotorUp() {
delete this.manualReferenceAngle;
delete this.manualAngle;
}
updateState(elapsed) {
//console.log(`motor: ${elapsed}ms - ${this.speed}% - ${this.angle}> - ${this.tacho}|`)
const interval = Math.min(20, elapsed);
let t = 0;
while (t < elapsed) {
let dt = interval;
if (t + dt > elapsed)
dt = elapsed - t;
this.updateStateStep(dt);
t += dt;
}
}
updateStateStep(elapsed) {
if (this.manualAngle === undefined) {
// compute new speed
switch (this.speedCmd) {
case 165 /* DAL.opOutputSpeed */:
case 164 /* DAL.opOutputPower */:
// assume power == speed
// TODO: PID
this.speed = this.speedCmdValues[0];
break;
case 175 /* DAL.opOutputTimeSpeed */:
case 173 /* DAL.opOutputTimePower */:
case 172 /* DAL.opOutputStepPower */:
case 174 /* DAL.opOutputStepSpeed */: {
// ramp up, run, ramp down, <brake> using time
const speed = this.speedCmdValues[0];
const step1 = this.speedCmdValues[1];
const step2 = this.speedCmdValues[2];
const step3 = this.speedCmdValues[3];
const brake = this.speedCmdValues[4];
const isTimeCommand = this.speedCmd == 173 /* DAL.opOutputTimePower */ || this.speedCmd == 175 /* DAL.opOutputTimeSpeed */;
const dstep = isTimeCommand
? pxsim.U.now() - this.speedCmdTime
: this.tacho - this.speedCmdTacho;
if (step1 && dstep < step1) { // rampup
this.speed = speed * dstep / step1;
// ensure non-zero speed
this.speed = Math.max(MIN_RAMP_SPEED, Math.ceil(Math.abs(this.speed))) * Math.sign(speed);
}
else if (dstep < step1 + step2) // run
this.speed = speed;
else if (step2 && dstep < step1 + step2 + step3) {
this.speed = speed * (step1 + step2 + step3 - dstep)
/ (step1 + step2 + step3) + 5;
// ensure non-zero speed
this.speed = Math.max(MIN_RAMP_SPEED, Math.ceil(Math.abs(this.speed))) * Math.sign(speed);
}
else {
if (brake)
this.speed = 0;
if (!isTimeCommand) {
// we need to patch the actual position of the motor when
// finishing the move as our integration step introduce errors
const deltaAngle = -Math.sign(speed) * (dstep - (step1 + step2 + step3));
if (deltaAngle) {
this.angle += deltaAngle;
this.tacho -= Math.abs(deltaAngle);
this.setChangedState();
}
}
this.clearSpeedCmd();
}
break;
}
case 176 /* DAL.opOutputStepSync */:
case 177 /* DAL.opOutputTimeSync */: {
const otherMotor = this._synchedMotor;
const speed = this.speedCmdValues[0];
const turnRatio = this.speedCmdValues[1];
// if turnratio is negative, right motor at power level
// right motor -> this.port > otherMotor.port
if (Math.sign(this.port - otherMotor.port)
== Math.sign(turnRatio))
break; // handled in other motor code
const stepsOrTime = this.speedCmdValues[2];
const brake = this.speedCmdValues[3];
const dstep = this.speedCmd == 177 /* DAL.opOutputTimeSync */
? pxsim.U.now() - this.speedCmdTime
: this.tacho - this.speedCmdTacho;
// 0 is special case, run infinite
if (!stepsOrTime || dstep < stepsOrTime)
this.speed = speed;
else {
if (brake)
this.speed = 0;
this.clearSpeedCmd();
}
// turn ratio is a bit weird to interpret
// see https://communities.theiet.org/blogs/698/1706
otherMotor.speed = this.speed * (100 - Math.abs(turnRatio)) / 100;
// clamp
this.speed = Math.max(-100, Math.min(100, this.speed >> 0));
otherMotor.speed = Math.max(-100, Math.min(100, otherMotor.speed >> 0));
;
// stop other motor if needed
if (!this._synchedMotor)
otherMotor.clearSpeedCmd();
break;
}
}
}
else {
// the user is holding the handle - so position is the angle
this.speed = 0;
// rotate by the desired angle change
this.angle = this.manualReferenceAngle + this.manualAngle;
this.setChangedState();
}
// don't round speed
// compute delta angle
const rotations = this.speed / 100 * this.rotationsPerMilliSecond * elapsed;
const deltaAngle = rotations * 360;
if (deltaAngle) {
this.angle += deltaAngle;
this.tacho += Math.abs(deltaAngle);
this.setChangedState();
}
// if the motor was stopped or there are no speed commands,
// let it coast to speed 0
if ((this.manualReferenceAngle === undefined)
&& this.speed && !(this.started || this.speedCmd)) {
// decay speed 5% per tick
this.speed = Math.round(Math.max(0, Math.abs(this.speed) - 10) * pxsim.sign(this.speed));
}
}
}
pxsim.MotorNode = MotorNode;
})(pxsim || (pxsim = {}));
(function (pxsim) {
// A re-implementation of Math.sign (since IE11 doesn't support it)
function sign(num) {
return num ? num < 0 ? -1 : 1 : 0;
}
pxsim.sign = sign;
})(pxsim || (pxsim = {}));
/// <reference path="./sensor.ts"/>
var pxsim;
(function (pxsim) {
let NXTLightSensorMode;
(function (NXTLightSensorMode) {
NXTLightSensorMode[NXTLightSensorMode["None"] = -1] = "None";
NXTLightSensorMode[NXTLightSensorMode["ReflectedLightRaw"] = 0] = "ReflectedLightRaw";
NXTLightSensorMode[NXTLightSensorMode["ReflectedLight"] = 1] = "ReflectedLight";
NXTLightSensorMode[NXTLightSensorMode["AmbientLightRaw"] = 2] = "AmbientLightRaw";
NXTLightSensorMode[NXTLightSensorMode["AmbientLight"] = 3] = "AmbientLight";
})(NXTLightSensorMode = pxsim.NXTLightSensorMode || (pxsim.NXTLightSensorMode = {}));
class NXTLightSensorNode extends pxsim.AnalogSensorNode {
constructor(port) {
super(port);
this.id = pxsim.NodeType.NXTLightSensor;
this.value = 0;
this.darkReflectedLight = 3372;
this.brightReflectedLight = 445;
this.darkAmbientLight = 3411;
this.brightAmbientLight = 633;
this.mode = -1;
}
getDeviceType() {
return 2 /* DAL.DEVICE_TYPE_NXT_LIGHT */;
}
setValue(value) {
this.value = value;
this.setChangedState();
}
getValue() {
return this.value;
}
setMode(mode) {
this.mode = mode;
if (this.mode == NXTLightSensorMode.ReflectedLight)
this.value = 1908;
else if (this.mode == NXTLightSensorMode.AmbientLight)
this.value = 2022;
else
this.value = 2048;
this.changed = true;
this.modeChanged = true;
}
getAnalogReadPin() {
return pxsim.AnalogOff.InPin1;
}
isNXT() {
return true;
}
}
pxsim.NXTLightSensorNode = NXTLightSensorNode;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
class EV3OutputState {
constructor() {
let data = new Uint8Array(10);
pxsim.MMapMethods.register("/dev/lms_pwm", {
data,
beforeMemRead: () => {
//console.log("pwm before read");
for (let i = 0; i < 10; ++i)
data[i] = 0;
},
read: buf => {
// console.log("pwm read");
if (buf.data.length == 0)
return 2;
const cmd = buf.data[0];
switch (cmd) {
case 169 /* DAL.opOutputTest */:
const port = buf.data[1];
let r = 0;
pxsim.ev3board().getMotor(port)
.filter(motor => !motor.isReady())
.forEach(motor => r |= (1 << motor.port));
pxsim.BufferMethods.setNumber(buf, pxsim.BufferMethods.NumberFormat.UInt8LE, 2, r);
break;
default:
let v = "vSIM";
for (let i = 0; i < buf.data.length; ++i)
buf.data[i] = v.charCodeAt(i) || 0;
break;
}
return buf.data.length;
},
write: buf => {
if (buf.data.length == 0)
return 2;
const cmd = buf.data[0];
switch (cmd) {
case 3 /* DAL.opProgramStart */: {
// init
return 2;
}
case 162 /* DAL.opOutputReset */: {
// reset
const port = buf.data[1];
const motors = pxsim.ev3board().getMotor(port);
motors.forEach(motor => motor.reset());
return 2;
}
case 178 /* DAL.opOutputClearCount */:
const port = buf.data[1];
const motors = pxsim.ev3board().getMotor(port);
motors.forEach(motor => motor.clearCount());
break;
case 172 /* DAL.opOutputStepPower */:
case 174 /* DAL.opOutputStepSpeed */:
case 173 /* DAL.opOutputTimePower */:
case 175 /* DAL.opOutputTimeSpeed */: {
// step speed
const port = buf.data[1];
const speed = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int8LE, 2); // signed byte
// note that b[3] is padding
const step1 = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int32LE, 4);
const step2 = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int32LE, 8);
const step3 = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int32LE, 12);
const brake = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int8LE, 16);
//console.log(buf);
const motors = pxsim.ev3board().getMotor(port);
motors.forEach(motor => motor.setSpeedCmd(cmd, [speed, step1, step2, step3, brake]));
return 2;
}
case 176 /* DAL.opOutputStepSync */:
case 177 /* DAL.opOutputTimeSync */: {
const port = buf.data[1];
const speed = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int8LE, 2); // signed byte
// note that b[3] is padding
const turnRatio = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int16LE, 4);
// b[6], b[7] is padding
const stepsOrTime = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int32LE, 8);
const brake = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int8LE, 12);
const motors = pxsim.ev3board().getMotor(port);
// cancel any other sync command
for (const motor of pxsim.ev3board().getMotors().filter(motor => motors.indexOf(motor) < 0)) {
motor.clearSyncCmd();
}
// apply commands to all motors
for (const motor of motors) {
const otherMotor = motors.filter(m => m.port != motor.port)[0];
motor.setSyncCmd(otherMotor, cmd, [speed, turnRatio, stepsOrTime, brake]);
}
return 2;
}
case 163 /* DAL.opOutputStop */: {
// stop
const port = buf.data[1];
const brake = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int8LE, 2);
const motors = pxsim.ev3board().getMotor(port);
motors.forEach(motor => motor.stop());
return 2;
}
case 164 /* DAL.opOutputPower */:
case 165 /* DAL.opOutputSpeed */: {
// setSpeed
const port = buf.data[1];
const speed = pxsim.BufferMethods.getNumber(buf, pxsim.BufferMethods.NumberFormat.Int8LE, 2);
const motors = pxsim.ev3board().getMotor(port);
motors.forEach(motor => motor.setSpeedCmd(cmd, [speed]));
return 2;
}
case 166 /* DAL.opOutputStart */: {
// start
const port = buf.data[1];
const motors = pxsim.ev3board().getMotor(port);
motors.forEach(motor => motor.start());
return 2;
}
case 167 /* DAL.opOutputPolarity */: {
console.error("opOutputPolarity not supported");
return 2;
}
case 161 /* DAL.opOutputSetType */: {
const portIndex = buf.data[1]; // not a port but a port index 0..3
const large = buf.data[2] == 0x07;
const motor = pxsim.ev3board().getMotors()[portIndex];
if (motor)
motor.setLarge(large);