blocklypy
Version:
BlocklyPy: SPIKE to Pybricks - word-block converter to Pybricks python code
1,535 lines • 727 kB
JavaScript
class qn {
constructor(e, n) {
this.id = qn.idToString(e), this.payload = n;
}
static idToString(e) {
return typeof e == "string" ? e : JSON.stringify(e);
}
}
function km(r) {
return !!r && typeof r.use == "function";
}
function Vm(r) {
return !!r;
}
function Gm(r) {
return !!r;
}
class hi {
constructor(e, n) {
this.context = e, this.factory = n, this.registry = /* @__PURE__ */ new Map();
}
get(e) {
return this.registry.get(qn.idToString(e))?.payload;
}
has(e) {
return this.registry.has(qn.idToString(e));
}
use(e, ...n) {
let o = this.registry.get(qn.idToString(e));
if (!o) {
const a = typeof this.factory == "function" ? this.factory(...n) : n[0];
Vm(a) && (a.id = e), Gm(a) && (a.parent = this), o = new qn(e, a), this.registry.set(qn.idToString(e), o);
}
return o.payload && km(o.payload) && o.payload.use(...n), o.payload;
}
entries() {
return [...this.registry.entries()];
}
values() {
return [...this.registry.values()];
}
payloads() {
return [...this.registry.values()].map((e) => e.payload);
}
clear() {
this.registry.clear();
}
}
const fs = " ", hs = "cm", ps = "inches", Pn = "rotations", Rs = "degrees", pi = "seconds", pn = "AUTO";
var ti = /* @__PURE__ */ ((r) => (r[r.DEBUG = 0] = "DEBUG", r[r.INFO = 1] = "INFO", r[r.WARNING = 2] = "WARNING", r[r.ERROR = 3] = "ERROR", r[r.CRITICAL = 4] = "CRITICAL", r))(ti || {});
let wc = 0, qo;
function $m(r) {
wc = r;
}
function ec(r) {
qo = r;
}
function ta(r, e, n = "-", o = 80) {
const a = o - r.length - 6 - e?.length;
return `#${e} ${n.repeat(
Math.floor(a / 2)
)} ${r} ${n.repeat(Math.ceil(a / 2))} #`;
}
function Wm(r, ...e) {
if (wc <= r)
if (qo)
qo(r, ...e);
else
switch (r) {
case 4:
case 3:
console.error(`::${ti[r]}::`, ...e);
break;
case 2:
console.warn(`::${ti[r]}::`, ...e);
break;
case 1:
console.info(`::${ti[r]}::`, ...e);
break;
case 0:
default:
console.debug(`::${ti[r]}::`, ...e);
break;
}
}
const fe = (...r) => Wm(2, ...r);
function ae(r, e = 1) {
let n = [];
return r === void 0 ? n = [] : Array.isArray(r) ? n = r.reduce(
(o, a) => [...o, ...ae(a, e)],
[]
) : r.includes(`
`) ? n = ae(r.split(/\r?\n/), e) : n = [fs.repeat(e) + r], n;
}
function Ar(r, e = !0) {
if (!r.filter((n) => !n.match(/^\s*$|^\s*#.*$/)).length) {
r.push("pass");
//!!
}
return r;
}
function Gn(r) {
return r?.trim().toLowerCase().replaceAll(/[ .-]/gi, "_").replace(/^(\d)/im, "_$1").replaceAll(
/[áéíóöőúüű]/g,
(e) => ({
á: "a",
é: "e",
í: "i",
ó: "o",
ö: "o",
ő: "o",
ú: "u",
ü: "u",
ű: "u"
})[e] ?? e
).replace(/[^a-zA-Z0-9_]/g, "");
}
function tr(r) {
return r?.replace(/\.[^/.]+$/, "").replaceAll(" ", "_");
}
function Qe(r, e = !0) {
return r = r?.replaceAll(/\\(.)/g, "$1").replaceAll(" ", "_").replaceAll(/[\/,.]/g, ""), e && (r = r?.toUpperCase()), r;
}
function Ym(r) {
return typeof r == "number";
}
function $n(r) {
const e = r?.lastIndexOf(".");
return r === void 0 || e === -1 || e === void 0 ? "" : r.substring(e);
}
function Je(r) {
return r?.replace(/^.*[\\/]/, "");
}
function _s(r) {
return r?.split(".").slice(0, -1).join(".");
}
function ca(r) {
const e = r?.trim().toLowerCase();
return e === "true" || e === "1";
}
function Se(r) {
return parseInt(r ?? "0");
}
function fa(r) {
return `"""\r
` + r.map(([e, n]) => `${(e + ":").padEnd(12, " ")}${n}`).join(`\r
`) + `\r
"""\r
`;
}
function ha(r, e) {
return e.extra || (e.extra = {}), e.extra[r] || (e.extra[r] = {}), e.extra[r];
}
class pa {
constructor(e) {
this.name = e;
}
// Generates the code for the broadcast registry payload
get_code(e) {
const n = e.map((o) => Gn(o));
return `${this.get_pyname()} = Message([${n.join(", ")}])`;
}
// Returns the sanitized Python name for the message
get_pyname() {
return `message_${Gn(this.name)}`;
}
// Creates a new registry manager for the broadcast registry payload
static createRegistry(e) {
return new hi(
e,
(...n) => new pa(n[0])
);
}
}
var Gt = /* @__PURE__ */ ((r) => (r[r.NONE = 0] = "NONE", r[r.SPIKE = 1] = "SPIKE", r[r.ROBOTINVENTOR = 2] = "ROBOTINVENTOR", r[r._GEN_EV5 = 3] = "_GEN_EV5", r[r.EV3CLASSROOM = 4] = "EV3CLASSROOM", r[r._SB3_FORMAT = 7] = "_SB3_FORMAT", r[r.EV3G = 8] = "EV3G", r[r.EV3B = 16] = "EV3B", r[r._GEN_EV3 = 28] = "_GEN_EV3", r[r._EV3Lab_FORMAT = 24] = "_EV3Lab_FORMAT", r[r.PYTHON = 32] = "PYTHON", r[r.WEDO2 = 64] = "WEDO2", r))(Gt || {}), Ht = /* @__PURE__ */ ((r) => (r[r.SIMPLE = 0] = "SIMPLE", r[r.PARENTHESIS = 1] = "PARENTHESIS", r[r.REFERENCE = 2] = "REFERENCE", r[r.EXPONENTIATION = 3] = "EXPONENTIATION", r[r.UNARY = 4] = "UNARY", r[r.BINARYOP_MUL = 5] = "BINARYOP_MUL", r[r.BINARY_ADD = 6] = "BINARY_ADD", r[r.BINARY_SHIFT = 7] = "BINARY_SHIFT", r[r.BINARY_AND = 8] = "BINARY_AND", r[r.BINARY_XOR = 9] = "BINARY_XOR", r[r.BINARY_OR = 10] = "BINARY_OR", r[r.BINARY_COMPARISON = 11] = "BINARY_COMPARISON", r[r.BOOLEAN_NOT = 12] = "BOOLEAN_NOT", r[r.BOOLEAN_AND = 13] = "BOOLEAN_AND", r[r.BOOLEAN_OR = 14] = "BOOLEAN_OR", r[r.WEAKEST = 99] = "WEAKEST", r))(Ht || {}), q = /* @__PURE__ */ ((r) => (r[r.UNKNOWN = 0] = "UNKNOWN", r[r.NUMBER = 10] = "NUMBER", r[r.STRING = 11] = "STRING", r[r.BOOLEAN = 12] = "BOOLEAN", r[r.ENUM = 13] = "ENUM", r[r.NUMBERARRAY = 20] = "NUMBERARRAY", r[r.BOOLEANARRAY = 21] = "BOOLEANARRAY", r[r.ENUMARRAY = 23] = "ENUMARRAY", r))(q || {});
class K {
constructor(e, n) {
this.options = n, e && K.is(e) ? this.value = e.value : this.value = e;
}
get is_string() {
return this.options?.type === q.STRING;
}
get is_numeric() {
return this.options?.type === q.NUMBER || !this.options?.is_dynamic && !this.is_string && typeof this.value == "number";
}
get raw() {
return !this.is_string || this.options?.is_dynamic ? this.value : `"${this.value}"`;
}
toString() {
return K.toString(this);
}
toInt() {
return K.toInt(this);
}
toFloat() {
return K.toFloat(this);
}
toBool() {
return K.toBool(this);
}
ensureString(e) {
return ea(e, this, q.STRING);
}
ensureNumber(e, n = !1) {
if (this.is_numeric) {
if (this.options?.is_dynamic)
return this;
{
const o = n ? this.toInt() : this.toFloat();
return new K(o, { type: q.NUMBER });
}
} else
return ea(e, this, q.NUMBER, n);
}
static is(e) {
return e instanceof K;
}
static is_dynamic(e) {
return K.is(e) && !!e.options?.is_dynamic;
}
static raw(e) {
return K.is(e) ? e.raw : e;
}
static value(e) {
return K.is(e) ? e.value : e;
}
static toString(e) {
return K.value(e)?.toString() ?? "";
}
static toInt(e) {
const n = K.is(e) ? e.value : e;
return parseInt(n?.toString() ?? "");
}
static toFloat(e) {
const n = K.is(e) ? e.value : e;
return parseFloat(n?.toString() ?? "");
}
static toBool(e) {
const n = K.is(e) ? e.value : e;
return ca(n?.toString());
}
static ensureNumber(e, n, o = !1) {
return K.is(n) ? n.ensureNumber(e) : e.helpers.use(o ? "int_safe" : "float_safe").call(n);
}
static isEqual(e, n) {
return K.is(e) && K.is(n) ? e.value === n.value && JSON.stringify(e.options) === JSON.stringify(n.options) : e === n;
}
}
function ie(r, e, n = !1) {
const [o, a, l] = Array.isArray(e) ? e : [e, void 0, void 0];
if (a === void 0) {
if (Array.isArray(o))
return ie(r, o, n);
if (!K.is_dynamic(o)) {
const f = Math.round(parseFloat(o?.toString() ?? "") * 1e3) / 1e3;
return new K(f, { type: q.NUMBER });
}
return !K.is(o) || o.options?.type !== q.NUMBER ? K.ensureNumber(r, o, n) : new K(o, {
type: q.NUMBER,
is_dynamic: o.options.is_dynamic,
precedence: o.options.precedence
});
} else if (l === void 0) {
const f = ie(r, a, n), c = !K.is_dynamic(f);
if (o === "-" || o === "+")
if (c) {
const _ = (o === "-" ? -1 : 1) * K.toFloat(f);
return new K(_, { type: q.NUMBER });
} else {
const h = `${o}${K.raw(f)}`;
return new K(h, {
is_dynamic: !0,
type: q.NUMBER,
precedence: Ht.UNARY
});
}
else if (o === "abs")
return c ? new K(Math.abs(K.toFloat(f)), {
type: q.NUMBER
}) : (r.imports.use("umath", null), new K(`umath.fabs(${K.raw(f)})`, {
is_dynamic: !0,
type: q.NUMBER,
precedence: Ht.SIMPLE
}));
} else {
const f = ie(r, o, n), c = ie(r, l, n);
let h = K.raw(f), _ = K.raw(c);
if (!K.is_dynamic(f) && !K.is_dynamic(c)) {
const C = (g, R, O) => {
switch (R) {
case "+":
return g + O;
case "-":
return g - O;
case "*":
return g * O;
case "/":
return g / O;
case "%":
return g % O;
}
};
if (h === void 0 || a === void 0 || _ === void 0)
return;
const N = ie(
r,
C(K.toFloat(h), a?.toString(), K.toFloat(_))
);
return new K(N, {
type: q.NUMBER,
precedence: Ht.SIMPLE
});
} else {
const C = ie(r, o, n), N = ie(r, l, n), g = a?.toString(), R = (S) => S?.toString() === "0", O = (S) => S?.toString() === "1", v = (S) => S?.toString() === "-1";
if (g === "*") {
if (R(C) || O(N)) return C;
if (R(N) || O(C)) return N;
if (v(C)) return ie(r, ["-", N]);
if (v(N)) return ie(r, ["-", C]);
} else if (g === "/") {
if (R(C) || O(N)) return C;
if (v(N)) return ie(r, ["-", C]);
} else if (g === "+") {
if (R(C)) return N;
if (R(N)) return C;
} else if (g === "-") {
if (R(C)) return ie(r, ["-", N]);
if (R(N)) return C;
}
const P = (() => {
switch (a.toString()) {
case "-":
case "+":
return Ht.BINARY_ADD;
case "*":
case "/":
case "%":
return Ht.BINARYOP_MUL;
default:
return Ht.WEAKEST;
}
})();
return P < (C?.options?.precedence ?? 0) && (h = `(${h})`), P < (N?.options?.precedence ?? 0) && (_ = `(${_})`), new K(`${h} ${a} ${_}`, {
is_dynamic: !0,
type: q.NUMBER,
precedence: P
});
}
}
}
function ea(r, e, n, o = !1) {
const a = e?.options?.type;
if (n !== a)
switch (n) {
case q.NUMBER:
e = o ? r.helpers.use("int_safe").call(e) : r.helpers.use("float_safe").call(e);
break;
case q.STRING:
e = r.helpers.use("str").call(e);
break;
case q.NUMBERARRAY:
switch (a) {
case q.NUMBER:
e = new K(`[${e?.raw}]`, {
is_dynamic: !0,
is_variable: !0,
type: q.NUMBERARRAY
});
break;
}
break;
case q.BOOLEANARRAY:
switch (a) {
case q.BOOLEAN:
e = new K(`[${e?.raw}]`, {
is_dynamic: !0,
is_variable: !0,
type: q.BOOLEANARRAY
});
break;
}
break;
}
return e;
}
const nc = /* @__PURE__ */ new Map([
[0, "Stop.COAST"],
[1, "Stop.BRAKE"],
[2, "Stop.HOLD"]
]), Sc = /* @__PURE__ */ new Map([
[0, "Stop.COAST"],
[1, "Stop.HOLD"]
]), zm = /* @__PURE__ */ new Map([
[1, "Side.TOP"],
[2, "Side.LEFT"],
[3, "Side.RIGHT"],
[4, "Side.BOTTOM"]
]), zo = /* @__PURE__ */ new Map([
[0, "Color.BLACK"],
[1, "Color.MAGENTA"],
[2, "Color.VIOLET"],
[3, "Color.BLUE"],
[4, "Color.CYAN"],
[5, "Color.GREEN"],
[6, "Color.GREEN"],
[7, "Color.YELLOW"],
[8, "Color.ORANGE"],
[9, "Color.RED"],
[10, "Color.WHITE"]
]), da = /* @__PURE__ */ new Map([
[0, "Button.NONE"],
[1, "Button.LEFT"],
[2, "Button.CENTER"],
[3, "Button.RIGHT"],
[4, "Button.UP"],
[5, "Button.DOWN"]
]), Hm = /* @__PURE__ */ new Map([
[0, "RELEASED"],
[1, "PRESSED"]
]);
var Lc = /* @__PURE__ */ ((r) => (r[r.OFF = 0] = "OFF", r[r.GREEN = 1] = "GREEN", r[r.RED = 2] = "RED", r[r.ORANGE = 3] = "ORANGE", r[r.GREEN_PULSE = 4] = "GREEN_PULSE", r[r.RED_PULSE = 5] = "RED_PULSE", r[r.ORANGE_PULSE = 6] = "ORANGE_PULSE", r))(Lc || {});
const Km = /* @__PURE__ */ new Map([
[0, "Color.NONE"],
[1, "Color.GREEN"],
[2, "Color.RED"],
[3, "Color.ORANGE"],
[4, "Color.GREEN_PULSE"],
[5, "Color.RED_PULSE"],
[6, "Color.ORANGE_PULSE"]
]), Qm = /* @__PURE__ */ new Map([
[0, "Color.GREEN"],
[1, "Color.RED"],
[2, "Color.ORANGE"]
]), _a = /* @__PURE__ */ new Map([
[0, "Color.NONE"],
[1, "Color.BLACK"],
[2, "Color.BLUE"],
[3, "Color.GREEN"],
[4, "Color.YELLOW"],
[5, "Color.RED"],
[6, "Color.WHITE"],
[7, "Color.BROWN"]
]);
function Mc(r, e = -1) {
if (r.imports.use("pybricks.parameters", "Stop"), nc.has(e))
return nc.get(e);
}
function rs(r, e = 0) {
const n = 10 ** e;
return Math.round(r * n) / n;
}
function xc(r) {
return r?.value === "=" ? "==" : r?.value?.toString();
}
function li(r) {
return ["==", "!=", ">", ">=", "<", "<="][r?.toInt() ?? -1];
}
class As {
constructor() {
this.isPyFnEnabled = !1;
}
call(...e) {
const n = this.parent.helperFunctionsMap.get(this.id);
if (n) {
if (n.imports?.forEach(
([o, a]) => this.parent.context.imports.use(o, a)
), n.local_fn) {
if (e.every((a) => !K.is_dynamic(a)) && (!n.local_fn_condition || n.local_fn_condition(
e.map((a) => K.value(a))
))) {
const a = n.local_fn(
...e.map((l) => K.value(l))
//? .raw
);
return K.is(a) ? a : new K(a, {
precedence: Ht.SIMPLE,
type: n.type ?? (typeof a == "string" ? q.STRING : q.NUMBER)
});
//!!
} else if (n.local_dynamic_fn) {
const a = n.local_fn(...e);
return K.is(a) ? a : new K(a, {
is_dynamic: !0,
type: n.type ?? (typeof a == "string" ? q.STRING : q.NUMBER)
});
}
}
n.py_fn && (this.isPyFnEnabled = !0, n.py_dependencies?.forEach(
(o) => this.parent.use(o).isPyFnEnabled = !0
));
} else
fe(`missing helper function called "${this.id}"`);
return new K(
`${this.id}(${e.map((o) => K.raw(o)).join(", ")})`,
{
is_dynamic: !0,
type: n?.type,
precedence: Ht.SIMPLE
}
);
}
static to_global_code(e) {
return Array.from(e.entries()).filter(([, o]) => o.payload.isPyFnEnabled).map(([o]) => {
const a = e.helperFunctionsMap.get(o);
return a?.py_fn ? a.py_fn()?.trim().split(`\r
`) : [];
}).flat();
}
static createRegistry(e) {
return new Ea(
e,
() => new As()
);
}
}
class Ea extends hi {
constructor(e, n) {
super(e, n), this.helperFunctionsMap = Ea.createHelperFunctionsMap(e);
}
static createHelperFunctionsMap(e) {
return /* @__PURE__ */ new Map(
[
// const
[
"convert_time",
{
py_fn: () => `
def convert_time(sec):
return float_safe(sec) * 1000`,
py_dependencies: ["float_safe"],
local_fn: (n) => K.toFloat(n) * 1e3,
type: q.NUMBER
}
],
[
"convert_time_back",
{
py_fn: () => `
def convert_time_back(msec):
return float_safe(msec) / 1000`,
py_dependencies: ["float_safe"],
local_fn: (n) => K.toFloat(n) / 1e3,
type: q.NUMBER
}
],
[
"convert_speed",
{
py_fn: () => `
def convert_speed(pct):
return float_safe(pct) * 10`,
local_fn: (n) => K.toFloat(n) * 10,
py_dependencies: ["float_safe"],
// 100 % = 1080 deg/s for the medium motor
// 100 % = 970 deg/s for the large motor.
type: q.NUMBER
}
],
[
"convert_speed_back",
{
py_fn: () => `
def convert_speed_back(deg_s):
return float_safe(deg_s) / 10`,
py_dependencies: ["float_safe"],
local_fn: (n) => K.toFloat(n) / 10,
type: q.NUMBER
}
],
[
"hub_speaker_flipper_play",
{
// py_fn: () => `
// ${context.asyncPrefix}def hub_speaker_flipper_play(note, duration):
// NOTES = ["C","C#","D","Eb","E","F","F#","G","G#","A","Bb","B"]
// note_abc = NOTES[note%12]
// octave = str(int(note/12))
// bpm = int(60000 / duration * 4)
// ${context.awaitPrefix}hub.speaker.play_notes([f"{note_abc}{octave}/1"], bpm)`,
py_fn: () => `
${e.asyncPrefix}def hub_speaker_flipper_play(note, duration):
NOTE_FREQS = [16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87]
freq = NOTE_FREQS[note%12] * (2 ** (note//12))
${e.awaitPrefix}hub.speaker.beep(freq, duration)`
}
],
[
"hub_speaker_iconblocks_play",
{
// 12 bmp with /4 note is 0.5 sec
py_fn: () => `
${e.asyncPrefix}def hub_speaker_iconblocks_play(note):
NOTES = [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25]
freq = NOTES[((int(note) if note != "?" else randint(1, 8))%8)-1]
${e.awaitPrefix}hub.speaker.beep(freq, 500)`
}
],
[
"round",
{
// py_fn: //none needed
local_fn: (n) => rs(K.toFloat(n)),
type: q.NUMBER
}
],
[
"float_safe",
{
py_fn: () => `
def float_safe(value, default=0):
try: return float(value)
except: return default
`,
local_fn_condition: (n) => n.every(
(o) => typeof o == "number" || typeof o == "string" && String(parseFloat(o)) === o
),
local_fn: (n) => K.toFloat(n),
type: q.NUMBER
}
],
[
"str",
{
// py_fn: //none needed
local_fn: (n) => String(n),
type: q.STRING
}
],
[
"int_safe",
{
py_fn: () => `
def int_safe(value, default=0, base=10):
try: return int(value, base)
except: return default
`,
local_fn_condition: (n) => n.every(
(o) => typeof o == "number" || typeof o == "string" && String(parseFloat(o)) === o
),
local_fn: (n, o = 0, a = 10) => parseInt(
K.toString(n),
K.toInt(a)
),
type: q.NUMBER
//TODO add int and float differentiation
}
],
[
"event_task",
{
py_fn: () => `
async def event_task(condition_fn, stack_fn):
while True:
while not await condition_fn(): yield
await stack_fn()
while await condition_fn(): yield`
}
],
[
"class_Message",
{
py_fn: () => `
class Message:
def __init__(self, stack_fns):
self.running = False
self.signalled = False
self.cancelling = False
self.stack_fns = stack_fns
async def main_fn(self):
while True:
while not self.signalled: yield
self.signalled = False
await self.action_fn()
async def action_fn(self):
await self.guard_single()
if self.running:
self.cancelling = True
while self.running: yield
self.cancelling = False
try:
self.running = True
await multitask(self.guard_fn(), multitask(*[stack_fn() for stack_fn in self.stack_fns]), race=True)
finally:
self.running = False
def guard_single(self):
if self.running:
self.cancelling = True
while self.running: yield
self.cancelling = False
def guard_fn(self):
while self.running and not self.cancelling: yield
async def broadcast_exec(self, wait):
if wait:
await self.action_fn()
else:
await self.guard_single()
self.signalled = True`
}
],
[
"convert_distance",
{
local_fn: (n, o) => {
switch (o) {
case hs:
return rs(K?.toFloat(n) * 10, 2);
// cm->mm
case ps:
return rs(K?.toFloat(n) * 25.4, 2);
// in->mm
default:
return n;
}
},
py_fn: () => `
def convert_distance(value, unit):
if unit == "cm": return value * 10
elif unit == "inches": return value * 25.4
else: return value`,
type: q.NUMBER
}
],
[
"convert_color",
{
local_fn: (n) => {
const o = Array.from(zo.values()), a = K.toInt(n);
return a in o ? o[a] : "Color.NONE";
},
py_fn: () => `
def convert_color(value):
color_list = [${Array.from(zo.values()).map((n, o) => `${n}`).join(", ")}]
return color_list[int_safe(value)]`,
py_dependencies: ["int_safe"],
type: q.ENUM,
imports: [["pybricks.parameters", "Color"]]
}
],
[
"convert_color_back",
{
py_fn: () => `
def convert_color_back(value):
color_list = [${Array.from(zo.values()).map((n, o) => `${n}`).join(", ")}]
return color_list.index(value) if value in color_list else None`,
type: q.NUMBER
}
],
[
"convert_ev3gcolor_back",
{
py_fn: () => `
def convert_ev3gcolor_back(value):
color_list = [${Array.from(_a.values()).map((n, o) => `${n}`).join(", ")}]
return color_list.index(value) if value in color_list else None`,
type: q.NUMBER
}
],
[
"convert_color_matrix",
{
local_fn: (n) => {
const o = n?.toString().split("").map(
(a) => e.helpers.use("convert_color").call(parseInt(a, 16))
);
return o?.every(
(a) => K.isEqual(a, o[0])
) ? o[0] : `[${o?.join(", ")}]`;
},
py_fn: () => `
def convert_color_matrix(colors: Color[]):
return [convert_color(int_safe(color,0,16) if color!='?' else randint(0, 10)) for color in colors]`,
py_dependencies: ["convert_color", "int_safe"],
type: q.ENUMARRAY
}
],
[
"convert_brightness",
{
local_fn: (n) => rs(
Math.min(9, Math.max(0, K.toFloat(n))) / 9 * 100,
3
),
py_fn: () => `
def convert_brightness(value):
return round(min(9, max(0, value)) / 9 * 100)`,
type: q.NUMBER
}
],
[
"convert_icon_matrix",
{
py_fn: () => `
def convert_icon_matrix(value, pixel_brightness=100):
return [[round((int(char) if '0' <= char <= '9' else
9 if char == 'x' else
randint(1, 9)) / 9 * pixel_brightness)
for char in value[i:i+5]] for i in range(0, len(value), 5)]`,
// return [[(round((int(char) if '0' <= char <= '9' else 9 if char=='x' else randint(1, 9))/9*brightness)) for char in value[i:i+5]] for i in range(0, len(value), 5)]`,
type: q.NUMBERARRAY
}
],
[
"convert_ussensor_distance",
{
local_fn: (n, o) => {
switch (K.raw(o)) {
case hs:
return K.toFloat(n) * 10;
// cm->mm
case ps:
return K.toFloat(n) * 25.4;
// in->mm
case "%":
return 2e3 * K.toFloat(n) / 100;
// 100% = 2000mm
default:
return n;
}
},
py_fn: () => `
def convert_ussensor_distance(value, unit):
if unit == "cm": return value * 10
elif unit == "inches": return value * 25.4
elif unit == "%": return 2000 * value / 100
else: return value`,
type: q.NUMBER
}
],
[
"convert_ussensor_distance_back",
{
local_fn: (n, o) => {
switch (K.raw(o)) {
case hs:
return K.toFloat(n) / 10;
// cm->mm
case ps:
return K.toFloat(n) / 25.4;
// in->mm
case "%":
return K.toFloat(n) * 100 / 2e3;
// 100% = 2000mm
default:
return n;
}
},
py_fn: () => `
def convert_ussensor_distance_back(value, unit):
if unit == "cm": return value / 10
elif unit == "inches": return value / 25.4
elif unit == "%": return value * 100 / 2000
else: return value`,
type: q.NUMBER
}
],
[
"convert_hub_orientation",
{
local_fn: (n) => "Side." + K.value(n)?.toString().replace("side", "").toUpperCase(),
py_fn: () => `
def convert_hub_orientation(value: str):
return 'Side.' + value.replace('side', '').upper()`,
type: q.ENUM
}
],
[
"convert_hub_orientation_back",
{
// NOOP - local_fn:
py_fn: () => `
def convert_hub_orientation_back(value: Side):
value = str(value).replace('Side.', '').lower()
return value + 'side' if value in ['left','right'] else value`,
type: q.STRING
}
],
[
"convert_display_orientation",
{
// value is 1..4
local_fn: (n) => {
const o = Array.from(
zm.entries()
).find(([l, f]) => l === K.toInt(n));
return o ? o[1] : "Side.ERROR";
},
py_fn: () => `
def convert_display_orientation(value):
return [Side.TOP, Side.LEFT, Side.RIGHT, Side.BOTTOM][value-1]`,
imports: [["pybricks.parameters", "Side"]]
}
],
[
"get_pupdevices",
{
py_fn: () => `
def get_pupdevices(class_type, *args):
for port in [Port.A,Port.B,Port.C,Port.D,Port.E,Port.F]:
try: return class_type(port, *args)
except: pass`
}
//TODO: wip, this returns the first device - program should use ALL
],
[
"pupdevice_type",
{
py_fn: () => `
def pupdevice_type(port):
try: return PUPDevice(port).info()['id']
except: return None`
}
],
[
"play_animation",
{
py_fn: () => `
${e.asyncPrefix}def play_animation(anim):
while True:
for frame in anim["frames"]:
hub.display.icon(frame)
${e.awaitPrefix}wait(1000/anim["fps"])
if not anim["loop"]: break
`
}
],
[
"relative_position",
{
py_fn: () => `
def relative_position(motor):
angle_mod = motor.angle() % 360
return angle_mod if angle_mod <= 180 else angle_mod - 360`,
type: q.NUMBER
}
],
[
"motorpair_move",
{
py_fn: () => `
def motorpair_move(motor_left, motor_right, steer, value):
secondary_value = (50 - abs(steer)) * 2 / 100 * value
motor_left.run(value if steer>=0 else secondary_value)
motor_right.run(value if steer<=0 else secondary_value)
`
}
],
[
"motorpair_move_dc",
{
py_fn: () => `
def motorpair_move_dc(motor_left, motor_right, steer, value):
secondary_value = (50 - abs(steer)) * 2 / 100 * value
motor_left.dc(value if steer>=0 else secondary_value)
motor_right.dc(value if steer<=0 else secondary_value)
`
}
],
[
"convert_ev3button_back",
{
py_fn: () => `
def convert_ev3button_back(value):
value_list = [${Array.from(da.values()).map((n, o) => `${n}`).join(", ")}]
return value_list.index(value) if value in value_list else None`,
type: q.NUMBER
}
],
[
"check_interrupt_flag",
{
py_fn: () => `
def check_interrupt_flag(*loop_ids):
for index, loop_id in enumerate(loop_ids):
if loop_id in g_interrupt_vector:
if index == 0:
g_interrupt_vector.remove(loop_id)
return True
return False`
}
],
[
"set_interrupt_flag",
{
py_fn: () => `
def set_interrupt_flag(loop_id):
g_interrupt_vector.add(loop_id)`
}
]
]
// num_eval: {
// local_fn: num_eval,
// local_dynamic_fn: num_eval,
// },
);
}
}
class ui {
constructor(e, n, o, a) {
this.id = e, this.blockid = o, this.name = n, this.args = a;
}
getPyName(e) {
return `${e ?? ""}${this.name}`;
}
getPyDefinition(e) {
const n = [...this.args.values()].map(
(o) => `${o.name}: ${o.type}`
);
return `${this.getPyName(e)}(${n.join(", ")})`;
}
// static getArgDefByArgBlockId(id: string) {
// for (const proc of proceduresRegistry.values()) {
// for (const arg of proc.payload.args.values()) {
// if (arg.id === id) return arg;
// }
// }
// }
static getProcName(e) {
return Gn(
/^(.*?)(?= %[sb])|^.*/.exec(e ?? "")?.[0] ?? ""
);
}
static create(e, n = !1) {
const o = e.getBlock("custom_block"), a = o._block.mutation?.proccode ?? "";
if (n)
return e.converter.context.procedures.get(a);
const l = this.getProcName(a);
if (!l)
return;
const f = [], c = [], h = [];
Object.entries(o._block.inputs).forEach(([m, C]) => {
const N = C[1];
if (typeof N != "string")
return;
const g = o.getPeerById(N);
if (!g)
return;
const R = g.opcode.replace("argument_reporter_", ""), O = Gn(g.get("VALUE")?.toString());
O && (h.indexOf(O) >= 0 && fe(`duplicate argname at ${a} - ${O}/${m}`), h.push(O), c.push(m), f.push(R === "string_number" ? "string" : "boolean"));
});
const _ = h.reduce((m, C, N) => (m.set(C, {
name: C,
id: c[N],
type: f[N]
}), m), /* @__PURE__ */ new Map());
return new ui(
a,
// will act as id
l,
e._id,
_
);
}
static createRegistry(e) {
return new Jm(e);
}
}
class Jm extends hi {
getByBlockId(e) {
return Array.from(this.registry.values()).find(
(n) => n.payload.blockid === e
)?.payload;
}
// getByBlockName(name: string): ProcedureRegistryPayload[] {
// return Array.from(this.registry.values())
// .filter((proc) => proc.payload.name === name)
// .map((elem) => elem.payload);
// }
}
class ga {
constructor(e) {
this.context = e;
}
get devicename() {
}
setupCode() {
return [];
}
get dependencies() {
return [];
}
ensureDependencies() {
}
static createRegistry(e) {
return /* @__PURE__ */ new Map();
}
}
class Te extends ga {
constructor(e, n, o) {
super(e), this.port = n, this.devicePyClass = o;
}
get portString() {
return Te.portToString(this.port);
}
get devicename() {
return Te.devicenameTemplate(this.port, this.devicePyClass);
}
static portToString(e) {
return `Port.${e}`;
}
setupCode() {
return this._setupCode_internal([]);
}
_setupCode_internal(e = []) {
const n = super.setupCode();
this.context.imports.use("pybricks.parameters", "Port"), this.context.imports.use(
this.devicePyClass === "PUPDevice" ? "pybricks.iodevices" : "pybricks.pupdevices",
this.devicePyClass
);
const o = this.port !== pn ? `${this.devicePyClass}(${[this.portString].concat(e).join(", ")})` : this.context.helpers.use("get_pupdevices").call([this.devicePyClass].concat(e).join(", ")).raw;
return n.push(`${this.devicename} = ${o}`), n;
}
getDeviceClass() {
return this.devicePyClass ?? "";
}
static devicenameTemplate(e, n) {
return `${n?.toLowerCase() ?? "device"}_${e?.toLowerCase()}`;
}
static devicenameFromPort(e, n, o) {
return n ? [...e.devicesRegistry.entries()].find(
(l) => l[1].port === n
)?.[0] ?? this.devicenameTemplate(n, o) : "";
}
static instance(e, n, o) {
const a = this.devicenameFromPort(e, n, o);
if (!a) throw new Error(`Device on port ${n} not found`);
let l = e.devicesRegistry.get(a);
return l ? l.devicePyClass !== o && fe(
`Device on port ${n} is already assigned (${a}) and new deviceClass "${o}" is not matching "${l.devicePyClass}"`
) : (l = this.factory(e, n, o), e.devicesRegistry.set(a, l)), l;
}
static factory(e, n, o) {
return new Te(e, n, o);
}
}
class Ae extends Te {
constructor(e, n, o = !0) {
super(e, n, "Motor"), this.direction_cw = o, this._default_then = void 0;
}
static factory(e, n, o) {
return new Ae(e, n);
}
static instance(e, n) {
return super.instance(e, n, "Motor");
}
get_then() {
return this._default_then;
}
get default_speed_variable() {
return `default_speeds[${this.devicename}]`;
}
set default_then(e) {
this._default_then = e;
}
get devicename() {
return Ae.devicenameFromPort(this.context, this.port);
}
setupCode() {
const e = this.direction_cw ? [] : ["Direction.COUNTERCLOCKWISE"];
this.context.imports.use("pybricks.parameters", "Direction");
const n = super._setupCode_internal(e);
return this.context.deviceDefaultSpeeds.set(
this.devicename,
this.context.helpers.use("convert_speed")?.call(50).toString()
), n;
}
}
const Xm = 175, jm = 55.7, Zm = 117;
class Ge extends ga {
constructor(e, n, o, a, l = !0) {
super(e), this._ports = n, this._wheel_diameter = o, this._axle_track = a, this.isExplicitlyUsed = l;
}
static {
this.DEVICENAME = "drivebase";
}
static instance(e, n, o, a, l = !0) {
let f = e.devicesRegistry.get(
Ge.DEVICENAME
);
return f ? (f.ports = n ?? f._ports, f.wheel_diameter = o ?? f._wheel_diameter, f.axle_track = a ?? f._axle_track, !f.isExplicitlyUsed && l && (f.isExplicitlyUsed = !0, f.ensureDependencies())) : (f = new Ge(
e,
n,
o,
a,
l
), e.devicesRegistry.set(Ge.DEVICENAME, f)), f;
}
get_then() {
return this._default_then && this.context.imports.use("pybricks.parameters", "Stop"), this._default_then;
}
get default_speed_variable() {
return `default_speeds[${this.devicename}]`;
}
set default_then(e) {
this._default_then = e;
}
get wheel_diameter() {
return this._wheel_diameter ?? jm;
}
set wheel_diameter(e) {
this._wheel_diameter = e;
}
get rotation_distance() {
return this._rotation_distance ?? Xm;
}
set rotation_distance(e) {
this._rotation_distance = e;
}
get rotation_distance_variable() {
return `${this.devicename}_rotation_distance`;
}
get axle_track() {
return this._axle_track ?? Zm;
}
set axle_track(e) {
this._axle_track = e;
}
get devicename() {
return Ge.DEVICENAME;
}
get dependencies() {
return this.ensureDependencies(), [this.motor_left, this.motor_right].filter(
(e) => !!e
);
}
get ports() {
return this._ports || ["A", "B"];
}
set ports(e) {
this._ports = e;
}
setupCode() {
if (!this.isExplicitlyUsed)
return [];
const e = super.setupCode();
return e.push(
`${this.devicename} = DriveBase(${this.motor_left?.devicename}, ${this.motor_right?.devicename}, ${this.wheel_diameter}, ${this.axle_track})`
), this.context.deviceDefaultSpeeds.set(
this.devicename,
this.context.helpers.use("convert_speed")?.call(50).toString()
), e;
}
ensureDependencies() {
if (!this.isExplicitlyUsed)
return;
const e = (n, o) => {
const a = Ae.instance(this.context, n);
return a && o !== void 0 && (a.direction_cw = o, a.ensureDependencies()), a;
};
if (this.context.imports.use("pybricks.robotics", "DriveBase"), !this.ports || !Array.isArray(this.ports))
throw new Error("Invalid ports");
this.motor_left = e(this.ports[0], !1), this.motor_right = e(this.ports[1], !0);
}
}
class Ns {
constructor() {
this.importedItems = /* @__PURE__ */ new Set();
}
// constructor(...importedItems: string[]) {
// this.use(...importedItems);
// }
use(...e) {
e.forEach((n) => {
n != null && !this.importedItems.has(n) && this.importedItems.add(n);
});
}
static to_global_code(e) {
return Array.from(
e.entries().sort(
([n, o], [a, l]) => n.localeCompare(a)
)
).map(
([n, o]) => o?.payload?.importedItems.size > 0 ? `from ${n} import ` + Array.from(o?.payload?.importedItems.keys()).join(", ") : `import ${n}`
);
}
static createRegistry(e) {
return new hi(e, () => new Ns());
}
}
class vs {
get py() {
return this._py_name_unique;
}
py_setValue(e, n) {
return e.variablesForWriteAccess.add(this), `${this.py} = ${n}`;
}
constructor(e, n) {
this._value = e, this._is_list = n;
}
use(...e) {
this.generateUniqueName();
}
generateUniqueName() {
const e = typeof this.id == "string" ? this.id : this.id[0];
this._py_name_base = `g_${Gn(e)}`, this._py_name_unique = this.createUniqueName(this._py_name_base);
}
createUniqueName(e) {
const n = [...this.parent.values()].filter(
(o) => o.payload._py_name_base === e || o.payload._py_name_unique === e
).length;
return n <= 1 ? e : `${e}_${n}`;
}
static to_global_code(e) {
return Array.from(e.entries()).map(
([n, o]) => `${o.payload._py_name_unique} = ${o.payload._value || (o.payload._is_list ? "[]" : "None")}`
);
}
static createRegistry(e) {
return new hi(
e,
(...n) => new vs(
n[0],
n[1]
)
);
}
}
class vr {
//TODO: later add and track lineid
constructor() {
this.isAsyncNeeded = !1, this.filetype = Gt.NONE, this.imports = Ns.createRegistry(this), this.broadcasts = pa.createRegistry(this), this.variables = vs.createRegistry(this), this.procedures = ui.createRegistry(this), this.helpers = As.createRegistry(this), this.devicesRegistry = ga.createRegistry(this), this.deviceDefaultSpeeds = /* @__PURE__ */ new Map(), this.pycodeByBlocks = {};
}
get awaitPrefix() {
return this.isAsyncNeeded ? "await " : "";
}
get asyncPrefix() {
return this.isAsyncNeeded ? "async " : "";
}
clear() {
this.imports.clear(), this.broadcasts.clear(), this.variables.clear(), this.procedures.clear(), this.helpers.clear(), this.devicesRegistry.clear(), this.deviceDefaultSpeeds.clear(), this.isAsyncNeeded = !1, this.filetype = Gt.NONE;
}
get filetypeIsSB3() {
return this.filetype !== void 0 && (this.filetype & Gt._SB3_FORMAT) !== 0;
}
get filetypeIsSPIKERI() {
return this.filetype !== void 0 && (this.filetype & Gt._GEN_EV5) !== 0;
}
get filetypeIsEV3Lab() {
return this.filetype !== void 0 && (this.filetype & Gt._EV3Lab_FORMAT) !== 0;
}
}
const qm = "_MultiStartBlockParent", t1 = "StartBlock", e1 = "Interrupt", n1 = "StartLoop", Es = "_ForkParent", gs = "_ForkItem", r1 = "Case", i1 = "ConfigurableWaitFor", ys = "ConfigurableWhileLoop", s1 = "MYBLOCK", o1 = "GlobalGet", a1 = "GlobalSet", l1 = "String", u1 = "Boolean", c1 = "Single", rc = "Array", f1 = "CaseSelector.String", h1 = "_ForkMergeItem", ic = "Dummy_StartLoop", p1 = "InterruptName", d1 = "InterruptId", sc = "Pattern", _1 = "MathEquation", E1 = "DEFAULT*", g1 = "MotorPort", m1 = "Ports", O1 = "Port", T1 = "Port_Number";
var Ut = /* @__PURE__ */ ((r) => (r[r.None = 0] = "None", r[r.WhileLoop = 1] = "WhileLoop", r[r.WaitFor = 2] = "WaitFor", r[r.Switch = 3] = "Switch", r[r.SwitchCase = 4] = "SwitchCase", r[r.ForkParent = 5] = "ForkParent", r[r.ForkItem = 6] = "ForkItem", r[r.MyBlockNode = 7] = "MyBlockNode", r[r.TopLevel = 8] = "TopLevel", r))(Ut || {});
const C1 = /* @__PURE__ */ new Map([
["ArrayBuildBoolean", "ArrayOperations.Append_Boolean"],
["ArrayBuild", "ArrayOperations.Append_Numeric"],
["ArrayGetSizeBoolean", "ArrayOperations.Length_Boolean"],
["ArrayGetSize", "ArrayOperations.Length_Numeric"],
["ArrayReadAtIndexBoolean", "ArrayOperations.ReadAtIndex_Boolean"],
["ArrayReadAtIndex", "ArrayOperations.ReadAtIndex_Numeric"],
["ArrayWriteAtIndexBoolean", "ArrayOperations.WriteAtIndex_Boolean"],
["ArrayWriteAtIndex", "ArrayOperations.WriteAtIndex_Numeric"],
["BluetoothMessagingClose", "Bluetooth.Clear"],
["BluetoothMessagingInitiate", "Bluetooth.Initiate"],
["BluetoothMessagingOff", "Bluetooth.Off"],
["BluetoothMessagingOn", "Bluetooth.On"],
["Boolean_And", "BooleanOperations.And"],
["Boolean_Not", "BooleanOperations.Not"],
["Boolean_Or", "BooleanOperations.Or"],
["Boolean_XOr", "BooleanOperations.XOR"],
["ButtonChange", "BrickButton.ChangeBrickButton"],
["ButtonCompare", "BrickButton.Compare"],
["ButtonValue", "BrickButton.Measure"],
["CaseSelector_Boolean", "CaseSelector.Boolean"],
["CaseSelector_Numeric", "CaseSelector.Numeric"],
["CaseSelector_String", "CaseSelector.String"],
["ColorCalibrateMax", "ColorSensor.CalibrateMaxColor"],
["ColorCalibrateMin", "ColorSensor.CalibrateMinColor"],
["ColorCalibrateDefault", "ColorSensor.CalibrateResetColor"],
["ColorAmbientIntensityChange", "ColorSensor.ChangeAmbientLight"],
["ColorChange", "ColorSensor.ChangeColor"],
["ColorReflectedIntensityChange", "ColorSensor.ChangeReflectedLight"],
["ColorAmbientIntensityCompare", "ColorSensor.CompareAmbientLight"],
["ColorCompare", "ColorSensor.CompareColor"],
["ColorReflectedIntensityCompare", "ColorSensor.CompareReflectedLight"],
["ColorAmbientIntensity", "ColorSensor.MeasureAmbientLight"],
["ColorValue", "ColorSensor.MeasureColor"],
["ColorReflectedIntensity", "ColorSensor.MeasureReflectedLight"],
["CommentBlock", "CommentBlock"],
["Comparison_Equal", "Compare.Equal"],
["Comparison_GreaterEqual", "Compare.GreaterOrEqual"],
["Comparison_Greater", "Compare.GreaterThan"],
["Comparison_LessEqual", "Compare.LessOrEqual"],
["Comparison_Less", "Compare.LessThan"],
["Comparison_NotEqual", "Compare.NotEqual"],
["GlobalConstBoolean", "Constant.Boolean"],
["GlobalConstBooleanArray", "Constant.BooleanArray"],
["GlobalConstSingle", "Constant.Numeric"],
["GlobalConstNumericArray", "Constant.NumericArray"],
["GlobalConstString", "Constant.Text"],
// ['X3.Lib:GlobalConstBoolean', 'Constant.Boolean'],
// ['X3.Lib:GlobalConstBooleanArray', 'Constant.BooleanArray'],
// ['X3.Lib:GlobalConstSingle', 'Constant.Numeric'],
// ['X3.Lib:GlobalConstNumericArray', 'Constant.NumericArray'],
// ['X3.Lib:GlobalConstString', 'Constant.Text'],
["DataLogMasterOn", "Datalogging.On"],
["DataLogMasterMinutes", "Datalogging.OnForTimeMinute"],
["DataLogMaster", "Datalogging.OnForTimeSeconds"],
["DataLogMasterSingle", "Datalogging.SingleMeasurement"],
["DataLogStop", "Datalogging.Stop"],
// [
// 'X3Placeholder_2A058539-ED76-4476-93FE-CCE8AA559C5A_DataLogMasterOn',
// 'Datalogging.On',
// ],
// [
// 'X3Placeholder_2A058539-ED76-4476-93FE-CCE8AA559C5A_DataLogMasterMinutes',
// 'Datalogging.OnForTimeMinute',
// ],
// [
// 'X3Placeholder_2A058539-ED76-4476-93FE-CCE8AA559C5A_DataLogMaster',
// 'Datalogging.OnForTimeSeconds',
// ],
// [
// 'X3Placeholder_2A058539-ED76-4476-93FE-CCE8AA559C5A_DataLogMasterSingle',
// 'Datalogging.SingleMeasurement',
// ],
// [
// 'X3Placeholder_2A058539-ED76-4476-93FE-CCE8AA559C5A_DataLogStop',
// 'Datalogging.Stop',
// ],
["DisplayCircle", "Display.Circle"],
["DisplayClear", "Display.Clear"],
["DisplayFile", "Display.File"],
["DisplayLine", "Display.Line"],
["DisplayPoint", "Display.Point"],
["DisplayRect", "Display.Rectangle"],
["DisplayString", "Display.String"],
["DisplayStringGrid", "Display.StringGrid"],
["EnergyMeterChangeInCurrent", "EnergyMeter.ChangeInCurrent"],
["EnergyMeterChangeInVoltage", "EnergyMeter.ChangeInVoltage"],
["EnergyMeterChangeInWatt", "EnergyMeter.ChangeInWatt"],
["EnergyMeterChangeJoule", "EnergyMeter.ChangeJoule"],
["EnergyMeterChangeOutCurrent", "EnergyMeter.ChangeOutCurrent"],
["EnergyMeterChangeOutVoltage", "EnergyMeter.ChangeOutVoltage"],
["EnergyMeterChangeOutWatt", "EnergyMeter.ChangeOutWatt"],
["EnergyMeterCompareInCurrent", "EnergyMeter.CompareInCurrent"],
["EnergyMeterCompareInVoltage", "EnergyMeter.CompareInVoltage"],
["EnergyMeterCompareInWatt", "EnergyMeter.CompareInWatt"],
["EnergyMeterCompareOutCurrent", "EnergyMeter.CompareOutCurrent"],
["EnergyMeterCompareJoule", "EnergyMeter.CompareOutJoule"],
["EnergyMeterCompareOutVoltage", "EnergyMeter.CompareOutVoltage"],
["EnergyMeterCompareOutWatt", "EnergyMeter.CompareOutWatt"],
["EnergyMeterInCurrent", "EnergyMeter.MeasureInCurrent"],
["EnergyMeterInVoltage", "EnergyMeter.MeasureInVoltage"],
["EnergyMeterInWattage", "EnergyMeter.MeasureInWatts"],
["EnergyMeterJoule", "EnergyMeter.MeasureJoule"],
["EnergyMeterOutCurrent", "EnergyMeter.MeasureOutCurrent"],
["EnergyMeterOutVoltage", "EnergyMeter.MeasureOutVoltage"],
["EnergyMeterOutWattage", "EnergyMeter.MeasureOutWatts"],
["FileClose", "FileAccess.Close"],
["FileDelete", "FileAccess.Delete"],
["FileReadNumeric", "FileAccess.Numeric"],
["FileReadText", "FileAccess.Text"],
["FileWriteText", "FileAccess.Write"],
["DeltaWaitGyroAngle", "Gyro.ChangeAngle"],
["DeltaWaitGyroRate", "Gyro.ChangeRate"],
["GyroAngleCompare", "Gyro.CompareAngle"],
["GyroRateCompare", "Gyro.CompareRate"],
["GyroDegrees", "Gyro.MeasureAngle"],
["GyroAngleAndRate", "Gyro.MeasureAngleAndRate"],
["GyroRate", "Gyro.MeasureRate"],
["GyroReset", "Gyro.Reset"],
["neverUsed_D04426AB-522F-477F-86FA-4A1EEEC45449", "HWPageManualInput"],
["neverUsed_24800E4C-0E91-4B6C-A3B7-2C75465F04B8", "HWPageManualOutput"],
["IRTrackerChangeProximity", "InfraredSensor.ChangeBeaconProximity"],
["IRTrackerChangeHeading", "InfraredSensor.ChangeHeading"],
["IRProximityChange", "InfraredSensor.ChangeProximity"],
["IRRemoteChange", "InfraredSensor.ChangeRemote"],
["IRTrackerCompareHeading", "InfraredSensor.CompareBeaconSeekerHeading"],
["IRTrackerCompareProximity", "InfraredSensor.CompareBeaconSeekerProximity"],
["IRProximityCompare", "InfraredSensor.CompareProximity"],
["IRRemoteCompare", "InfraredSensor.CompareRemote"],
["IRRemote", "InfraredSensor.MeasureBeaconRemote"],
["IRSeeker", "InfraredSensor.MeasureBeaconSeeker"],
[
"IRSeekerDataloggingHeading",
"InfraredSensor.MeasureBeaconSeekerDataloggingHeading"
],
[
"IRSeekerDataloggingProximity",
"InfraredSensor.MeasureBeaconSeekerDataloggingProximity"
],
["IRProximity", "InfraredSensor.MeasureProximity"],
["ToggleInterrupt", "Interrupt"],
["InvertMotor", "InvertMotor"],
["KeepAlive", "KeepAlive"],
["LedOff", "LED.Off"],
["LedOn", "LED.On"],
["LedReset", "LED.Reset"],
["StopIfTrue", "LoopCondition.Boolean"],
["StopAfterNumberIterations", "LoopCondition.Count"],
["TimeCompareLoop", "LoopCondition.Time"],
["StopNever", "LoopCondition.Unlimited"],
["LoopIndex", "LoopIndex"],
["Arithmetic_AbsoluteValue", "Math.AbsoluteValue"],
["Arithmetic_Add", "Math.Add"],
["MathEquation", "Math.Advanced"],
// [
// 'X3Placeholder_2A058539-ED76-4476-93FE-CCE8AA559C5A_MathEquation',
// 'Math.Advanced',
// ],
["Arithmetic_Divide", "Math.Divide"],
["Arithmetic_Power", "Math.Exponent"],
["Arithmetic_Multiply", "Math.Multiply"],
["Arithmetic_SquareRoot", "Math.SquareRoot"],
["Arithmetic_Subtract", "Math.Subtract"],
["MediumMotorDistance", "MediumMotor.Degrees"],
["MediumMotorDistanceRotations", "MediumMotor.Rotations"],
["MediumMotorStop", "MediumMotor.Stop"],
["MediumMotorTime", "MediumMotor.Time"],
["MediumMotorUnlimited", "MediumMotor.Unlimited"],
["MessageUpdateBoolean", "Messaging.ChangeBooleanUpdate"],
["MessageChangeValueBoolean", "Messaging.ChangeBooleanValue"],
["MessageUpdateNumeric", "Messaging.ChangeNumericUpdate"],
["MessageChangeValueNumeric", "Messaging.ChangeNumericValue"],
["MessageUpdateText", "Messaging.ChangeTextUpdate"],
["MessageChangeValueText", "Messaging.ChangeTextValue"],
["MessageCompareBoolean", "Messaging.CompareBoolean"],
["MessageCompareNumeric", "Messaging.CompareNumeric"],
["MessageCompareText", "Messaging.CompareText"],
["ReceiveMessageBoolean", "Messaging.ReceiveBoolean"],
["ReceiveMessageNumeric", "Messaging.ReceiveNumeric"],
["ReceiveMessage", "Messaging.ReceiveText"],
["SendMessageBoolean", "Messaging.SendBoolean"],
["SendMessageNumeric", "Messaging.SendNumeric"],
["SendMessage", "Messaging.SendText"],
["MotorDistance", "Motor.Degrees"],
["MotorDistanceRotations", "Motor.Rotations"],
["MotorStop", "Motor.Stop"],
["MotorTime", "Motor.Time"],
["MotorUnlimited", "Motor.Unlimited"],
["MoveDistance", "Move.Degrees"],
["MoveDistanceRotations", "Move.Rotations"],
["MoveStop", "Move.Stop"],
["MoveTime", "Move.