Compare commits

...

40 Commits

Author SHA1 Message Date
236e7337e2 0.3.87 2016-09-09 20:49:21 +01:00
4cf223271f Bump pxt-core to 0.3.100 2016-09-09 20:49:21 +01:00
10180f4729 Merge branch 'master' of github.com:Microsoft/pxt-microbit 2016-09-09 20:49:19 +01:00
618dd33221 No longer used 2016-09-09 20:33:11 +01:00
7356e5e52e New index.html expansion 2016-09-09 20:26:18 +01:00
a59d148eb5 0.3.86 2016-09-09 12:23:25 -07:00
34ce687bbd Bump pxt-core to 0.3.98 2016-09-09 12:23:24 -07:00
5e241ed566 Use new doc templates 2016-09-09 20:19:52 +01:00
dcb2b21e66 removing dal dependency 2016-09-09 11:36:36 -07:00
95669e174a Merge branch 'master' of https://github.com/Microsoft/pxt-microbit 2016-09-09 09:59:21 -07:00
aca7d6113d enable streams in pxtarget 2016-09-09 09:59:07 -07:00
ef4b06a087 Merge branch 'master' of https://github.com/Microsoft/pxt-microbit 2016-09-09 15:19:58 +01:00
afa69c23c4 0.3.85 2016-09-09 11:35:45 +01:00
8229e71d0a Bump pxt-core to 0.3.97 2016-09-09 11:35:45 +01:00
93770e5821 Merge branch 'master' of github.com:Microsoft/pxt-microbit 2016-09-09 10:17:35 +01:00
cb8e28beb0 Bump pxt-microbit-core to 0.5.0 2016-09-09 10:17:28 +01:00
8c7e6055ff enabling parts in target 2016-09-09 01:39:52 -07:00
8450db55ac 0.3.84 2016-09-09 01:25:28 -07:00
c63e2c85f1 new part definitions (#247)
* working on new part definitions

* draft of new part definitions

* updates comments

* starting new allocator

* starting from the old allocator

* alloc internals renaming

* alloc minor renaming

* alloc internal renaming

* progress on new parts definition

* progress on new part defs allocator

* refactors BBLoc; progress on new allocator

* more progress on new allocator

* finishing new allocator

* deleting old allocator

* moves new allocator and part definitions

* porting to new part definitions

* refactors instructions for new definitions

* debugging new allocator

* fixes ground and power wire colros

* fixing new part definition bugs

* fixes wire end offsets; fixes NeoPixel placement

* fixes colorGroup issue

* fixes led matrix wiring

* naming tweaks

* fixes instructions regressions

* typo
2016-09-09 01:23:39 -07:00
d8fc11a688 0.3.83 2016-09-09 01:20:45 -07:00
847a29848d Bump pxt-core to 0.3.96 2016-09-09 01:20:43 -07:00
39f4372ee5 0.3.82 2016-09-08 23:39:26 -07:00
a8bbbea8ed Bump pxt-core to 0.3.95 2016-09-08 23:39:24 -07:00
91a8d05e45 updated loc strings 2016-09-08 23:11:58 -07:00
3ecedd3a32 0.3.81 2016-09-08 01:02:46 -07:00
b6390d77dd Bump pxt-core to 0.3.94 2016-09-08 01:02:40 -07:00
e8f9e0f023 lighter rendering of breadboard in grayed 2016-09-08 00:54:19 -07:00
2bacb58fdd simpler instructions 2016-09-08 00:46:43 -07:00
af8babfea2 Merge branch 'master' of https://github.com/Microsoft/pxt-microbit 2016-09-08 00:03:33 -07:00
1ee6274100 added raspberry-pi instructions 2016-09-08 00:00:07 -07:00
7448db5b98 makes instruction panels inline-block 2016-09-07 15:00:42 -07:00
983645403b 0.3.80 2016-09-07 11:49:41 -07:00
7a9f382bee Merge pull request #240 from Microsoft/neopixel-improvement
improves neopixel simulator
2016-09-07 11:22:40 -07:00
e59fd8469b improves neopixel simulator 2016-09-07 10:58:44 -07:00
008c886de9 0.3.79 2016-09-07 16:51:08 +01:00
c87fa30738 Bump pxt-core to 0.3.93 2016-09-07 16:51:08 +01:00
dd9d1299fa Merge branch 'master' of github.com:Microsoft/pxt-microbit 2016-09-07 16:27:42 +01:00
6a7d1bd95c Support for object literals
bump pxt-microbit-core to 0.4.4
see https://github.com/Microsoft/pxt/issues/287
2016-09-07 16:27:28 +01:00
dfe270d259 Correct filename of data.csv in radio challenge 2016-09-07 11:32:41 +01:00
6deb0683b6 Add GitHub URL property 2016-09-07 10:33:36 +01:00
26 changed files with 900 additions and 584 deletions

13
docfiles/tracking.html Normal file
View File

@ -0,0 +1,13 @@
<script type="text/javascript">
var appInsights=window.appInsights||function(config){
function r(config){t[config]=function(){var i=arguments;t.queue.push(function(){t[config].apply(t,i)})}}var t={config:config},u=document,e=window,o="script",s=u.createElement(o),i,f;for(s.src=config.url||"//az416426.vo.msecnd.net/scripts/a/ai.0.js",u.getElementsByTagName(o)[0].parentNode.appendChild(s),t.cookie=u.cookie,t.queue=[],i=["Event","Exception","Metric","PageView","Trace"];i.length;)r("track"+i.pop());return r("setAuthenticatedUserContext"),r("clearAuthenticatedUserContext"),config.disableExceptionTracking||(i="onerror",r("_"+i),f=e[i],e[i]=function(config,r,u,e,o){var s=f&&f(config,r,u,e,o);return s!==!0&&t["_"+i](config,r,u,e,o),s}),t
}({
instrumentationKey:"e9ae05ca-350b-427a-9775-3ba3f6efabce"
});window.appInsights=appInsights;
</script>
<script type="text/javascript">
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable time_event track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config reset people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
mixpanel.init("762fef19c053a0ea4cec43d2fecae76e");
</script>

View File

@ -58,7 +58,9 @@ After running this simulation several seconds by moving the micro:bit side to si
![](/static/mb/acc2.png)
### ~
Finally, you must open the Excel CSV file by clicking on the data.xls file that was downloaded to Downloads Folder.
Finally, you must open the Excel CSV file by clicking on the `data.csv` file
that was downloaded to Downloads Folder.
![](/static/mb/data3.png)
@ -93,4 +95,4 @@ NEXT: The Watch
```package
microbit-radio
```
```

29
docs/raspberry-pi.md Normal file
View File

@ -0,0 +1,29 @@
# Raspberry Pi and Raspbian
It is possible to run the web editor or [command line interface](/cli) from Raspbian on Raspberry Pi 2 or 3.
## Web editor
The web editor requires to install IceWeasel (Firefox) as the built-in browser cannot handle it.
```
sudo apt-get install iceweasel
```
Once installed simply navigate to https://codethemicrobit.com or type
```
firefox https://codethemicrobit.com
```
## Command line
The PXT command line also works on Raspbian and allows to run a local server and/or edit programs from any text editor.
* Node.JS 6.0 needs installed
To install all the tools,
```
curl -s https://raw.githubusercontent.com/Microsoft/pxt-rpi/master/install.sh | sh -
```

View File

@ -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|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",
@ -10,6 +9,7 @@
"bluetooth.startLEDService": "Starts the Bluetooth LED service",
"bluetooth.startMagnetometerService": "Starts the Bluetooth magnetometer 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.uartWrite": "Writes to the Bluetooth UART service buffer. From there the data is transmitted over Bluetooth to a connected device."
}

View File

@ -7,6 +7,7 @@
"bluetooth.startLEDService|block": "bluetooth led service",
"bluetooth.startMagnetometerService|block": "bluetooth magnetometer 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.uartWrite|block": "bluetooth uart write %data",
"bluetooth|block": "bluetooth"

View File

@ -22,6 +22,8 @@
"control": "Runtime and event utilities.",
"control.inBackground": "Schedules code that run in the background.",
"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.addScore": "Adds points to the current score",
"game.addScore|param|points": "amount of points to change, eg: 1",
@ -52,9 +54,12 @@
"input.onLogoDown|param|body": "TODO",
"input.onLogoUp": "Attaches code to run when the logo is oriented upwards and the board is vertical.",
"input.onLogoUp|param|body": "TODO",
"input.onPinPressed": "Do something when a pin(``P0``, ``P1`` or both ``P2``) is pressed.",
"input.onPinPressed|param|body": "TODO",
"input.onPinPressed|param|name": "TODO",
"input.onPinPressed": "Do something when a pin is pressed.",
"input.onPinPressed|param|body": "the code to run when the pin is pressed",
"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|param|body": "TODO",
"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|param|name": "pin to set the pull mode on",
"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.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.",

View File

@ -12,6 +12,7 @@
"basic|block": "basic",
"control.inBackground|block": "run in background",
"control.reset|block": "reset",
"control.waitMicros|block": "wait (µs)%micros",
"control|block": "control",
"game.addScore|block": "change score by|%points",
"game.gameOver|block": "game over",
@ -28,7 +29,8 @@
"input.magneticForce|block": "magnetic force (µT)|%NAME",
"input.onButtonPressed|block": "on button|%NAME|pressed",
"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.rotation|block": "rotation (°)|%NAME",
"input.runningTime|block": "running time (ms)",
@ -41,6 +43,7 @@
"led.point|block": "point|x %x|y %y",
"led.setBrightness|block": "set brightness %value",
"led.stopAnimation|block": "stop animation",
"led.toggle|block": "toggle|x %x|y %y",
"led.unplot|block": "unplot|x %x|y %y",
"led|block": "led",
"music.beat|block": "%fraction|beat",
@ -65,6 +68,7 @@
"pins.servoSetPulse|block": "servo set pulse|pin %value|to (µs) %micros",
"pins.servoWritePin|block": "servo write|pin %name|to %value",
"pins.setPull|block": "set pull|pin %pin|to %pull",
"pins.spiWrite|block": "spi write %value",
"pins|block": "pins",
"serial.readLine|block": "serial read line",
"serial.redirect|block": "serial redirect to|TX %tx|RX %rx|at baud rate %rate",

View File

@ -166,6 +166,7 @@ namespace Array_ {
int removeElement(RefCollection *c, uint32_t x) { return c->removeElement(x); }
}
// Import some stuff directly
namespace pxt {
//%
@ -181,10 +182,12 @@ namespace pxt {
//%
Action mkAction(int reflen, int totallen, int startptr);
//%
RefRecord* mkRecord(int reflen, int totallen);
//%
RefRecord* mkClassInstance(int offset);
//%
void RefRecord_destroy(RefRecord *r);
//%
void RefRecord_print(RefRecord *r);
//%
void debugMemLeaks();
//%
int incr(uint32_t e);
@ -308,6 +311,72 @@ namespace pxtrt {
}
}
//%
RefMap *mkMap() {
return new RefMap();
}
//%
uint32_t mapGet(RefMap *map, uint32_t key) {
int i = map->findIdx(key);
if (i < 0) {
map->unref();
return 0;
}
uint32_t r = map->data[i].val;
map->unref();
return r;
}
//%
uint32_t mapGetRef(RefMap *map, uint32_t key) {
int i = map->findIdx(key);
if (i < 0) {
map->unref();
return 0;
}
uint32_t r = incr(map->data[i].val);
map->unref();
return r;
}
//%
void mapSet(RefMap *map, uint32_t key, uint32_t val) {
int i = map->findIdx(key);
if (i < 0) {
map->data.push_back({
key << 1,
val
});
} else {
if (map->data[i].key & 1) {
decr(map->data[i].val);
map->data[i].key = key << 1;
}
map->data[i].val = val;
}
map->unref();
}
//%
void mapSetRef(RefMap *map, uint32_t key, uint32_t val) {
int i = map->findIdx(key);
if (i < 0) {
map->data.push_back({
(key << 1) | 1,
val
});
} else {
if (map->data[i].key & 1) {
decr(map->data[i].val);
} else {
map->data[i].key = (key << 1) | 1;
}
map->data[i].val = val;
}
map->unref();
}
//
// Debugger
//

View File

@ -1,78 +1,126 @@
{
"ledmatrix": {
"visual": "ledmatrix",
"breadboardColumnsNeeded": 8,
"breadboardStartRow": "h",
"pinAllocation": {
"type": "auto",
"gpioPinsNeeded": [5, 5]
"buttonpair": {
"simulationBehavior": "buttonpair",
"visual": {
"builtIn": "buttonpair",
"width": 75,
"height": 45,
"pinDistance": 15,
"pinLocations": [
{"x": 0, "y": 0},
{"x": 30, "y": 45},
{"x": 45, "y": 0},
{"x": 75, "y": 45}
]
},
"assemblyStep": 0,
"wires": [
{"start": ["breadboard", "j", 0], "end": ["GPIO", 5], "color": "purple", "assemblyStep": 1},
{"start": ["breadboard", "j", 1], "end": ["GPIO", 6], "color": "purple", "assemblyStep": 1},
{"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": {
"visual": "buttonpair",
"breadboardColumnsNeeded": 6,
"breadboardStartRow": "f",
"pinAllocation": {
"type": "predefined",
"pins": ["P13", "P12"]
"numberOfPins": 4,
"pinDefinitions": [
{"target": "P14", "style": "male", "orientation": "-Z"},
{"target": "ground", "style": "male", "orientation": "-Z"},
{"target": "P15", "style": "male", "orientation": "-Z"},
{"target": "ground", "style": "male", "orientation": "-Z"}
],
"instantiation": {
"kind": "singleton"
},
"assemblyStep": 0,
"wires": [
{"start": ["breadboard", "j", 0], "end": ["GPIO", 0], "color": "yellow", "assemblyStep": 1},
{"start": ["breadboard", "a", 2], "end": "ground", "color": "blue", "assemblyStep": 1},
{"start": ["breadboard", "j", 3], "end": ["GPIO", 1], "color": "orange", "assemblyStep": 2},
{"start": ["breadboard", "a", 5], "end": "ground", "color": "blue", "assemblyStep": 2}
"assembly": [
{"part": true},
{"pinIndices": [0, 1]},
{"pinIndices": [2, 3]}
]
},
"neopixel": {
"visual": "neopixel",
"breadboardColumnsNeeded": 5,
"breadboardStartRow": "h",
"pinAllocation": {
"type": "factoryfunction",
"functionName": "neopixel.create",
"pinArgPositions": [0],
"otherArgPositions": [1]
"simulationBehavior": "neopixel",
"visual": {
"builtIn": "neopixel",
"width": 58,
"height": 113,
"pinDistance": 9,
"pinLocations": [
{"x": 10, "y": 0},
{"x": 19, "y": 0},
{"x": 28, "y": 0}
]
},
"assemblyStep": 0,
"wires": [
{"start": ["breadboard", "j", 1], "end": "ground", "color": "blue", "assemblyStep": 1},
{"start": ["breadboard", "j", 2], "end": "threeVolt", "color": "red", "assemblyStep": 2},
{"start": ["breadboard", "j", 3], "end": ["GPIO", 0], "color": "green", "assemblyStep": 2}
"numberOfPins": 3,
"pinDefinitions": [
{"target": {"pinInstantiationIdx": 0}, "style": "solder", "orientation": "+Z"},
{"target": "threeVolt", "style": "solder", "orientation": "+Z"},
{"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": {
"numberOfPins": 2,
"visual": {
"image": "/parts/speaker.svg",
"width": 500,
"height": 500,
"firstPin": [180, 135],
"pinDist": 70,
"extraColumnOffset": 1
"pinDistance": 70,
"pinLocations": [
{"x": 180, "y": 135},
{"x": 320, "y": 135}
]
},
"breadboardColumnsNeeded": 5,
"breadboardStartRow": "f",
"pinAllocation": {
"type": "auto",
"gpioPinsNeeded": 1
},
"assemblyStep": 0,
"wires": [
{"start": ["breadboard", "j", 1], "end": ["GPIO", 0], "color": "#ff80fa", "assemblyStep": 1},
{"start": ["breadboard", "j", 3], "end": "ground", "color": "blue", "assemblyStep": 1}
"pinDefinitions": [
{"target": "P0", "style": "male", "orientation": "-Z"},
{"target": "ground", "style": "male", "orientation": "-Z"}
],
"instantiation": {"kind": "singleton"},
"assembly": [
{"part": true, "pinIndices": [0]},
{"pinIndices": [1]}
]
}
}

View File

@ -1,6 +1,6 @@
{
"name": "pxt-microbit",
"version": "0.3.78",
"version": "0.3.87",
"description": "micro:bit target for PXT",
"keywords": [
"JavaScript",
@ -29,6 +29,6 @@
"typescript": "^1.8.7"
},
"dependencies": {
"pxt-core": "0.3.92"
"pxt-core": "0.3.100"
}
}

View File

@ -75,7 +75,9 @@
},
"simulator": {
"autoRun": true,
"streams": true,
"aspectRatio": 1.22,
"parts": true,
"partsAspectRatio": 0.69,
"builtinParts": {
"accelerometer": true,
@ -91,7 +93,7 @@
"yottaTarget": "bbc-microbit-classic-gcc",
"yottaCorePackage": "pxt-microbit-core",
"githubCorePackage": "microsoft/pxt-microbit-core",
"gittag": "v0.4.2",
"gittag": "v0.5.0",
"serviceId": "ws"
},
"serial": {
@ -113,6 +115,7 @@
"embedUrl": "https://codethemicrobit.com/",
"privacyUrl": "https://go.microsoft.com/fwlink/?LinkId=521839",
"termsOfUseUrl": "https://go.microsoft.com/fwlink/?LinkID=206977",
"githubUrl": "https://github.com/Microsoft/pxt-microbit",
"boardName": "BBC micro:bit",
"docMenu": [
{
@ -146,4 +149,4 @@
"userVoiceApiKey": "WEkkIGaj1WtJnSUF59iwaA",
"userVoiceForumId": 402381
}
}
}

View File

@ -1,60 +1,81 @@
namespace pxsim {
export interface AllocatorOpts {
const GROUND_COLOR = "blue";
const POWER_COLOR = "red";
export interface AllocatorOpts {
boardDef: BoardDefinition,
cmpDefs: Map<PartDefinition>,
partDefs: Map<PartDefinition>,
partsList: string[]
fnArgs: any,
getBBCoord: (loc: BBRowCol) => visuals.Coord,
cmpList: string[]
// Used for finding the nearest available power pins
getBBCoord: (loc: BBLoc) => visuals.Coord,
};
export interface AllocatorResult {
powerWires: WireInst[],
components: CmpAndWireInst[]
partsAndWires: PartAndWiresInst[],
}
export interface CmpAndWireInst {
component: CmpInst,
wires: WireInst[]
}
export interface CmpInst {
export interface PartInst {
name: string,
breadboardStartColumn: number,
breadboardStartRow: string,
assemblyStep: number,
visual: string | PartVisualDefinition,
microbitPins: string[],
otherArgs?: string[],
simulationBehavior?: string,
visual: PartVisualDefinition,
bbFit: PartBBFit,
startColumnIdx: number,
startRowIdx: number,
breadboardConnections: BBLoc[],
params: Map<string>,
}
export interface WireInst {
start: Loc,
end: Loc,
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,
def: PartDefinition,
pinsAssigned: string[],
pinsNeeded: number | number[],
breadboardColumnsNeeded: number,
otherArgs?: string[],
}
interface AllocLocOpts {
nearestBBPin?: BBRowCol,
startColumn?: number,
cmpGPIOPins?: string[],
partParams: Map<string>,
pins: PinIR[],
bbFit: PartBBFit,
};
interface AllocWireOpts {
startColumn: number,
cmpGPIOPins: string[],
}
interface AllocBlock {
cmpIdx: number,
cmpBlkIdx: number,
gpioNeeded: number,
gpioAssigned: string[]
interface PartPlacement extends PartIR {
startColumnIdx: number,
startRowIdx: number,
};
type WireIRLoc = PinTarget | BBLoc;
interface WireIR {
pinIdx: number,
start: WireIRLoc,
end: WireIRLoc,
color: string,
}
interface PartIRAndWireIRs extends PartPlacement {
wires: WireIR[],
};
interface PowerUsage {
topGround: boolean,
topThreeVolt: boolean,
@ -63,18 +84,27 @@ namespace pxsim {
singleGround: 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;
if (location[0] === "breadboard") {
let row = <string>location[1];
if (typeof location !== "string" && (<BBLoc>location).type === "breadboard") {
let bbLoc = <BBLoc>location;
let row = bbLoc.row;
isBot = 0 <= ["a", "b", "c", "d", "e"].indexOf(row);
}
return isBot;
}
const arrCount = (a: boolean[]) => a.reduce((p, n) => p + (n ? 1 : 0), 0);
const arrAny = (a: boolean[]) => arrCount(a) > 0;
function computePowerUsage(wireDef: WireDefinition): PowerUsage {
let ends = [wireDef.start, wireDef.end];
function computePowerUsage(wire: WireIR): PowerUsage {
let ends = [wire.start, wire.end];
let endIsGround = ends.map(e => e === "ground");
let endIsThreeVolt = ends.map(e => e === "threeVolt");
let endIsBot = ends.map(e => isOnBreadboardBottom(e));
@ -115,10 +145,21 @@ namespace pxsim {
function copyDoubleArray(a: string[][]) {
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);
let pin = arg.split("DigitalPin.")[1];
return pin;
return <MicrobitPin>pin;
}
function mkReverseMap(map: {[key: string]: string}) {
let origKeys: string[] = [];
@ -135,25 +176,239 @@ namespace pxsim {
}
return newMap;
}
function isConnectedToBB(pin: PartPinDefinition): boolean {
return pin.orientation === "-Z" && pin.style === "male";
}
class Allocator {
//TODO: better handling of allocation errors
private opts: AllocatorOpts;
private availablePowerPins = {
top: {
threeVolt: mkRange(26, 51).map(n => <BBRowCol>["+", `${n}`]),
ground: 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 => <BBLoc>{type: "breadboard", row: "-", col: `${n}`}),
},
bottom: {
threeVolt: mkRange(1, 26).map(n => <BBRowCol>["+", `${n}`]),
ground: 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 => <BBLoc>{type: "breadboard", row: "-", col: `${n}`}),
},
};
private powerUsage: PowerUsage;
private availableWireColors: string[];
constructor(opts: AllocatorOpts) {
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") {
//special case if there is only a single ground or three volt pin in the whole build
if (location === "ground" && this.powerUsage.singleGround) {
@ -164,8 +419,8 @@ namespace pxsim {
return {type: "dalboard", pin: boardThreeVoltPin};
}
U.assert(!!opts.nearestBBPin);
let nearestCoord = this.opts.getBBCoord(opts.nearestBBPin);
U.assert(!!opts.referenceBBPin);
let nearestCoord = this.opts.getBBCoord(opts.referenceBBPin);
let firstTopAndBot = [
this.availablePowerPins.top.ground[0] || this.availablePowerPins.top.threeVolt[0],
this.availablePowerPins.bottom.ground[0] || this.availablePowerPins.bottom.threeVolt[0]
@ -177,7 +432,7 @@ namespace pxsim {
//TODO
}
let nearTop = visuals.findClosestCoordIdx(nearestCoord, firstTopAndBot) == 0;
let barPins: BBRowCol[];
let barPins: BBLoc[];
if (nearTop) {
if (location === "ground") {
barPins = this.availablePowerPins.top.ground;
@ -203,17 +458,9 @@ namespace pxsim {
this.availablePowerPins.bottom.ground.splice(closestPinIdx, 1);
this.availablePowerPins.bottom.threeVolt.splice(closestPinIdx, 1);
}
return {type: "breadboard", rowCol: pin};
} else if (location[0] === "breadboard") {
U.assert(!!opts.startColumn);
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};
return pin;
} else if ((<BBLoc>location).type === "breadboard") {
return <BBLoc>location;
} else if (location === "MOSI" || location === "MISO" || location === "SCK") {
if (!this.opts.boardDef.spiPins)
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;
return {type: "dalboard", pin: pin};
} else {
//TODO
U.assert(false);
return null;
//it must be a MicrobitPin
U.assert(typeof location === "string", "Unknown location type: " + location);
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;
if (!boardGround) {
console.log("No available ground pin on board!");
@ -238,7 +488,7 @@ namespace pxsim {
}
return boardGround;
}
private getBoardThreeVoltPin() {
private getBoardThreeVoltPin(): string {
let threeVoltPin = this.opts.boardDef.threeVoltPins[0] || null;
if (!threeVoltPin) {
console.log("No available 3.3V pin on board!");
@ -246,14 +496,14 @@ namespace pxsim {
}
return threeVoltPin;
}
private allocatePowerWires(powerUsage: PowerUsage): WireInst[] {
private allocPowerWires(powerUsage: PowerUsage): PartAndWiresInst {
let boardGroundPin = this.getBoardGroundPin();
let threeVoltPin = this.getBoardThreeVoltPin();
let topLeft: BBRowCol = ["-", "26"];
let botLeft: BBRowCol = ["-", "1"];
let topRight: BBRowCol = ["-", "50"];
let botRight: BBRowCol = ["-", "25"];
let top: BBRowCol, bot: BBRowCol;
const topLeft: BBLoc = {type: "breadboard", row: "-", col: "26"};
const botLeft: BBLoc = {type: "breadboard", row: "-", col: "1"};
const topRight: BBLoc = {type: "breadboard", row: "-", col: "50"};
const botRight: BBLoc = {type: "breadboard", row: "-", col: "25"};
let top: BBLoc, bot: BBLoc;
if (this.opts.boardDef.attachPowerOnRight) {
top = topRight;
bot = botRight;
@ -261,296 +511,162 @@ namespace pxsim {
top = topLeft;
bot = botLeft;
}
const GROUND_COLOR = "blue";
const POWER_COLOR = "red";
const wires: WireInst[] = [];
let groundStep = 0;
let threeVoltStep = (powerUsage.bottomGround || powerUsage.topGround) ? 1 : 0;
let groundWires: WireInst[] = [];
let threeVoltWires: WireInst[] = [];
if (powerUsage.bottomGround && powerUsage.topGround) {
//bb top - <==> bb bot -
wires.push({
start: this.allocateLocation("ground", {nearestBBPin: top}),
end: this.allocateLocation("ground", {nearestBBPin: bot}),
color: GROUND_COLOR, assemblyStep: groundStep
groundWires.push({
start: this.allocLocation("ground", {referenceBBPin: top}),
end: this.allocLocation("ground", {referenceBBPin: bot}),
color: GROUND_COLOR,
});
}
if (powerUsage.topGround) {
//board - <==> bb top -
wires.push({
start: this.allocateLocation("ground", {nearestBBPin: top}),
groundWires.push({
start: this.allocLocation("ground", {referenceBBPin: top}),
end: {type: "dalboard", pin: boardGroundPin},
color: GROUND_COLOR, assemblyStep: groundStep
color: GROUND_COLOR,
});
} else if (powerUsage.bottomGround) {
//board - <==> bb bot -
wires.push({
start: this.allocateLocation("ground", {nearestBBPin: bot}),
groundWires.push({
start: this.allocLocation("ground", {referenceBBPin: bot}),
end: {type: "dalboard", pin: boardGroundPin},
color: GROUND_COLOR, assemblyStep: groundStep
color: GROUND_COLOR,
});
}
if (powerUsage.bottomThreeVolt && powerUsage.bottomGround) {
//bb top + <==> bb bot +
wires.push({
start: this.allocateLocation("threeVolt", {nearestBBPin: top}),
end: this.allocateLocation("threeVolt", {nearestBBPin: bot}),
color: POWER_COLOR, assemblyStep: threeVoltStep
threeVoltWires.push({
start: this.allocLocation("threeVolt", {referenceBBPin: top}),
end: this.allocLocation("threeVolt", {referenceBBPin: bot}),
color: POWER_COLOR,
});
}
if (powerUsage.topThreeVolt) {
//board + <==> bb top +
wires.push({
start: this.allocateLocation("threeVolt", {nearestBBPin: top}),
threeVoltWires.push({
start: this.allocLocation("threeVolt", {referenceBBPin: top}),
end: {type: "dalboard", pin: threeVoltPin},
color: POWER_COLOR, assemblyStep: threeVoltStep
color: POWER_COLOR,
});
} else if (powerUsage.bottomThreeVolt) {
//board + <==> bb bot +
wires.push({
start: this.allocateLocation("threeVolt", {nearestBBPin: bot}),
threeVoltWires.push({
start: this.allocLocation("threeVolt", {referenceBBPin: bot}),
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 {
let ends = [wireDef.start, wireDef.end];
private allocWire(wireIR: WireIR): WireInst {
let ends = [wireIR.start, wireIR.end];
let endIsPower = ends.map(e => e === "ground" || e === "threeVolt");
//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
endInsts = endInsts.map((e, idx) => {
if (e)
return e;
let locInst = <BBLoc>endInsts[1 - idx]; // non-power end
let l = this.allocateLocation(ends[idx], {
nearestBBPin: locInst.rowCol,
startColumn: opts.startColumn,
cmpGPIOPins: opts.cmpGPIOPins,
let l = this.allocLocation(ends[idx], {
referenceBBPin: locInst,
});
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[] {
let cmpNmAndDefs = this.opts.cmpList.map(cmpName => <[string, PartDefinition]>[cmpName, this.opts.cmpDefs[cmpName]]).filter(d => !!d[1]);
let cmpNmsList = cmpNmAndDefs.map(p => p[0]);
let cmpDefsList = cmpNmAndDefs.map(p => p[1]);
let partialCmps: PartialCmpAlloc[] = [];
cmpDefsList.forEach((def, idx) => {
let nm = cmpNmsList[idx];
if (def.pinAllocation.type === "predefined") {
let mbPins = (<PredefinedPinAlloc>def.pinAllocation).pins;
let pinsAssigned = mbPins.map(p => this.opts.boardDef.gpioPinMap[p]);
partialCmps.push({
name: nm,
def: def,
pinsAssigned: pinsAssigned,
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,
});
private allocPart(ir: PartPlacement): PartInst {
let bbConnections = ir.pins
.filter(p => isConnectedToBB(p.def))
.map(p => {
let rowIdx = ir.startRowIdx + p.bbFit.partRelativeRowIdx;
if (rowIdx >= 7) //account for middle gap
rowIdx -= 2;
let rowName = visuals.getRowName(rowIdx);
let colIdx = ir.startColumnIdx + p.bbFit.partRelativeColIdx;
let colName = visuals.getColumnName(colIdx);
return <BBLoc>{
type: "breadboard",
row: rowName,
col: colName,
}
} 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;
}
private allocateGPIOPins(partialCmps: PartialCmpAlloc[]): string[][] {
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 {
//blocks of pins
let blocks = <number[]>cmp.pinsNeeded;
blocks.forEach((numNeeded, blkIdx) => {
blockAssignments.push(
{cmpIdx: idx, cmpBlkIdx: blkIdx, gpioNeeded: numNeeded, gpioAssigned: []});
});
}
}
});
// remove assigned blocks
availableGPIOBlocks.forEach(blks => {
for (let i = blks.length - 1; 0 <= i; i--) {
let pin = blks[i];
if (0 <= preassignedPins.indexOf(pin)) {
blks.splice(i, 1);
}
}
});
// 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);
basicWires = this.allocatePowerWires(this.powerUsage);
let cmpGPIOPins = this.allocateGPIOPins(partialCmps);
let reverseMap = mkReverseMap(this.opts.boardDef.gpioPinMap);
let cmpMicrobitPins = cmpGPIOPins.map(pins => pins.map(p => reverseMap[p]));
let cmpStartCol = this.allocateColumns(partialCmps);
let cmps = partialCmps.map((c, idx) => this.allocateComponent(c, cmpStartCol[idx], cmpMicrobitPins[idx]));
let wires = partialCmps.map((c, idx) => c.def.wires.map(d => this.allocateWire(d, {
cmpGPIOPins: cmpGPIOPins[idx],
startColumn: cmpStartCol[idx],
})));
cmpsAndWires = cmps.map((c, idx) => {
return {component: c, wires: wires[idx]}
});
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
}
return part;
}
public allocAll(): AllocatorResult {
let partNmAndDefs = this.opts.partsList
.map(partName => {return {name: partName, def: this.opts.partDefs[partName]}})
.filter(d => !!d.def);
if (partNmAndDefs.length > 0) {
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) => {
let dims = dimensions[idx];
let irs = this.allocPartIRs(nmAndDef.def, nmAndDef.name, dims);
partIRs = partIRs.concat(irs);
})
let partPlacements = this.placeParts(partIRs);
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));
this.powerUsage = mergePowerUsage(allPowerUsage);
let basicWires = this.allocPowerWires(this.powerUsage);
let partsAndWires: PartAndWiresInst[] = partsAndWireIRs.map((irs, idx) => {
let part = this.allocPart(irs);
let wires = irs.wires.map(w => this.allocWire(w));
let pinIdxToWireIdx: number[] = [];
irs.wires.forEach((wIR, idx) => {
pinIdxToWireIdx[wIR.pinIdx] = idx;
});
let assembly: AssemblyStep[] = irs.def.assembly.map(stepDef => {
return {
part: stepDef.part,
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: []
}
}
return {
powerWires: basicWires,
components: cmpsAndWires
};
}
}
export function allocateDefinitions(opts: AllocatorOpts): AllocatorResult {
return new Allocator(opts).allocateAll();
return new Allocator(opts).allocAll();
}
}

View File

@ -84,8 +84,8 @@ namespace pxsim {
let viewHost = new visuals.BoardHost({
state: this,
boardDef: boardDef,
cmpsList: cmpsList,
cmpDefs: cmpDefs,
partsList: cmpsList,
partDefs: cmpDefs,
fnArgs: fnArgs,
maxWidth: "100%",
maxHeight: "100%",

View File

@ -53,7 +53,7 @@ namespace pxsim {
marginWhenBreadboarding: [0, 0, 80, 0],
}
export const builtinComponentSimVisual: Map<() => visuals.IBoardComponent<any>> = {
export const builtinComponentSimVisual: Map<() => visuals.IBoardPart<any>> = {
"buttonpair": () => new visuals.ButtonPairView(),
"ledmatrix": () => new visuals.LedMatrixView(),
"neopixel": () => new visuals.NeoPixelView(),

View File

@ -1,7 +1,6 @@
/// <reference path="../../node_modules/pxt-core/typings/bluebird/bluebird.d.ts"/>
/// <reference path="../../node_modules/pxt-core/built/pxtsim.d.ts"/>
/// <reference path="../../node_modules/pxt-core/built/pxtrunner.d.ts"/>
/// <reference path="../../libs/microbit/dal.d.ts"/>
/// <reference path="../visuals/genericboard.ts"/>
/// <reference path="../visuals/wiring.ts"/>
@ -20,7 +19,7 @@ namespace pxsim.instructions {
const LBL_LEFT_PAD = 5;
const REQ_WIRE_HEIGHT = 45;
const REQ_CMP_HEIGHT = 55;
const REQ_CMP_SCALE = 0.5 * 4;
const REQ_CMP_SCALE = 0.5 * 3;
type Orientation = "landscape" | "portrait";
const ORIENTATION: Orientation = "portrait";
const PPI = 96.0;
@ -59,7 +58,7 @@ namespace pxsim.instructions {
border-color: ${BORDER_COLOR};
border-style: solid;
border-radius: ${BORDER_RADIUS}px;
display: block;
display: inline-block;
width: ${PANEL_WIDTH}px;
height: ${PANEL_HEIGHT}px;
position: relative;
@ -284,18 +283,18 @@ namespace pxsim.instructions {
div.appendChild(svgEl);
return div;
}
function mkCmpDiv(cmp: "wire" | string | PartVisualDefinition, opts: mkCmpDivOpts): HTMLElement {
function mkCmpDiv(cmp: "wire" | PartVisualDefinition, opts: mkCmpDivOpts): HTMLElement {
let el: visuals.SVGElAndSize;
if (cmp == "wire") {
//TODO: support non-croc wire parts
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 {
let partVis = <PartVisualDefinition>cmp;
el = visuals.mkGenericPartSVG(partVis);
if (typeof partVis.builtIn == "string") {
let cnstr = builtinComponentPartVisual[partVis.builtIn];
el = cnstr([0, 0]);
} else {
el = visuals.mkGenericPartSVG(partVis);
}
}
return wrapSvg(el, opts);
}
@ -305,40 +304,33 @@ namespace pxsim.instructions {
fnArgs: any,
allAlloc: AllocatorResult,
stepToWires: WireInst[][],
stepToCmps: CmpInst[][]
stepToCmps: PartInst[][]
allWires: WireInst[],
allCmps: CmpInst[],
allCmps: PartInst[],
lastStep: number,
colorToWires: Map<WireInst[]>,
allWireColors: string[],
};
function mkBoardProps(allocOpts: AllocatorOpts): BoardProps {
let allocRes = allocateDefinitions(allocOpts);
let {powerWires, components} = allocRes;
let stepToWires: WireInst[][] = [];
let stepToCmps: CmpInst[][] = [];
powerWires.forEach(w => {
let step = w.assemblyStep + 1;
(stepToWires[step] || (stepToWires[step] = [])).push(w)
});
let getMaxStep = (ns: { assemblyStep: number }[]) => ns.reduce((m, n) => Math.max(m, n.assemblyStep), 0);
let stepOffset = powerWires.length > 0 ? getMaxStep(powerWires) + 2 : 1;
components.forEach(cAndWs => {
let {component, wires} = cAndWs;
let cStep = component.assemblyStep + stepOffset;
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);
let stepToCmps: PartInst[][] = [];
let stepOffset = 0;
allocRes.partsAndWires.forEach(cAndWs => {
let part = cAndWs.part;
let wires = cAndWs.wires;
cAndWs.assembly.forEach((step, idx) => {
if (step.part && part)
stepToCmps[stepOffset + idx] = [part]
if (step.wireIndices && step.wireIndices.length > 0 && wires)
stepToWires[stepOffset + idx] = step.wireIndices.map(i => wires[i])
})
stepOffset = Math.max(cStep, wSteps.reduce((m, n) => Math.max(m, n), 0)) + 1;
stepOffset += cAndWs.assembly.length;
});
let lastStep = stepOffset - 1;
let allCmps = components.map(p => p.component);
let allWires = powerWires.concat(components.map(p => p.wires).reduce((p, n) => p.concat(n), []));
let numSteps = stepOffset;
let lastStep = numSteps - 1;
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 allWireColors: string[] = [];
allWires.forEach(w => {
@ -350,7 +342,7 @@ namespace pxsim.instructions {
});
return {
boardDef: allocOpts.boardDef,
cmpDefs: allocOpts.cmpDefs,
cmpDefs: allocOpts.partDefs,
fnArgs: allocOpts.fnArgs,
allAlloc: allocRes,
stepToWires: stepToWires,
@ -368,7 +360,7 @@ namespace pxsim.instructions {
state: state,
boardDef: boardDef,
forceBreadboard: true,
cmpDefs: cmpDefs,
partDefs: cmpDefs,
maxWidth: `${width}px`,
fnArgs: fnArgs,
wireframe: buildMode,
@ -397,6 +389,19 @@ namespace pxsim.instructions {
}
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];
if (wires) {
wires.forEach(w => {
@ -405,13 +410,12 @@ namespace pxsim.instructions {
if (i === step) {
//location highlights
if (w.start.type == "breadboard") {
let lbls = board.highlightBreadboardPin((<BBLoc>w.start).rowCol);
let lbls = board.highlightBreadboardPin((<BBLoc>w.start));
} else {
board.highlightBoardPin((<BoardLoc>w.start).pin);
}
if (w.end.type == "breadboard") {
let [row, col] = (<BBLoc>w.end).rowCol;
let lbls = board.highlightBreadboardPin((<BBLoc>w.end).rowCol);
let lbls = board.highlightBreadboardPin((<BBLoc>w.end));
} else {
board.highlightBoardPin((<BoardLoc>w.end).pin);
}
@ -420,24 +424,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() {
@ -463,7 +449,7 @@ namespace pxsim.instructions {
cmps.forEach(c => {
let quant = 1;
// TODO: don't special case this
if (c.visual === "buttonpair") {
if (c.visual.builtIn === "buttonpair") {
quant = 2;
}
let cmp = mkCmpDiv(c.visual, {
@ -516,7 +502,7 @@ namespace pxsim.instructions {
let wires = (props.stepToWires[step] || []);
let mkLabel = (loc: Loc) => {
if (loc.type === "breadboard") {
let [row, col] = (<BBLoc>loc).rowCol;
let {row, col} = (<BBLoc>loc);
return `(${row},${col})`
} else
return (<BoardLoc>loc).pin;
@ -536,17 +522,23 @@ namespace pxsim.instructions {
});
let cmps = (props.stepToCmps[step] || []);
cmps.forEach(c => {
let l: BBRowCol = [`${c.breadboardStartRow}`, `${c.breadboardStartColumn}`];
let locs = [l];
if (c.visual === "buttonpair") {
let locs: BBLoc[];
if (c.visual.builtIn === "buttonpair") {
//TODO: don't special case this
let l2: BBRowCol = [`${c.breadboardStartRow}`, `${c.breadboardStartColumn + 3}`];
locs.push(l2);
locs = [c.breadboardConnections[0], c.breadboardConnections[2]]
} else {
locs = [c.breadboardConnections[0]];
}
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, {
top: `(${row},${col})`,
top: topLbl,
topSize: LOC_LBL_SIZE,
cmpHeight: REQ_CMP_HEIGHT,
cmpScale: REQ_CMP_SCALE
@ -656,8 +648,8 @@ ${tsPackage}
activeComponents.sort();
let props = mkBoardProps({
boardDef: boardDef,
cmpDefs: cmpDefs,
cmpList: activeComponents,
partDefs: cmpDefs,
partsList: activeComponents,
fnArgs: fnArgs,
getBBCoord: dummyBreadboard.getCoord.bind(dummyBreadboard)
});

View File

@ -3,10 +3,18 @@
/// <reference path="../libs/microbit/dal.d.ts"/>
namespace pxsim {
export type BBRowCol = [/*row*/string, /*column*/string];
export type BoardPin = string;
export interface BBLoc { type: "breadboard", rowCol: BBRowCol };
export interface BoardLoc { type: "dalboard", pin: BoardPin };
export interface BBLoc {
type: "breadboard",
row: string,
col: string
xOffset?: number,
yOffset?: number
};
export interface BoardLoc {
type: "dalboard",
pin: BoardPin
};
export type Loc = BBLoc | BoardLoc;
export function initRuntimeWithDalBoard() {
@ -197,11 +205,12 @@ namespace pxsim.visuals {
return minIdx;
}
export interface IBoardComponent<T> {
export interface IBoardPart<T> {
style: string,
element: SVGElement,
overElement?: 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,
updateState(): void,
updateTheme(): void,
@ -224,7 +233,8 @@ namespace pxsim.visuals {
}
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> = {
black: "#514f4d",
white: "#fcfdfc",
@ -236,6 +246,7 @@ namespace pxsim.visuals {
orange: "#fdb262",
red: "#f44f43",
brown: "#c89764",
pink: "#ff80fa"
}
export function mapWireColor(clr: WireColor | string): string {
return WIRE_COLOR_MAP[clr] || clr;

View File

@ -2,8 +2,8 @@ namespace pxsim.visuals {
export interface BoardHostOpts {
state: DalBoard,
boardDef: BoardDefinition,
cmpsList?: string[],
cmpDefs: Map<PartDefinition>,
partsList?: string[],
partDefs: Map<PartDefinition>,
fnArgs: any,
forceBreadboard?: boolean,
maxWidth?: string,
@ -11,13 +11,15 @@ namespace pxsim.visuals {
wireframe?: boolean
}
export class BoardHost {
private components: IBoardComponent<any>[] = [];
private parts: IBoardPart<any>[] = [];
private wireFactory: WireFactory;
private breadboard: Breadboard;
private fromBBCoord: (xy: Coord) => Coord;
private fromMBCoord: (xy: Coord) => Coord;
private boardView: BoardView;
private view: SVGSVGElement;
private partGroup: SVGGElement;
private partOverGroup: SVGGElement;
private style: SVGStyleElement;
private defs: SVGDefsElement;
private state: DalBoard;
@ -26,7 +28,7 @@ namespace pxsim.visuals {
constructor(opts: BoardHostOpts) {
this.state = opts.state;
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();
this.useCrocClips = opts.boardDef.useCrocClips;
@ -68,6 +70,8 @@ namespace pxsim.visuals {
this.fromMBCoord = composition.toHostCoord1;
this.fromBBCoord = composition.toHostCoord2;
let pinDist = composition.scaleUnit;
this.partGroup = over;
this.partOverGroup = <SVGGElement>svg.child(this.view, "g");
this.style = <SVGStyleElement>svg.child(this.view, "style", {});
this.defs = <SVGDefsElement>svg.child(this.view, "defs", {});
@ -76,16 +80,18 @@ namespace pxsim.visuals {
let allocRes = allocateDefinitions({
boardDef: opts.boardDef,
cmpDefs: opts.cmpDefs,
partDefs: opts.partDefs,
fnArgs: opts.fnArgs,
getBBCoord: this.breadboard.getCoord.bind(this.breadboard),
cmpList: activeComponents,
partsList: activeComponents,
});
this.addAll(allocRes);
} else {
let el = this.boardView.getView().el;
this.view = el;
this.partGroup = <SVGGElement>svg.child(this.view, "g");
this.partOverGroup = <SVGGElement>svg.child(this.view, "g");
if (opts.maxWidth)
svg.hydrate(this.view, { width: opts.maxWidth });
if (opts.maxHeight)
@ -99,7 +105,7 @@ namespace pxsim.visuals {
this.boardView.highlightPin(pinNm);
}
public highlightBreadboardPin(rowCol: BBRowCol) {
public highlightBreadboardPin(rowCol: BBLoc) {
this.breadboard.highlightLoc(rowCol);
}
@ -120,10 +126,10 @@ namespace pxsim.visuals {
}
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);
return this.fromBBCoord(bbCoord);
}
@ -134,7 +140,7 @@ namespace pxsim.visuals {
public getLocCoord(loc: Loc): Coord {
let coord: Coord;
if (loc.type === "breadboard") {
let rowCol = (<BBLoc>loc).rowCol;
let rowCol = (<BBLoc>loc);
coord = this.getBBCoord(rowCol);
} else {
let pinNm = (<BoardLoc>loc).pin;
@ -147,47 +153,62 @@ namespace pxsim.visuals {
return coord;
}
public addComponent(cmpDesc: CmpInst): IBoardComponent<any> {
let cmp: IBoardComponent<any> = null;
public addPart(partInst: PartInst): IBoardPart<any> {
let part: IBoardPart<any> = null;
let colOffset = 0;
if (typeof cmpDesc.visual === "string") {
let builtinVisual = cmpDesc.visual as string;
let cnstr = builtinComponentSimVisual[builtinVisual];
let stateFn = builtinComponentSimState[builtinVisual];
cmp = cnstr();
cmp.init(this.state.bus, stateFn(this.state), this.view, cmpDesc.microbitPins, cmpDesc.otherArgs);
if (partInst.simulationBehavior) {
//TODO: seperate simulation behavior from builtin visual
let builtinBehavior = partInst.simulationBehavior;
let cnstr = builtinComponentSimVisual[builtinBehavior];
let stateFn = builtinComponentSimState[builtinBehavior];
part = cnstr();
part.init(this.state.bus, stateFn(this.state), this.view, partInst.params);
} else {
let vis = cmpDesc.visual as PartVisualDefinition;
cmp = new GenericPart(vis);
colOffset = vis.extraColumnOffset || 0;
let vis = partInst.visual as PartVisualDefinition;
part = new GenericPart(vis);
}
this.components.push(cmp);
this.view.appendChild(cmp.element);
if (cmp.defs)
cmp.defs.forEach(d => this.defs.appendChild(d));
this.style.textContent += cmp.style || "";
let rowCol = <BBRowCol>[`${cmpDesc.breadboardStartRow}`, `${colOffset + cmpDesc.breadboardStartColumn}`];
this.parts.push(part);
this.partGroup.appendChild(part.element);
if (part.overElement)
this.partOverGroup.appendChild(part.overElement);
if (part.defs)
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);
cmp.moveToCoord(coord);
part.moveToCoord(coord);
let getCmpClass = (type: string) => `sim-${type}-cmp`;
let cls = getCmpClass(name);
svg.addClass(cmp.element, cls);
svg.addClass(cmp.element, "sim-cmp");
cmp.updateTheme();
cmp.updateState();
return cmp;
let cls = getCmpClass(partInst.name);
svg.addClass(part.element, cls);
svg.addClass(part.element, "sim-cmp");
part.updateTheme();
part.updateState();
return part;
}
public addWire(inst: WireInst): Wire {
return this.wireFactory.addWire(inst.start, inst.end, inst.color, this.useCrocClips);
}
public addAll(basicWiresAndCmpsAndWires: AllocatorResult) {
let {powerWires, components} = basicWiresAndCmpsAndWires;
powerWires.forEach(w => this.addWire(w));
components.forEach((cAndWs, idx) => {
let {component, wires} = cAndWs;
wires.forEach(w => this.addWire(w));
this.addComponent(component);
});
public addAll(allocRes: AllocatorResult) {
allocRes.partsAndWires.forEach(pAndWs => {
let part = pAndWs.part;
if (part)
this.addPart(part)
let wires = pAndWs.wires;
if (wires)
wires.forEach(w => this.addWire(w));
})
}
}
}

View File

@ -80,7 +80,8 @@ namespace pxsim.visuals {
fill: #BBB;
}
.grayed .sim-bb-pin {
fill: #BBB;
fill:none;
stroke: #BBB;
}
.grayed .sim-bb-label {
fill: #BBB;
@ -106,10 +107,10 @@ namespace pxsim.visuals {
}
`
// 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_AND_GAPS = MID_ROWS + MID_ROW_GAPS.length;
const MID_COLS = 30;
const MID_ROW_AND_GAPS = BREADBOARD_MID_ROWS + MID_ROW_GAPS.length;
const BAR_ROWS = 2;
const BAR_COLS = 25;
const POWER_ROWS = BAR_ROWS * 2;
@ -117,14 +118,14 @@ namespace pxsim.visuals {
const BAR_COL_GAPS = [4, 9, 14, 19];
const BAR_COL_AND_GAPS = BAR_COLS + BAR_COL_GAPS.length;
// 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 MID_RATIO = 2.0 / 3.0;
const BAR_RATIO = (1.0 - MID_RATIO) * 0.5;
const MID_HEIGHT = HEIGHT * MID_RATIO;
const BAR_HEIGHT = HEIGHT * BAR_RATIO;
// 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_X = (WIDTH - MID_GRID_WIDTH) / 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;
// Background
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 {
el: SVGElement,
@ -320,12 +325,14 @@ namespace pxsim.visuals {
return null;
return pin;
}
public getCoord(rowCol: BBRowCol): Coord {
let [row, col] = rowCol;
public getCoord(rowCol: BBLoc): Coord {
let {row, col, xOffset, yOffset} = rowCol;
let pin = this.getPin(row, col);
if (!pin)
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() {
@ -370,14 +377,11 @@ namespace pxsim.visuals {
mkChannel(BAR_HEIGHT + MID_HEIGHT, SMALL_CHANNEL_HEIGHT);
//-----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 alphabet = "abcdefghij".split("").reverse();
const getColName = (colIdx: number) => `${colIdx + 1}`;
const getMidRowName = (rowIdx: number) => alphabet[rowIdx];
const getMidGroupName = (rowIdx: number, colIdx: number) => {
let botOrTop = getMidTopOrBot(rowIdx);
let colNm = getColName(colIdx);
let colNm = getColumnName(colIdx);
return `${botOrTop}${colNm}`;
};
const getBarRowName = (rowIdx: number) => rowIdx === 0 ? "-" : "+";
@ -391,13 +395,13 @@ namespace pxsim.visuals {
let midGridRes = mkGrid({
xOffset: MID_GRID_X,
yOffset: MID_GRID_Y,
rowCount: MID_ROWS,
colCount: MID_COLS,
rowCount: BREADBOARD_MID_ROWS,
colCount: BREADBOARD_MID_COLS,
pinDist: PIN_DIST,
mkPin: mkBBPin,
mkHoverPin: mkBBHoverPin,
getRowName: getMidRowName,
getColName: getColName,
getRowName: getRowName,
getColName: getColumnName,
getGroupName: getMidGroupName,
rowIdxsWithGap: MID_ROW_GAPS,
});
@ -414,7 +418,7 @@ namespace pxsim.visuals {
mkPin: mkBBPin,
mkHoverPin: mkBBHoverPin,
getRowName: getBarRowName,
getColName: getColName,
getColName: getColumnName,
getGroupName: getBarGroupName,
colIdxsWithGap: BAR_COL_GAPS,
});
@ -432,7 +436,7 @@ namespace pxsim.visuals {
mkPin: mkBBPin,
mkHoverPin: mkBBHoverPin,
getRowName: getBarRowName,
getColName: getColName,
getColName: getColumnName,
getGroupName: getBarGroupName,
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 => {
let size = PIN_LBL_SIZE;
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 t = mkBBLabel(cx + xOffset, cy + yOffset, size, rotation, txt, group);
return t;
}
//columns
for (let colIdx = 0; colIdx < MID_COLS; colIdx++) {
let colNm = getColName(colIdx);
for (let colIdx = 0; colIdx < BREADBOARD_MID_COLS; colIdx++) {
let colNm = getColumnName(colIdx);
//top
let rowTIdx = 0;
let rowTNm = getMidRowName(rowTIdx);
let rowTNm = getRowName(rowTIdx);
let groupT = getMidGroupName(rowTIdx, colIdx);
let lblT = mkBBLabelAtPin(rowTNm, colNm, 0, -PIN_DIST, colNm, groupT);
this.allLabels.push(lblT);
//bottom
let rowBIdx = MID_ROWS - 1;
let rowBNm = getMidRowName(rowBIdx);
let rowBIdx = BREADBOARD_MID_ROWS - 1;
let rowBNm = getRowName(rowBIdx);
let groupB = getMidGroupName(rowBIdx, colIdx);
let lblB = mkBBLabelAtPin(rowBNm, colNm, 0, +PIN_DIST, colNm, groupB);
this.allLabels.push(lblB);
}
//rows
for (let rowIdx = 0; rowIdx < MID_ROWS; rowIdx++) {
let rowNm = getMidRowName(rowIdx);
for (let rowIdx = 0; rowIdx < BREADBOARD_MID_ROWS; rowIdx++) {
let rowNm = getRowName(rowIdx);
//top
let colTIdx = 0;
let colTNm = getColName(colTIdx);
let colTNm = getColumnName(colTIdx);
let lblT = mkBBLabelAtPin(rowNm, colTNm, -PIN_DIST, 0, rowNm);
this.allLabels.push(lblT);
//top
let colBIdx = MID_COLS - 1;
let colBNm = getColName(colBIdx);
let colBIdx = BREADBOARD_MID_COLS - 1;
let colBNm = getColumnName(colBIdx);
let lblB = mkBBLabelAtPin(rowNm, colBNm, +PIN_DIST, 0, rowNm);
this.allLabels.push(lblB);
}
@ -634,8 +638,8 @@ namespace pxsim.visuals {
return {el: this.bb, y: 0, x: 0, w: WIDTH, h: HEIGHT};
}
public highlightLoc(rowCol: BBRowCol) {
let [row, col] = rowCol;
public highlightLoc(rowCol: BBLoc) {
let {row, col} = rowCol;
let pin = this.rowColToPin[row][col];
let {cx, cy} = pin;
let lbls = this.rowColToLbls[row][col];

View File

@ -92,7 +92,7 @@ namespace pxsim.visuals {
pointer-events:none;
}
`;
export class ButtonPairView implements IBoardComponent<ButtonPairState> {
export class ButtonPairView implements IBoardPart<ButtonPairState> {
public element: SVGElement;
public defs: SVGElement[];
public style = BUTTON_PAIR_STYLE;

View File

@ -5,13 +5,13 @@ namespace pxsim.visuals {
image: partVisual.image,
width: partVisual.width,
height: partVisual.height,
imageUnitDist: partVisual.pinDist,
imageUnitDist: partVisual.pinDistance,
targetUnitDist: PIN_DIST
});
return imgAndSize;
}
export class GenericPart implements IBoardComponent<any> {
export class GenericPart implements IBoardPart<any> {
public style: string = "";
public element: SVGElement;
defs: SVGElement[] = [];
@ -19,11 +19,6 @@ namespace pxsim.visuals {
constructor(partVisual: PartVisualDefinition) {
let imgAndSize = mkGenericPartSVG(partVisual);
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.appendChild(img);
}
@ -33,7 +28,7 @@ namespace pxsim.visuals {
}
//unused
init(bus: EventBus, state: any, svgEl: SVGSVGElement, gpioPins: string[], otherArgs: string[]): void { }
init(bus: EventBus, state: any, svgEl: SVGSVGElement): void { }
updateState(): void { }
updateTheme(): void { }
}

View File

@ -68,7 +68,7 @@ namespace pxsim.visuals {
}
`
export class LedMatrixView implements IBoardComponent<LedMatrixState> {
export class LedMatrixView implements IBoardPart<LedMatrixState> {
private background: SVGElement;
private ledsOuter: SVGElement[];
private leds: SVGElement[];

View File

@ -102,26 +102,17 @@ namespace pxsim.visuals {
});
return { el: img, x: l, y: t, w: w, h: h };
}
export class NeoPixel implements SVGAndSize<SVGCircleElement> {
public el: SVGCircleElement;
public w: number;
public h: number;
public x: number;
public y: number;
public cx: number;
export class NeoPixel {
public el: SVGElement;
public cy: number;
constructor(xy: Coord = [0, 0]) {
let circle = <SVGCircleElement>svg.elt("circle");
let el = <SVGElement>svg.elt("rect");
let r = PIXEL_RADIUS;
let [cx, cy] = xy;
svg.hydrate(circle, { cx: cx, cy: cy, r: r, class: "sim-neopixel" });
this.el = circle;
this.w = r * 2;
this.h = r * 2;
this.x = cx - r;
this.y = cy - r;
this.cx = cx;
let y = cy - r;
svg.hydrate(el, { x: "-50%", y: y, width: "100%", height: r * 2, class: "sim-neopixel" });
this.el = el;
this.cy = cy;
}
@ -200,9 +191,15 @@ namespace pxsim.visuals {
}
};
function gpioPinToPinNumber(gpioPin: string): number {
let pinNumStr = gpioPin.split("P")[1];
let pinNum = Number(pinNumStr) + 7 /*MICROBIT_ID_IO_P0; TODO: don't hardcode this, import enums.d.ts*/;
function digitalPinToPinNumber(gpioPin: string): number {
const MICROBIT_ID_IO_P0 = 7; //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
}
function parseNeoPixelMode(modeStr: string): NeoPixelMode {
@ -222,7 +219,7 @@ namespace pxsim.visuals {
return mode;
}
export class NeoPixelView implements IBoardComponent<NeoPixelState> {
export class NeoPixelView implements IBoardPart<NeoPixelState> {
public style: string = `
.sim-neopixel-canvas {
}
@ -240,6 +237,7 @@ namespace pxsim.visuals {
}
`;
public element: SVGElement;
public overElement: SVGElement;
public defs: SVGElement[];
private state: NeoPixelState;
private canvas: NeoPixelCanvas;
@ -249,22 +247,24 @@ namespace pxsim.visuals {
private pin: number;
private mode: NeoPixelMode;
public init(bus: EventBus, state: NeoPixelState, svgEl: SVGSVGElement, gpioPins: string[], otherArgs: string[]): void {
U.assert(otherArgs.length === 1, "NeoPixels assumes a RGB vs RGBW mode is passed to it");
let modeStr = otherArgs[0];
public init(bus: EventBus, state: NeoPixelState, svgEl: SVGSVGElement, otherParams: Map<string>): void {
U.assert(!!otherParams["mode"], "NeoPixels assumes a RGB vs RGBW mode is passed to it");
U.assert(!!otherParams["pin"], "NeoPixels assumes a pin is passed to it");
let modeStr = otherParams["mode"];
this.mode = parseNeoPixelMode(modeStr);
this.state = state;
this.stripGroup = <SVGGElement>svg.elt("g");
this.element = this.stripGroup;
let pinStr = gpioPins[0];
this.pin = gpioPinToPinNumber(pinStr);
let pinStr = otherParams["pin"];
this.pin = digitalPinToPinNumber(pinStr);
this.lastLocation = [0, 0];
let part = mkNeoPixelPart();
this.part = part;
this.stripGroup.appendChild(part.el);
let canvas = new NeoPixelCanvas(this.pin);
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);
this.updateStripLoc();
}
@ -276,6 +276,7 @@ namespace pxsim.visuals {
}
private updateStripLoc() {
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]);
svg.hydrate(this.part.el, { transform: `translate(${x} ${y})` }); //TODO: update part's l,h, etc.
}

View File

@ -455,7 +455,7 @@ namespace pxsim.visuals {
let wireEls: Wire;
if (withCrocs && end.type == "dalboard") {
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
wireEls = this.drawWireWithCrocs(startLoc, endLoc, color);
} else {