2.1.28, initiation update to PXT v5.28.24 (#54)

This commit is contained in:
Amerlander
2019-12-02 05:58:26 +01:00
committed by Peli de Halleux
parent 38a964516e
commit 5c114a0c57
1261 changed files with 50692 additions and 21604 deletions

View File

@@ -19,6 +19,7 @@ namespace pxsim {
fileSystem: FileSystemState;
// visual
viewHost: visuals.BoardHost;
view: SVGElement;
constructor() {
@@ -92,15 +93,15 @@ namespace pxsim {
switch (msg.type || "") {
case "eventbus":
let ev = <SimulatorEventBusMessage>msg;
const ev = <SimulatorEventBusMessage>msg;
this.bus.queue(ev.id, ev.eventid, ev.value);
break;
case "serial":
let data = (<SimulatorSerialMessage>msg).data || "";
const data = (<SimulatorSerialMessage>msg).data || "";
this.serialState.receiveData(data);
break;
case "radiopacket":
let packet = <SimulatorRadioPacketMessage>msg;
const packet = <SimulatorRadioPacketMessage>msg;
this.radioState.receivePacket(packet);
break;
}
@@ -126,19 +127,20 @@ namespace pxsim {
maxHeight: "100%",
highContrast: msg.highContrast
};
const viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
this.viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
visual: boardDef.visual,
boardDef: boardDef,
highContrast: msg.highContrast
}), opts);
document.body.innerHTML = ""; // clear children
document.body.appendChild(this.view = viewHost.getView());
document.body.appendChild(this.view = this.viewHost.getView());
return Promise.resolve();
}
screenshot(): string {
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
screenshotAsync(width?: number): Promise<ImageData> {
return this.viewHost.screenshotAsync(width);
}
}
@@ -174,4 +176,4 @@ namespace pxsim {
export function board() {
return runtime.board as DalBoard;
}
}
}

View File

@@ -1,92 +0,0 @@
/// <reference path="../node_modules/pxt-core/typings/globals/bluebird/index.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtrunner.d.ts"/>
//HACK: allows instructions.html to access pxtblocks without requiring simulator.html to import blocks as well
if (!(<any>window).pxt) (<any>window).pxt = {};
import pxtrunner = pxt.runner;
import pxtdocs = pxt.docs;
namespace pxsim.instructions {
export function drawInstructions() {
pxsim.visuals.mkBoardView = (opts: pxsim.visuals.BoardViewOptions): pxsim.visuals.BoardView => {
return new visuals.MicrobitBoardSvg({
runtime: runtime,
theme: visuals.randomTheme(),
disableTilt: false,
wireframe: opts.wireframe,
});
}
let getQsVal = parseQueryString();
//project name
let name = getQsVal("name") || "Untitled";
// board def
const boardDef = JSON.parse(getQsVal("board")) as pxsim.BoardDefinition;
//parts list
let parts = (getQsVal("parts") || "").split(" ");
parts.sort();
// parts definitions
let partDefinitions = JSON.parse(getQsVal("partdefs") || "{}") as pxsim.Map<PartDefinition>
//fn args
let fnArgs = JSON.parse((getQsVal("fnArgs") || "{}"));
//project code
let tsCode = getQsVal("code");
let tsPackage = getQsVal("package") || "";
let codeSpinnerDiv = document.getElementById("proj-code-spinner");
let codeContainerDiv = document.getElementById("proj-code-container");
if (tsCode) {
//we use the docs renderer to decompile the code to blocks and render it
//TODO: render the blocks code directly
let md =
`\`\`\`blocks
${tsCode}
\`\`\`
\`\`\`package
${tsPackage}
\`\`\`
`
pxtdocs.requireMarked = function () { return (<any>window).marked; }
pxtrunner.renderMarkdownAsync(codeContainerDiv, md)
.done(function () {
let codeSvg = $("#proj-code-container svg");
if (codeSvg.length > 0) {
//code rendered successfully as blocks
codeSvg.css("width", "inherit");
codeSvg.css("height", "inherit");
//takes the svg out of the wrapper markdown
codeContainerDiv.innerHTML = "";
codeContainerDiv.appendChild(codeSvg[0]);
} else {
//code failed to convert to blocks, display as typescript instead
codeContainerDiv.innerText = tsCode;
}
$(codeContainerDiv).show();
$(codeSpinnerDiv).hide();
});
}
if (name)
$("#proj-title").text(name);
//init runtime
if (!pxsim.initCurrentRuntime)
pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
renderParts({
name,
boardDef,
parts,
partDefinitions,
fnArgs
})
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,203 +0,0 @@
<!doctype html>
<html lang="en" data-framework="typescript">
<head>
<meta charset="utf-8">
<title>Assembly Instructions</title>
<style>
svg {
max-width: 100%;
}
.blocklyText, .ace_editor {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace !important;
}
.blocklyText, .ace_editor {
font-size: 1rem !important;
}
.blocklyTreeLabel {
font-size: 1.25rem !important;
}
.blocklyCheckbox {
fill: #ff3030 !important;
text-shadow: 0px 0px 6px #f00;
font-size: 17pt !important;
}
.ui.card .blocklyPreview {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: calc(100% - 1em);
max-height: calc(100% - 1em);
}
code {
white-space: pre-wrap;
}
code.lang-config, code.lang-package { display:none; }
code.lang-blocks::before,
code.lang-sig::before,
code.lang-block::before,
code.lang-shuffle::before,
code.lang-sim::before,
code.lang-cards::before,
code.lang-namespaces::before,
code.lang-codecard::before {
content: "...";
position: absolute;
top: calc(50% - 0.5em);
left: calc(50% - 5em);
}
code.lang-blocks,
code.lang-sig,
code.lang-block,
code.lang-shuffle,
code.lang-sim,
code.lang-cards,
code.lang-namespaces,
code.lang-codecard {
color: transparent;
}
</style>
<style type="text/css">
@import "/cdn/semantic.css";
@import "/cdn/icons.css";
</style>
<style>
html {
padding: 0;
margin: 0;
}
body {
padding: 0;
margin: 0;
font-family: "Lucida Console", Monaco, monospace;
}
div {
/*undo semantic UI*/
box-sizing: content-box;
line-height: normal;
}
img {
border: 0;
}
/*TODO: Share CSS with main webpage*/
.organization {
position: absolute;
bottom: 2rem;
right: 2rem;
height: 4rem;
}
h1 {
font-size: 2em;
font-weight: normal;
color: rgba(0, 0, 0, 0.87);
font-family: 'Segoe UI', 'Helvetica Neue', Arial, Helvetica, sans-serif;
display: block;
text-align: center;
}
#front-panel .board-svg {
position: absolute;
left: 2rem;
width: 300px;
top: 16rem;
}
#proj-title {
width: 100%;
font-size: 70px;
margin-top: 20px;
}
#proj-code {
width: 300px;
height: 400px;
position: absolute;
right: 2rem;
top: 16rem;
}
#proj-code-container {
width: 100%;
height: 100%;
font-size: 4px;
overflow: hidden;
display: none;
}
#proj-code-spinner {
width: 100%;
}
.back-panel svg {
position: relative;
margin: 0 auto;
left: inherit;
bottom: -7px;
}
</style>
</head>
<body>
<div id='loading' class="ui active inverted dimmer">
<div class="ui large loader"></div>
</div>
<!-- start Mixpanel --><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><!-- end Mixpanel -->
<script>
// This line gets patched up by the cloud
var pxtConfig = null;
</script>
<script type="text/javascript" src="/cdn/lzma/lzma_worker-min.js"></script>
<script type="text/javascript" src="/cdn/marked/marked.min.js"></script>
<script type="text/javascript" src="/cdn/jquery.js"></script>
<script type="text/javascript" src="/cdn/typescript.js"></script>
<script type="text/javascript" src="/cdn/blockly/blockly_compressed.js"></script>
<script type="text/javascript" src="/cdn/blockly/blocks_compressed.js"></script>
<script type="text/javascript" src="/cdn/blockly/msg/js/en.js"></script>
<script type="text/javascript" src="/cdn/pxtlib.js"></script>
<script type="text/javascript" src="/cdn/pxtblocks.js"></script>
<script type="text/javascript" src="/cdn/pxtsim.js"></script>
<script type="text/javascript" src="/cdn/pxtrunner.js"></script>
<script type="text/javascript" src="/cdn/semantic.js"></script>
<script type="text/javascript" src="/embed.js"></script>
<script type="text/javascript" src="/sim/sim.js"></script>
<script type="text/javascript">
(function () {
ksRunnerReady(function() {
var orgLogo = pxt.appTarget.appTheme.organizationLogo;
if (orgLogo)
$('#front-panel').append(
$('<img/>').attr('class', 'organization').attr('src', orgLogo)
);
var loading = document.getElementById('loading');
pxsim.instructions.drawInstructions();
$(loading).hide();
});
})();
</script>
<div id="front-panel" class="instr-panel">
<h1 id="proj-title"></h1>
<!--TODO: extract real code snapshot from PXT -->
<div id="proj-code">
<i id="proj-code-spinner" class="spinner loading icon"></i>
<div id="proj-code-container">
</div>
</div>
</div>
</body>
</html>

View File

@@ -1,37 +1,53 @@
namespace pxsim.input {
export function onGesture(gesture: number, handler: RefAction) {
function accForGesture(gesture: number) {
let b = board().accelerometerState;
b.accelerometer.activate();
if (gesture == 11 && !b.useShake) { // SAKE
if (gesture == 11 && !b.useShake) { // SHAKE
b.useShake = true;
runtime.queueDisplayUpdate();
}
return b;
}
export function onGesture(gesture: number, handler: RefAction) {
const b = accForGesture(gesture);
pxtcore.registerWithDal(DAL.MICROBIT_ID_GESTURE, gesture, handler);
}
export function isGesture(gesture: number): boolean {
const b = accForGesture(gesture);
return b.accelerometer.getGesture() == gesture;
}
export function acceleration(dimension: number): number {
let b = board().accelerometerState;
let acc = b.accelerometer;
acc.activate();
switch (dimension) {
case 0: return acc.getX();
case 1: return acc.getY();
case 2: return acc.getZ();
default: return Math.floor(Math.sqrt(acc.instantaneousAccelerationSquared()));
case 0:
acc.activate(AccelerometerFlag.X);
return acc.getX();
case 1:
acc.activate(AccelerometerFlag.Y);
return acc.getY();
case 2:
acc.activate(AccelerometerFlag.Z);
return acc.getZ();
default:
acc.activate();
return Math.floor(Math.sqrt(acc.instantaneousAccelerationSquared()));
}
}
export function rotation(kind: number): number {
let b = board().accelerometerState;
let acc = b.accelerometer;
const b = board().accelerometerState;
const acc = b.accelerometer;
acc.activate();
let x = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let y = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let z = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
const x = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
const y = acc.getY(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
const z = acc.getZ(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
let roll = Math.atan2(y, z);
let pitch = Math.atan(-x / (y * Math.sin(roll) + z * Math.cos(roll)));
const roll = Math.atan2(y, z);
const pitch = Math.atan(-x / (y * Math.sin(roll) + z * Math.cos(roll)));
let r = 0;
switch (kind) {
@@ -99,6 +115,12 @@ namespace pxsim {
NORTH_EAST_DOWN
}
export enum AccelerometerFlag {
X = 1,
Y = 2,
Z = 4
}
export class Accelerometer {
private sigma: number = 0; // the number of ticks that the instantaneous gesture has been stable.
private lastGesture: number = 0; // the last, stable gesture recorded.
@@ -110,6 +132,7 @@ namespace pxsim {
private id: number;
public isActive = false;
public sampleRange = 2;
public flags: AccelerometerFlag = 0;
constructor(public runtime: Runtime) {
this.id = DAL.MICROBIT_ID_ACCELEROMETER;
@@ -120,11 +143,13 @@ namespace pxsim {
this.sampleRange = Math.max(1, Math.min(8, range));
}
public activate() {
public activate(flags?: AccelerometerFlag) {
if (!this.isActive) {
this.isActive = true;
this.runtime.queueDisplayUpdate();
}
if (flags)
this.flags |= flags;
}
/**
@@ -199,9 +224,8 @@ namespace pxsim {
if (force < sq(DAL.MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE))
return DAL.MICROBIT_ACCELEROMETER_EVT_FREEFALL;
// TODO: fix this
//if (force > sq(DAL.MICROBIT_ACCELEROMETER_3G_TOLERANCE))
// return DAL.MICROBIT_ACCELEROMETER_EVT_3G;
if (force > sq(DAL.MICROBIT_ACCELEROMETER_3G_TOLERANCE))
return DAL.MICROBIT_ACCELEROMETER_EVT_3G;
if (force > sq(DAL.MICROBIT_ACCELEROMETER_6G_TOLERANCE))
return DAL.MICROBIT_ACCELEROMETER_EVT_6G;
@@ -223,10 +247,10 @@ namespace pxsim {
return DAL.MICROBIT_ACCELEROMETER_EVT_TILT_UP;
if (this.getZ() < (-1000 + DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_DOWN;
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_UP;
if (this.getZ() > (1000 - DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_UP;
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_DOWN;
return 0;
}
@@ -236,22 +260,28 @@ namespace pxsim {
let g = this.instantaneousPosture();
// Perform some low pass filtering to reduce jitter from any detected effects
if (g == this.currentGesture) {
if (this.sigma < DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING)
this.sigma++;
}
else {
if (g != this.currentGesture) {
this.currentGesture = g;
this.sigma = 0;
} else if (this.sigma < DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING) {
++this.sigma;
}
// If we've reached threshold, update our record and raise the relevant event...
if (this.currentGesture != this.lastGesture && this.sigma >= DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING) {
this.lastGesture = this.currentGesture;
board().bus.queue(DAL.MICROBIT_ID_GESTURE, this.lastGesture);
if (this.currentGesture !== this.lastGesture && this.sigma >= DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING) {
this.enqueueCurrentGesture();
}
}
forceGesture(gesture: number) {
this.currentGesture = gesture;
this.enqueueCurrentGesture();
}
private enqueueCurrentGesture() {
this.lastGesture = this.currentGesture;
board().bus.queue(DAL.MICROBIT_ID_GESTURE, this.lastGesture);
}
/**
* Reads the X axis value of the latest update from the accelerometer.
* @param system The coordinate system to use. By default, a simple cartesian system is provided.
@@ -363,6 +393,10 @@ namespace pxsim {
return this.roll;
}
getGesture(): number {
return this.lastGesture;
}
/**
* Recalculate roll and pitch values for the current sample.
* We only do this at most once per sample, as the necessary trigonemteric functions are rather
@@ -384,7 +418,11 @@ namespace pxsim {
useShake = false;
constructor(runtime: Runtime) {
this.accelerometer = new Accelerometer(runtime);
this.accelerometer = new Accelerometer(runtime);
}
shake() {
this.accelerometer.forceGesture(DAL.MICROBIT_ACCELEROMETER_EVT_SHAKE); // SHAKE == 11
}
}
}

201
sim/state/buttonpairsim.ts Normal file
View File

@@ -0,0 +1,201 @@
namespace pxsim.visuals {
export function mkBtnSvg(xy: Coord): SVGAndSize<SVGGElement> {
let [innerCls, outerCls] = ["sim-button", "sim-button-outer"];
const tabSize = PIN_DIST / 2.5;
const pegR = PIN_DIST / 5;
const btnR = PIN_DIST * .8;
const pegMargin = PIN_DIST / 8;
const plateR = PIN_DIST / 12;
const pegOffset = pegMargin + pegR;
let [x, y] = xy;
const left = x - tabSize / 2;
const top = y - tabSize / 2;
const plateH = 3 * PIN_DIST - tabSize;
const plateW = 2 * PIN_DIST + tabSize;
const plateL = left;
const plateT = top + tabSize;
const btnCX = plateL + plateW / 2;
const btnCY = plateT + plateH / 2;
let btng = <SVGGElement>svg.elt("g");
//tabs
const mkTab = (x: number, y: number) => {
svg.child(btng, "rect", { class: "sim-button-tab", x: x, y: y, width: tabSize, height: tabSize})
}
mkTab(left, top);
mkTab(left + 2 * PIN_DIST, top);
mkTab(left, top + 3 * PIN_DIST);
mkTab(left + 2 * PIN_DIST, top + 3 * PIN_DIST);
//plate
svg.child(btng, "rect", { class: outerCls, x: plateL, y: plateT, rx: plateR, ry: plateR, width: plateW, height: plateH });
//pegs
const mkPeg = (x: number, y: number) => {
svg.child(btng, "circle", { class: "sim-button-nut", cx: x, cy: y, r: pegR });
}
mkPeg(plateL + pegOffset, plateT + pegOffset)
mkPeg(plateL + plateW - pegOffset, plateT + pegOffset)
mkPeg(plateL + pegOffset, plateT + plateH - pegOffset)
mkPeg(plateL + plateW - pegOffset, plateT + plateH - pegOffset)
//inner btn
let innerBtn = svg.child(btng, "circle", { class: innerCls, cx: btnCX, cy: btnCY, r: btnR });
//return
return { el: btng, y: top, x: left, w: plateW, h: plateH + 2 * tabSize };
}
export const BUTTON_PAIR_STYLE = `
.sim-button {
pointer-events: none;
fill: #000;
}
.sim-button-outer:active ~ .sim-button,
.sim-button-virtual:active {
fill: #FFA500;
}
.sim-button-outer {
cursor: pointer;
fill: #979797;
}
.sim-button-outer:hover {
stroke:gray;
stroke-width: ${PIN_DIST / 5}px;
}
.sim-button-nut {
fill:#000;
pointer-events:none;
}
.sim-button-nut:hover {
stroke:${PIN_DIST / 15}px solid #704A4A;
}
.sim-button-tab {
fill:#FFF;
pointer-events:none;
}
.sim-button-virtual {
cursor: pointer;
fill: rgba(255, 255, 255, 0.6);
stroke: rgba(255, 255, 255, 1);
stroke-width: ${PIN_DIST / 5}px;
}
.sim-button-virtual:hover {
stroke: rgba(128, 128, 128, 1);
}
.sim-text-virtual {
fill: #000;
pointer-events:none;
}
`;
export class ButtonPairView implements IBoardPart<ButtonPairState> {
public element: SVGElement;
public defs: SVGElement[];
public style = BUTTON_PAIR_STYLE;
private state: ButtonPairState;
private bus: EventBus;
private aBtn: SVGGElement;
private bBtn: SVGGElement;
private abBtn: SVGGElement;
public init(bus: EventBus, state: ButtonPairState) {
this.state = state;
this.bus = bus;
this.defs = [];
this.element = this.mkBtns();
this.updateState();
this.attachEvents();
}
public moveToCoord(xy: Coord) {
let btnWidth = PIN_DIST * 3;
let [x, y] = xy;
translateEl(this.aBtn, [x, y])
translateEl(this.bBtn, [x + btnWidth, y])
translateEl(this.abBtn, [x + PIN_DIST * 1.5, y + PIN_DIST * 4])
}
public updateState() {
let stateBtns = [this.state.aBtn, this.state.bBtn, this.state.abBtn];
let svgBtns = [this.aBtn, this.bBtn, this.abBtn];
if (this.state.usesButtonAB && this.abBtn.style.visibility != "visible") {
this.abBtn.style.visibility = "visible";
}
}
public updateTheme() {}
private mkBtns() {
this.aBtn = mkBtnSvg([0, 0]).el;
this.bBtn = mkBtnSvg([0, 0]).el;
const mkVirtualBtn = () => {
const numPins = 2;
const w = PIN_DIST * 2.8;
const offset = (w - (numPins * PIN_DIST)) / 2;
const corner = PIN_DIST / 2;
const cx = 0 - offset + w / 2;
const cy = cx;
const txtSize = PIN_DIST * 1.3;
const x = -offset;
const y = -offset;
const txtXOff = PIN_DIST / 7;
const txtYOff = PIN_DIST / 10;
let btng = <SVGGElement>svg.elt("g");
let btn = svg.child(btng, "rect", { class: "sim-button-virtual", x: x, y: y, rx: corner, ry: corner, width: w, height: w});
let btnTxt = mkTxt(cx + txtXOff, cy + txtYOff, txtSize, 0, "A+B");
U.addClass(btnTxt, "sim-text")
U.addClass(btnTxt, "sim-text-virtual");
btng.appendChild(btnTxt);
return btng;
}
this.abBtn = mkVirtualBtn();
this.abBtn.style.visibility = "hidden";
let el = svg.elt("g");
U.addClass(el, "sim-buttonpair")
el.appendChild(this.aBtn);
el.appendChild(this.bBtn);
el.appendChild(this.abBtn);
return el;
}
private attachEvents() {
let btnStates = [this.state.aBtn, this.state.bBtn];
let btnSvgs = [this.aBtn, this.bBtn];
btnSvgs.forEach((btn, index) => {
pxsim.pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
btnStates[index].pressed = true;
}))
btn.addEventListener(pointerEvents.leave, ev => {
btnStates[index].pressed = false;
})
btn.addEventListener(pointerEvents.up, ev => {
btnStates[index].pressed = false;
this.bus.queue(btnStates[index].id, this.state.props.BUTTON_EVT_UP);
this.bus.queue(btnStates[index].id, this.state.props.BUTTON_EVT_CLICK);
})
})
let updateBtns = (s: boolean) => {
btnStates.forEach(b => b.pressed = s)
};
pxsim.pointerEvents.down.forEach(evid => this.abBtn.addEventListener(evid, ev => {
updateBtns(true);
}));
this.abBtn.addEventListener(pointerEvents.leave, ev => {
updateBtns(false);
})
this.abBtn.addEventListener(pointerEvents.up, ev => {
updateBtns(false);
this.bus.queue(this.state.abBtn.id, this.state.props.BUTTON_EVT_UP);
this.bus.queue(this.state.abBtn.id, this.state.props.BUTTON_EVT_CLICK);
})
}
}
}

View File

@@ -3,6 +3,7 @@ namespace pxsim.input {
let pin = getPin(pinId);
if (!pin) return;
pin.isTouched();
runtime.queueDisplayUpdate();
pxtcore.registerWithDal(pin.id, DAL.MICROBIT_BUTTON_EVT_CLICK, handler);
}
@@ -10,6 +11,7 @@ namespace pxsim.input {
let pin = getPin(pinId);
if (!pin) return;
pin.isTouched();
runtime.queueDisplayUpdate();
pxtcore.registerWithDal(pin.id, DAL.MICROBIT_BUTTON_EVT_UP, handler);
}
@@ -29,7 +31,7 @@ namespace pxsim {
namespace pxsim.pins {
export function digitalReadPin(pinId: number): number {
let pin = getPin(pinId);
if (!pin) return;
if (!pin) return -1;
pin.mode = PinFlags.Digital | PinFlags.Input;
return pin.value > 100 ? 1 : 0;
}
@@ -50,7 +52,7 @@ namespace pxsim.pins {
export function analogReadPin(pinId: number): number {
let pin = getPin(pinId);
if (!pin) return;
if (!pin) return -1;
pin.mode = PinFlags.Analog | PinFlags.Input;
return pin.value || 0;
}
@@ -76,7 +78,7 @@ namespace pxsim.pins {
if (!pin) return;
analogSetPeriod(pinId, 20000);
pin.servoAngle = Math.max(0, Math.min(180, value));
pin.servoAngle = value;
}
export function servoSetPulse(pinId: number, micros: number) {

View File

@@ -0,0 +1,89 @@
namespace pxsim {
export enum PinFlags {
Unused = 0,
Digital = 0x0001,
Analog = 0x0002,
Input = 0x0004,
Output = 0x0008,
Touch = 0x0010
}
export class Pin {
constructor(public id: number) { }
touched = false;
value = 0;
period = 0;
servoAngle = 0;
mode = PinFlags.Unused;
pitch = false;
pull = 0; // PullDown
digitalReadPin(): number {
this.mode = PinFlags.Digital | PinFlags.Input;
return this.value > 100 ? 1 : 0;
}
digitalWritePin(value: number) {
this.mode = PinFlags.Digital | PinFlags.Output;
this.value = value > 0 ? 200 : 0;
runtime.queueDisplayUpdate();
}
setPull(pull: number) {
this.pull = pull;
}
analogReadPin(): number {
this.mode = PinFlags.Analog | PinFlags.Input;
return this.value || 0;
}
analogWritePin(value: number) {
value = value >> 0;
this.mode = PinFlags.Analog | PinFlags.Output;
this.value = Math.max(0, Math.min(1023, value));
runtime.queueDisplayUpdate();
}
analogSetPeriod(micros: number) {
micros = micros >> 0;
this.mode = PinFlags.Analog | PinFlags.Output;
this.period = micros;
runtime.queueDisplayUpdate();
}
servoWritePin(value: number) {
value = value >> 0;
this.analogSetPeriod(20000);
this.servoAngle = Math.max(0, Math.min(180, value));
runtime.queueDisplayUpdate();
}
servoSetPulse(pinId: number, micros: number) {
// TODO
}
isTouched(): boolean {
this.mode = PinFlags.Touch | PinFlags.Analog | PinFlags.Input;
return this.touched;
}
}
export interface EdgeConnectorProps {
pins: number[];
servos?: { [name: string]: number; }
}
export class EdgeConnectorState {
pins: Pin[];
constructor(public props: EdgeConnectorProps) {
this.pins = props.pins.map(id => id != undefined ? new Pin(id) : null);
}
public getPin(id: number) {
return this.pins.filter(p => p && p.id == id)[0] || null
}
}
}

View File

@@ -15,4 +15,10 @@ namespace pxsim.files {
const b = board();
b.fileSystem.remove(filename);
}
export function readToSerial(filename: string) {
const b = board();
let f = b.fileSystem.files[filename];
if (f)
b.serialState.writeSerial(f);
}
}

View File

@@ -28,17 +28,24 @@ namespace pxsim {
this.data = data;
}
public print() {
// console.debug(`Image id:${this.id} refs:${this.refcnt} size:${this.width}x${Image.height}`)
console.debug(`Image id:${this.id} size:${this.width}x${Image.height}`)
}
public get(x: number, y: number): number {
x = x >> 0;
y = y >> 0;
if (x < 0 || x >= this.width || y < 0 || y >= 5) return 0;
return this.data[y * this.width + x];
}
public set(x: number, y: number, v: number) {
x = x >> 0;
y = y >> 0;
if (x < 0 || x >= this.width || y < 0 || y >= 5) return;
this.data[y * this.width + x] = Math.max(0, Math.min(255, v));
}
public copyTo(xSrcIndex: number, length: number, target: Image, xTargetIndex: number): void {
xSrcIndex = xSrcIndex >> 0;
length = length >> 0;
xTargetIndex = xTargetIndex >> 0;
for (let x = 0; x < length; x++) {
for (let y = 0; y < 5; y++) {
let value = this.get(xSrcIndex + x, y);
@@ -47,12 +54,14 @@ namespace pxsim {
}
}
public shiftLeft(cols: number) {
cols = cols >> 0;
for (let x = 0; x < this.width; ++x)
for (let y = 0; y < 5; ++y)
this.set(x, y, x < this.width - cols ? this.get(x + cols, y) : 0);
}
public shiftRight(cols: number) {
cols = cols >> 0;
for (let x = this.width - 1; x >= 0; --x)
for (let y = 0; y < 5; ++y)
this.set(x, y, x >= cols ? this.get(x - cols, y) : 0);
@@ -65,12 +74,13 @@ namespace pxsim {
}
export function createInternalImage(width: number): Image {
width = width >> 0;
let img = createImage(width)
pxsim.noLeakTracking(img)
return img
}
export function createImage(width: number): Image {
width = width >> 0;
return new Image(width, new Array(width * 5));
}
@@ -131,9 +141,12 @@ namespace pxsim.images {
namespace pxsim.ImageMethods {
export function showImage(leds: Image, offset: number, interval: number) {
pxtrt.nullCheck(leds)
offset = offset >> 0;
interval = interval >> 0;
let cb = getResume();
let first = true;
leds = clampPixelBrightness(leds);
board().ledMatrixState.animationQ.enqueue({
interval,
frame: () => {
@@ -150,7 +163,9 @@ namespace pxsim.ImageMethods {
export function plotImage(leds: Image, offset: number): void {
pxtrt.nullCheck(leds)
offset = offset >> 0;
leds = clampPixelBrightness(leds);
board().ledMatrixState.animationQ.enqueue({
interval: 0,
frame: () => {
@@ -205,12 +220,15 @@ namespace pxsim.ImageMethods {
export function scrollImage(leds: Image, stride: number, interval: number): void {
pxtrt.nullCheck(leds)
stride = stride >> 0;
interval = interval >> 0;
if (stride == 0) stride = 1;
let cb = getResume();
let off = stride > 0 ? 0 : leds.width - 1;
let display = board().ledMatrixState.image;
leds = clampPixelBrightness(leds);
board().ledMatrixState.animationQ.enqueue({
interval: interval,
frame: () => {
@@ -230,10 +248,27 @@ namespace pxsim.ImageMethods {
whenDone: cb
})
}
function clampPixelBrightness(img: Image): Image {
let res = img;
if (led.displayMode() === DisplayMode.greyscale && led.brightness() < 0xff) {
res = new Image(img.width, img.data);
const b = led.brightness();
for (let x = 0; x < res.width; ++x) {
for (let y = 0; y < 5; ++y) {
if (pixelBrightness(res, x, y) > b) {
setPixelBrightness(res, x, y, b);
}
}
}
}
return res;
}
}
namespace pxsim.basic {
export function showNumber(x: number, interval: number) {
interval = interval >> 0;
if (interval <= 0)
interval = 1;
let leds = createImageFromString(x.toString());
@@ -242,19 +277,21 @@ namespace pxsim.basic {
}
export function showString(s: string, interval: number) {
interval = interval >> 0;
if (interval <= 0)
interval = 1;
if (s.length == 0) {
clearScreen();
pause(interval * 5);
} else if (s.length > 1) {
ImageMethods.scrollImage(createImageFromString(s + " "), 1, interval);
} else {
if (s.length == 1) showLeds(createImageFromString(s), 0);
else ImageMethods.scrollImage(createImageFromString(s + " "), 1, interval);
showLeds(createImageFromString(s), interval * 5);
}
}
export function showLeds(leds: Image, delay: number): void {
showAnimation(leds, delay);
export function showLeds(leds: Image, interval: number): void {
showAnimation(leds, interval);
}
export function clearScreen() {
@@ -279,9 +316,10 @@ namespace pxsim.led {
export function plotBrightness(x: number, y: number, brightness: number) {
const state = board().ledMatrixState;
brightness = Math.max(0, Math.min(0xff, brightness));
brightness = brightness >> 0;
brightness = Math.max(0, Math.min(led.brightness(), brightness));
if (brightness != 0 && brightness != 0xff && state.displayMode != DisplayMode.greyscale)
state.displayMode = DisplayMode.greyscale;
state.displayMode = DisplayMode.greyscale;
state.image.set(x, y, brightness);
runtime.queueDisplayUpdate()
}
@@ -300,6 +338,7 @@ namespace pxsim.led {
}
export function setBrightness(value: number): void {
value = value >> 0;
board().ledMatrixState.brigthness = Math.max(0, Math.min(255, value));
runtime.queueDisplayUpdate()
}

83
sim/state/microservo.ts Normal file
View File

@@ -0,0 +1,83 @@
namespace pxsim.visuals {
function createMicroServoElement() {
return svg.parseString(`
<svg xmlns="http://www.w3.org/2000/svg" id="svg2" width="112.188" height="299.674">
<g id="layer1" stroke-linecap="round" stroke-linejoin="round" transform="scale(0.8)">
<path id="path8212" fill="#0061ff" stroke-width="6.6" d="M.378 44.61v255.064h112.188V44.61H.378z"/>
<path id="crankbase" fill="#00f" stroke-width="6.6" d="M56.57 88.047C25.328 88.047 0 113.373 0 144.615c.02 22.352 11.807 42.596 32.238 51.66.03 3.318.095 5.24.088 7.938 0 13.947 11.307 25.254 25.254 25.254 13.947 0 25.254-11.307 25.254-25.254-.006-2.986-.415-5.442-.32-8.746 19.487-9.45 30.606-29.195 30.625-50.852 0-31.24-25.33-56.568-56.57-56.568z"/>
<path id="lowertip" fill="#00a2ff" stroke-width="2" d="M.476 260.78v38.894h53.82v-10.486a6.82 6.566 0 0 1-4.545-6.182 6.82 6.566 0 0 1 6.82-6.566 6.82 6.566 0 0 1 6.82 6.566 6.82 6.566 0 0 1-4.545 6.182v10.486h53.82V260.78H.475z"/>
<path id="uppertip" fill="#00a2ff" stroke-width="2" d="M112.566 83.503V44.61h-53.82v10.487a6.82 6.566 0 0 1 4.544 6.18 6.82 6.566 0 0 1-6.818 6.568 6.82 6.566 0 0 1-6.82-6.567 6.82 6.566 0 0 1 4.546-6.18V44.61H.378v38.893h112.188z"/>
<path id="VCC" fill="red" stroke-width="2" d="M53.72 21.93h5.504v22.627H53.72z"/>
<path id="LOGIC" fill="#fc0" stroke-width="2" d="M47.3 21.93h5.503v22.627H47.3z"/>
<path id="GND" fill="#a02c2c" stroke-width="2" d="M60.14 21.93h5.505v22.627H60.14z"/>
<path id="connector" fill="#111" stroke-width="2" d="M45.064 0a1.488 1.488 0 0 0-1.488 1.488v24.5a1.488 1.488 0 0 0 1.488 1.487h22.71a1.488 1.488 0 0 0 1.49-1.488v-24.5A1.488 1.488 0 0 0 67.774 0h-22.71z"/>
<g id="crank" transform="translate(0 -752.688)">
<path id="arm" fill="#ececec" stroke="#000" stroke-width="1.372" d="M47.767 880.88c-4.447 1.162-8.412 8.278-8.412 18.492s3.77 18.312 8.412 18.494c8.024.314 78.496 5.06 78.51-16.952.012-22.013-74.377-21.117-78.51-20.035z"/>
<circle id="path8216" cx="56.661" cy="899.475" r="8.972" fill="gray" stroke-width="2"/>
</g>
</g>
</svg>
`).firstElementChild as SVGGElement;
}
export function mkMicroServoPart(xy: Coord = [0, 0]): SVGElAndSize {
return { el: createMicroServoElement(), x: xy[0], y: xy[1], w: 112.188, h: 299.674 };
}
export class MicroServoView implements IBoardPart<EdgeConnectorState> {
public style: string = "";
public overElement: SVGElement = undefined;
public element: SVGElement;
public defs: SVGElement[] = [];
public state: EdgeConnectorState;
public bus: EventBus;
private currentAngle = 0;
private targetAngle = 0;
private lastAngleTime = 0;
private pin: number;
private crankEl: SVGGElement;
private crankTransform: string;
public init(bus: EventBus, state: EdgeConnectorState, svgEl: SVGSVGElement, otherParams: Map<string>) {
this.state = state;
this.pin = this.state.props.servos[
pxsim.readPin(otherParams["name"] || otherParams["pin"])
];
this.bus = bus;
this.defs = [];
this.initDom();
this.updateState();
}
initDom() {
this.element = createMicroServoElement();
this.crankEl = this.element.querySelector("#crank") as SVGGElement;
this.crankTransform = this.crankEl.getAttribute("transform");
}
moveToCoord(xy: visuals.Coord): void {
let [x, y] = xy;
translateEl(this.element, [x, y])
}
updateState(): void {
this.targetAngle = 180.0 - this.state.getPin(this.pin).servoAngle;
if (this.targetAngle != this.currentAngle) {
const now = U.now();
const cx = 56.661;
const cy = 899.475;
const speed = 300; // 0.1s/60 degree
const dt = Math.min(now - this.lastAngleTime, 50) / 1000;
const delta = this.targetAngle - this.currentAngle;
this.currentAngle += Math.min(Math.abs(delta), speed * dt) * (delta > 0 ? 1 : -1);
this.crankEl.setAttribute("transform", this.crankTransform
+ ` rotate(${this.currentAngle}, ${cx}, ${cy})`)
this.lastAngleTime = now;
setTimeout(() => runtime.updateDisplay(), 20);
}
}
updateTheme(): void {
}
}
}

6
sim/state/midi.ts Normal file
View File

@@ -0,0 +1,6 @@
namespace pxsim.control {
export function __midiSend(data: RefBuffer) {
const b = board();
pxsim.AudioContextManager.sendMidiMessage(data);
}
}

View File

@@ -35,8 +35,13 @@ namespace pxsim.basic {
namespace pxsim.control {
export var inBackground = thread.runInBackground;
export function createBuffer(sz: number) {
return pxsim.BufferMethods.createBuffer(sz)
}
export function reset() {
U.userError("reset not implemented in simulator yet")
const cb = getResume();
pxsim.runtime.restart();
}
export function waitMicros(micros: number) {
@@ -58,6 +63,13 @@ namespace pxsim.control {
}
export function onEvent(id: number, evid: number, handler: RefAction) {
if (id == DAL.MICROBIT_ID_BUTTON_AB) {
const b = board().buttonPairState;
if (!b.usesButtonAB) {
b.usesButtonAB = true;
runtime.queueDisplayUpdate();
}
}
pxtcore.registerWithDal(id, evid, handler)
}
@@ -65,6 +77,14 @@ namespace pxsim.control {
// TODO mode?
board().bus.queue(id, evid)
}
export function eventTimestamp() {
return board().bus.getLastEventTime()
}
export function eventValue() {
return board().bus.getLastEventValue()
}
}
namespace pxsim.pxtcore {
@@ -78,7 +98,12 @@ namespace pxsim.input {
return runtime.runningTime();
}
export function calibrate() {
export function runningTimeMicros(): number {
return runtime.runningTimeUs();
}
export function calibrateCompass() {
// device calibrates...
}
}
@@ -106,6 +131,18 @@ namespace pxsim.pins {
return 0;
}
export function spiFrequency(f: number): void {
// TODO
}
export function spiFormat(bits: number, mode: number): void {
// TODO
}
export function spiPins(mosi: number, miso: number, sck: number) {
// TODO
}
export function i2cReadBuffer(address: number, size: number, repeat?: boolean): RefBuffer {
// fake reading zeros
return createBuffer(size)
@@ -137,7 +174,7 @@ namespace pxsim.devices {
export function onSignalStrengthChanged(action: number) {
// TODO
}
export function signalStrength() : number {
export function signalStrength(): number {
// TODO
return 0;
}
@@ -168,25 +205,34 @@ namespace pxsim.bluetooth {
export function startUartService(): void {
// TODO
}
export function uartWrite(s : string): void {
export function uartWriteString(s: string): void {
serial.writeString(s)
}
export function uartWriteBuffer(b: RefBuffer): void {
serial.writeBuffer(b);
}
export function uartReadBuffer(): RefBuffer {
return pins.createBuffer(0);
}
export function uartReadUntil(del: string): string {
return serial.readUntil(del);
}
export function onDataReceived(delimiters: string, handler: RefAction) {
export function onUartDataReceived(delimiters: string, handler: RefAction) {
let b = board();
b.bus.listen(DAL.MICROBIT_ID_BLE_UART, DAL.MICROBIT_UART_S_EVT_DELIM_MATCH, handler);
}
export function onBluetoothConnected(a : RefAction) {
export function onBluetoothConnected(a: RefAction) {
// TODO
}
export function onBluetoothDisconnected(a : RefAction) {
export function onBluetoothDisconnected(a: RefAction) {
// TODO
}
export function advertiseUrl(url: string, power: number, connectable: boolean) { }
export function advertiseUidBuffer(nsAndInstance: Buffer, power: number, connectable: boolean) { }
export function advertiseUidBuffer(nsAndInstance: RefBuffer, power: number, connectable: boolean) { }
export function stopAdvertising() { }
export function setTransmitPower(power: number) {}
export function setTransmitPower(power: number) { }
}

View File

@@ -1,11 +1,11 @@
namespace pxsim {
export function sendBufferAsm(buffer: Buffer, pin: DigitalPin) {
export function sendBufferAsm(buffer: RefBuffer, pin: DigitalPin) {
let b = board();
if (b) {
let np = b.neopixelState;
if (np) {
let buf = <Uint8Array>(<any>buffer).data;
np.updateBuffer(buf, pin);
let buf = buffer.data;
np.updateBuffer(buf as any, pin); // TODO this is wrong
runtime.queueDisplayUpdate();
}
}

View File

@@ -6,6 +6,11 @@ namespace pxsim {
time: number;
}
// Extends interface in pxt-core
export interface SimulatorRadioPacketPayload {
bufferData?: Uint8Array;
}
export class RadioDatagram {
datagram: PacketBuffer[] = [];
lastReceived: PacketBuffer = RadioDatagram.defaultPacket();
@@ -24,8 +29,9 @@ namespace pxsim {
const b = board();
Runtime.postMessage(<SimulatorRadioPacketMessage>{
type: "radiopacket",
rssi: 70, // Not yet supported
serial: b.radioState.bus.transmitSerialNumber ? pxsim.control.deviceSerialNumber() : 0,
broadcast: true,
rssi: -42, // -42 is the strongest signal
serial: b.radioState.transmitSerialNumber ? pxsim.control.deviceSerialNumber() : 0,
time: new Date().getTime(),
payload
})
@@ -42,22 +48,31 @@ namespace pxsim {
rssi: -1,
serial: 0,
time: 0,
payload: { type: -1, groupId: 0 }
payload: { type: -1, groupId: 0, bufferData: new Uint8Array(0) }
};
}
}
export class RadioBus {
// uint8_t radioDefaultGroup = MICROBIT_RADIO_DEFAULT_GROUP;
export class RadioState {
power = 0;
transmitSerialNumber = false;
datagram: RadioDatagram;
groupId: number;
band: number;
constructor(private runtime: Runtime) {
constructor(runtime: Runtime) {
this.datagram = new RadioDatagram(runtime);
this.power = 6; // default value
this.groupId = 0;
this.band = 7; // https://github.com/lancaster-university/microbit-dal/blob/master/inc/core/MicroBitConfig.h#L320
}
public setGroup(id: number) {
this.groupId = id & 0xff; // byte only
}
setTransmitPower(power: number) {
power = power | 0;
this.power = Math.max(0, Math.min(7, power));
}
@@ -65,54 +80,33 @@ namespace pxsim {
this.transmitSerialNumber = !!sn;
}
broadcast(msg: number, groupId: number) {
setFrequencyBand(band: number) {
band = band | 0;
if (band < 0 || band > 83) return;
this.band = band;
}
raiseEvent(id: number, eventid: number) {
Runtime.postMessage(<SimulatorEventBusMessage>{
type: "eventbus",
id: DAL.MES_BROADCAST_GENERAL_ID,
eventid: msg,
broadcast: true,
id,
eventid,
power: this.power,
group: groupId
group: this.groupId
})
}
}
export class RadioState {
bus: RadioBus;
groupId: number;
constructor(runtime: Runtime) {
this.bus = new RadioBus(runtime);
this.groupId = 0;
}
public setGroup(id: number) {
this.groupId = id & 0xff; // byte only
}
public broadcast(msg: number) {
this.bus.broadcast(msg, this.groupId)
}
public receivePacket(packet: SimulatorRadioPacketMessage) {
receivePacket(packet: SimulatorRadioPacketMessage) {
if (this.groupId == packet.payload.groupId)
this.bus.datagram.queue(packet)
this.datagram.queue(packet)
}
}
}
namespace pxsim.radio {
enum PacketPayloadType {
NUMBER = 0,
VALUE = 1,
STRING = 2
}
export function broadcastMessage(msg: number): void {
board().radioState.broadcast(msg);
}
export function onBroadcastMessageReceived(msg: number, handler: RefAction): void {
pxtcore.registerWithDal(DAL.MES_BROADCAST_GENERAL_ID, msg, handler);
export function raiseEvent(id: number, eventid: number): void {
board().radioState.raiseEvent(id, eventid);
}
export function setGroup(id: number): void {
@@ -120,99 +114,34 @@ namespace pxsim.radio {
}
export function setTransmitPower(power: number): void {
board().radioState.bus.setTransmitPower(power);
board().radioState.setTransmitPower(power);
}
export function setTransmitSerialNumber(transmit: boolean): void {
board().radioState.bus.setTransmitSerialNumber(transmit);
export function setFrequencyBand(band: number) {
board().radioState.setFrequencyBand(band);
}
export function sendNumber(value: number): void {
board().radioState.bus.datagram.send({
type: PacketPayloadType.NUMBER,
export function sendRawPacket(buf: RefBuffer) {
let cb = getResume();
board().radioState.datagram.send({
type: 0,
groupId: board().radioState.groupId,
numberData: value,
bufferData: buf.data
});
setTimeout(cb, 1);
}
export function sendString(msg: string): void {
msg = msg.substr(0, 19);
board().radioState.bus.datagram.send({
type: PacketPayloadType.STRING,
groupId: board().radioState.groupId,
stringData: msg,
});
}
export function writeValueToSerial(): void {
const b = board();
writePacketToSerial(b, b.radioState.bus.datagram.recv())
}
export function writeReceivedPacketToSerial(): void {
const b = board();
writePacketToSerial(b, b.radioState.bus.datagram.lastReceived);
}
export function sendValue(name: string, value: number) {
name = name.substr(0, 12);
const msg: number[] = [];
msg.push()
board().radioState.bus.datagram.send({
type: PacketPayloadType.VALUE,
groupId: board().radioState.groupId,
stringData: name,
numberData: value
});
}
export function receiveNumber(): number {
const packet = board().radioState.bus.datagram.recv();
return receivedNumber();
}
export function receiveString(): string {
const packet = board().radioState.bus.datagram.recv();
return receivedString();
export function readRawPacket() {
const packet = board().radioState.datagram.recv();
return new RefBuffer(packet.payload.bufferData)
}
export function receivedSignalStrength(): number {
return board().radioState.bus.datagram.lastReceived.rssi;
return board().radioState.datagram.lastReceived.rssi;
}
export function onDataReceived(handler: RefAction): void {
pxtcore.registerWithDal(DAL.MICROBIT_ID_RADIO, DAL.MICROBIT_RADIO_EVT_DATAGRAM, handler);
radio.receiveNumber();
}
export function receivedNumber(): number {
return board().radioState.bus.datagram.lastReceived.payload.numberData || 0;
}
export function receivedSerial(): number {
return board().radioState.bus.datagram.lastReceived.serial;
}
export function receivedString(): string {
return initString(board().radioState.bus.datagram.lastReceived.payload.stringData || "");
}
export function receivedTime(): number {
return board().radioState.bus.datagram.lastReceived.time;
}
function writePacketToSerial(b: DalBoard, p: PacketBuffer) {
switch(p.payload.type) {
case PacketPayloadType.NUMBER:
b.writeSerial(`{"t":${p.time},"s":${p.serial},"v":${p.payload.numberData}}\r\n`)
break;
case PacketPayloadType.VALUE:
b.writeSerial(`{"t":${p.time},"s":${p.serial},"n":"${p.payload.stringData}","v":${p.payload.numberData}}\r\n`)
break;
case PacketPayloadType.STRING:
b.writeSerial(`{"t":${p.time},"s":${p.serial},"n":"${p.payload.stringData}"}\r\n`)
break;
default:
}
readRawPacket();
}
}

View File

@@ -28,11 +28,21 @@ namespace pxsim {
}
}
namespace pxsim.control {
export function __log(s: string) {
board().writeSerial(s + "\r\n");
}
}
namespace pxsim.serial {
export function writeString(s: string) {
board().writeSerial(s);
}
export function writeBuffer(buf: RefBuffer) {
// TODO
}
export function readUntil(del: string): string {
return readString();
}
@@ -53,4 +63,18 @@ namespace pxsim.serial {
export function redirectToUSB() {
// TODO
}
export function setRxBufferSize(size: number) {
// TODO
}
export function setTxBufferSize(size: number) {
// TODO
}
export function readBuffer(length: number) {
if (length <= 0)
length = 64;
return pins.createBuffer(length);
}
}

View File

@@ -7,6 +7,9 @@
"out": "../built/sim.js",
"rootDir": ".",
"newLine": "LF",
"sourceMap": false
"sourceMap": false,
"lib": ["dom", "dom.iterable", "scripthost", "es6"],
"types": ["jquery", "bluebird"],
"typeRoots": ["../node_modules/@types"]
}
}

View File

@@ -7,4 +7,4 @@ namespace pxsim.visuals {
wireframe: opts.wireframe,
});
}
}
}

View File

@@ -1,4 +1,3 @@
/// <reference path="../../node_modules/pxt-core/typings/globals/bluebird/index.d.ts"/>
/// <reference path="../../node_modules/pxt-core/built/pxtsim.d.ts"/>
namespace pxsim.visuals {
@@ -40,7 +39,7 @@ namespace pxsim.visuals {
svg.fills(result.ledsOuter, defaultLedMatrixTheme.ledOff);
//turn off LEDs
result.leds.forEach(l => (<SVGStylable><any>l).style.opacity = 0 + "");
result.leds.forEach(l => (<SVGStyleElement><any>l).style.opacity = 0 + "");
return result;
}
@@ -103,7 +102,7 @@ namespace pxsim.visuals {
public updateState() {
if (this.state.disabled) {
this.leds.forEach((led, i) => {
let sel = (<SVGStylable><any>led)
let sel = (<SVGStyleElement><any>led)
sel.style.opacity = 0 + "";
});
return;
@@ -112,7 +111,7 @@ namespace pxsim.visuals {
const bw = this.state.displayMode == pxsim.DisplayMode.bw
const img = this.state.image;
this.leds.forEach((led, i) => {
let sel = (<SVGStylable><any>led)
let sel = (<SVGStyleElement><any>led)
let dx = i % this.DRAW_SIZE;
let dy = (i - dx) / this.DRAW_SIZE;
if (dx < this.ACTIVE_SIZE && dy < this.ACTIVE_SIZE) {

View File

@@ -1059,7 +1059,7 @@ namespace pxsim.visuals {
constructor(public props: IBoardProps) {
this.buildDom();
if (props && props.wireframe)
svg.addClass(this.element, "sim-wireframe");
U.addClass(this.element, "sim-wireframe");
if (props && props.theme)
this.updateTheme();
@@ -1133,15 +1133,25 @@ namespace pxsim.visuals {
if (state.ledMatrixState.disabled) {
this.leds.forEach((led, i) => {
const sel = (<SVGStylable><any>led)
const sel = (<SVGStyleElement><any>led)
sel.style.opacity = "0";
})
} else {
const bw = state.ledMatrixState.displayMode == pxsim.DisplayMode.bw
const img = state.ledMatrixState.image;
const br = state.ledMatrixState.brigthness != undefined ? state.ledMatrixState.brigthness : 255;
this.leds.forEach((led, i) => {
const sel = (<SVGStylable><any>led)
sel.style.opacity = ((bw ? img.data[i] > 0 ? 255 : 0 : img.data[i]) / 255.0) + "";
const sel = (<SVGStyleElement><any>led)
let imgbr = bw ? (img.data[i] > 0 ? br : 0) : img.data[i];
// correct brightness
const opacity = imgbr > 0 ? imgbr / 255 * 155 + 100 : 0;
const transfrom = imgbr > 0 ? imgbr / 255 * 0.4 + 0.6 : 0;
sel.style.opacity = (opacity / 255) + "";
if (transfrom > 0) {
(sel.style as any).transformBox = 'fill-box';
sel.style.transformOrigin = '50% 50%';
sel.style.transform = `scale(${transfrom})`;
}
})
}
this.updatePins();
@@ -1154,8 +1164,8 @@ namespace pxsim.visuals {
this.updateRgbLed();
this.updateSpeaker();
if (!runtime || runtime.dead) svg.addClass(this.element, "grayscale");
else svg.removeClass(this.element, "grayscale");
if (!runtime || runtime.dead) U.addClass(this.element, "grayscale");
else U.removeClass(this.element, "grayscale");
}
private updateRgbLed() {
@@ -1445,7 +1455,7 @@ namespace pxsim.visuals {
this.pins = pinNames.map(n => {
let p = this.element.getElementById(n) as SVGElement;
if(!p) console.log("missing "+n);
svg.addClass(p, "sim-pin");
U.addClass(p, "sim-pin");
return p;
});
@@ -1463,9 +1473,9 @@ namespace pxsim.visuals {
// BTN A, B
const btnids = ["BTN_A", "BTN_B"];
this.buttonsOuter = btnids.map(n => this.element.getElementById(n + "_BOX") as SVGElement);
this.buttonsOuter.forEach(b => svg.addClass(b, "sim-button-outer"));
this.buttonsOuter.forEach(b => U.addClass(b, "sim-button-outer"));
this.buttons = btnids.map(n => this.element.getElementById(n) as SVGElement);
this.buttons.forEach(b => svg.addClass(b, "sim-button"));
this.buttons.forEach(b => U.addClass(b, "sim-button"));
// BTN A+B
const outerBtn = (left: number, top: number) => {
@@ -1581,7 +1591,7 @@ namespace pxsim.visuals {
let state = this.board;
let pin = state.edgeConnectorState.pins[index];
let svgpin = this.pins[index];
svg.addClass(svgpin, "touched");
U.addClass(svgpin, "touched");
if (pin.mode & PinFlags.Input) {
let cursor = svg.cursorPoint(pt, this.element, ev);
let v = (400 - cursor.y) / 40 * 1023
@@ -1594,17 +1604,18 @@ namespace pxsim.visuals {
let state = this.board;
let pin = state.edgeConnectorState.pins[index];
let svgpin = this.pins[index];
svg.removeClass(svgpin, "touched");
U.removeClass(svgpin, "touched");
this.updatePin(pin, index);
return false;
});
})
this.pins.slice(0, 3).forEach((btn, index) => {
btn.addEventListener(pointerEvents.down, ev => {
pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
let state = this.board;
state.edgeConnectorState.pins[index].touched = true;
this.updatePin(state.edgeConnectorState.pins[index], index);
})
this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
}));
btn.addEventListener(pointerEvents.leave, ev => {
let state = this.board;
state.edgeConnectorState.pins[index].touched = false;
@@ -1622,11 +1633,12 @@ namespace pxsim.visuals {
let bpState = this.board.buttonPairState;
let stateButtons = [bpState.aBtn, bpState.bBtn, bpState.abBtn];
this.buttonsOuter.slice(0, 2).forEach((btn, index) => {
btn.addEventListener(pointerEvents.down, ev => {
pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
let state = this.board;
stateButtons[index].pressed = true;
svg.fill(this.buttons[index], this.props.theme.buttonDown);
})
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
}));
btn.addEventListener(pointerEvents.leave, ev => {
let state = this.board;
stateButtons[index].pressed = false;
@@ -1640,7 +1652,7 @@ namespace pxsim.visuals {
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
})
})
this.buttonsOuter[2].addEventListener(pointerEvents.down, ev => {
pointerEvents.down.forEach(evid => this.buttonsOuter[2].addEventListener(evid, ev => {
let state = this.board;
stateButtons[0].pressed = true;
stateButtons[1].pressed = true;
@@ -1648,7 +1660,8 @@ namespace pxsim.visuals {
svg.fill(this.buttons[0], this.props.theme.buttonDown);
svg.fill(this.buttons[1], this.props.theme.buttonDown);
svg.fill(this.buttons[2], this.props.theme.buttonDown);
})
this.board.bus.queue(stateButtons[2].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
}));
this.buttonsOuter[2].addEventListener(pointerEvents.leave, ev => {
let state = this.board;
stateButtons[0].pressed = false;
@@ -1672,4 +1685,4 @@ namespace pxsim.visuals {
})
}
}
}
}

View File

@@ -1,6 +1,5 @@
/// <reference path="../../node_modules/pxt-core/built/pxtsim.d.ts"/>
/// <reference path="../../libs/core/dal.d.ts"/>
/// <reference path="../../libs/core/shims.d.ts"/>
/// <reference path="../../libs/core/enums.d.ts"/>
namespace pxsim.visuals {
@@ -133,7 +132,7 @@ namespace pxsim.visuals {
}
//show the canvas if it's hidden
svg.removeClass(this.background, "hidden");
U.removeClass(this.background, "hidden");
//resize if necessary
let [first, last] = [this.pixels[0], this.pixels[this.pixels.length - 1]]
@@ -179,6 +178,7 @@ namespace pxsim.visuals {
.sim-neopixel-canvas-parent:hover {
transform-origin: center;
transform: scale(4) translateY(-60px);
-moz-transform: scale(4) translateY(-220px);
}
.sim-neopixel-canvas .hidden {
visibility:hidden;
@@ -239,4 +239,4 @@ namespace pxsim.visuals {
}
public updateTheme(): void { }
}
}
}