Merge branch 'master' of github.com:Microsoft/pxt-microbit
This commit is contained in:
commit
93770e5821
|
@ -25,7 +25,5 @@ The PXT command line also works on Raspbian and allows to run a local server and
|
||||||
To install all the tools,
|
To install all the tools,
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone https://github.com/Microsoft/pxt-rpi.git
|
curl -s https://raw.githubusercontent.com/Microsoft/pxt-rpi/master/install.sh | sh -
|
||||||
cd pxt-rpi
|
|
||||||
sh ./install.sh
|
|
||||||
```
|
```
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"bluetooth": "Support for additional Bluetooth services.",
|
|
||||||
"bluetooth.onBluetoothConnected": "Register code to run when the micro:bit is connected to over Bluetooth",
|
"bluetooth.onBluetoothConnected": "Register code to run when the micro:bit is connected to over Bluetooth",
|
||||||
"bluetooth.onBluetoothConnected|param|body": "Code to run when a Bluetooth connection is established",
|
"bluetooth.onBluetoothConnected|param|body": "Code to run when a Bluetooth connection is established",
|
||||||
"bluetooth.onBluetoothDisconnected": "Register code to run when a bluetooth connection to the micro:bit is lost",
|
"bluetooth.onBluetoothDisconnected": "Register code to run when a bluetooth connection to the micro:bit is lost",
|
||||||
|
@ -10,6 +9,7 @@
|
||||||
"bluetooth.startLEDService": "Starts the Bluetooth LED service",
|
"bluetooth.startLEDService": "Starts the Bluetooth LED service",
|
||||||
"bluetooth.startMagnetometerService": "Starts the Bluetooth magnetometer service",
|
"bluetooth.startMagnetometerService": "Starts the Bluetooth magnetometer service",
|
||||||
"bluetooth.startTemperatureService": "Starts the Bluetooth temperature service",
|
"bluetooth.startTemperatureService": "Starts the Bluetooth temperature service",
|
||||||
|
"bluetooth.startUartService": "Starts the Bluetooth UART service",
|
||||||
"bluetooth.uartRead": "Reads from the Bluetooth UART service buffer, returning its contents when the specified delimiter character is encountered.",
|
"bluetooth.uartRead": "Reads from the Bluetooth UART service buffer, returning its contents when the specified delimiter character is encountered.",
|
||||||
"bluetooth.uartWrite": "Writes to the Bluetooth UART service buffer. From there the data is transmitted over Bluetooth to a connected device."
|
"bluetooth.uartWrite": "Writes to the Bluetooth UART service buffer. From there the data is transmitted over Bluetooth to a connected device."
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
"bluetooth.startLEDService|block": "bluetooth led service",
|
"bluetooth.startLEDService|block": "bluetooth led service",
|
||||||
"bluetooth.startMagnetometerService|block": "bluetooth magnetometer service",
|
"bluetooth.startMagnetometerService|block": "bluetooth magnetometer service",
|
||||||
"bluetooth.startTemperatureService|block": "bluetooth temperature service",
|
"bluetooth.startTemperatureService|block": "bluetooth temperature service",
|
||||||
|
"bluetooth.startUartService|block": "bluetooth uart service",
|
||||||
"bluetooth.uartRead|block": "bluetooth uart read %del=bluetooth_uart_delimiter_conv",
|
"bluetooth.uartRead|block": "bluetooth uart read %del=bluetooth_uart_delimiter_conv",
|
||||||
"bluetooth.uartWrite|block": "bluetooth uart write %data",
|
"bluetooth.uartWrite|block": "bluetooth uart write %data",
|
||||||
"bluetooth|block": "bluetooth"
|
"bluetooth|block": "bluetooth"
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
"control": "Runtime and event utilities.",
|
"control": "Runtime and event utilities.",
|
||||||
"control.inBackground": "Schedules code that run in the background.",
|
"control.inBackground": "Schedules code that run in the background.",
|
||||||
"control.reset": "Resets the BBC micro:bit.",
|
"control.reset": "Resets the BBC micro:bit.",
|
||||||
|
"control.waitMicros": "Blocks the current fiber for the given microseconds",
|
||||||
|
"control.waitMicros|param|micros": "number of micro-seconds to wait. eg: 4",
|
||||||
"game": "A single-LED sprite game engine",
|
"game": "A single-LED sprite game engine",
|
||||||
"game.addScore": "Adds points to the current score",
|
"game.addScore": "Adds points to the current score",
|
||||||
"game.addScore|param|points": "amount of points to change, eg: 1",
|
"game.addScore|param|points": "amount of points to change, eg: 1",
|
||||||
|
@ -52,9 +54,12 @@
|
||||||
"input.onLogoDown|param|body": "TODO",
|
"input.onLogoDown|param|body": "TODO",
|
||||||
"input.onLogoUp": "Attaches code to run when the logo is oriented upwards and the board is vertical.",
|
"input.onLogoUp": "Attaches code to run when the logo is oriented upwards and the board is vertical.",
|
||||||
"input.onLogoUp|param|body": "TODO",
|
"input.onLogoUp|param|body": "TODO",
|
||||||
"input.onPinPressed": "Do something when a pin(``P0``, ``P1`` or both ``P2``) is pressed.",
|
"input.onPinPressed": "Do something when a pin is pressed.",
|
||||||
"input.onPinPressed|param|body": "TODO",
|
"input.onPinPressed|param|body": "the code to run when the pin is pressed",
|
||||||
"input.onPinPressed|param|name": "TODO",
|
"input.onPinPressed|param|name": "the pin that needs to be pressed",
|
||||||
|
"input.onPinReleased": "Do something when a pin is released.",
|
||||||
|
"input.onPinReleased|param|body": "the code to run when the pin is released",
|
||||||
|
"input.onPinReleased|param|name": "the pin that needs to be released",
|
||||||
"input.onScreenDown": "Attaches code to run when the screen is facing down.",
|
"input.onScreenDown": "Attaches code to run when the screen is facing down.",
|
||||||
"input.onScreenDown|param|body": "TODO",
|
"input.onScreenDown|param|body": "TODO",
|
||||||
"input.onScreenUp": "Attaches code to run when the screen is facing up.",
|
"input.onScreenUp": "Attaches code to run when the screen is facing up.",
|
||||||
|
@ -152,6 +157,8 @@
|
||||||
"pins.setPull": "Configures the pull of this pin.",
|
"pins.setPull": "Configures the pull of this pin.",
|
||||||
"pins.setPull|param|name": "pin to set the pull mode on",
|
"pins.setPull|param|name": "pin to set the pull mode on",
|
||||||
"pins.setPull|param|pull": "one of the mbed pull configurations: PullUp, PullDown, PullNone ",
|
"pins.setPull|param|pull": "one of the mbed pull configurations: PullUp, PullDown, PullNone ",
|
||||||
|
"pins.spiWrite": "Write to the SPI slave and return the response",
|
||||||
|
"pins.spiWrite|param|value": "Data to be sent to the SPI slave",
|
||||||
"serial": "Reading and writing data over a serial connection.",
|
"serial": "Reading and writing data over a serial connection.",
|
||||||
"serial.readLine": "Reads a line of text from the serial port.",
|
"serial.readLine": "Reads a line of text from the serial port.",
|
||||||
"serial.redirect": "Dynamically configuring the serial instance to use pins other than USBTX and USBRX.",
|
"serial.redirect": "Dynamically configuring the serial instance to use pins other than USBTX and USBRX.",
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"basic|block": "basic",
|
"basic|block": "basic",
|
||||||
"control.inBackground|block": "run in background",
|
"control.inBackground|block": "run in background",
|
||||||
"control.reset|block": "reset",
|
"control.reset|block": "reset",
|
||||||
|
"control.waitMicros|block": "wait (µs)%micros",
|
||||||
"control|block": "control",
|
"control|block": "control",
|
||||||
"game.addScore|block": "change score by|%points",
|
"game.addScore|block": "change score by|%points",
|
||||||
"game.gameOver|block": "game over",
|
"game.gameOver|block": "game over",
|
||||||
|
@ -28,7 +29,8 @@
|
||||||
"input.magneticForce|block": "magnetic force (µT)|%NAME",
|
"input.magneticForce|block": "magnetic force (µT)|%NAME",
|
||||||
"input.onButtonPressed|block": "on button|%NAME|pressed",
|
"input.onButtonPressed|block": "on button|%NAME|pressed",
|
||||||
"input.onGesture|block": "on |%NAME",
|
"input.onGesture|block": "on |%NAME",
|
||||||
"input.onPinPressed|block": "on pin|%NAME|pressed",
|
"input.onPinPressed|block": "on pin %NAME|pressed",
|
||||||
|
"input.onPinReleased|block": "on pin %NAME|released",
|
||||||
"input.pinIsPressed|block": "pin %NAME|is pressed",
|
"input.pinIsPressed|block": "pin %NAME|is pressed",
|
||||||
"input.rotation|block": "rotation (°)|%NAME",
|
"input.rotation|block": "rotation (°)|%NAME",
|
||||||
"input.runningTime|block": "running time (ms)",
|
"input.runningTime|block": "running time (ms)",
|
||||||
|
@ -41,6 +43,7 @@
|
||||||
"led.point|block": "point|x %x|y %y",
|
"led.point|block": "point|x %x|y %y",
|
||||||
"led.setBrightness|block": "set brightness %value",
|
"led.setBrightness|block": "set brightness %value",
|
||||||
"led.stopAnimation|block": "stop animation",
|
"led.stopAnimation|block": "stop animation",
|
||||||
|
"led.toggle|block": "toggle|x %x|y %y",
|
||||||
"led.unplot|block": "unplot|x %x|y %y",
|
"led.unplot|block": "unplot|x %x|y %y",
|
||||||
"led|block": "led",
|
"led|block": "led",
|
||||||
"music.beat|block": "%fraction|beat",
|
"music.beat|block": "%fraction|beat",
|
||||||
|
@ -65,6 +68,7 @@
|
||||||
"pins.servoSetPulse|block": "servo set pulse|pin %value|to (µs) %micros",
|
"pins.servoSetPulse|block": "servo set pulse|pin %value|to (µs) %micros",
|
||||||
"pins.servoWritePin|block": "servo write|pin %name|to %value",
|
"pins.servoWritePin|block": "servo write|pin %name|to %value",
|
||||||
"pins.setPull|block": "set pull|pin %pin|to %pull",
|
"pins.setPull|block": "set pull|pin %pin|to %pull",
|
||||||
|
"pins.spiWrite|block": "spi write %value",
|
||||||
"pins|block": "pins",
|
"pins|block": "pins",
|
||||||
"serial.readLine|block": "serial read line",
|
"serial.readLine|block": "serial read line",
|
||||||
"serial.redirect|block": "serial redirect to|TX %tx|RX %rx|at baud rate %rate",
|
"serial.redirect|block": "serial redirect to|TX %tx|RX %rx|at baud rate %rate",
|
||||||
|
|
|
@ -1,78 +1,126 @@
|
||||||
{
|
{
|
||||||
"ledmatrix": {
|
"buttonpair": {
|
||||||
"visual": "ledmatrix",
|
"simulationBehavior": "buttonpair",
|
||||||
"breadboardColumnsNeeded": 8,
|
"visual": {
|
||||||
"breadboardStartRow": "h",
|
"builtIn": "buttonpair",
|
||||||
"pinAllocation": {
|
"width": 75,
|
||||||
"type": "auto",
|
"height": 45,
|
||||||
"gpioPinsNeeded": [5, 5]
|
"pinDistance": 15,
|
||||||
},
|
"pinLocations": [
|
||||||
"assemblyStep": 0,
|
{"x": 0, "y": 0},
|
||||||
"wires": [
|
{"x": 30, "y": 45},
|
||||||
{"start": ["breadboard", "j", 0], "end": ["GPIO", 5], "color": "purple", "assemblyStep": 1},
|
{"x": 45, "y": 0},
|
||||||
{"start": ["breadboard", "j", 1], "end": ["GPIO", 6], "color": "purple", "assemblyStep": 1},
|
{"x": 75, "y": 45}
|
||||||
{"start": ["breadboard", "j", 2], "end": ["GPIO", 7], "color": "purple", "assemblyStep": 1},
|
|
||||||
{"start": ["breadboard", "j", 3], "end": ["GPIO", 8], "color": "purple", "assemblyStep": 1},
|
|
||||||
{"start": ["breadboard", "a", 7], "end": ["GPIO", 9], "color": "purple", "assemblyStep": 1},
|
|
||||||
{"start": ["breadboard", "a", 0], "end": ["GPIO", 0], "color": "green", "assemblyStep": 2},
|
|
||||||
{"start": ["breadboard", "a", 1], "end": ["GPIO", 1], "color": "green", "assemblyStep": 2},
|
|
||||||
{"start": ["breadboard", "a", 2], "end": ["GPIO", 2], "color": "green", "assemblyStep": 2},
|
|
||||||
{"start": ["breadboard", "a", 3], "end": ["GPIO", 3], "color": "green", "assemblyStep": 2},
|
|
||||||
{"start": ["breadboard", "j", 4], "end": ["GPIO", 4], "color": "green", "assemblyStep": 2}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"buttonpair": {
|
"numberOfPins": 4,
|
||||||
"visual": "buttonpair",
|
"pinDefinitions": [
|
||||||
"breadboardColumnsNeeded": 6,
|
{"target": "P14", "style": "male", "orientation": "-Z"},
|
||||||
"breadboardStartRow": "f",
|
{"target": "ground", "style": "male", "orientation": "-Z"},
|
||||||
"pinAllocation": {
|
{"target": "P15", "style": "male", "orientation": "-Z"},
|
||||||
"type": "predefined",
|
{"target": "ground", "style": "male", "orientation": "-Z"}
|
||||||
"pins": ["P13", "P12"]
|
],
|
||||||
|
"instantiation": {
|
||||||
|
"kind": "singleton"
|
||||||
},
|
},
|
||||||
"assemblyStep": 0,
|
"assembly": [
|
||||||
"wires": [
|
{"part": true},
|
||||||
{"start": ["breadboard", "j", 0], "end": ["GPIO", 0], "color": "yellow", "assemblyStep": 1},
|
{"pinIndices": [0, 1]},
|
||||||
{"start": ["breadboard", "a", 2], "end": "ground", "color": "blue", "assemblyStep": 1},
|
{"pinIndices": [2, 3]}
|
||||||
{"start": ["breadboard", "j", 3], "end": ["GPIO", 1], "color": "orange", "assemblyStep": 2},
|
|
||||||
{"start": ["breadboard", "a", 5], "end": "ground", "color": "blue", "assemblyStep": 2}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"neopixel": {
|
"neopixel": {
|
||||||
"visual": "neopixel",
|
"simulationBehavior": "neopixel",
|
||||||
"breadboardColumnsNeeded": 5,
|
"visual": {
|
||||||
"breadboardStartRow": "h",
|
"builtIn": "neopixel",
|
||||||
"pinAllocation": {
|
"width": 58,
|
||||||
"type": "factoryfunction",
|
"height": 113,
|
||||||
"functionName": "neopixel.create",
|
"pinDistance": 9,
|
||||||
"pinArgPositions": [0],
|
"pinLocations": [
|
||||||
"otherArgPositions": [1]
|
{"x": 10, "y": 0},
|
||||||
|
{"x": 19, "y": 0},
|
||||||
|
{"x": 28, "y": 0}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"assemblyStep": 0,
|
"numberOfPins": 3,
|
||||||
"wires": [
|
"pinDefinitions": [
|
||||||
{"start": ["breadboard", "j", 1], "end": "ground", "color": "blue", "assemblyStep": 1},
|
{"target": {"pinInstantiationIdx": 0}, "style": "solder", "orientation": "+Z"},
|
||||||
{"start": ["breadboard", "j", 2], "end": "threeVolt", "color": "red", "assemblyStep": 2},
|
{"target": "threeVolt", "style": "solder", "orientation": "+Z"},
|
||||||
{"start": ["breadboard", "j", 3], "end": ["GPIO", 0], "color": "green", "assemblyStep": 2}
|
{"target": "ground", "style": "solder", "orientation": "+Z"}
|
||||||
|
],
|
||||||
|
"instantiation": {
|
||||||
|
"kind": "function",
|
||||||
|
"fullyQualifiedName": "neopixel.create",
|
||||||
|
"argumentRoles": [
|
||||||
|
{"pinInstantiationIdx": 0, "partParameter": "pin"},
|
||||||
|
{"partParameter": "mode"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"assembly": [
|
||||||
|
{"part": true, "pinIndices": [2]},
|
||||||
|
{"pinIndices": [0, 1]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ledmatrix": {
|
||||||
|
"visual": {
|
||||||
|
"builtIn": "ledmatrix",
|
||||||
|
"width": 105,
|
||||||
|
"height": 105,
|
||||||
|
"pinDistance": 15,
|
||||||
|
"pinLocations": [
|
||||||
|
{"x": 0, "y": 0},
|
||||||
|
{"x": 15, "y": 0},
|
||||||
|
{"x": 30, "y": 0},
|
||||||
|
{"x": 45, "y": 0},
|
||||||
|
{"x": 105, "y": 105},
|
||||||
|
{"x": 0, "y": 105},
|
||||||
|
{"x": 15, "y": 105},
|
||||||
|
{"x": 30, "y": 105},
|
||||||
|
{"x": 45, "y": 105},
|
||||||
|
{"x": 60, "y": 0}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"simulationBehavior": "ledmatrix",
|
||||||
|
"numberOfPins": 10,
|
||||||
|
"instantiation": {"kind": "singleton"},
|
||||||
|
"pinDefinitions": [
|
||||||
|
{"target": "P6", "style": "male", "orientation": "-Z", "colorGroup": 0},
|
||||||
|
{"target": "P7", "style": "male", "orientation": "-Z", "colorGroup": 0},
|
||||||
|
{"target": "P8", "style": "male", "orientation": "-Z", "colorGroup": 0},
|
||||||
|
{"target": "P9", "style": "male", "orientation": "-Z", "colorGroup": 0},
|
||||||
|
{"target": "P10", "style": "male", "orientation": "-Z", "colorGroup": 0},
|
||||||
|
{"target": "P12", "style": "male", "orientation": "-Z", "colorGroup": 1},
|
||||||
|
{"target": "P13", "style": "male", "orientation": "-Z", "colorGroup": 1},
|
||||||
|
{"target": "P16", "style": "male", "orientation": "-Z", "colorGroup": 1},
|
||||||
|
{"target": "P19", "style": "male", "orientation": "-Z", "colorGroup": 1},
|
||||||
|
{"target": "P20", "style": "male", "orientation": "-Z", "colorGroup": 1}
|
||||||
|
],
|
||||||
|
"assembly": [
|
||||||
|
{"part": true},
|
||||||
|
{"pinIndices": [0, 1, 2, 3, 4]},
|
||||||
|
{"pinIndices": [5, 6, 7, 8, 9]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"speaker": {
|
"speaker": {
|
||||||
|
"numberOfPins": 2,
|
||||||
"visual": {
|
"visual": {
|
||||||
"image": "/parts/speaker.svg",
|
"image": "/parts/speaker.svg",
|
||||||
"width": 500,
|
"width": 500,
|
||||||
"height": 500,
|
"height": 500,
|
||||||
"firstPin": [180, 135],
|
"pinDistance": 70,
|
||||||
"pinDist": 70,
|
"pinLocations": [
|
||||||
"extraColumnOffset": 1
|
{"x": 180, "y": 135},
|
||||||
|
{"x": 320, "y": 135}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"breadboardColumnsNeeded": 5,
|
"pinDefinitions": [
|
||||||
"breadboardStartRow": "f",
|
{"target": "P0", "style": "male", "orientation": "-Z"},
|
||||||
"pinAllocation": {
|
{"target": "ground", "style": "male", "orientation": "-Z"}
|
||||||
"type": "auto",
|
],
|
||||||
"gpioPinsNeeded": 1
|
"instantiation": {"kind": "singleton"},
|
||||||
},
|
"assembly": [
|
||||||
"assemblyStep": 0,
|
{"part": true, "pinIndices": [0]},
|
||||||
"wires": [
|
{"pinIndices": [1]}
|
||||||
{"start": ["breadboard", "j", 1], "end": ["GPIO", 0], "color": "#ff80fa", "assemblyStep": 1},
|
|
||||||
{"start": ["breadboard", "j", 3], "end": "ground", "color": "blue", "assemblyStep": 1}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "pxt-microbit",
|
"name": "pxt-microbit",
|
||||||
"version": "0.3.80",
|
"version": "0.3.84",
|
||||||
"description": "micro:bit target for PXT",
|
"description": "micro:bit target for PXT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"JavaScript",
|
"JavaScript",
|
||||||
|
@ -29,6 +29,6 @@
|
||||||
"typescript": "^1.8.7"
|
"typescript": "^1.8.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pxt-core": "0.3.93"
|
"pxt-core": "0.3.96"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
"simulator": {
|
"simulator": {
|
||||||
"autoRun": true,
|
"autoRun": true,
|
||||||
"aspectRatio": 1.22,
|
"aspectRatio": 1.22,
|
||||||
|
"parts": true,
|
||||||
"partsAspectRatio": 0.69,
|
"partsAspectRatio": 0.69,
|
||||||
"builtinParts": {
|
"builtinParts": {
|
||||||
"accelerometer": true,
|
"accelerometer": true,
|
||||||
|
|
752
sim/allocator.ts
752
sim/allocator.ts
|
@ -1,60 +1,81 @@
|
||||||
|
|
||||||
namespace pxsim {
|
namespace pxsim {
|
||||||
|
const GROUND_COLOR = "blue";
|
||||||
|
const POWER_COLOR = "red";
|
||||||
|
|
||||||
export interface AllocatorOpts {
|
export interface AllocatorOpts {
|
||||||
boardDef: BoardDefinition,
|
boardDef: BoardDefinition,
|
||||||
cmpDefs: Map<PartDefinition>,
|
partDefs: Map<PartDefinition>,
|
||||||
|
partsList: string[]
|
||||||
fnArgs: any,
|
fnArgs: any,
|
||||||
getBBCoord: (loc: BBRowCol) => visuals.Coord,
|
// Used for finding the nearest available power pins
|
||||||
cmpList: string[]
|
getBBCoord: (loc: BBLoc) => visuals.Coord,
|
||||||
};
|
};
|
||||||
export interface AllocatorResult {
|
export interface AllocatorResult {
|
||||||
powerWires: WireInst[],
|
partsAndWires: PartAndWiresInst[],
|
||||||
components: CmpAndWireInst[]
|
|
||||||
}
|
}
|
||||||
|
export interface PartInst {
|
||||||
export interface CmpAndWireInst {
|
|
||||||
component: CmpInst,
|
|
||||||
wires: WireInst[]
|
|
||||||
}
|
|
||||||
export interface CmpInst {
|
|
||||||
name: string,
|
name: string,
|
||||||
breadboardStartColumn: number,
|
simulationBehavior?: string,
|
||||||
breadboardStartRow: string,
|
visual: PartVisualDefinition,
|
||||||
assemblyStep: number,
|
bbFit: PartBBFit,
|
||||||
visual: string | PartVisualDefinition,
|
startColumnIdx: number,
|
||||||
microbitPins: string[],
|
startRowIdx: number,
|
||||||
otherArgs?: string[],
|
breadboardConnections: BBLoc[],
|
||||||
|
params: Map<string>,
|
||||||
}
|
}
|
||||||
export interface WireInst {
|
export interface WireInst {
|
||||||
start: Loc,
|
start: Loc,
|
||||||
end: Loc,
|
end: Loc,
|
||||||
color: string,
|
color: string,
|
||||||
assemblyStep: number
|
|
||||||
};
|
};
|
||||||
interface PartialCmpAlloc {
|
export interface AssemblyStep {
|
||||||
|
part?: boolean,
|
||||||
|
wireIndices?: number[],
|
||||||
|
}
|
||||||
|
export interface PartAndWiresInst {
|
||||||
|
part?: PartInst,
|
||||||
|
wires?: WireInst[],
|
||||||
|
assembly: AssemblyStep[],
|
||||||
|
}
|
||||||
|
export interface PartBBFit {
|
||||||
|
xOffset: number,
|
||||||
|
yOffset: number,
|
||||||
|
rowCount: number,
|
||||||
|
colCount: number,
|
||||||
|
}
|
||||||
|
interface PinBBFit {
|
||||||
|
partRelativeColIdx: number,
|
||||||
|
partRelativeRowIdx: number,
|
||||||
|
xOffset: number,
|
||||||
|
yOffset: number,
|
||||||
|
}
|
||||||
|
interface PinIR {
|
||||||
|
loc: XY,
|
||||||
|
def: PartPinDefinition,
|
||||||
|
target: PinTarget,
|
||||||
|
bbFit: PinBBFit,
|
||||||
|
}
|
||||||
|
interface PartIR {
|
||||||
name: string,
|
name: string,
|
||||||
def: PartDefinition,
|
def: PartDefinition,
|
||||||
pinsAssigned: string[],
|
partParams: Map<string>,
|
||||||
pinsNeeded: number | number[],
|
pins: PinIR[],
|
||||||
breadboardColumnsNeeded: number,
|
bbFit: PartBBFit,
|
||||||
otherArgs?: string[],
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AllocLocOpts {
|
|
||||||
nearestBBPin?: BBRowCol,
|
|
||||||
startColumn?: number,
|
|
||||||
cmpGPIOPins?: string[],
|
|
||||||
};
|
};
|
||||||
interface AllocWireOpts {
|
interface PartPlacement extends PartIR {
|
||||||
startColumn: number,
|
startColumnIdx: number,
|
||||||
cmpGPIOPins: string[],
|
startRowIdx: number,
|
||||||
}
|
};
|
||||||
interface AllocBlock {
|
type WireIRLoc = PinTarget | BBLoc;
|
||||||
cmpIdx: number,
|
interface WireIR {
|
||||||
cmpBlkIdx: number,
|
pinIdx: number,
|
||||||
gpioNeeded: number,
|
start: WireIRLoc,
|
||||||
gpioAssigned: string[]
|
end: WireIRLoc,
|
||||||
|
color: string,
|
||||||
}
|
}
|
||||||
|
interface PartIRAndWireIRs extends PartPlacement {
|
||||||
|
wires: WireIR[],
|
||||||
|
};
|
||||||
interface PowerUsage {
|
interface PowerUsage {
|
||||||
topGround: boolean,
|
topGround: boolean,
|
||||||
topThreeVolt: boolean,
|
topThreeVolt: boolean,
|
||||||
|
@ -63,18 +84,27 @@ namespace pxsim {
|
||||||
singleGround: boolean,
|
singleGround: boolean,
|
||||||
singleThreeVolt: boolean,
|
singleThreeVolt: boolean,
|
||||||
}
|
}
|
||||||
function isOnBreadboardBottom(location: WireLocationDefinition) {
|
interface AllocLocOpts {
|
||||||
|
referenceBBPin?: BBLoc,
|
||||||
|
};
|
||||||
|
interface AllocWireOpts {
|
||||||
|
//TODO: port
|
||||||
|
startColumn: number,
|
||||||
|
partGPIOPins: string[],
|
||||||
|
}
|
||||||
|
function isOnBreadboardBottom(location: WireIRLoc) {
|
||||||
let isBot = false;
|
let isBot = false;
|
||||||
if (location[0] === "breadboard") {
|
if (typeof location !== "string" && (<BBLoc>location).type === "breadboard") {
|
||||||
let row = <string>location[1];
|
let bbLoc = <BBLoc>location;
|
||||||
|
let row = bbLoc.row;
|
||||||
isBot = 0 <= ["a", "b", "c", "d", "e"].indexOf(row);
|
isBot = 0 <= ["a", "b", "c", "d", "e"].indexOf(row);
|
||||||
}
|
}
|
||||||
return isBot;
|
return isBot;
|
||||||
}
|
}
|
||||||
const arrCount = (a: boolean[]) => a.reduce((p, n) => p + (n ? 1 : 0), 0);
|
const arrCount = (a: boolean[]) => a.reduce((p, n) => p + (n ? 1 : 0), 0);
|
||||||
const arrAny = (a: boolean[]) => arrCount(a) > 0;
|
const arrAny = (a: boolean[]) => arrCount(a) > 0;
|
||||||
function computePowerUsage(wireDef: WireDefinition): PowerUsage {
|
function computePowerUsage(wire: WireIR): PowerUsage {
|
||||||
let ends = [wireDef.start, wireDef.end];
|
let ends = [wire.start, wire.end];
|
||||||
let endIsGround = ends.map(e => e === "ground");
|
let endIsGround = ends.map(e => e === "ground");
|
||||||
let endIsThreeVolt = ends.map(e => e === "threeVolt");
|
let endIsThreeVolt = ends.map(e => e === "threeVolt");
|
||||||
let endIsBot = ends.map(e => isOnBreadboardBottom(e));
|
let endIsBot = ends.map(e => isOnBreadboardBottom(e));
|
||||||
|
@ -115,10 +145,21 @@ namespace pxsim {
|
||||||
function copyDoubleArray(a: string[][]) {
|
function copyDoubleArray(a: string[][]) {
|
||||||
return a.map(b => b.map(p => p));
|
return a.map(b => b.map(p => p));
|
||||||
}
|
}
|
||||||
function readPin(arg: string): string {
|
function merge2<A, B>(a: A, b: B): A & B {
|
||||||
|
let res: any = {};
|
||||||
|
for (let aKey in a)
|
||||||
|
res[aKey] = (<any>a)[aKey];
|
||||||
|
for (let bKey in b)
|
||||||
|
res[bKey] = (<any>b)[bKey];
|
||||||
|
return <A & B>res;
|
||||||
|
}
|
||||||
|
function merge3<A, B, C>(a: A, b: B, c: C): A & B & C {
|
||||||
|
return merge2(merge2(a, b), c);
|
||||||
|
}
|
||||||
|
function readPin(arg: string): MicrobitPin {
|
||||||
U.assert(!!arg, "Invalid pin: " + arg);
|
U.assert(!!arg, "Invalid pin: " + arg);
|
||||||
let pin = arg.split("DigitalPin.")[1];
|
let pin = arg.split("DigitalPin.")[1];
|
||||||
return pin;
|
return <MicrobitPin>pin;
|
||||||
}
|
}
|
||||||
function mkReverseMap(map: {[key: string]: string}) {
|
function mkReverseMap(map: {[key: string]: string}) {
|
||||||
let origKeys: string[] = [];
|
let origKeys: string[] = [];
|
||||||
|
@ -135,25 +176,239 @@ namespace pxsim {
|
||||||
}
|
}
|
||||||
return newMap;
|
return newMap;
|
||||||
}
|
}
|
||||||
|
function isConnectedToBB(pin: PartPinDefinition): boolean {
|
||||||
|
return pin.orientation === "-Z" && pin.style === "male";
|
||||||
|
}
|
||||||
class Allocator {
|
class Allocator {
|
||||||
|
//TODO: better handling of allocation errors
|
||||||
private opts: AllocatorOpts;
|
private opts: AllocatorOpts;
|
||||||
private availablePowerPins = {
|
private availablePowerPins = {
|
||||||
top: {
|
top: {
|
||||||
threeVolt: mkRange(26, 51).map(n => <BBRowCol>["+", `${n}`]),
|
threeVolt: mkRange(26, 51).map(n => <BBLoc>{type: "breadboard", row: "+", col: `${n}`}),
|
||||||
ground: mkRange(26, 51).map(n => <BBRowCol>["-", `${n}`]),
|
ground: mkRange(26, 51).map(n => <BBLoc>{type: "breadboard", row: "-", col: `${n}`}),
|
||||||
},
|
},
|
||||||
bottom: {
|
bottom: {
|
||||||
threeVolt: mkRange(1, 26).map(n => <BBRowCol>["+", `${n}`]),
|
threeVolt: mkRange(1, 26).map(n => <BBLoc>{type: "breadboard", row: "+", col: `${n}`}),
|
||||||
ground: mkRange(1, 26).map(n => <BBRowCol>["-", `${n}`]),
|
ground: mkRange(1, 26).map(n => <BBLoc>{type: "breadboard", row: "-", col: `${n}`}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
private powerUsage: PowerUsage;
|
private powerUsage: PowerUsage;
|
||||||
|
private availableWireColors: string[];
|
||||||
|
|
||||||
constructor(opts: AllocatorOpts) {
|
constructor(opts: AllocatorOpts) {
|
||||||
this.opts = opts;
|
this.opts = opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private allocateLocation(location: WireLocationDefinition, opts: AllocLocOpts): Loc {
|
private allocPartIRs(def: PartDefinition, name: string, bbFit: PartBBFit): PartIR[] {
|
||||||
|
let partIRs: PartIR[] = [];
|
||||||
|
let mkIR = (def: PartDefinition, name: string, instPins?: PinTarget[], partParams?: Map<string>): PartIR => {
|
||||||
|
let pinIRs: PinIR[] = [];
|
||||||
|
for (let i = 0; i < def.numberOfPins; i++) {
|
||||||
|
let pinDef = def.pinDefinitions[i];
|
||||||
|
let pinTarget: PinTarget;
|
||||||
|
if (typeof pinDef.target === "string") {
|
||||||
|
pinTarget = <PinTarget>pinDef.target;
|
||||||
|
} else {
|
||||||
|
let instIdx = (<PinInstantiationIdx>pinDef.target).pinInstantiationIdx;
|
||||||
|
U.assert(!!instPins && instPins[instIdx] !== undefined,
|
||||||
|
`No pin found for PinInstantiationIdx: ${instIdx}. (Is the part missing an ArguementRole or "trackArgs=" annotations?)`);
|
||||||
|
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: 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
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (def.instantiation.kind === "singleton") {
|
||||||
|
partIRs.push(mkIR(def, name));
|
||||||
|
} else if (def.instantiation.kind === "function") {
|
||||||
|
let fnAlloc = def.instantiation as PartFunctionDefinition;
|
||||||
|
let fnNm = fnAlloc.fullyQualifiedName;
|
||||||
|
let callsitesTrackedArgs = <string[]>this.opts.fnArgs[fnNm];
|
||||||
|
U.assert(!!callsitesTrackedArgs && !!callsitesTrackedArgs.length, "Failed to read pin(s) from callsite for: " + fnNm);
|
||||||
|
callsitesTrackedArgs.forEach(fnArgsStr => {
|
||||||
|
let fnArgsSplit = fnArgsStr.split(",");
|
||||||
|
U.assert(fnArgsSplit.length === fnAlloc.argumentRoles.length,
|
||||||
|
`Mismatch between number of arguments at callsite (function name: ${fnNm}) vs number of argument roles in part definition (part: ${name}).`);
|
||||||
|
let instPins: PinTarget[] = [];
|
||||||
|
let paramArgs: Map<string> = {};
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
private computePartDimensions(def: PartDefinition, name: string): PartBBFit {
|
||||||
|
let pinLocs = def.visual.pinLocations;
|
||||||
|
let pinDefs = def.pinDefinitions;
|
||||||
|
let numPins = def.numberOfPins;
|
||||||
|
U.assert(pinLocs.length === numPins, `Mismatch between "numberOfPins" and length of "visual.pinLocations" for "${name}"`);
|
||||||
|
U.assert(pinDefs.length === numPins, `Mismatch between "numberOfPins" and length of "pinDefinitions" for "${name}"`);
|
||||||
|
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: number;
|
||||||
|
let yOff: number;
|
||||||
|
let colCount: number;
|
||||||
|
let rowCount: number;
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private allocColumns(colCounts: {colCount: number}[]): number[] {
|
||||||
|
let partsCount = colCounts.length;
|
||||||
|
const totalColumnsCount = 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) {
|
||||||
|
console.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;
|
||||||
|
}
|
||||||
|
private placeParts(parts: PartIR[]): PartPlacement[] {
|
||||||
|
const totalRowsCount = 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;
|
||||||
|
}
|
||||||
|
private nextColor(): string {
|
||||||
|
if (!this.availableWireColors || this.availableWireColors.length <= 0) {
|
||||||
|
this.availableWireColors = visuals.GPIO_WIRE_COLORS.map(c => c);
|
||||||
|
}
|
||||||
|
return this.availableWireColors.pop();
|
||||||
|
}
|
||||||
|
private allocWireIRs(part: PartPlacement): PartIRAndWireIRs {
|
||||||
|
let groupToColor: string[] = [];
|
||||||
|
let wires: WireIR[] = part.pins.map((pin, pinIdx) => {
|
||||||
|
let end = pin.target;
|
||||||
|
let start: WireIRLoc;
|
||||||
|
let colIdx = part.startColumnIdx + pin.bbFit.partRelativeColIdx;
|
||||||
|
let colName = 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,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
//make a wire directly from pin to target
|
||||||
|
let rowName = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let color: string;
|
||||||
|
if (end === "ground") {
|
||||||
|
color = GROUND_COLOR;
|
||||||
|
} else if (end === "threeVolt") {
|
||||||
|
color = POWER_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});
|
||||||
|
}
|
||||||
|
private allocLocation(location: WireIRLoc, opts: AllocLocOpts): Loc {
|
||||||
if (location === "ground" || location === "threeVolt") {
|
if (location === "ground" || location === "threeVolt") {
|
||||||
//special case if there is only a single ground or three volt pin in the whole build
|
//special case if there is only a single ground or three volt pin in the whole build
|
||||||
if (location === "ground" && this.powerUsage.singleGround) {
|
if (location === "ground" && this.powerUsage.singleGround) {
|
||||||
|
@ -164,8 +419,8 @@ namespace pxsim {
|
||||||
return {type: "dalboard", pin: boardThreeVoltPin};
|
return {type: "dalboard", pin: boardThreeVoltPin};
|
||||||
}
|
}
|
||||||
|
|
||||||
U.assert(!!opts.nearestBBPin);
|
U.assert(!!opts.referenceBBPin);
|
||||||
let nearestCoord = this.opts.getBBCoord(opts.nearestBBPin);
|
let nearestCoord = this.opts.getBBCoord(opts.referenceBBPin);
|
||||||
let firstTopAndBot = [
|
let firstTopAndBot = [
|
||||||
this.availablePowerPins.top.ground[0] || this.availablePowerPins.top.threeVolt[0],
|
this.availablePowerPins.top.ground[0] || this.availablePowerPins.top.threeVolt[0],
|
||||||
this.availablePowerPins.bottom.ground[0] || this.availablePowerPins.bottom.threeVolt[0]
|
this.availablePowerPins.bottom.ground[0] || this.availablePowerPins.bottom.threeVolt[0]
|
||||||
|
@ -177,7 +432,7 @@ namespace pxsim {
|
||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
let nearTop = visuals.findClosestCoordIdx(nearestCoord, firstTopAndBot) == 0;
|
let nearTop = visuals.findClosestCoordIdx(nearestCoord, firstTopAndBot) == 0;
|
||||||
let barPins: BBRowCol[];
|
let barPins: BBLoc[];
|
||||||
if (nearTop) {
|
if (nearTop) {
|
||||||
if (location === "ground") {
|
if (location === "ground") {
|
||||||
barPins = this.availablePowerPins.top.ground;
|
barPins = this.availablePowerPins.top.ground;
|
||||||
|
@ -203,17 +458,9 @@ namespace pxsim {
|
||||||
this.availablePowerPins.bottom.ground.splice(closestPinIdx, 1);
|
this.availablePowerPins.bottom.ground.splice(closestPinIdx, 1);
|
||||||
this.availablePowerPins.bottom.threeVolt.splice(closestPinIdx, 1);
|
this.availablePowerPins.bottom.threeVolt.splice(closestPinIdx, 1);
|
||||||
}
|
}
|
||||||
return {type: "breadboard", rowCol: pin};
|
return pin;
|
||||||
} else if (location[0] === "breadboard") {
|
} else if ((<BBLoc>location).type === "breadboard") {
|
||||||
U.assert(!!opts.startColumn);
|
return <BBLoc>location;
|
||||||
let row = <string>location[1];
|
|
||||||
let col = (<number>location[2] + opts.startColumn).toString();
|
|
||||||
return {type: "breadboard", rowCol: [row, col]}
|
|
||||||
} else if (location[0] === "GPIO") {
|
|
||||||
U.assert(!!opts.cmpGPIOPins);
|
|
||||||
let idx = <number>location[1];
|
|
||||||
let pin = opts.cmpGPIOPins[idx];
|
|
||||||
return {type: "dalboard", pin: pin};
|
|
||||||
} else if (location === "MOSI" || location === "MISO" || location === "SCK") {
|
} else if (location === "MOSI" || location === "MISO" || location === "SCK") {
|
||||||
if (!this.opts.boardDef.spiPins)
|
if (!this.opts.boardDef.spiPins)
|
||||||
console.debug("No SPI pin mappings found!");
|
console.debug("No SPI pin mappings found!");
|
||||||
|
@ -225,12 +472,15 @@ namespace pxsim {
|
||||||
let pin = (<any>this.opts.boardDef.i2cPins)[location as string] as string;
|
let pin = (<any>this.opts.boardDef.i2cPins)[location as string] as string;
|
||||||
return {type: "dalboard", pin: pin};
|
return {type: "dalboard", pin: pin};
|
||||||
} else {
|
} else {
|
||||||
//TODO
|
//it must be a MicrobitPin
|
||||||
U.assert(false);
|
U.assert(typeof location === "string", "Unknown location type: " + location);
|
||||||
return null;
|
let mbPin = <MicrobitPin>location;
|
||||||
|
let boardPin = this.opts.boardDef.gpioPinMap[mbPin];
|
||||||
|
U.assert(!!boardPin, "Unknown pin: " + location);
|
||||||
|
return {type: "dalboard", pin: boardPin};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private getBoardGroundPin() {
|
private getBoardGroundPin(): string {
|
||||||
let boardGround = this.opts.boardDef.groundPins[0] || null;
|
let boardGround = this.opts.boardDef.groundPins[0] || null;
|
||||||
if (!boardGround) {
|
if (!boardGround) {
|
||||||
console.log("No available ground pin on board!");
|
console.log("No available ground pin on board!");
|
||||||
|
@ -238,7 +488,7 @@ namespace pxsim {
|
||||||
}
|
}
|
||||||
return boardGround;
|
return boardGround;
|
||||||
}
|
}
|
||||||
private getBoardThreeVoltPin() {
|
private getBoardThreeVoltPin(): string {
|
||||||
let threeVoltPin = this.opts.boardDef.threeVoltPins[0] || null;
|
let threeVoltPin = this.opts.boardDef.threeVoltPins[0] || null;
|
||||||
if (!threeVoltPin) {
|
if (!threeVoltPin) {
|
||||||
console.log("No available 3.3V pin on board!");
|
console.log("No available 3.3V pin on board!");
|
||||||
|
@ -246,14 +496,14 @@ namespace pxsim {
|
||||||
}
|
}
|
||||||
return threeVoltPin;
|
return threeVoltPin;
|
||||||
}
|
}
|
||||||
private allocatePowerWires(powerUsage: PowerUsage): WireInst[] {
|
private allocPowerWires(powerUsage: PowerUsage): PartAndWiresInst {
|
||||||
let boardGroundPin = this.getBoardGroundPin();
|
let boardGroundPin = this.getBoardGroundPin();
|
||||||
let threeVoltPin = this.getBoardThreeVoltPin();
|
let threeVoltPin = this.getBoardThreeVoltPin();
|
||||||
let topLeft: BBRowCol = ["-", "26"];
|
const topLeft: BBLoc = {type: "breadboard", row: "-", col: "26"};
|
||||||
let botLeft: BBRowCol = ["-", "1"];
|
const botLeft: BBLoc = {type: "breadboard", row: "-", col: "1"};
|
||||||
let topRight: BBRowCol = ["-", "50"];
|
const topRight: BBLoc = {type: "breadboard", row: "-", col: "50"};
|
||||||
let botRight: BBRowCol = ["-", "25"];
|
const botRight: BBLoc = {type: "breadboard", row: "-", col: "25"};
|
||||||
let top: BBRowCol, bot: BBRowCol;
|
let top: BBLoc, bot: BBLoc;
|
||||||
if (this.opts.boardDef.attachPowerOnRight) {
|
if (this.opts.boardDef.attachPowerOnRight) {
|
||||||
top = topRight;
|
top = topRight;
|
||||||
bot = botRight;
|
bot = botRight;
|
||||||
|
@ -261,296 +511,162 @@ namespace pxsim {
|
||||||
top = topLeft;
|
top = topLeft;
|
||||||
bot = botLeft;
|
bot = botLeft;
|
||||||
}
|
}
|
||||||
const GROUND_COLOR = "blue";
|
let groundWires: WireInst[] = [];
|
||||||
const POWER_COLOR = "red";
|
let threeVoltWires: WireInst[] = [];
|
||||||
const wires: WireInst[] = [];
|
|
||||||
let groundStep = 0;
|
|
||||||
let threeVoltStep = (powerUsage.bottomGround || powerUsage.topGround) ? 1 : 0;
|
|
||||||
if (powerUsage.bottomGround && powerUsage.topGround) {
|
if (powerUsage.bottomGround && powerUsage.topGround) {
|
||||||
//bb top - <==> bb bot -
|
//bb top - <==> bb bot -
|
||||||
wires.push({
|
groundWires.push({
|
||||||
start: this.allocateLocation("ground", {nearestBBPin: top}),
|
start: this.allocLocation("ground", {referenceBBPin: top}),
|
||||||
end: this.allocateLocation("ground", {nearestBBPin: bot}),
|
end: this.allocLocation("ground", {referenceBBPin: bot}),
|
||||||
color: GROUND_COLOR, assemblyStep: groundStep
|
color: GROUND_COLOR,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (powerUsage.topGround) {
|
if (powerUsage.topGround) {
|
||||||
//board - <==> bb top -
|
//board - <==> bb top -
|
||||||
wires.push({
|
groundWires.push({
|
||||||
start: this.allocateLocation("ground", {nearestBBPin: top}),
|
start: this.allocLocation("ground", {referenceBBPin: top}),
|
||||||
end: {type: "dalboard", pin: boardGroundPin},
|
end: {type: "dalboard", pin: boardGroundPin},
|
||||||
color: GROUND_COLOR, assemblyStep: groundStep
|
color: GROUND_COLOR,
|
||||||
});
|
});
|
||||||
} else if (powerUsage.bottomGround) {
|
} else if (powerUsage.bottomGround) {
|
||||||
//board - <==> bb bot -
|
//board - <==> bb bot -
|
||||||
wires.push({
|
groundWires.push({
|
||||||
start: this.allocateLocation("ground", {nearestBBPin: bot}),
|
start: this.allocLocation("ground", {referenceBBPin: bot}),
|
||||||
end: {type: "dalboard", pin: boardGroundPin},
|
end: {type: "dalboard", pin: boardGroundPin},
|
||||||
color: GROUND_COLOR, assemblyStep: groundStep
|
color: GROUND_COLOR,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (powerUsage.bottomThreeVolt && powerUsage.bottomGround) {
|
if (powerUsage.bottomThreeVolt && powerUsage.bottomGround) {
|
||||||
//bb top + <==> bb bot +
|
//bb top + <==> bb bot +
|
||||||
wires.push({
|
threeVoltWires.push({
|
||||||
start: this.allocateLocation("threeVolt", {nearestBBPin: top}),
|
start: this.allocLocation("threeVolt", {referenceBBPin: top}),
|
||||||
end: this.allocateLocation("threeVolt", {nearestBBPin: bot}),
|
end: this.allocLocation("threeVolt", {referenceBBPin: bot}),
|
||||||
color: POWER_COLOR, assemblyStep: threeVoltStep
|
color: POWER_COLOR,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (powerUsage.topThreeVolt) {
|
if (powerUsage.topThreeVolt) {
|
||||||
//board + <==> bb top +
|
//board + <==> bb top +
|
||||||
wires.push({
|
threeVoltWires.push({
|
||||||
start: this.allocateLocation("threeVolt", {nearestBBPin: top}),
|
start: this.allocLocation("threeVolt", {referenceBBPin: top}),
|
||||||
end: {type: "dalboard", pin: threeVoltPin},
|
end: {type: "dalboard", pin: threeVoltPin},
|
||||||
color: POWER_COLOR, assemblyStep: threeVoltStep
|
color: POWER_COLOR,
|
||||||
});
|
});
|
||||||
} else if (powerUsage.bottomThreeVolt) {
|
} else if (powerUsage.bottomThreeVolt) {
|
||||||
//board + <==> bb bot +
|
//board + <==> bb bot +
|
||||||
wires.push({
|
threeVoltWires.push({
|
||||||
start: this.allocateLocation("threeVolt", {nearestBBPin: bot}),
|
start: this.allocLocation("threeVolt", {referenceBBPin: bot}),
|
||||||
end: {type: "dalboard", pin: threeVoltPin},
|
end: {type: "dalboard", pin: threeVoltPin},
|
||||||
color: POWER_COLOR, assemblyStep: threeVoltStep
|
color: POWER_COLOR,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return wires;
|
let assembly: AssemblyStep[] = [];
|
||||||
|
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)});
|
||||||
|
return {
|
||||||
|
wires: groundWires.concat(threeVoltWires),
|
||||||
|
assembly: assembly
|
||||||
|
};
|
||||||
}
|
}
|
||||||
private allocateWire(wireDef: WireDefinition, opts: AllocWireOpts): WireInst {
|
private allocWire(wireIR: WireIR): WireInst {
|
||||||
let ends = [wireDef.start, wireDef.end];
|
let ends = [wireIR.start, wireIR.end];
|
||||||
let endIsPower = ends.map(e => e === "ground" || e === "threeVolt");
|
let endIsPower = ends.map(e => e === "ground" || e === "threeVolt");
|
||||||
//allocate non-power first so we know the nearest pin for the power end
|
//allocate non-power first so we know the nearest pin for the power end
|
||||||
let endInsts = ends.map((e, idx) => !endIsPower[idx] ? this.allocateLocation(e, opts) : null)
|
let endInsts = ends.map((e, idx) => !endIsPower[idx] ? this.allocLocation(e, {}) : null)
|
||||||
//allocate power pins closest to the other end of the wire
|
//allocate power pins closest to the other end of the wire
|
||||||
endInsts = endInsts.map((e, idx) => {
|
endInsts = endInsts.map((e, idx) => {
|
||||||
if (e)
|
if (e)
|
||||||
return e;
|
return e;
|
||||||
let locInst = <BBLoc>endInsts[1 - idx]; // non-power end
|
let locInst = <BBLoc>endInsts[1 - idx]; // non-power end
|
||||||
let l = this.allocateLocation(ends[idx], {
|
let l = this.allocLocation(ends[idx], {
|
||||||
nearestBBPin: locInst.rowCol,
|
referenceBBPin: locInst,
|
||||||
startColumn: opts.startColumn,
|
|
||||||
cmpGPIOPins: opts.cmpGPIOPins,
|
|
||||||
});
|
});
|
||||||
return l;
|
return l;
|
||||||
});
|
});
|
||||||
return {start: endInsts[0], end: endInsts[1], color: wireDef.color, assemblyStep: wireDef.assemblyStep};
|
return {start: endInsts[0], end: endInsts[1], color: wireIR.color};
|
||||||
}
|
}
|
||||||
private allocatePartialCmps(): PartialCmpAlloc[] {
|
private allocPart(ir: PartPlacement): PartInst {
|
||||||
let cmpNmAndDefs = this.opts.cmpList.map(cmpName => <[string, PartDefinition]>[cmpName, this.opts.cmpDefs[cmpName]]).filter(d => !!d[1]);
|
let bbConnections = ir.pins
|
||||||
let cmpNmsList = cmpNmAndDefs.map(p => p[0]);
|
.filter(p => isConnectedToBB(p.def))
|
||||||
let cmpDefsList = cmpNmAndDefs.map(p => p[1]);
|
.map(p => {
|
||||||
let partialCmps: PartialCmpAlloc[] = [];
|
let rowIdx = ir.startRowIdx + p.bbFit.partRelativeRowIdx;
|
||||||
cmpDefsList.forEach((def, idx) => {
|
if (rowIdx >= 7) //account for middle gap
|
||||||
let nm = cmpNmsList[idx];
|
rowIdx -= 2;
|
||||||
if (def.pinAllocation.type === "predefined") {
|
let rowName = visuals.getRowName(rowIdx);
|
||||||
let mbPins = (<PredefinedPinAlloc>def.pinAllocation).pins;
|
let colIdx = ir.startColumnIdx + p.bbFit.partRelativeColIdx;
|
||||||
let pinsAssigned = mbPins.map(p => this.opts.boardDef.gpioPinMap[p]);
|
let colName = visuals.getColumnName(colIdx);
|
||||||
partialCmps.push({
|
return <BBLoc>{
|
||||||
name: nm,
|
type: "breadboard",
|
||||||
def: def,
|
row: rowName,
|
||||||
pinsAssigned: pinsAssigned,
|
col: colName,
|
||||||
pinsNeeded: 0,
|
|
||||||
breadboardColumnsNeeded: def.breadboardColumnsNeeded,
|
|
||||||
});
|
|
||||||
} else if (def.pinAllocation.type === "factoryfunction") {
|
|
||||||
let fnPinAlloc = (<FactoryFunctionPinAlloc>def.pinAllocation);
|
|
||||||
let fnNm = fnPinAlloc.functionName;
|
|
||||||
let fnsAndArgs = <string[]>this.opts.fnArgs[fnNm];
|
|
||||||
let success = false;
|
|
||||||
if (fnsAndArgs && fnsAndArgs.length) {
|
|
||||||
let pinArgPoses = fnPinAlloc.pinArgPositions;
|
|
||||||
let otherArgPoses = fnPinAlloc.otherArgPositions || [];
|
|
||||||
fnsAndArgs.forEach(fnArgsStr => {
|
|
||||||
let fnArgsSplit = fnArgsStr.split(",");
|
|
||||||
let pinArgs: string[] = [];
|
|
||||||
pinArgPoses.forEach(i => {
|
|
||||||
pinArgs.push(fnArgsSplit[i]);
|
|
||||||
});
|
|
||||||
let mbPins = pinArgs.map(arg => readPin(arg));
|
|
||||||
let otherArgs: string[] = [];
|
|
||||||
otherArgPoses.forEach(i => {
|
|
||||||
otherArgs.push(fnArgsSplit[i]);
|
|
||||||
});
|
|
||||||
let pinsAssigned = mbPins.map(p => this.opts.boardDef.gpioPinMap[p]);
|
|
||||||
partialCmps.push({
|
|
||||||
name: nm,
|
|
||||||
def: def,
|
|
||||||
pinsAssigned: pinsAssigned,
|
|
||||||
pinsNeeded: 0,
|
|
||||||
breadboardColumnsNeeded: def.breadboardColumnsNeeded,
|
|
||||||
otherArgs: otherArgs.length ? otherArgs : null,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// failed to find pin allocation from callsites
|
|
||||||
console.debug("Failed to read pin(s) from callsite for: " + fnNm);
|
|
||||||
let pinsNeeded = fnPinAlloc.pinArgPositions.length;
|
|
||||||
partialCmps.push({
|
|
||||||
name: nm,
|
|
||||||
def: def,
|
|
||||||
pinsAssigned: [],
|
|
||||||
pinsNeeded: pinsNeeded,
|
|
||||||
breadboardColumnsNeeded: def.breadboardColumnsNeeded,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (def.pinAllocation.type === "auto") {
|
|
||||||
let pinsNeeded = (<AutoPinAlloc>def.pinAllocation).gpioPinsNeeded;
|
|
||||||
partialCmps.push({
|
|
||||||
name: nm,
|
|
||||||
def: def,
|
|
||||||
pinsAssigned: [],
|
|
||||||
pinsNeeded: pinsNeeded,
|
|
||||||
breadboardColumnsNeeded: def.breadboardColumnsNeeded,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return partialCmps;
|
let part: PartInst = {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
private allocateGPIOPins(partialCmps: PartialCmpAlloc[]): string[][] {
|
return part;
|
||||||
let availableGPIOBlocks = copyDoubleArray(this.opts.boardDef.gpioPinBlocks);
|
|
||||||
let sortAvailableGPIOBlocks = () => availableGPIOBlocks.sort((a, b) => a.length - b.length); //smallest blocks first
|
|
||||||
// determine blocks needed
|
|
||||||
let blockAssignments: AllocBlock[] = [];
|
|
||||||
let preassignedPins: string[] = [];
|
|
||||||
partialCmps.forEach((cmp, idx) => {
|
|
||||||
if (cmp.pinsAssigned && cmp.pinsAssigned.length) {
|
|
||||||
//already assigned
|
|
||||||
blockAssignments.push({cmpIdx: idx, cmpBlkIdx: 0, gpioNeeded: 0, gpioAssigned: cmp.pinsAssigned});
|
|
||||||
preassignedPins = preassignedPins.concat(cmp.pinsAssigned);
|
|
||||||
} else if (cmp.pinsNeeded) {
|
|
||||||
if (typeof cmp.pinsNeeded === "number") {
|
|
||||||
//individual pins
|
|
||||||
for (let i = 0; i < cmp.pinsNeeded; i++) {
|
|
||||||
blockAssignments.push(
|
|
||||||
{cmpIdx: idx, cmpBlkIdx: 0, gpioNeeded: 1, gpioAssigned: []});
|
|
||||||
}
|
}
|
||||||
} else {
|
public allocAll(): AllocatorResult {
|
||||||
//blocks of pins
|
let partNmAndDefs = this.opts.partsList
|
||||||
let blocks = <number[]>cmp.pinsNeeded;
|
.map(partName => {return {name: partName, def: this.opts.partDefs[partName]}})
|
||||||
blocks.forEach((numNeeded, blkIdx) => {
|
.filter(d => !!d.def);
|
||||||
blockAssignments.push(
|
if (partNmAndDefs.length > 0) {
|
||||||
{cmpIdx: idx, cmpBlkIdx: blkIdx, gpioNeeded: numNeeded, gpioAssigned: []});
|
let partNmsList = partNmAndDefs.map(p => p.name);
|
||||||
});
|
let partDefsList = partNmAndDefs.map(p => p.def);
|
||||||
}
|
let dimensions = partNmAndDefs.map(nmAndPart => this.computePartDimensions(nmAndPart.def, nmAndPart.name));
|
||||||
}
|
let partIRs: PartIR[] = [];
|
||||||
});
|
partNmAndDefs.forEach((nmAndDef, idx) => {
|
||||||
// remove assigned blocks
|
let dims = dimensions[idx];
|
||||||
availableGPIOBlocks.forEach(blks => {
|
let irs = this.allocPartIRs(nmAndDef.def, nmAndDef.name, dims);
|
||||||
for (let i = blks.length - 1; 0 <= i; i--) {
|
partIRs = partIRs.concat(irs);
|
||||||
let pin = blks[i];
|
})
|
||||||
if (0 <= preassignedPins.indexOf(pin)) {
|
let partPlacements = this.placeParts(partIRs);
|
||||||
blks.splice(i, 1);
|
let partsAndWireIRs = partPlacements.map(p => this.allocWireIRs(p));
|
||||||
}
|
let allWireIRs = partsAndWireIRs.map(p => p.wires).reduce((p, n) => p.concat(n), []);
|
||||||
}
|
let allPowerUsage = allWireIRs.map(w => computePowerUsage(w));
|
||||||
});
|
|
||||||
// sort by size of blocks
|
|
||||||
let sortBlockAssignments = () => blockAssignments.sort((a, b) => b.gpioNeeded - a.gpioNeeded); //largest blocks first
|
|
||||||
// allocate each block
|
|
||||||
if (0 < blockAssignments.length && 0 < availableGPIOBlocks.length) {
|
|
||||||
do {
|
|
||||||
sortBlockAssignments();
|
|
||||||
sortAvailableGPIOBlocks();
|
|
||||||
let assignment = blockAssignments[0];
|
|
||||||
let smallestAvailableBlockThatFits: string[];
|
|
||||||
for (let j = 0; j < availableGPIOBlocks.length; j++) {
|
|
||||||
smallestAvailableBlockThatFits = availableGPIOBlocks[j];
|
|
||||||
if (assignment.gpioNeeded <= availableGPIOBlocks[j].length) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!smallestAvailableBlockThatFits || smallestAvailableBlockThatFits.length <= 0) {
|
|
||||||
break; // out of pins
|
|
||||||
}
|
|
||||||
while (0 < assignment.gpioNeeded && 0 < smallestAvailableBlockThatFits.length) {
|
|
||||||
assignment.gpioNeeded--;
|
|
||||||
let pin = smallestAvailableBlockThatFits[0];
|
|
||||||
smallestAvailableBlockThatFits.splice(0, 1);
|
|
||||||
assignment.gpioAssigned.push(pin);
|
|
||||||
}
|
|
||||||
sortBlockAssignments();
|
|
||||||
} while (0 < blockAssignments[0].gpioNeeded);
|
|
||||||
}
|
|
||||||
if (0 < blockAssignments.length && 0 < blockAssignments[0].gpioNeeded) {
|
|
||||||
console.debug("Not enough GPIO pins!");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let cmpGPIOPinBlocks: string[][][] = partialCmps.map((def, cmpIdx) => {
|
|
||||||
if (!def)
|
|
||||||
return null;
|
|
||||||
let assignments = blockAssignments.filter(a => a.cmpIdx === cmpIdx);
|
|
||||||
let gpioPins: string[][] = [];
|
|
||||||
for (let i = 0; i < assignments.length; i++) {
|
|
||||||
let a = assignments[i];
|
|
||||||
let blk = gpioPins[a.cmpBlkIdx] || (gpioPins[a.cmpBlkIdx] = []);
|
|
||||||
a.gpioAssigned.forEach(p => blk.push(p));
|
|
||||||
}
|
|
||||||
return gpioPins;
|
|
||||||
});
|
|
||||||
let cmpGPIOPins = cmpGPIOPinBlocks.map(blks => blks.reduce((p, n) => p.concat(n), []));
|
|
||||||
return cmpGPIOPins;
|
|
||||||
}
|
|
||||||
private allocateColumns(partialCmps: PartialCmpAlloc[]): number[] {
|
|
||||||
let componentsCount = partialCmps.length;
|
|
||||||
let totalAvailableSpace = 30; //TODO allow multiple breadboards
|
|
||||||
let totalSpaceNeeded = partialCmps.map(d => d.breadboardColumnsNeeded).reduce((p, n) => p + n, 0);
|
|
||||||
let extraSpace = totalAvailableSpace - totalSpaceNeeded;
|
|
||||||
if (extraSpace <= 0) {
|
|
||||||
console.log("Not enough breadboard space!");
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
let padding = Math.floor(extraSpace / (componentsCount - 1 + 2));
|
|
||||||
let componentSpacing = padding; //Math.floor(extraSpace/(componentsCount-1));
|
|
||||||
let totalCmpPadding = extraSpace - componentSpacing * (componentsCount - 1);
|
|
||||||
let leftPadding = Math.floor(totalCmpPadding / 2);
|
|
||||||
let rightPadding = Math.ceil(totalCmpPadding / 2);
|
|
||||||
let nextAvailableCol = 1 + leftPadding;
|
|
||||||
let cmpStartCol = partialCmps.map(cmp => {
|
|
||||||
let col = nextAvailableCol;
|
|
||||||
nextAvailableCol += cmp.breadboardColumnsNeeded + componentSpacing;
|
|
||||||
return col;
|
|
||||||
});
|
|
||||||
return cmpStartCol;
|
|
||||||
}
|
|
||||||
private allocateComponent(partialCmp: PartialCmpAlloc, startColumn: number, microbitPins: string[]): CmpInst {
|
|
||||||
return {
|
|
||||||
name: partialCmp.name,
|
|
||||||
breadboardStartColumn: startColumn,
|
|
||||||
breadboardStartRow: partialCmp.def.breadboardStartRow,
|
|
||||||
assemblyStep: partialCmp.def.assemblyStep,
|
|
||||||
visual: partialCmp.def.visual,
|
|
||||||
microbitPins: microbitPins,
|
|
||||||
otherArgs: partialCmp.otherArgs,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public allocateAll(): AllocatorResult {
|
|
||||||
let cmpList = this.opts.cmpList;
|
|
||||||
let basicWires: WireInst[] = [];
|
|
||||||
let cmpsAndWires: CmpAndWireInst[] = [];
|
|
||||||
if (cmpList.length > 0) {
|
|
||||||
let partialCmps = this.allocatePartialCmps();
|
|
||||||
let allWireDefs = partialCmps.map(p => p.def.wires).reduce((p, n) => p.concat(n), []);
|
|
||||||
let allPowerUsage = allWireDefs.map(w => computePowerUsage(w));
|
|
||||||
this.powerUsage = mergePowerUsage(allPowerUsage);
|
this.powerUsage = mergePowerUsage(allPowerUsage);
|
||||||
basicWires = this.allocatePowerWires(this.powerUsage);
|
let basicWires = this.allocPowerWires(this.powerUsage);
|
||||||
let cmpGPIOPins = this.allocateGPIOPins(partialCmps);
|
let partsAndWires: PartAndWiresInst[] = partsAndWireIRs.map((irs, idx) => {
|
||||||
let reverseMap = mkReverseMap(this.opts.boardDef.gpioPinMap);
|
let part = this.allocPart(irs);
|
||||||
let cmpMicrobitPins = cmpGPIOPins.map(pins => pins.map(p => reverseMap[p]));
|
let wires = irs.wires.map(w => this.allocWire(w));
|
||||||
let cmpStartCol = this.allocateColumns(partialCmps);
|
let pinIdxToWireIdx: number[] = [];
|
||||||
let cmps = partialCmps.map((c, idx) => this.allocateComponent(c, cmpStartCol[idx], cmpMicrobitPins[idx]));
|
irs.wires.forEach((wIR, idx) => {
|
||||||
let wires = partialCmps.map((c, idx) => c.def.wires.map(d => this.allocateWire(d, {
|
pinIdxToWireIdx[wIR.pinIdx] = idx;
|
||||||
cmpGPIOPins: cmpGPIOPins[idx],
|
|
||||||
startColumn: cmpStartCol[idx],
|
|
||||||
})));
|
|
||||||
cmpsAndWires = cmps.map((c, idx) => {
|
|
||||||
return {component: c, wires: wires[idx]}
|
|
||||||
});
|
});
|
||||||
}
|
let assembly: AssemblyStep[] = irs.def.assembly.map(stepDef => {
|
||||||
return {
|
return {
|
||||||
powerWires: basicWires,
|
part: stepDef.part,
|
||||||
components: cmpsAndWires
|
wireIndices: (stepDef.pinIndices || []).map(i => pinIdxToWireIdx[i])
|
||||||
};
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
part: part,
|
||||||
|
wires: wires,
|
||||||
|
assembly: assembly
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let all = [basicWires].concat(partsAndWires);
|
||||||
|
return {
|
||||||
|
partsAndWires: all
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
partsAndWires: []
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function allocateDefinitions(opts: AllocatorOpts): AllocatorResult {
|
export function allocateDefinitions(opts: AllocatorOpts): AllocatorResult {
|
||||||
return new Allocator(opts).allocateAll();
|
return new Allocator(opts).allocAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -84,8 +84,8 @@ namespace pxsim {
|
||||||
let viewHost = new visuals.BoardHost({
|
let viewHost = new visuals.BoardHost({
|
||||||
state: this,
|
state: this,
|
||||||
boardDef: boardDef,
|
boardDef: boardDef,
|
||||||
cmpsList: cmpsList,
|
partsList: cmpsList,
|
||||||
cmpDefs: cmpDefs,
|
partDefs: cmpDefs,
|
||||||
fnArgs: fnArgs,
|
fnArgs: fnArgs,
|
||||||
maxWidth: "100%",
|
maxWidth: "100%",
|
||||||
maxHeight: "100%",
|
maxHeight: "100%",
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace pxsim {
|
||||||
marginWhenBreadboarding: [0, 0, 80, 0],
|
marginWhenBreadboarding: [0, 0, 80, 0],
|
||||||
}
|
}
|
||||||
|
|
||||||
export const builtinComponentSimVisual: Map<() => visuals.IBoardComponent<any>> = {
|
export const builtinComponentSimVisual: Map<() => visuals.IBoardPart<any>> = {
|
||||||
"buttonpair": () => new visuals.ButtonPairView(),
|
"buttonpair": () => new visuals.ButtonPairView(),
|
||||||
"ledmatrix": () => new visuals.LedMatrixView(),
|
"ledmatrix": () => new visuals.LedMatrixView(),
|
||||||
"neopixel": () => new visuals.NeoPixelView(),
|
"neopixel": () => new visuals.NeoPixelView(),
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace pxsim.instructions {
|
||||||
const LBL_LEFT_PAD = 5;
|
const LBL_LEFT_PAD = 5;
|
||||||
const REQ_WIRE_HEIGHT = 45;
|
const REQ_WIRE_HEIGHT = 45;
|
||||||
const REQ_CMP_HEIGHT = 55;
|
const REQ_CMP_HEIGHT = 55;
|
||||||
const REQ_CMP_SCALE = 0.5 * 4;
|
const REQ_CMP_SCALE = 0.5 * 3;
|
||||||
type Orientation = "landscape" | "portrait";
|
type Orientation = "landscape" | "portrait";
|
||||||
const ORIENTATION: Orientation = "portrait";
|
const ORIENTATION: Orientation = "portrait";
|
||||||
const PPI = 96.0;
|
const PPI = 96.0;
|
||||||
|
@ -284,19 +284,19 @@ namespace pxsim.instructions {
|
||||||
div.appendChild(svgEl);
|
div.appendChild(svgEl);
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
function mkCmpDiv(cmp: "wire" | string | PartVisualDefinition, opts: mkCmpDivOpts): HTMLElement {
|
function mkCmpDiv(cmp: "wire" | PartVisualDefinition, opts: mkCmpDivOpts): HTMLElement {
|
||||||
let el: visuals.SVGElAndSize;
|
let el: visuals.SVGElAndSize;
|
||||||
if (cmp == "wire") {
|
if (cmp == "wire") {
|
||||||
//TODO: support non-croc wire parts
|
|
||||||
el = visuals.mkWirePart([0, 0], opts.wireClr || "red", opts.crocClips);
|
el = visuals.mkWirePart([0, 0], opts.wireClr || "red", opts.crocClips);
|
||||||
} else if (typeof cmp == "string") {
|
|
||||||
let builtinVis = <string>cmp;
|
|
||||||
let cnstr = builtinComponentPartVisual[builtinVis];
|
|
||||||
el = cnstr([0, 0]);
|
|
||||||
} else {
|
} else {
|
||||||
let partVis = <PartVisualDefinition>cmp;
|
let partVis = <PartVisualDefinition>cmp;
|
||||||
|
if (typeof partVis.builtIn == "string") {
|
||||||
|
let cnstr = builtinComponentPartVisual[partVis.builtIn];
|
||||||
|
el = cnstr([0, 0]);
|
||||||
|
} else {
|
||||||
el = visuals.mkGenericPartSVG(partVis);
|
el = visuals.mkGenericPartSVG(partVis);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return wrapSvg(el, opts);
|
return wrapSvg(el, opts);
|
||||||
}
|
}
|
||||||
type BoardProps = {
|
type BoardProps = {
|
||||||
|
@ -305,40 +305,33 @@ namespace pxsim.instructions {
|
||||||
fnArgs: any,
|
fnArgs: any,
|
||||||
allAlloc: AllocatorResult,
|
allAlloc: AllocatorResult,
|
||||||
stepToWires: WireInst[][],
|
stepToWires: WireInst[][],
|
||||||
stepToCmps: CmpInst[][]
|
stepToCmps: PartInst[][]
|
||||||
allWires: WireInst[],
|
allWires: WireInst[],
|
||||||
allCmps: CmpInst[],
|
allCmps: PartInst[],
|
||||||
lastStep: number,
|
lastStep: number,
|
||||||
colorToWires: Map<WireInst[]>,
|
colorToWires: Map<WireInst[]>,
|
||||||
allWireColors: string[],
|
allWireColors: string[],
|
||||||
};
|
};
|
||||||
function mkBoardProps(allocOpts: AllocatorOpts): BoardProps {
|
function mkBoardProps(allocOpts: AllocatorOpts): BoardProps {
|
||||||
let allocRes = allocateDefinitions(allocOpts);
|
let allocRes = allocateDefinitions(allocOpts);
|
||||||
let {powerWires, components} = allocRes;
|
|
||||||
let stepToWires: WireInst[][] = [];
|
let stepToWires: WireInst[][] = [];
|
||||||
let stepToCmps: CmpInst[][] = [];
|
let stepToCmps: PartInst[][] = [];
|
||||||
powerWires.forEach(w => {
|
let stepOffset = 0;
|
||||||
let step = w.assemblyStep + 1;
|
allocRes.partsAndWires.forEach(cAndWs => {
|
||||||
(stepToWires[step] || (stepToWires[step] = [])).push(w)
|
let part = cAndWs.part;
|
||||||
});
|
let wires = cAndWs.wires;
|
||||||
let getMaxStep = (ns: { assemblyStep: number }[]) => ns.reduce((m, n) => Math.max(m, n.assemblyStep), 0);
|
cAndWs.assembly.forEach((step, idx) => {
|
||||||
let stepOffset = powerWires.length > 0 ? getMaxStep(powerWires) + 2 : 1;
|
if (step.part && part)
|
||||||
components.forEach(cAndWs => {
|
stepToCmps[stepOffset + idx] = [part]
|
||||||
let {component, wires} = cAndWs;
|
if (step.wireIndices && step.wireIndices.length > 0 && wires)
|
||||||
let cStep = component.assemblyStep + stepOffset;
|
stepToWires[stepOffset + idx] = step.wireIndices.map(i => wires[i])
|
||||||
let arr = stepToCmps[cStep] || (stepToCmps[cStep] = []);
|
|
||||||
arr.push(component);
|
|
||||||
let wSteps = wires.map(w => w.assemblyStep + stepOffset);
|
|
||||||
wires.forEach((w, i) => {
|
|
||||||
let wStep = wSteps[i];
|
|
||||||
let arr = stepToWires[wStep] || (stepToWires[wStep] = []);
|
|
||||||
arr.push(w);
|
|
||||||
})
|
})
|
||||||
stepOffset = Math.max(cStep, wSteps.reduce((m, n) => Math.max(m, n), 0)) + 1;
|
stepOffset += cAndWs.assembly.length;
|
||||||
});
|
});
|
||||||
let lastStep = stepOffset - 1;
|
let numSteps = stepOffset;
|
||||||
let allCmps = components.map(p => p.component);
|
let lastStep = numSteps - 1;
|
||||||
let allWires = powerWires.concat(components.map(p => p.wires).reduce((p, n) => p.concat(n), []));
|
let allCmps = allocRes.partsAndWires.map(r => r.part).filter(p => !!p);
|
||||||
|
let allWires = allocRes.partsAndWires.map(r => r.wires || []).reduce((p, n) => p.concat(n), []);
|
||||||
let colorToWires: Map<WireInst[]> = {}
|
let colorToWires: Map<WireInst[]> = {}
|
||||||
let allWireColors: string[] = [];
|
let allWireColors: string[] = [];
|
||||||
allWires.forEach(w => {
|
allWires.forEach(w => {
|
||||||
|
@ -350,7 +343,7 @@ namespace pxsim.instructions {
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
boardDef: allocOpts.boardDef,
|
boardDef: allocOpts.boardDef,
|
||||||
cmpDefs: allocOpts.cmpDefs,
|
cmpDefs: allocOpts.partDefs,
|
||||||
fnArgs: allocOpts.fnArgs,
|
fnArgs: allocOpts.fnArgs,
|
||||||
allAlloc: allocRes,
|
allAlloc: allocRes,
|
||||||
stepToWires: stepToWires,
|
stepToWires: stepToWires,
|
||||||
|
@ -368,7 +361,7 @@ namespace pxsim.instructions {
|
||||||
state: state,
|
state: state,
|
||||||
boardDef: boardDef,
|
boardDef: boardDef,
|
||||||
forceBreadboard: true,
|
forceBreadboard: true,
|
||||||
cmpDefs: cmpDefs,
|
partDefs: cmpDefs,
|
||||||
maxWidth: `${width}px`,
|
maxWidth: `${width}px`,
|
||||||
fnArgs: fnArgs,
|
fnArgs: fnArgs,
|
||||||
wireframe: buildMode,
|
wireframe: buildMode,
|
||||||
|
@ -397,6 +390,19 @@ namespace pxsim.instructions {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i <= step; i++) {
|
for (let i = 0; i <= step; i++) {
|
||||||
|
let cmps = props.stepToCmps[i];
|
||||||
|
if (cmps) {
|
||||||
|
cmps.forEach(partInst => {
|
||||||
|
let cmp = board.addPart(partInst)
|
||||||
|
//last step
|
||||||
|
if (i === step) {
|
||||||
|
//highlight locations pins
|
||||||
|
partInst.breadboardConnections.forEach(bbLoc => board.highlightBreadboardPin(bbLoc));
|
||||||
|
svg.addClass(cmp.element, "notgrayed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let wires = props.stepToWires[i];
|
let wires = props.stepToWires[i];
|
||||||
if (wires) {
|
if (wires) {
|
||||||
wires.forEach(w => {
|
wires.forEach(w => {
|
||||||
|
@ -405,13 +411,12 @@ namespace pxsim.instructions {
|
||||||
if (i === step) {
|
if (i === step) {
|
||||||
//location highlights
|
//location highlights
|
||||||
if (w.start.type == "breadboard") {
|
if (w.start.type == "breadboard") {
|
||||||
let lbls = board.highlightBreadboardPin((<BBLoc>w.start).rowCol);
|
let lbls = board.highlightBreadboardPin((<BBLoc>w.start));
|
||||||
} else {
|
} else {
|
||||||
board.highlightBoardPin((<BoardLoc>w.start).pin);
|
board.highlightBoardPin((<BoardLoc>w.start).pin);
|
||||||
}
|
}
|
||||||
if (w.end.type == "breadboard") {
|
if (w.end.type == "breadboard") {
|
||||||
let [row, col] = (<BBLoc>w.end).rowCol;
|
let lbls = board.highlightBreadboardPin((<BBLoc>w.end));
|
||||||
let lbls = board.highlightBreadboardPin((<BBLoc>w.end).rowCol);
|
|
||||||
} else {
|
} else {
|
||||||
board.highlightBoardPin((<BoardLoc>w.end).pin);
|
board.highlightBoardPin((<BoardLoc>w.end).pin);
|
||||||
}
|
}
|
||||||
|
@ -420,24 +425,6 @@ namespace pxsim.instructions {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let cmps = props.stepToCmps[i];
|
|
||||||
if (cmps) {
|
|
||||||
cmps.forEach(cmpInst => {
|
|
||||||
let cmp = board.addComponent(cmpInst)
|
|
||||||
let colOffset = (<any>cmpInst.visual).breadboardStartColIdx || 0;
|
|
||||||
let rowCol: BBRowCol = [`${cmpInst.breadboardStartRow}`, `${colOffset + cmpInst.breadboardStartColumn}`];
|
|
||||||
//last step
|
|
||||||
if (i === step) {
|
|
||||||
board.highlightBreadboardPin(rowCol);
|
|
||||||
if (cmpInst.visual === "buttonpair") {
|
|
||||||
//TODO: don't specialize this
|
|
||||||
let rowCol2: BBRowCol = [`${cmpInst.breadboardStartRow}`, `${cmpInst.breadboardStartColumn + 3}`];
|
|
||||||
board.highlightBreadboardPin(rowCol2);
|
|
||||||
}
|
|
||||||
svg.addClass(cmp.element, "notgrayed");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function mkPanel() {
|
function mkPanel() {
|
||||||
|
@ -463,7 +450,7 @@ namespace pxsim.instructions {
|
||||||
cmps.forEach(c => {
|
cmps.forEach(c => {
|
||||||
let quant = 1;
|
let quant = 1;
|
||||||
// TODO: don't special case this
|
// TODO: don't special case this
|
||||||
if (c.visual === "buttonpair") {
|
if (c.visual.builtIn === "buttonpair") {
|
||||||
quant = 2;
|
quant = 2;
|
||||||
}
|
}
|
||||||
let cmp = mkCmpDiv(c.visual, {
|
let cmp = mkCmpDiv(c.visual, {
|
||||||
|
@ -516,7 +503,7 @@ namespace pxsim.instructions {
|
||||||
let wires = (props.stepToWires[step] || []);
|
let wires = (props.stepToWires[step] || []);
|
||||||
let mkLabel = (loc: Loc) => {
|
let mkLabel = (loc: Loc) => {
|
||||||
if (loc.type === "breadboard") {
|
if (loc.type === "breadboard") {
|
||||||
let [row, col] = (<BBLoc>loc).rowCol;
|
let {row, col} = (<BBLoc>loc);
|
||||||
return `(${row},${col})`
|
return `(${row},${col})`
|
||||||
} else
|
} else
|
||||||
return (<BoardLoc>loc).pin;
|
return (<BoardLoc>loc).pin;
|
||||||
|
@ -536,17 +523,23 @@ namespace pxsim.instructions {
|
||||||
});
|
});
|
||||||
let cmps = (props.stepToCmps[step] || []);
|
let cmps = (props.stepToCmps[step] || []);
|
||||||
cmps.forEach(c => {
|
cmps.forEach(c => {
|
||||||
let l: BBRowCol = [`${c.breadboardStartRow}`, `${c.breadboardStartColumn}`];
|
let locs: BBLoc[];
|
||||||
let locs = [l];
|
if (c.visual.builtIn === "buttonpair") {
|
||||||
if (c.visual === "buttonpair") {
|
|
||||||
//TODO: don't special case this
|
//TODO: don't special case this
|
||||||
let l2: BBRowCol = [`${c.breadboardStartRow}`, `${c.breadboardStartColumn + 3}`];
|
locs = [c.breadboardConnections[0], c.breadboardConnections[2]]
|
||||||
locs.push(l2);
|
} else {
|
||||||
|
locs = [c.breadboardConnections[0]];
|
||||||
}
|
}
|
||||||
locs.forEach((l, i) => {
|
locs.forEach((l, i) => {
|
||||||
let [row, col] = l;
|
let topLbl: string;
|
||||||
|
if (l) {
|
||||||
|
let {row, col} = l;
|
||||||
|
topLbl = `(${row},${col})`;
|
||||||
|
} else {
|
||||||
|
topLbl = "";
|
||||||
|
}
|
||||||
let cmp = mkCmpDiv(c.visual, {
|
let cmp = mkCmpDiv(c.visual, {
|
||||||
top: `(${row},${col})`,
|
top: topLbl,
|
||||||
topSize: LOC_LBL_SIZE,
|
topSize: LOC_LBL_SIZE,
|
||||||
cmpHeight: REQ_CMP_HEIGHT,
|
cmpHeight: REQ_CMP_HEIGHT,
|
||||||
cmpScale: REQ_CMP_SCALE
|
cmpScale: REQ_CMP_SCALE
|
||||||
|
@ -656,8 +649,8 @@ ${tsPackage}
|
||||||
activeComponents.sort();
|
activeComponents.sort();
|
||||||
let props = mkBoardProps({
|
let props = mkBoardProps({
|
||||||
boardDef: boardDef,
|
boardDef: boardDef,
|
||||||
cmpDefs: cmpDefs,
|
partDefs: cmpDefs,
|
||||||
cmpList: activeComponents,
|
partsList: activeComponents,
|
||||||
fnArgs: fnArgs,
|
fnArgs: fnArgs,
|
||||||
getBBCoord: dummyBreadboard.getCoord.bind(dummyBreadboard)
|
getBBCoord: dummyBreadboard.getCoord.bind(dummyBreadboard)
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,10 +3,18 @@
|
||||||
/// <reference path="../libs/microbit/dal.d.ts"/>
|
/// <reference path="../libs/microbit/dal.d.ts"/>
|
||||||
|
|
||||||
namespace pxsim {
|
namespace pxsim {
|
||||||
export type BBRowCol = [/*row*/string, /*column*/string];
|
|
||||||
export type BoardPin = string;
|
export type BoardPin = string;
|
||||||
export interface BBLoc { type: "breadboard", rowCol: BBRowCol };
|
export interface BBLoc {
|
||||||
export interface BoardLoc { type: "dalboard", pin: BoardPin };
|
type: "breadboard",
|
||||||
|
row: string,
|
||||||
|
col: string
|
||||||
|
xOffset?: number,
|
||||||
|
yOffset?: number
|
||||||
|
};
|
||||||
|
export interface BoardLoc {
|
||||||
|
type: "dalboard",
|
||||||
|
pin: BoardPin
|
||||||
|
};
|
||||||
export type Loc = BBLoc | BoardLoc;
|
export type Loc = BBLoc | BoardLoc;
|
||||||
|
|
||||||
export function initRuntimeWithDalBoard() {
|
export function initRuntimeWithDalBoard() {
|
||||||
|
@ -197,11 +205,12 @@ namespace pxsim.visuals {
|
||||||
return minIdx;
|
return minIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBoardComponent<T> {
|
export interface IBoardPart<T> {
|
||||||
style: string,
|
style: string,
|
||||||
element: SVGElement,
|
element: SVGElement,
|
||||||
|
overElement?: SVGElement,
|
||||||
defs: SVGElement[],
|
defs: SVGElement[],
|
||||||
init(bus: EventBus, state: T, svgEl: SVGSVGElement, gpioPins: string[], otherArgs: string[]): void, //NOTE: constructors not supported in interfaces
|
init(bus: EventBus, state: T, svgEl: SVGSVGElement, otherParams: Map<string>): void, //NOTE: constructors not supported in interfaces
|
||||||
moveToCoord(xy: Coord): void,
|
moveToCoord(xy: Coord): void,
|
||||||
updateState(): void,
|
updateState(): void,
|
||||||
updateTheme(): void,
|
updateTheme(): void,
|
||||||
|
@ -224,7 +233,8 @@ namespace pxsim.visuals {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WireColor =
|
export type WireColor =
|
||||||
"black" | "white" | "gray" | "purple" | "blue" | "green" | "yellow" | "orange" | "red" | "brown";
|
"black" | "white" | "gray" | "purple" | "blue" | "green" | "yellow" | "orange" | "red" | "brown" | "pink";
|
||||||
|
export const GPIO_WIRE_COLORS = ["pink", "green", "purple", "orange", "yellow"];
|
||||||
export const WIRE_COLOR_MAP: Map<string> = {
|
export const WIRE_COLOR_MAP: Map<string> = {
|
||||||
black: "#514f4d",
|
black: "#514f4d",
|
||||||
white: "#fcfdfc",
|
white: "#fcfdfc",
|
||||||
|
@ -236,6 +246,7 @@ namespace pxsim.visuals {
|
||||||
orange: "#fdb262",
|
orange: "#fdb262",
|
||||||
red: "#f44f43",
|
red: "#f44f43",
|
||||||
brown: "#c89764",
|
brown: "#c89764",
|
||||||
|
pink: "#ff80fa"
|
||||||
}
|
}
|
||||||
export function mapWireColor(clr: WireColor | string): string {
|
export function mapWireColor(clr: WireColor | string): string {
|
||||||
return WIRE_COLOR_MAP[clr] || clr;
|
return WIRE_COLOR_MAP[clr] || clr;
|
||||||
|
|
|
@ -2,8 +2,8 @@ namespace pxsim.visuals {
|
||||||
export interface BoardHostOpts {
|
export interface BoardHostOpts {
|
||||||
state: DalBoard,
|
state: DalBoard,
|
||||||
boardDef: BoardDefinition,
|
boardDef: BoardDefinition,
|
||||||
cmpsList?: string[],
|
partsList?: string[],
|
||||||
cmpDefs: Map<PartDefinition>,
|
partDefs: Map<PartDefinition>,
|
||||||
fnArgs: any,
|
fnArgs: any,
|
||||||
forceBreadboard?: boolean,
|
forceBreadboard?: boolean,
|
||||||
maxWidth?: string,
|
maxWidth?: string,
|
||||||
|
@ -11,13 +11,15 @@ namespace pxsim.visuals {
|
||||||
wireframe?: boolean
|
wireframe?: boolean
|
||||||
}
|
}
|
||||||
export class BoardHost {
|
export class BoardHost {
|
||||||
private components: IBoardComponent<any>[] = [];
|
private parts: IBoardPart<any>[] = [];
|
||||||
private wireFactory: WireFactory;
|
private wireFactory: WireFactory;
|
||||||
private breadboard: Breadboard;
|
private breadboard: Breadboard;
|
||||||
private fromBBCoord: (xy: Coord) => Coord;
|
private fromBBCoord: (xy: Coord) => Coord;
|
||||||
private fromMBCoord: (xy: Coord) => Coord;
|
private fromMBCoord: (xy: Coord) => Coord;
|
||||||
private boardView: BoardView;
|
private boardView: BoardView;
|
||||||
private view: SVGSVGElement;
|
private view: SVGSVGElement;
|
||||||
|
private partGroup: SVGGElement;
|
||||||
|
private partOverGroup: SVGGElement;
|
||||||
private style: SVGStyleElement;
|
private style: SVGStyleElement;
|
||||||
private defs: SVGDefsElement;
|
private defs: SVGDefsElement;
|
||||||
private state: DalBoard;
|
private state: DalBoard;
|
||||||
|
@ -26,7 +28,7 @@ namespace pxsim.visuals {
|
||||||
constructor(opts: BoardHostOpts) {
|
constructor(opts: BoardHostOpts) {
|
||||||
this.state = opts.state;
|
this.state = opts.state;
|
||||||
let onboardCmps = opts.boardDef.onboardComponents || [];
|
let onboardCmps = opts.boardDef.onboardComponents || [];
|
||||||
let activeComponents = (opts.cmpsList || []).filter(c => onboardCmps.indexOf(c) < 0);
|
let activeComponents = (opts.partsList || []).filter(c => onboardCmps.indexOf(c) < 0);
|
||||||
activeComponents.sort();
|
activeComponents.sort();
|
||||||
this.useCrocClips = opts.boardDef.useCrocClips;
|
this.useCrocClips = opts.boardDef.useCrocClips;
|
||||||
|
|
||||||
|
@ -68,6 +70,8 @@ namespace pxsim.visuals {
|
||||||
this.fromMBCoord = composition.toHostCoord1;
|
this.fromMBCoord = composition.toHostCoord1;
|
||||||
this.fromBBCoord = composition.toHostCoord2;
|
this.fromBBCoord = composition.toHostCoord2;
|
||||||
let pinDist = composition.scaleUnit;
|
let pinDist = composition.scaleUnit;
|
||||||
|
this.partGroup = over;
|
||||||
|
this.partOverGroup = <SVGGElement>svg.child(this.view, "g");
|
||||||
|
|
||||||
this.style = <SVGStyleElement>svg.child(this.view, "style", {});
|
this.style = <SVGStyleElement>svg.child(this.view, "style", {});
|
||||||
this.defs = <SVGDefsElement>svg.child(this.view, "defs", {});
|
this.defs = <SVGDefsElement>svg.child(this.view, "defs", {});
|
||||||
|
@ -76,16 +80,18 @@ namespace pxsim.visuals {
|
||||||
|
|
||||||
let allocRes = allocateDefinitions({
|
let allocRes = allocateDefinitions({
|
||||||
boardDef: opts.boardDef,
|
boardDef: opts.boardDef,
|
||||||
cmpDefs: opts.cmpDefs,
|
partDefs: opts.partDefs,
|
||||||
fnArgs: opts.fnArgs,
|
fnArgs: opts.fnArgs,
|
||||||
getBBCoord: this.breadboard.getCoord.bind(this.breadboard),
|
getBBCoord: this.breadboard.getCoord.bind(this.breadboard),
|
||||||
cmpList: activeComponents,
|
partsList: activeComponents,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addAll(allocRes);
|
this.addAll(allocRes);
|
||||||
} else {
|
} else {
|
||||||
let el = this.boardView.getView().el;
|
let el = this.boardView.getView().el;
|
||||||
this.view = el;
|
this.view = el;
|
||||||
|
this.partGroup = <SVGGElement>svg.child(this.view, "g");
|
||||||
|
this.partOverGroup = <SVGGElement>svg.child(this.view, "g");
|
||||||
if (opts.maxWidth)
|
if (opts.maxWidth)
|
||||||
svg.hydrate(this.view, { width: opts.maxWidth });
|
svg.hydrate(this.view, { width: opts.maxWidth });
|
||||||
if (opts.maxHeight)
|
if (opts.maxHeight)
|
||||||
|
@ -99,7 +105,7 @@ namespace pxsim.visuals {
|
||||||
this.boardView.highlightPin(pinNm);
|
this.boardView.highlightPin(pinNm);
|
||||||
}
|
}
|
||||||
|
|
||||||
public highlightBreadboardPin(rowCol: BBRowCol) {
|
public highlightBreadboardPin(rowCol: BBLoc) {
|
||||||
this.breadboard.highlightLoc(rowCol);
|
this.breadboard.highlightLoc(rowCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,10 +126,10 @@ namespace pxsim.visuals {
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateState() {
|
private updateState() {
|
||||||
this.components.forEach(c => c.updateState());
|
this.parts.forEach(c => c.updateState());
|
||||||
}
|
}
|
||||||
|
|
||||||
private getBBCoord(rowCol: BBRowCol) {
|
private getBBCoord(rowCol: BBLoc) {
|
||||||
let bbCoord = this.breadboard.getCoord(rowCol);
|
let bbCoord = this.breadboard.getCoord(rowCol);
|
||||||
return this.fromBBCoord(bbCoord);
|
return this.fromBBCoord(bbCoord);
|
||||||
}
|
}
|
||||||
|
@ -134,7 +140,7 @@ namespace pxsim.visuals {
|
||||||
public getLocCoord(loc: Loc): Coord {
|
public getLocCoord(loc: Loc): Coord {
|
||||||
let coord: Coord;
|
let coord: Coord;
|
||||||
if (loc.type === "breadboard") {
|
if (loc.type === "breadboard") {
|
||||||
let rowCol = (<BBLoc>loc).rowCol;
|
let rowCol = (<BBLoc>loc);
|
||||||
coord = this.getBBCoord(rowCol);
|
coord = this.getBBCoord(rowCol);
|
||||||
} else {
|
} else {
|
||||||
let pinNm = (<BoardLoc>loc).pin;
|
let pinNm = (<BoardLoc>loc).pin;
|
||||||
|
@ -147,47 +153,62 @@ namespace pxsim.visuals {
|
||||||
return coord;
|
return coord;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addComponent(cmpDesc: CmpInst): IBoardComponent<any> {
|
public addPart(partInst: PartInst): IBoardPart<any> {
|
||||||
let cmp: IBoardComponent<any> = null;
|
let part: IBoardPart<any> = null;
|
||||||
let colOffset = 0;
|
let colOffset = 0;
|
||||||
if (typeof cmpDesc.visual === "string") {
|
if (partInst.simulationBehavior) {
|
||||||
let builtinVisual = cmpDesc.visual as string;
|
//TODO: seperate simulation behavior from builtin visual
|
||||||
let cnstr = builtinComponentSimVisual[builtinVisual];
|
let builtinBehavior = partInst.simulationBehavior;
|
||||||
let stateFn = builtinComponentSimState[builtinVisual];
|
let cnstr = builtinComponentSimVisual[builtinBehavior];
|
||||||
cmp = cnstr();
|
let stateFn = builtinComponentSimState[builtinBehavior];
|
||||||
cmp.init(this.state.bus, stateFn(this.state), this.view, cmpDesc.microbitPins, cmpDesc.otherArgs);
|
part = cnstr();
|
||||||
|
part.init(this.state.bus, stateFn(this.state), this.view, partInst.params);
|
||||||
} else {
|
} else {
|
||||||
let vis = cmpDesc.visual as PartVisualDefinition;
|
let vis = partInst.visual as PartVisualDefinition;
|
||||||
cmp = new GenericPart(vis);
|
part = new GenericPart(vis);
|
||||||
colOffset = vis.extraColumnOffset || 0;
|
|
||||||
}
|
}
|
||||||
this.components.push(cmp);
|
this.parts.push(part);
|
||||||
this.view.appendChild(cmp.element);
|
this.partGroup.appendChild(part.element);
|
||||||
if (cmp.defs)
|
if (part.overElement)
|
||||||
cmp.defs.forEach(d => this.defs.appendChild(d));
|
this.partOverGroup.appendChild(part.overElement);
|
||||||
this.style.textContent += cmp.style || "";
|
if (part.defs)
|
||||||
let rowCol = <BBRowCol>[`${cmpDesc.breadboardStartRow}`, `${colOffset + cmpDesc.breadboardStartColumn}`];
|
part.defs.forEach(d => this.defs.appendChild(d));
|
||||||
|
this.style.textContent += part.style || "";
|
||||||
|
let colIdx = partInst.startColumnIdx;
|
||||||
|
let rowIdx = partInst.startRowIdx;
|
||||||
|
let row = getRowName(rowIdx);
|
||||||
|
let col = getColumnName(colIdx);
|
||||||
|
let xOffset = partInst.bbFit.xOffset / partInst.visual.pinDistance;
|
||||||
|
let yOffset = partInst.bbFit.yOffset / partInst.visual.pinDistance;
|
||||||
|
let rowCol = <BBLoc>{
|
||||||
|
type: "breadboard",
|
||||||
|
row: row,
|
||||||
|
col: col,
|
||||||
|
xOffset: xOffset,
|
||||||
|
yOffset: yOffset
|
||||||
|
};
|
||||||
let coord = this.getBBCoord(rowCol);
|
let coord = this.getBBCoord(rowCol);
|
||||||
cmp.moveToCoord(coord);
|
part.moveToCoord(coord);
|
||||||
let getCmpClass = (type: string) => `sim-${type}-cmp`;
|
let getCmpClass = (type: string) => `sim-${type}-cmp`;
|
||||||
let cls = getCmpClass(name);
|
let cls = getCmpClass(partInst.name);
|
||||||
svg.addClass(cmp.element, cls);
|
svg.addClass(part.element, cls);
|
||||||
svg.addClass(cmp.element, "sim-cmp");
|
svg.addClass(part.element, "sim-cmp");
|
||||||
cmp.updateTheme();
|
part.updateTheme();
|
||||||
cmp.updateState();
|
part.updateState();
|
||||||
return cmp;
|
return part;
|
||||||
}
|
}
|
||||||
public addWire(inst: WireInst): Wire {
|
public addWire(inst: WireInst): Wire {
|
||||||
return this.wireFactory.addWire(inst.start, inst.end, inst.color, this.useCrocClips);
|
return this.wireFactory.addWire(inst.start, inst.end, inst.color, this.useCrocClips);
|
||||||
}
|
}
|
||||||
public addAll(basicWiresAndCmpsAndWires: AllocatorResult) {
|
public addAll(allocRes: AllocatorResult) {
|
||||||
let {powerWires, components} = basicWiresAndCmpsAndWires;
|
allocRes.partsAndWires.forEach(pAndWs => {
|
||||||
powerWires.forEach(w => this.addWire(w));
|
let part = pAndWs.part;
|
||||||
components.forEach((cAndWs, idx) => {
|
if (part)
|
||||||
let {component, wires} = cAndWs;
|
this.addPart(part)
|
||||||
|
let wires = pAndWs.wires;
|
||||||
|
if (wires)
|
||||||
wires.forEach(w => this.addWire(w));
|
wires.forEach(w => this.addWire(w));
|
||||||
this.addComponent(component);
|
})
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -80,7 +80,8 @@ namespace pxsim.visuals {
|
||||||
fill: #BBB;
|
fill: #BBB;
|
||||||
}
|
}
|
||||||
.grayed .sim-bb-pin {
|
.grayed .sim-bb-pin {
|
||||||
fill: #BBB;
|
fill:none;
|
||||||
|
stroke: #BBB;
|
||||||
}
|
}
|
||||||
.grayed .sim-bb-label {
|
.grayed .sim-bb-label {
|
||||||
fill: #BBB;
|
fill: #BBB;
|
||||||
|
@ -106,10 +107,10 @@ namespace pxsim.visuals {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
// Pin rows and coluns
|
// Pin rows and coluns
|
||||||
const MID_ROWS = 10;
|
export const BREADBOARD_MID_ROWS = 10;
|
||||||
|
export const BREADBOARD_MID_COLS = 30;
|
||||||
const MID_ROW_GAPS = [4, 4];
|
const MID_ROW_GAPS = [4, 4];
|
||||||
const MID_ROW_AND_GAPS = MID_ROWS + MID_ROW_GAPS.length;
|
const MID_ROW_AND_GAPS = BREADBOARD_MID_ROWS + MID_ROW_GAPS.length;
|
||||||
const MID_COLS = 30;
|
|
||||||
const BAR_ROWS = 2;
|
const BAR_ROWS = 2;
|
||||||
const BAR_COLS = 25;
|
const BAR_COLS = 25;
|
||||||
const POWER_ROWS = BAR_ROWS * 2;
|
const POWER_ROWS = BAR_ROWS * 2;
|
||||||
|
@ -117,14 +118,14 @@ namespace pxsim.visuals {
|
||||||
const BAR_COL_GAPS = [4, 9, 14, 19];
|
const BAR_COL_GAPS = [4, 9, 14, 19];
|
||||||
const BAR_COL_AND_GAPS = BAR_COLS + BAR_COL_GAPS.length;
|
const BAR_COL_AND_GAPS = BAR_COLS + BAR_COL_GAPS.length;
|
||||||
// Essential dimensions
|
// Essential dimensions
|
||||||
const WIDTH = PIN_DIST * (MID_COLS + 3);
|
const WIDTH = PIN_DIST * (BREADBOARD_MID_COLS + 3);
|
||||||
const HEIGHT = PIN_DIST * (MID_ROW_AND_GAPS + POWER_ROWS + 5.5);
|
const HEIGHT = PIN_DIST * (MID_ROW_AND_GAPS + POWER_ROWS + 5.5);
|
||||||
const MID_RATIO = 2.0 / 3.0;
|
const MID_RATIO = 2.0 / 3.0;
|
||||||
const BAR_RATIO = (1.0 - MID_RATIO) * 0.5;
|
const BAR_RATIO = (1.0 - MID_RATIO) * 0.5;
|
||||||
const MID_HEIGHT = HEIGHT * MID_RATIO;
|
const MID_HEIGHT = HEIGHT * MID_RATIO;
|
||||||
const BAR_HEIGHT = HEIGHT * BAR_RATIO;
|
const BAR_HEIGHT = HEIGHT * BAR_RATIO;
|
||||||
// Pin grids
|
// Pin grids
|
||||||
const MID_GRID_WIDTH = (MID_COLS - 1) * PIN_DIST;
|
const MID_GRID_WIDTH = (BREADBOARD_MID_COLS - 1) * PIN_DIST;
|
||||||
const MID_GRID_HEIGHT = (MID_ROW_AND_GAPS - 1) * PIN_DIST;
|
const MID_GRID_HEIGHT = (MID_ROW_AND_GAPS - 1) * PIN_DIST;
|
||||||
const MID_GRID_X = (WIDTH - MID_GRID_WIDTH) / 2.0;
|
const MID_GRID_X = (WIDTH - MID_GRID_WIDTH) / 2.0;
|
||||||
const MID_GRID_Y = BAR_HEIGHT + (MID_HEIGHT - MID_GRID_HEIGHT) / 2.0;
|
const MID_GRID_Y = BAR_HEIGHT + (MID_HEIGHT - MID_GRID_HEIGHT) / 2.0;
|
||||||
|
@ -151,6 +152,10 @@ namespace pxsim.visuals {
|
||||||
const SMALL_CHANNEL_HEIGHT = PIN_DIST * 0.05;
|
const SMALL_CHANNEL_HEIGHT = PIN_DIST * 0.05;
|
||||||
// Background
|
// Background
|
||||||
const BACKGROUND_ROUNDING = PIN_DIST * 0.3;
|
const BACKGROUND_ROUNDING = PIN_DIST * 0.3;
|
||||||
|
// Row and column helpers
|
||||||
|
const alphabet = "abcdefghij".split("").reverse();
|
||||||
|
export function getColumnName(colIdx: number): string { return `${colIdx + 1}` };
|
||||||
|
export function getRowName(rowIdx: number): string { return alphabet[rowIdx] };
|
||||||
|
|
||||||
export interface GridPin {
|
export interface GridPin {
|
||||||
el: SVGElement,
|
el: SVGElement,
|
||||||
|
@ -320,12 +325,14 @@ namespace pxsim.visuals {
|
||||||
return null;
|
return null;
|
||||||
return pin;
|
return pin;
|
||||||
}
|
}
|
||||||
public getCoord(rowCol: BBRowCol): Coord {
|
public getCoord(rowCol: BBLoc): Coord {
|
||||||
let [row, col] = rowCol;
|
let {row, col, xOffset, yOffset} = rowCol;
|
||||||
let pin = this.getPin(row, col);
|
let pin = this.getPin(row, col);
|
||||||
if (!pin)
|
if (!pin)
|
||||||
return null;
|
return null;
|
||||||
return [pin.cx, pin.cy];
|
let xOff = (xOffset || 0) * PIN_DIST;
|
||||||
|
let yOff = (yOffset || 0) * PIN_DIST;
|
||||||
|
return [pin.cx + xOff, pin.cy + yOff];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPinDist() {
|
public getPinDist() {
|
||||||
|
@ -370,14 +377,11 @@ namespace pxsim.visuals {
|
||||||
mkChannel(BAR_HEIGHT + MID_HEIGHT, SMALL_CHANNEL_HEIGHT);
|
mkChannel(BAR_HEIGHT + MID_HEIGHT, SMALL_CHANNEL_HEIGHT);
|
||||||
|
|
||||||
//-----pins
|
//-----pins
|
||||||
const getMidTopOrBot = (rowIdx: number) => rowIdx < MID_ROWS / 2.0 ? "b" : "t";
|
const getMidTopOrBot = (rowIdx: number) => rowIdx < BREADBOARD_MID_ROWS / 2.0 ? "b" : "t";
|
||||||
const getBarTopOrBot = (colIdx: number) => colIdx < POWER_COLS / 2.0 ? "b" : "t";
|
const getBarTopOrBot = (colIdx: number) => colIdx < POWER_COLS / 2.0 ? "b" : "t";
|
||||||
const alphabet = "abcdefghij".split("").reverse();
|
|
||||||
const getColName = (colIdx: number) => `${colIdx + 1}`;
|
|
||||||
const getMidRowName = (rowIdx: number) => alphabet[rowIdx];
|
|
||||||
const getMidGroupName = (rowIdx: number, colIdx: number) => {
|
const getMidGroupName = (rowIdx: number, colIdx: number) => {
|
||||||
let botOrTop = getMidTopOrBot(rowIdx);
|
let botOrTop = getMidTopOrBot(rowIdx);
|
||||||
let colNm = getColName(colIdx);
|
let colNm = getColumnName(colIdx);
|
||||||
return `${botOrTop}${colNm}`;
|
return `${botOrTop}${colNm}`;
|
||||||
};
|
};
|
||||||
const getBarRowName = (rowIdx: number) => rowIdx === 0 ? "-" : "+";
|
const getBarRowName = (rowIdx: number) => rowIdx === 0 ? "-" : "+";
|
||||||
|
@ -391,13 +395,13 @@ namespace pxsim.visuals {
|
||||||
let midGridRes = mkGrid({
|
let midGridRes = mkGrid({
|
||||||
xOffset: MID_GRID_X,
|
xOffset: MID_GRID_X,
|
||||||
yOffset: MID_GRID_Y,
|
yOffset: MID_GRID_Y,
|
||||||
rowCount: MID_ROWS,
|
rowCount: BREADBOARD_MID_ROWS,
|
||||||
colCount: MID_COLS,
|
colCount: BREADBOARD_MID_COLS,
|
||||||
pinDist: PIN_DIST,
|
pinDist: PIN_DIST,
|
||||||
mkPin: mkBBPin,
|
mkPin: mkBBPin,
|
||||||
mkHoverPin: mkBBHoverPin,
|
mkHoverPin: mkBBHoverPin,
|
||||||
getRowName: getMidRowName,
|
getRowName: getRowName,
|
||||||
getColName: getColName,
|
getColName: getColumnName,
|
||||||
getGroupName: getMidGroupName,
|
getGroupName: getMidGroupName,
|
||||||
rowIdxsWithGap: MID_ROW_GAPS,
|
rowIdxsWithGap: MID_ROW_GAPS,
|
||||||
});
|
});
|
||||||
|
@ -414,7 +418,7 @@ namespace pxsim.visuals {
|
||||||
mkPin: mkBBPin,
|
mkPin: mkBBPin,
|
||||||
mkHoverPin: mkBBHoverPin,
|
mkHoverPin: mkBBHoverPin,
|
||||||
getRowName: getBarRowName,
|
getRowName: getBarRowName,
|
||||||
getColName: getColName,
|
getColName: getColumnName,
|
||||||
getGroupName: getBarGroupName,
|
getGroupName: getBarGroupName,
|
||||||
colIdxsWithGap: BAR_COL_GAPS,
|
colIdxsWithGap: BAR_COL_GAPS,
|
||||||
});
|
});
|
||||||
|
@ -432,7 +436,7 @@ namespace pxsim.visuals {
|
||||||
mkPin: mkBBPin,
|
mkPin: mkBBPin,
|
||||||
mkHoverPin: mkBBHoverPin,
|
mkHoverPin: mkBBHoverPin,
|
||||||
getRowName: getBarRowName,
|
getRowName: getBarRowName,
|
||||||
getColName: getColName,
|
getColName: getColumnName,
|
||||||
getGroupName: getBarGroupName,
|
getGroupName: getBarGroupName,
|
||||||
colIdxsWithGap: BAR_COL_GAPS.map(g => g + BAR_COLS),
|
colIdxsWithGap: BAR_COL_GAPS.map(g => g + BAR_COLS),
|
||||||
});
|
});
|
||||||
|
@ -459,39 +463,39 @@ namespace pxsim.visuals {
|
||||||
const mkBBLabelAtPin = (row: string, col: string, xOffset: number, yOffset: number, txt: string, group?: string): GridLabel => {
|
const mkBBLabelAtPin = (row: string, col: string, xOffset: number, yOffset: number, txt: string, group?: string): GridLabel => {
|
||||||
let size = PIN_LBL_SIZE;
|
let size = PIN_LBL_SIZE;
|
||||||
let rotation = LBL_ROTATION;
|
let rotation = LBL_ROTATION;
|
||||||
let loc = this.getCoord([row, col]);
|
let loc = this.getCoord({type: "breadboard", row: row, col: col});
|
||||||
let [cx, cy] = loc;
|
let [cx, cy] = loc;
|
||||||
let t = mkBBLabel(cx + xOffset, cy + yOffset, size, rotation, txt, group);
|
let t = mkBBLabel(cx + xOffset, cy + yOffset, size, rotation, txt, group);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
//columns
|
//columns
|
||||||
for (let colIdx = 0; colIdx < MID_COLS; colIdx++) {
|
for (let colIdx = 0; colIdx < BREADBOARD_MID_COLS; colIdx++) {
|
||||||
let colNm = getColName(colIdx);
|
let colNm = getColumnName(colIdx);
|
||||||
//top
|
//top
|
||||||
let rowTIdx = 0;
|
let rowTIdx = 0;
|
||||||
let rowTNm = getMidRowName(rowTIdx);
|
let rowTNm = getRowName(rowTIdx);
|
||||||
let groupT = getMidGroupName(rowTIdx, colIdx);
|
let groupT = getMidGroupName(rowTIdx, colIdx);
|
||||||
let lblT = mkBBLabelAtPin(rowTNm, colNm, 0, -PIN_DIST, colNm, groupT);
|
let lblT = mkBBLabelAtPin(rowTNm, colNm, 0, -PIN_DIST, colNm, groupT);
|
||||||
this.allLabels.push(lblT);
|
this.allLabels.push(lblT);
|
||||||
//bottom
|
//bottom
|
||||||
let rowBIdx = MID_ROWS - 1;
|
let rowBIdx = BREADBOARD_MID_ROWS - 1;
|
||||||
let rowBNm = getMidRowName(rowBIdx);
|
let rowBNm = getRowName(rowBIdx);
|
||||||
let groupB = getMidGroupName(rowBIdx, colIdx);
|
let groupB = getMidGroupName(rowBIdx, colIdx);
|
||||||
let lblB = mkBBLabelAtPin(rowBNm, colNm, 0, +PIN_DIST, colNm, groupB);
|
let lblB = mkBBLabelAtPin(rowBNm, colNm, 0, +PIN_DIST, colNm, groupB);
|
||||||
this.allLabels.push(lblB);
|
this.allLabels.push(lblB);
|
||||||
}
|
}
|
||||||
//rows
|
//rows
|
||||||
for (let rowIdx = 0; rowIdx < MID_ROWS; rowIdx++) {
|
for (let rowIdx = 0; rowIdx < BREADBOARD_MID_ROWS; rowIdx++) {
|
||||||
let rowNm = getMidRowName(rowIdx);
|
let rowNm = getRowName(rowIdx);
|
||||||
//top
|
//top
|
||||||
let colTIdx = 0;
|
let colTIdx = 0;
|
||||||
let colTNm = getColName(colTIdx);
|
let colTNm = getColumnName(colTIdx);
|
||||||
let lblT = mkBBLabelAtPin(rowNm, colTNm, -PIN_DIST, 0, rowNm);
|
let lblT = mkBBLabelAtPin(rowNm, colTNm, -PIN_DIST, 0, rowNm);
|
||||||
this.allLabels.push(lblT);
|
this.allLabels.push(lblT);
|
||||||
//top
|
//top
|
||||||
let colBIdx = MID_COLS - 1;
|
let colBIdx = BREADBOARD_MID_COLS - 1;
|
||||||
let colBNm = getColName(colBIdx);
|
let colBNm = getColumnName(colBIdx);
|
||||||
let lblB = mkBBLabelAtPin(rowNm, colBNm, +PIN_DIST, 0, rowNm);
|
let lblB = mkBBLabelAtPin(rowNm, colBNm, +PIN_DIST, 0, rowNm);
|
||||||
this.allLabels.push(lblB);
|
this.allLabels.push(lblB);
|
||||||
}
|
}
|
||||||
|
@ -634,8 +638,8 @@ namespace pxsim.visuals {
|
||||||
return {el: this.bb, y: 0, x: 0, w: WIDTH, h: HEIGHT};
|
return {el: this.bb, y: 0, x: 0, w: WIDTH, h: HEIGHT};
|
||||||
}
|
}
|
||||||
|
|
||||||
public highlightLoc(rowCol: BBRowCol) {
|
public highlightLoc(rowCol: BBLoc) {
|
||||||
let [row, col] = rowCol;
|
let {row, col} = rowCol;
|
||||||
let pin = this.rowColToPin[row][col];
|
let pin = this.rowColToPin[row][col];
|
||||||
let {cx, cy} = pin;
|
let {cx, cy} = pin;
|
||||||
let lbls = this.rowColToLbls[row][col];
|
let lbls = this.rowColToLbls[row][col];
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace pxsim.visuals {
|
||||||
pointer-events:none;
|
pointer-events:none;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export class ButtonPairView implements IBoardComponent<ButtonPairState> {
|
export class ButtonPairView implements IBoardPart<ButtonPairState> {
|
||||||
public element: SVGElement;
|
public element: SVGElement;
|
||||||
public defs: SVGElement[];
|
public defs: SVGElement[];
|
||||||
public style = BUTTON_PAIR_STYLE;
|
public style = BUTTON_PAIR_STYLE;
|
||||||
|
|
|
@ -5,13 +5,13 @@ namespace pxsim.visuals {
|
||||||
image: partVisual.image,
|
image: partVisual.image,
|
||||||
width: partVisual.width,
|
width: partVisual.width,
|
||||||
height: partVisual.height,
|
height: partVisual.height,
|
||||||
imageUnitDist: partVisual.pinDist,
|
imageUnitDist: partVisual.pinDistance,
|
||||||
targetUnitDist: PIN_DIST
|
targetUnitDist: PIN_DIST
|
||||||
});
|
});
|
||||||
return imgAndSize;
|
return imgAndSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GenericPart implements IBoardComponent<any> {
|
export class GenericPart implements IBoardPart<any> {
|
||||||
public style: string = "";
|
public style: string = "";
|
||||||
public element: SVGElement;
|
public element: SVGElement;
|
||||||
defs: SVGElement[] = [];
|
defs: SVGElement[] = [];
|
||||||
|
@ -19,11 +19,6 @@ namespace pxsim.visuals {
|
||||||
constructor(partVisual: PartVisualDefinition) {
|
constructor(partVisual: PartVisualDefinition) {
|
||||||
let imgAndSize = mkGenericPartSVG(partVisual);
|
let imgAndSize = mkGenericPartSVG(partVisual);
|
||||||
let img = imgAndSize.el;
|
let img = imgAndSize.el;
|
||||||
let scaleFn = mkScaleFn(partVisual.pinDist, PIN_DIST);
|
|
||||||
let [pinX, pinY] = partVisual.firstPin;
|
|
||||||
let left = -scaleFn(pinX);
|
|
||||||
let top = -scaleFn(pinY);
|
|
||||||
translateEl(img, [left, top]); // So that 0,0 is on the first pin
|
|
||||||
this.element = svg.elt("g");
|
this.element = svg.elt("g");
|
||||||
this.element.appendChild(img);
|
this.element.appendChild(img);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +28,7 @@ namespace pxsim.visuals {
|
||||||
}
|
}
|
||||||
|
|
||||||
//unused
|
//unused
|
||||||
init(bus: EventBus, state: any, svgEl: SVGSVGElement, gpioPins: string[], otherArgs: string[]): void { }
|
init(bus: EventBus, state: any, svgEl: SVGSVGElement): void { }
|
||||||
updateState(): void { }
|
updateState(): void { }
|
||||||
updateTheme(): void { }
|
updateTheme(): void { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ namespace pxsim.visuals {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export class LedMatrixView implements IBoardComponent<LedMatrixState> {
|
export class LedMatrixView implements IBoardPart<LedMatrixState> {
|
||||||
private background: SVGElement;
|
private background: SVGElement;
|
||||||
private ledsOuter: SVGElement[];
|
private ledsOuter: SVGElement[];
|
||||||
private leds: SVGElement[];
|
private leds: SVGElement[];
|
||||||
|
|
|
@ -107,13 +107,12 @@ namespace pxsim.visuals {
|
||||||
public cy: number;
|
public cy: number;
|
||||||
|
|
||||||
constructor(xy: Coord = [0, 0]) {
|
constructor(xy: Coord = [0, 0]) {
|
||||||
let circle = <SVGElement>svg.elt("rect");
|
let el = <SVGElement>svg.elt("rect");
|
||||||
let r = PIXEL_RADIUS;
|
let r = PIXEL_RADIUS;
|
||||||
let [cx, cy] = xy;
|
let [cx, cy] = xy;
|
||||||
let y = cy - r;
|
let y = cy - r;
|
||||||
let x = 0;
|
svg.hydrate(el, { x: "-50%", y: y, width: "100%", height: r * 2, class: "sim-neopixel" });
|
||||||
svg.hydrate(circle, { x: "-50%", y: y, width: "100%", height: r*2, class: "sim-neopixel" });
|
this.el = el;
|
||||||
this.el = circle;
|
|
||||||
this.cy = cy;
|
this.cy = cy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,9 +191,15 @@ namespace pxsim.visuals {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function gpioPinToPinNumber(gpioPin: string): number {
|
function digitalPinToPinNumber(gpioPin: string): number {
|
||||||
let pinNumStr = gpioPin.split("P")[1];
|
const MICROBIT_ID_IO_P0 = 7; //TODO: don't hardcode this, import enums.d.ts
|
||||||
let pinNum = Number(pinNumStr) + 7 /*MICROBIT_ID_IO_P0; TODO: don't hardcode this, import enums.d.ts*/;
|
if (gpioPin == "*") {
|
||||||
|
return MICROBIT_ID_IO_P0;
|
||||||
|
}
|
||||||
|
let pinSplit = gpioPin.split("DigitalPin.P");
|
||||||
|
U.assert(pinSplit.length === 2, "Unknown format for pin (for NeoPixel): " + gpioPin);
|
||||||
|
let pinNumStr = pinSplit[1];
|
||||||
|
let pinNum = Number(pinNumStr) + MICROBIT_ID_IO_P0;
|
||||||
return pinNum
|
return pinNum
|
||||||
}
|
}
|
||||||
function parseNeoPixelMode(modeStr: string): NeoPixelMode {
|
function parseNeoPixelMode(modeStr: string): NeoPixelMode {
|
||||||
|
@ -214,7 +219,7 @@ namespace pxsim.visuals {
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NeoPixelView implements IBoardComponent<NeoPixelState> {
|
export class NeoPixelView implements IBoardPart<NeoPixelState> {
|
||||||
public style: string = `
|
public style: string = `
|
||||||
.sim-neopixel-canvas {
|
.sim-neopixel-canvas {
|
||||||
}
|
}
|
||||||
|
@ -232,6 +237,7 @@ namespace pxsim.visuals {
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
public element: SVGElement;
|
public element: SVGElement;
|
||||||
|
public overElement: SVGElement;
|
||||||
public defs: SVGElement[];
|
public defs: SVGElement[];
|
||||||
private state: NeoPixelState;
|
private state: NeoPixelState;
|
||||||
private canvas: NeoPixelCanvas;
|
private canvas: NeoPixelCanvas;
|
||||||
|
@ -241,22 +247,24 @@ namespace pxsim.visuals {
|
||||||
private pin: number;
|
private pin: number;
|
||||||
private mode: NeoPixelMode;
|
private mode: NeoPixelMode;
|
||||||
|
|
||||||
public init(bus: EventBus, state: NeoPixelState, svgEl: SVGSVGElement, gpioPins: string[], otherArgs: string[]): void {
|
public init(bus: EventBus, state: NeoPixelState, svgEl: SVGSVGElement, otherParams: Map<string>): void {
|
||||||
U.assert(otherArgs.length === 1, "NeoPixels assumes a RGB vs RGBW mode is passed to it");
|
U.assert(!!otherParams["mode"], "NeoPixels assumes a RGB vs RGBW mode is passed to it");
|
||||||
let modeStr = otherArgs[0];
|
U.assert(!!otherParams["pin"], "NeoPixels assumes a pin is passed to it");
|
||||||
|
let modeStr = otherParams["mode"];
|
||||||
this.mode = parseNeoPixelMode(modeStr);
|
this.mode = parseNeoPixelMode(modeStr);
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.stripGroup = <SVGGElement>svg.elt("g");
|
this.stripGroup = <SVGGElement>svg.elt("g");
|
||||||
this.element = this.stripGroup;
|
this.element = this.stripGroup;
|
||||||
let pinStr = gpioPins[0];
|
let pinStr = otherParams["pin"];
|
||||||
this.pin = gpioPinToPinNumber(pinStr);
|
this.pin = digitalPinToPinNumber(pinStr);
|
||||||
this.lastLocation = [0, 0];
|
this.lastLocation = [0, 0];
|
||||||
let part = mkNeoPixelPart();
|
let part = mkNeoPixelPart();
|
||||||
this.part = part;
|
this.part = part;
|
||||||
this.stripGroup.appendChild(part.el);
|
this.stripGroup.appendChild(part.el);
|
||||||
let canvas = new NeoPixelCanvas(this.pin);
|
let canvas = new NeoPixelCanvas(this.pin);
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
let canvasG = svg.child(this.stripGroup, "g", { class: "sim-neopixel-canvas-parent" });
|
let canvasG = svg.elt("g", { class: "sim-neopixel-canvas-parent" });
|
||||||
|
this.overElement = canvasG;
|
||||||
canvasG.appendChild(canvas.canvas);
|
canvasG.appendChild(canvas.canvas);
|
||||||
this.updateStripLoc();
|
this.updateStripLoc();
|
||||||
}
|
}
|
||||||
|
@ -268,6 +276,7 @@ namespace pxsim.visuals {
|
||||||
}
|
}
|
||||||
private updateStripLoc() {
|
private updateStripLoc() {
|
||||||
let [x, y] = this.lastLocation;
|
let [x, y] = this.lastLocation;
|
||||||
|
U.assert(typeof x === "number" && typeof y === "number", "invalid x,y for NeoPixel strip");
|
||||||
this.canvas.setLoc([x + CANVAS_LEFT, y + CANVAS_TOP]);
|
this.canvas.setLoc([x + CANVAS_LEFT, y + CANVAS_TOP]);
|
||||||
svg.hydrate(this.part.el, { transform: `translate(${x} ${y})` }); //TODO: update part's l,h, etc.
|
svg.hydrate(this.part.el, { transform: `translate(${x} ${y})` }); //TODO: update part's l,h, etc.
|
||||||
}
|
}
|
||||||
|
|
|
@ -455,7 +455,7 @@ namespace pxsim.visuals {
|
||||||
let wireEls: Wire;
|
let wireEls: Wire;
|
||||||
if (withCrocs && end.type == "dalboard") {
|
if (withCrocs && end.type == "dalboard") {
|
||||||
let boardPin = (<BoardLoc>end).pin;
|
let boardPin = (<BoardLoc>end).pin;
|
||||||
if (boardPin == "P0" || boardPin == "P1" || boardPin == "P0" || boardPin == "GND" || boardPin == "+3v3" ) {
|
if (boardPin == "P0" || boardPin == "P1" || boardPin == "P2" || boardPin == "GND" || boardPin == "+3v3" ) {
|
||||||
//HACK
|
//HACK
|
||||||
wireEls = this.drawWireWithCrocs(startLoc, endLoc, color);
|
wireEls = this.drawWireWithCrocs(startLoc, endLoc, color);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user