UNPKG

pxt-core

Version:

Microsoft MakeCode provides Blocks / JavaScript / Python tools and editors

1,165 lines • 498 kB
// Helpers designed to help to make a simulator accessible. var pxsim; (function (pxsim) { var accessibility; (function (accessibility) { let liveRegion; let keydownListenerAdded = false; function makeFocusable(elem) { elem.setAttribute("focusable", "true"); elem.setAttribute("tabindex", "0"); } accessibility.makeFocusable = makeFocusable; function getGlobalAction(e) { const isMac = window.navigator && /Mac/i.test(window.navigator.platform); const meta = isMac ? e.metaKey : e.ctrlKey; if (e.key === "Escape") { e.preventDefault(); return "escape"; } else if (e.key === "/" && meta) { e.preventDefault(); return "togglekeyboardcontrolshelp"; } else if (e.key === "b" && meta) { e.preventDefault(); return "toggleareamenu"; } return null; } accessibility.getGlobalAction = getGlobalAction; function postKeyboardEvent() { if (keydownListenerAdded) { return; } keydownListenerAdded = true; document.addEventListener("keydown", (e) => { const action = getGlobalAction(e); if (action) { const message = { type: "action", action }; pxsim.Runtime.postMessage(message); } }); } accessibility.postKeyboardEvent = postKeyboardEvent; function enableKeyboardInteraction(elem, handlerKeyDown, handlerKeyUp) { if (handlerKeyDown) { elem.addEventListener('keydown', (e) => { const charCode = (typeof e.which == "number") ? e.which : e.keyCode; if (charCode === 32 || charCode === 13) { // Enter or Space key handlerKeyDown(); } }); } if (handlerKeyUp) { elem.addEventListener('keyup', (e) => { const charCode = (typeof e.which == "number") ? e.which : e.keyCode; if (charCode === 32 || charCode === 13) { // Enter or Space key handlerKeyUp(); } }); } } accessibility.enableKeyboardInteraction = enableKeyboardInteraction; function setAria(elem, role, label) { if (role && !elem.hasAttribute("role")) { elem.setAttribute("role", role); } if (label && !elem.hasAttribute("aria-label")) { elem.setAttribute("aria-label", label); } } accessibility.setAria = setAria; function setLiveContent(value) { if (!liveRegion) { let style = "position: absolute !important;" + "display: block;" + "visibility: visible;" + "overflow: hidden;" + "width: 1px;" + "height: 1px;" + "margin: -1px;" + "border: 0;" + "padding: 0;" + "clip: rect(0 0 0 0);"; liveRegion = document.createElement("div"); liveRegion.setAttribute("role", "status"); liveRegion.setAttribute("aria-live", "polite"); liveRegion.setAttribute("aria-hidden", "false"); liveRegion.setAttribute("style", style); document.body.appendChild(liveRegion); } if (liveRegion.textContent !== value) { liveRegion.textContent = value; } } accessibility.setLiveContent = setLiveContent; })(accessibility = pxsim.accessibility || (pxsim.accessibility = {})); })(pxsim || (pxsim = {})); var pxsim; (function (pxsim) { const GROUND_COLOR = "blue"; const POWER_COLOR = "red"; const POWER5V_COLOR = "orange"; ; ; ; ; ; ; function isOnBreadboardBottom(location) { let isBot = false; if (typeof location !== "string" && location.type === "breadboard") { let bbLoc = location; let row = bbLoc.row; isBot = 0 <= ["a", "b", "c", "d", "e"].indexOf(row); } return isBot; } const arrCount = (a) => a.reduce((p, n) => p + (n ? 1 : 0), 0); const arrAny = (a) => arrCount(a) > 0; function computePowerUsage(wire) { let ends = [wire.start, wire.end]; let endIsGround = ends.map(e => e === "ground"); let endIsThreeVolt = ends.map(e => e === "threeVolt"); let endIsFiveVolt = ends.map(e => e === "fiveVolt"); let endIsBot = ends.map(e => isOnBreadboardBottom(e)); let hasGround = arrAny(endIsGround); let hasThreeVolt = arrAny(endIsThreeVolt); let hasFiveVolt = arrAny(endIsFiveVolt); let hasBot = arrAny(endIsBot); return { topGround: hasGround && !hasBot, topThreeVolt: hasThreeVolt && !hasBot, topFiveVolt: hasFiveVolt && !hasBot, bottomGround: hasGround && hasBot, bottomThreeVolt: hasThreeVolt && hasBot, bottomFiveVolt: hasFiveVolt && hasBot, singleGround: hasGround, singleThreeVolt: hasThreeVolt, singleFiveVolt: hasFiveVolt }; } function mergePowerUsage(powerUsages) { const finalPowerUsage = powerUsages.reduce((p, n) => ({ topGround: p.topGround || n.topGround, topThreeVolt: p.topThreeVolt || n.topThreeVolt, topFiveVolt: p.topFiveVolt || n.topFiveVolt, bottomGround: p.bottomGround || n.bottomGround, bottomThreeVolt: p.bottomThreeVolt || n.bottomThreeVolt, bottomFiveVolt: p.bottomFiveVolt || n.bottomFiveVolt, singleGround: n.singleGround ? p.singleGround === null : p.singleGround, singleThreeVolt: n.singleThreeVolt ? p.singleThreeVolt === null : p.singleThreeVolt, singleFiveVolt: n.singleFiveVolt ? p.singleFiveVolt === null : p.singleFiveVolt, }), { topGround: false, topThreeVolt: false, topFiveVolt: false, bottomGround: false, bottomThreeVolt: false, bottomFiveVolt: false, singleGround: null, singleThreeVolt: null, singleFiveVolt: null }); if (finalPowerUsage.singleGround) finalPowerUsage.topGround = finalPowerUsage.bottomGround = false; if (finalPowerUsage.singleThreeVolt) finalPowerUsage.topThreeVolt = finalPowerUsage.bottomThreeVolt = false; if (finalPowerUsage.singleFiveVolt) finalPowerUsage.topFiveVolt = finalPowerUsage.bottomFiveVolt = false; return finalPowerUsage; } function copyDoubleArray(a) { return a.map(b => b.map(p => p)); } function merge2(a, b) { let res = {}; for (let aKey in a) res[aKey] = a[aKey]; for (let bKey in b) res[bKey] = b[bKey]; return res; } function merge3(a, b, c) { return merge2(merge2(a, b), c); } function readPin(arg) { pxsim.U.assert(!!arg, "Invalid pin: " + arg); const pin = /^(\w+)\.\s*(?:[a-z]*)?([A-Z][A-Z\d_]+)$/.exec(arg); return pin ? pin[2] : undefined; } pxsim.readPin = readPin; function mkReverseMap(map) { let origKeys = []; let origVals = []; for (let key in map) { origKeys.push(key); origVals.push(map[key]); } let newMap = {}; for (let i = 0; i < origKeys.length; i++) { let newKey = origVals[i]; let newVal = origKeys[i]; newMap[newKey] = newVal; } return newMap; } function isConnectedToBB(pin) { return pin.orientation === "-Z" && pin.style === "male"; } class Allocator { constructor(opts) { this.availablePowerPins = { top: { fiveVolt: pxsim.mkRange(26, 51).map(n => ({ type: "breadboard", row: "+", col: `${n}` })), threeVolt: pxsim.mkRange(26, 51).map(n => ({ type: "breadboard", row: "+", col: `${n}` })), ground: pxsim.mkRange(26, 51).map(n => ({ type: "breadboard", row: "-", col: `${n}` })), }, bottom: { fiveVolt: pxsim.mkRange(1, 26).map(n => ({ type: "breadboard", row: "+", col: `${n}` })), threeVolt: pxsim.mkRange(1, 26).map(n => ({ type: "breadboard", row: "+", col: `${n}` })), ground: pxsim.mkRange(1, 26).map(n => ({ type: "breadboard", row: "-", col: `${n}` })), }, }; this.opts = opts; } allocPartIRs(def, name, bbFit) { let partIRs = []; const mkIR = (def, name, instPins, partParams) => { let pinIRs = []; for (let i = 0; i < def.numberOfPins; i++) { let pinDef = def.pinDefinitions[i]; let pinTarget; if (typeof pinDef.target === "string") { pinTarget = pinDef.target; } else { let instIdx = pinDef.target.pinInstantiationIdx; if (!(!!instPins && instPins[instIdx] !== undefined)) { pxsim.log(`error: parts no pin found for PinInstantiationIdx: ${instIdx}. (Is the part missing an ArgumentRole or "trackArgs=" annotations?)`); return undefined; } pinTarget = instPins[instIdx]; } let pinLoc = def.visual.pinLocations[i]; let adjustedY = bbFit.yOffset + pinLoc.y; let relativeRowIdx = Math.round(adjustedY / def.visual.pinDistance); let relativeYOffset = adjustedY - relativeRowIdx * def.visual.pinDistance; let adjustedX = bbFit.xOffset + pinLoc.x; let relativeColIdx = Math.round(adjustedX / def.visual.pinDistance); let relativeXOffset = adjustedX - relativeColIdx * def.visual.pinDistance; let pinBBFit = { partRelativeRowIdx: relativeRowIdx, partRelativeColIdx: relativeColIdx, xOffset: relativeXOffset, yOffset: relativeYOffset }; pinIRs.push({ def: pinDef, loc: pinLoc, target: pinTarget, bbFit: pinBBFit, }); } return { name: name, def: def, pins: pinIRs, partParams: partParams || {}, bbFit: bbFit }; }; // support for multiple possible instantions const instantiations = def.instantiations || []; if (def.instantiation) instantiations.push(def.instantiation); instantiations.forEach(instantiation => { if (instantiation.kind === "singleton") { partIRs.push(mkIR(def, name)); } else if (instantiation.kind === "function") { let fnAlloc = instantiation; let fnNms = fnAlloc.fullyQualifiedName.split(','); let callsitesTrackedArgsHash = {}; fnNms.forEach(fnNm => { if (this.opts.fnArgs[fnNm]) this.opts.fnArgs[fnNm].forEach((targetArg) => { callsitesTrackedArgsHash[targetArg] = 1; }); }); let callsitesTrackedArgs = Object.keys(callsitesTrackedArgsHash); if (!(!!callsitesTrackedArgs && !!callsitesTrackedArgs.length)) { pxsim.log(`error: parts failed to read pin(s) from callsite for: ${fnNms}`); return undefined; } callsitesTrackedArgs.forEach(fnArgsStr => { const fnArgsSplit = fnArgsStr.split(","); if (fnArgsSplit.length != fnAlloc.argumentRoles.length) { pxsim.log(`error: parts mismatch between number of arguments at callsite (function name: ${fnNms}) vs number of argument roles in part definition (part: ${name}).`); return; } let instPins = []; let paramArgs = {}; fnArgsSplit.forEach((arg, idx) => { let role = fnAlloc.argumentRoles[idx]; if (role.partParameter !== undefined) { paramArgs[role.partParameter] = arg; } if (role.pinInstantiationIdx !== undefined) { let instIdx = role.pinInstantiationIdx; let pin = readPin(arg); instPins[instIdx] = pin; } }); partIRs.push(mkIR(def, name, instPins, paramArgs)); }); } }); return partIRs.filter(ir => !!ir); } computePartDimensions(def, name) { let pinLocs = def.visual.pinLocations; let pinDefs = def.pinDefinitions; let numPins = def.numberOfPins; pxsim.U.assert(pinLocs.length === numPins, `Mismatch between "numberOfPins" and length of "visual.pinLocations" for "${name}"`); pxsim.U.assert(pinDefs.length === numPins, `Mismatch between "numberOfPins" and length of "pinDefinitions" for "${name}"`); pxsim.U.assert(numPins > 0, `Part "${name}" has no pins`); let pins = pinLocs.map((loc, idx) => merge3({ idx: idx }, loc, pinDefs[idx])); let bbPins = pins.filter(p => p.orientation === "-Z"); let hasBBPins = bbPins.length > 0; let pinDist = def.visual.pinDistance; let xOff; let yOff; let colCount; let rowCount; if (hasBBPins) { let refPin = bbPins[0]; let refPinColIdx = Math.ceil(refPin.x / pinDist); let refPinRowIdx = Math.ceil(refPin.y / pinDist); xOff = refPinColIdx * pinDist - refPin.x; yOff = refPinRowIdx * pinDist - refPin.y; colCount = Math.ceil((xOff + def.visual.width) / pinDist) + 1; rowCount = Math.ceil((yOff + def.visual.height) / pinDist) + 1; } else { colCount = Math.ceil(def.visual.width / pinDist); rowCount = Math.ceil(def.visual.height / pinDist); xOff = colCount * pinDist - def.visual.width; yOff = rowCount * pinDist - def.visual.height; } return { xOffset: xOff, yOffset: yOff, rowCount: rowCount, colCount: colCount }; } allocColumns(colCounts) { let partsCount = colCounts.length; const totalColumnsCount = pxsim.visuals.BREADBOARD_MID_COLS; //TODO allow multiple breadboards let totalSpaceNeeded = colCounts.map(d => d.colCount).reduce((p, n) => p + n, 0); let extraSpace = totalColumnsCount - totalSpaceNeeded; if (extraSpace <= 0) { pxsim.log("Not enough breadboard space!"); //TODO } let padding = Math.floor(extraSpace / (partsCount - 1 + 2)); let partSpacing = padding; //Math.floor(extraSpace/(partsCount-1)); let totalPartPadding = extraSpace - partSpacing * (partsCount - 1); let leftPadding = Math.floor(totalPartPadding / 2); let rightPadding = Math.ceil(totalPartPadding / 2); let nextAvailableCol = 1 + leftPadding; let partStartCol = colCounts.map(part => { let col = nextAvailableCol; nextAvailableCol += part.colCount + partSpacing; return col; }); return partStartCol; } placeParts(parts) { const totalRowsCount = pxsim.visuals.BREADBOARD_MID_ROWS + 2; // 10 letters + 2 for the middle gap let startColumnIndices = this.allocColumns(parts.map(p => p.bbFit)); let startRowIndicies = parts.map(p => { let extraRows = totalRowsCount - p.bbFit.rowCount; let topPad = Math.floor(extraRows / 2); let startIdx = topPad; if (startIdx > 4) startIdx = 4; if (startIdx < 1) startIdx = 1; return startIdx; }); let placements = parts.map((p, idx) => { let row = startRowIndicies[idx]; let col = startColumnIndices[idx]; return merge2({ startColumnIdx: col, startRowIdx: row }, p); }); return placements; } nextColor() { if (!this.availableWireColors || this.availableWireColors.length <= 0) { this.availableWireColors = pxsim.visuals.GPIO_WIRE_COLORS.map(c => c); } return this.availableWireColors.pop(); } allocWireIRs(part) { let groupToColor = []; let wires = part.pins.map((pin, pinIdx) => { let end = pin.target; let start; let colIdx = part.startColumnIdx + pin.bbFit.partRelativeColIdx; let colName = pxsim.visuals.getColumnName(colIdx); let pinRowIdx = part.startRowIdx + pin.bbFit.partRelativeRowIdx; if (pinRowIdx >= 7) //account for middle gap pinRowIdx -= 2; if (isConnectedToBB(pin.def)) { //make a wire from bb top or bottom to target let connectedToTop = pinRowIdx < 5; let rowName = connectedToTop ? "j" : "a"; start = { type: "breadboard", row: rowName, col: colName, style: pin.def.style }; } else { //make a wire directly from pin to target let rowName = pxsim.visuals.getRowName(pinRowIdx); start = { type: "breadboard", row: rowName, col: colName, xOffset: pin.bbFit.xOffset / part.def.visual.pinDistance, yOffset: pin.bbFit.yOffset / part.def.visual.pinDistance, style: pin.def.style }; } let color; if (end === "ground") { color = GROUND_COLOR; } else if (end === "threeVolt") { color = POWER_COLOR; } else if (end === "fiveVolt") { color = POWER5V_COLOR; } else if (typeof pin.def.colorGroup === "number") { if (groupToColor[pin.def.colorGroup]) { color = groupToColor[pin.def.colorGroup]; } else { color = groupToColor[pin.def.colorGroup] = this.nextColor(); } } else { color = this.nextColor(); } return { start: start, end: end, color: color, pinIdx: pinIdx, }; }); return merge2(part, { wires: wires }); } allocLocation(location, opts) { if (location === "ground" || location === "threeVolt" || location == "fiveVolt") { //special case if there is only a single ground or three volt pin in the whole build if (location === "ground" && this.powerUsage.singleGround) { let boardGroundPin = this.getBoardGroundPin(); return { type: "dalboard", pin: boardGroundPin }; } else if (location === "threeVolt" && this.powerUsage.singleThreeVolt) { let boardThreeVoltPin = this.getBoardThreeVoltPin(); return { type: "dalboard", pin: boardThreeVoltPin }; } else if (location === "fiveVolt" && this.powerUsage.singleFiveVolt) { let boardFiveVoltPin = this.getBoardFiveVoltPin(); return { type: "dalboard", pin: boardFiveVoltPin }; } pxsim.U.assert(!!opts.referenceBBPin); let nearestCoord = this.opts.getBBCoord(opts.referenceBBPin); let firstTopAndBot = [ this.availablePowerPins.top.ground[0] || this.availablePowerPins.top.threeVolt[0], this.availablePowerPins.bottom.ground[0] || this.availablePowerPins.bottom.threeVolt[0] ].map(loc => { return this.opts.getBBCoord(loc); }); if (!firstTopAndBot[0] || !firstTopAndBot[1]) { pxsim.debug(`No more available "${location}" locations!`); //TODO } let nearTop = pxsim.visuals.findClosestCoordIdx(nearestCoord, firstTopAndBot) == 0; let barPins; if (nearTop) { if (location === "ground") { barPins = this.availablePowerPins.top.ground; } else if (location === "threeVolt") { barPins = this.availablePowerPins.top.threeVolt; } else if (location === "fiveVolt") { barPins = this.availablePowerPins.top.fiveVolt; } } else { if (location === "ground") { barPins = this.availablePowerPins.bottom.ground; } else if (location === "threeVolt") { barPins = this.availablePowerPins.bottom.threeVolt; } else if (location === "fiveVolt") { barPins = this.availablePowerPins.bottom.fiveVolt; } } let pinCoords = barPins.map(rowCol => { return this.opts.getBBCoord(rowCol); }); let closestPinIdx = pxsim.visuals.findClosestCoordIdx(nearestCoord, pinCoords); let pin = barPins[closestPinIdx]; if (nearTop) { this.availablePowerPins.top.ground.splice(closestPinIdx, 1); this.availablePowerPins.top.threeVolt.splice(closestPinIdx, 1); } else { this.availablePowerPins.bottom.ground.splice(closestPinIdx, 1); this.availablePowerPins.bottom.threeVolt.splice(closestPinIdx, 1); } return pin; } else if (location.type === "breadboard") { return location; } else if (location === "MOSI" || location === "MISO" || location === "SCK") { if (!this.opts.boardDef.spiPins) pxsim.debug("No SPI pin mappings found!"); let pin = this.opts.boardDef.spiPins[location]; return { type: "dalboard", pin: pin }; } else if (location === "SDA" || location === "SCL") { if (!this.opts.boardDef.i2cPins) pxsim.debug("No I2C pin mappings found!"); let pin = this.opts.boardDef.i2cPins[location]; return { type: "dalboard", pin: pin }; } else { //it must be a MicrobitPin pxsim.U.assert(typeof location === "string", "Unknown location type: " + location); let mbPin = location; let boardPin = this.opts.boardDef.gpioPinMap[mbPin] || mbPin; if (!boardPin) { // this pin is internal pxsim.debug(`unknown pin location for ${mbPin}`); return undefined; } return { type: "dalboard", pin: boardPin }; } } getBoardGroundPin() { let pin = this.opts.boardDef.groundPins && this.opts.boardDef.groundPins[0] || null; if (!pin) { pxsim.debug("No available ground pin on board!"); //TODO } return pin; } getBoardThreeVoltPin() { let pin = this.opts.boardDef.threeVoltPins && this.opts.boardDef.threeVoltPins[0] || null; if (!pin) { pxsim.debug("No available 3.3V pin on board!"); //TODO } return pin; } getBoardFiveVoltPin() { let pin = this.opts.boardDef.fiveVoltPins && this.opts.boardDef.fiveVoltPins[0] || null; if (!pin) { pxsim.debug("No available 5V pin on board!"); //TODO } return pin; } allocPowerWires(powerUsage) { let boardGroundPin = this.getBoardGroundPin(); let threeVoltPin = this.getBoardThreeVoltPin(); let fiveVoltPin = this.getBoardFiveVoltPin(); const topLeft = { type: "breadboard", row: "-", col: "26" }; const botLeft = { type: "breadboard", row: "-", col: "1" }; const topRight = { type: "breadboard", row: "-", col: "50" }; const botRight = { type: "breadboard", row: "-", col: "25" }; let top, bot; if (this.opts.boardDef.attachPowerOnRight) { top = topRight; bot = botRight; } else { top = topLeft; bot = botLeft; } let groundWires = []; let threeVoltWires = []; let fiveVoltWires = []; if (powerUsage.bottomGround && powerUsage.topGround) { //bb top - <==> bb bot - groundWires.push({ start: this.allocLocation("ground", { referenceBBPin: top }), end: this.allocLocation("ground", { referenceBBPin: bot }), color: GROUND_COLOR, }); } if (powerUsage.topGround) { //board - <==> bb top - groundWires.push({ start: this.allocLocation("ground", { referenceBBPin: top }), end: { type: "dalboard", pin: boardGroundPin }, color: GROUND_COLOR, }); } else if (powerUsage.bottomGround) { //board - <==> bb bot - groundWires.push({ start: this.allocLocation("ground", { referenceBBPin: bot }), end: { type: "dalboard", pin: boardGroundPin }, color: GROUND_COLOR, }); } if (powerUsage.bottomThreeVolt && powerUsage.bottomGround) { //bb top + <==> bb bot + threeVoltWires.push({ start: this.allocLocation("threeVolt", { referenceBBPin: top }), end: this.allocLocation("threeVolt", { referenceBBPin: bot }), color: POWER_COLOR, }); } else if (powerUsage.bottomFiveVolt && powerUsage.bottomGround) { //bb top + <==> bb bot + fiveVoltWires.push({ start: this.allocLocation("fiveVolt", { referenceBBPin: top }), end: this.allocLocation("fiveVolt", { referenceBBPin: bot }), color: POWER5V_COLOR, }); } if (powerUsage.topThreeVolt) { //board + <==> bb top + threeVoltWires.push({ start: this.allocLocation("threeVolt", { referenceBBPin: top }), end: { type: "dalboard", pin: threeVoltPin }, color: POWER_COLOR, }); } else if (powerUsage.bottomThreeVolt) { //board + <==> bb bot + threeVoltWires.push({ start: this.allocLocation("threeVolt", { referenceBBPin: bot }), end: { type: "dalboard", pin: threeVoltPin }, color: POWER5V_COLOR, }); } if (powerUsage.topFiveVolt && !powerUsage.topThreeVolt) { //board + <==> bb top + fiveVoltWires.push({ start: this.allocLocation("fiveVolt", { referenceBBPin: top }), end: { type: "dalboard", pin: fiveVoltPin }, color: POWER_COLOR, }); } else if (powerUsage.bottomFiveVolt && !powerUsage.bottomThreeVolt) { //board + <==> bb bot + fiveVoltWires.push({ start: this.allocLocation("fiveVolt", { referenceBBPin: bot }), end: { type: "dalboard", pin: fiveVoltPin }, color: POWER5V_COLOR, }); } let assembly = []; if (groundWires.length > 0) assembly.push({ wireIndices: groundWires.map((w, i) => i) }); let numGroundWires = groundWires.length; if (threeVoltWires.length > 0) assembly.push({ wireIndices: threeVoltWires.map((w, i) => i + numGroundWires) }); if (fiveVoltWires.length > 0) assembly.push({ wireIndices: threeVoltWires.map((w, i) => i + numGroundWires + threeVoltWires.length) }); return { wires: groundWires.concat(threeVoltWires).concat(fiveVoltWires), assembly: assembly }; } allocWire(wireIR) { const ends = [wireIR.start, wireIR.end]; const endIsPower = ends.map(e => e === "ground" || e === "threeVolt" || e === "fiveVolt"); //allocate non-power first so we know the nearest pin for the power end let endInsts = ends.map((e, idx) => !endIsPower[idx] ? this.allocLocation(e, {}) : undefined); //allocate power pins closest to the other end of the wire endInsts = endInsts.map((e, idx) => { if (e) return e; const locInst = endInsts[1 - idx]; // non-power end const l = this.allocLocation(ends[idx], { referenceBBPin: locInst, }); return l; }); // one of the pins is not accessible if (!endInsts[0] || !endInsts[1]) return undefined; return { start: endInsts[0], end: endInsts[1], color: wireIR.color }; } allocPart(ir) { let bbConnections = ir.pins .filter(p => isConnectedToBB(p.def)) .map(p => { let rowIdx = ir.startRowIdx + p.bbFit.partRelativeRowIdx; if (rowIdx >= 7) //account for middle gap rowIdx -= 2; let rowName = pxsim.visuals.getRowName(rowIdx); let colIdx = ir.startColumnIdx + p.bbFit.partRelativeColIdx; let colName = pxsim.visuals.getColumnName(colIdx); return { type: "breadboard", row: rowName, col: colName, }; }); let part = { name: ir.name, visual: ir.def.visual, bbFit: ir.bbFit, startColumnIdx: ir.startColumnIdx, startRowIdx: ir.startRowIdx, breadboardConnections: bbConnections, params: ir.partParams, simulationBehavior: ir.def.simulationBehavior }; return part; } allocAll() { let partNmAndDefs = this.opts.partsList .map(partName => { return { name: partName, def: this.opts.partDefs[partName] }; }) .filter(d => !!d.def); if (partNmAndDefs.length > 0) { let dimensions = partNmAndDefs.map(nmAndPart => this.computePartDimensions(nmAndPart.def, nmAndPart.name)); let partIRs = []; partNmAndDefs.forEach((nmAndDef, idx) => { let dims = dimensions[idx]; let irs = this.allocPartIRs(nmAndDef.def, nmAndDef.name, dims); partIRs = partIRs.concat(irs); }); const partPlacements = this.placeParts(partIRs); const partsAndWireIRs = partPlacements.map(p => this.allocWireIRs(p)); const allWireIRs = partsAndWireIRs.map(p => p.wires).reduce((p, n) => p.concat(n), []); const allPowerUsage = allWireIRs.map(w => computePowerUsage(w)); this.powerUsage = mergePowerUsage(allPowerUsage); const basicWires = this.allocPowerWires(this.powerUsage); const partsAndWires = partsAndWireIRs.map((irs, idx) => { const part = this.allocPart(irs); const wires = irs.wires.map(w => this.allocWire(w)); if (wires.some(w => !w)) return undefined; const pinIdxToWireIdx = []; irs.wires.forEach((wIR, idx) => { pinIdxToWireIdx[wIR.pinIdx] = idx; }); const assembly = irs.def.assembly.map(stepDef => { return { part: stepDef.part, wireIndices: (stepDef.pinIndices || []).map(i => pinIdxToWireIdx[i]) }; }); return { part: part, wires: wires, assembly: assembly }; }).filter(p => !!p); const all = [basicWires].concat(partsAndWires) .filter(pw => pw.assembly && pw.assembly.length); // only keep steps with something to do // hide breadboard if not used const hideBreadboard = !all.some(r => (r.part && r.part.breadboardConnections && r.part.breadboardConnections.length > 0) || r.wires && r.wires.some(w => (w.end.type == "breadboard" && w.end.style != "croc") || (w.start.type == "breadboard" && w.start.style != "croc"))); return { partsAndWires: all, wires: [], parts: [], hideBreadboard }; } else { return { partsAndWires: [], wires: [], parts: [] }; } } } function allocateDefinitions(opts) { return new Allocator(opts).allocAll(); } pxsim.allocateDefinitions = allocateDefinitions; })(pxsim || (pxsim = {})); /// <reference path="../localtypings/vscode-debug-protocol.d.ts" /> /** * Heavily adapted from https://github.com/microsoft/vscode-debugadapter-node * and altered to run in a browser and communcate via JSON over a websocket * rather than through stdin and stdout */ var pxsim; (function (pxsim) { var protocol; (function (protocol) { class Message { constructor(type) { this.seq = 0; this.type = type; } } protocol.Message = Message; class Response extends Message { constructor(request, message) { super('response'); this.request_seq = request.seq; this.command = request.command; if (message) { this.success = false; this.message = message; } else { this.success = true; } } } protocol.Response = Response; class Event extends Message { constructor(event, body) { super('event'); this.event = event; if (body) { this.body = body; } } } protocol.Event = Event; class Source { constructor(name, path, id = 0, origin, data) { this.name = name; this.path = path; this.sourceReference = id; if (origin) { this.origin = origin; } if (data) { this.adapterData = data; } } } protocol.Source = Source; class Scope { constructor(name, reference, expensive = false) { this.name = name; this.variablesReference = reference; this.expensive = expensive; } } protocol.Scope = Scope; class StackFrame { constructor(i, nm, src, ln = 0, col = 0) { this.id = i; this.source = src; this.line = ln; this.column = col; this.name = nm; } } protocol.StackFrame = StackFrame; class Thread { constructor(id, name) { this.id = id; if (name) { this.name = name; } else { this.name = 'Thread #' + id; } } } protocol.Thread = Thread; class Variable { constructor(name, value, ref = 0, indexedVariables, namedVariables) { this.name = name; this.value = value; this.variablesReference = ref; if (typeof namedVariables === 'number') { this.namedVariables = namedVariables; } if (typeof indexedVariables === 'number') { this.indexedVariables = indexedVariables; } } } protocol.Variable = Variable; class Breakpoint { constructor(verified, line, column, source) { this.verified = verified; const e = this; if (typeof line === 'number') { e.line = line; } if (typeof column === 'number') { e.column = column; } if (source) { e.source = source; } } } protocol.Breakpoint = Breakpoint; class Module { constructor(id, name) { this.id = id; this.name = name; } } protocol.Module = Module; class CompletionItem { constructor(label, start, length = 0) { this.label = label; this.start = start; this.length = length; } } protocol.CompletionItem = CompletionItem; class StoppedEvent extends Event { constructor(reason, threadId, exception_text = null) { super('stopped'); this.body = { reason: reason, threadId: threadId }; if (exception_text) { const e = this; e.body.text = exception_text; } } } protocol.StoppedEvent = StoppedEvent; class ContinuedEvent extends Event { constructor(threadId, allThreadsContinued) { super('continued'); this.body = { threadId: threadId }; if (typeof allThreadsContinued === 'boolean') { this.body.allThreadsContinued = allThreadsContinued; } } } protocol.ContinuedEvent = ContinuedEvent; class InitializedEvent extends Event { constructor() { super('initialized'); } } protocol.InitializedEvent = InitializedEvent; class TerminatedEvent extends Event { constructor(restart) { super('terminated'); if (typeof restart === 'boolean') { const e = this; e.body = { restart: restart }; } } } protocol.TerminatedEvent = TerminatedEvent; class OutputEvent extends Event { constructor(output, category = 'console', data) { super('output'); this.body = { category: category, output: output }; if (data !== undefined) { this.body.data = data; } } } protocol.OutputEvent = OutputEvent; class ThreadEvent extends Event { constructor(reason, threadId) { super('thread'); this.body = { reason: reason, threadId: threadId }; } } protocol.ThreadEvent = ThreadEvent; class BreakpointEvent extends Event { constructor(reason, breakpoint) { super('breakpoint'); this.body = { reason: reason, breakpoint: breakpoint }; } } protocol.BreakpointEvent = BreakpointEvent; class ModuleEvent extends Event { constructor(reason, module) { super('module'); this.body = { reason: reason, module: module }; } } protocol.ModuleEvent = ModuleEvent; class ProtocolServer { constructor() { this._pendingRequests = {}; } start(host) { this._sequence = 1; this.host = host; this.host.onData(msg => { if (msg.type === 'request') { this.dispatchRequest(msg); } else if (msg.type === 'response') { const response = msg; const clb = this._pendingRequests[response.seq]; if (clb) { delete this._pendingRequests[response.seq]; clb(response); } } }); } stop() { if (this.host) { this.host.close(); } } sendEvent(event) { this.send('event', event); } sendResponse(response) { if (response.seq > 0) { pxsim.error(`attempt to send more than one response for command ${response.command}`); } else { this.send('response', response); } } sendRequest(command, args, timeout, cb) { const request = { command: command }; if (args && Object.keys(args).length > 0) { request.arguments = args; } this.send('request', request); if (cb) { this._pendingRequests[request.seq] = cb; const timer = setTimeout(() => { clearTimeout(timer); const clb = this._pendingRequests[request.seq]; if (clb) { delete this._pendingRequests[request.seq]; clb(new protocol.Response(request, 'timeout')); } }, timeout); } } send(typ, message) { message.type = typ; message.seq = this._sequence++; if (this.host) { const json = JSON.stringify(message); this.host.send(json); } } // ---- protected ---------------------------------------------------------- dispatchRequest(request) { } } protocol.ProtocolServer = ProtocolServer; class DebugSession extends ProtocolServer { constructor() { super(...arguments); this._debuggerLinesStartAt1 = false; this._debuggerColumnsStartAt1 = false; this._clientLinesStartAt1 = true; this._clientColumnsStartAt1 = true; } shutdown() { } dispatchRequest(request) { const response = new protocol.Response(request); try { if (request.command === 'initialize') { let args = request.arguments; if (typeof args.linesStartAt1 === 'boolean') { this._clientLinesStartAt1 = args.linesStartAt1; } if (typeof args.columnsStartAt1 === 'boolean') { this._clientColumnsStartAt1 = args.columnsStartAt1; } if (args.pathFormat !== 'path') { this.sendErrorResponse(response, 2018, 'debug adapter only supports native paths', null); } else { const initializeResponse = response; initializeResponse.body = {}; this.initializeRequest(initializeResponse, args); } } else if (request.command === 'launch') { this.launchRequest(response, request.arguments); } else if (request.command === 'attach') { this.attachRequest(response, request.arguments); } else if (request.command === 'disconnect') { this.disconnectRequest(response, request.arguments); } else if (request.command === 'setBreakpoints') { this.setBreakPointsRequest(response, request.arguments); } else if (request.command === 'setFunctionBreakpoints') { this.setFunctionBreakPointsRequest(response, request.arguments); } else if (request.command === 'setExceptionBreakpoints') { this.setExceptionBreakPointsRequest(response, request.arguments); } else if (request.command === 'configurationDone') { this.configurationDoneRequest(response, request.arguments); } else if (request.command === 'continue') { this.continueRequest(response, request.arguments); } else if (request.command === 'next') { this.nextRequest(response, request.arguments); } else if (request.command === 'stepIn') { this.stepInRequest(response, request.arguments); } else if (request.command === 'stepOut') { this.stepOutRequest(response, request.arguments); } else if (request.command === 'stepBack') { this.stepBackRequest(response, request.arguments); } else if (request.command === 'restartFrame') { this.restartFrameRequest(response, request.arguments); } else if (request.command === 'goto') { this.gotoRequest(respons