Compare commits

..

9 Commits

Author SHA1 Message Date
peli
33e6372bf0 1.2.30 2020-09-10 23:42:19 -07:00
Maciej Mroziński
83bba14a4d Change timeout 1s -> 5s to reduce Timeout errors (#988) 2020-09-10 23:42:10 -07:00
Maciej Mroziński
0d7c1f0d50 Read default volume from device settings at start (#987) 2020-09-09 12:07:45 -07:00
peli
623e57a30e 1.2.29 2020-09-08 23:56:34 -07:00
Maciej Mroziński
91235fb6d3 Change sound volume to level (#986) 2020-09-08 23:55:35 -07:00
Peli de Halleux
4a73c5d700 1.2.28 2020-08-21 06:07:32 +02:00
Peli de Halleux
a47a06ac19 Cooperate (#985)
* cooperate pause

* fix math

* update lastPause before pausing

* faster cooperation

Co-authored-by: Peli de Halleux <peli@DESKTOP-5B7QRAM.corp.microsoft.com>
2020-08-21 06:06:59 +02:00
Peli
3f598a3eee 1.2.27 2020-01-19 21:49:57 -08:00
Peli
ded2e9d82c don't close bt connection 2020-01-19 21:41:43 -08:00
62 changed files with 6474 additions and 1394 deletions

View File

@@ -1,12 +0,0 @@
name: Compress images
on: pull_request
jobs:
build:
name: calibreapp/image-actions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: calibreapp/image-actions
uses: calibreapp/image-actions@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@@ -17,7 +17,6 @@ clients/**/bin/**
clients/**/obj/**
clients/electron/projects
libs/**/_locales/**
package-lock.json
videos/**

14
cmds/cmds.ts Normal file
View File

@@ -0,0 +1,14 @@
/// <reference path="../node_modules/pxt-core/built/pxtlib.d.ts" />
import * as fs from 'fs';
const deploy = require("./editor/deploy")
export function deployCoreAsync(resp: pxtc.CompileResult) {
return deploy.deployCoreAsync(resp, process.env["PXT_SERIAL"] ? false : true)
.then(() => {
fs.writeFileSync("built/full-" + pxtc.BINARY_UF2, resp.outfiles[pxtc.BINARY_UF2], {
encoding: "base64"
})
})
}

14
cmds/tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es5",
"noImplicitAny": true,
"noImplicitReturns": true,
"declaration": true,
"outDir": "../built",
"module": "commonjs",
"rootDir": ".",
"newLine": "LF",
"sourceMap": false,
"types": ["node"]
}
}

View File

@@ -27,7 +27,6 @@ while (true) {
music.playSoundEffectUntilDone(sounds.mechanicalMotorStart)
music.playSoundEffectUntilDone(sounds.mechanicalMotorIdle);
}
pause(1);
}
```
@@ -43,6 +42,5 @@ while (true) {
music.playSoundEffectUntilDone(sounds.mechanicalMotorStart)
music.playSoundEffectUntilDone(sounds.mechanicalMotorIdle);
}
pause(1);
}
```

View File

@@ -73,12 +73,6 @@ Verify that the program you just created shows eyes on the Brick Display, and th
**Well done!**
## Run it Again
![EV3 Brick with Try in BrkProg_Save Folder in File Manager](/static/getting-started/try-in-file-manager.png)
Use the Brick Buttons and navigate to the File Manager tab. Open the **BrkProg_SAVE** folder, select **Try** and click the center button to run it again.
## Connect a Large Motor @unplugged
Now you will learn to control the Large Motor.

View File

@@ -1,3 +1,3 @@
{
"appref": "v1.2.26"
"appref": "v1.2.22"
}

View File

@@ -388,12 +388,12 @@
}
function downloadWin64() {
// TODO: Keep this link up-to-date with the desired release version
window.open("https://makecode.com/api/release/ev3/v1.2.26/win64");
window.open("https://makecode.com/api/release/ev3/v1.2.22/win64");
tickEvent("offlineapp.download", { "target": "ev3", "platform": "win64" });
}
function downloadMac64() {
// TODO: Keep this link up-to-date with the desired release version
window.open("https://makecode.com/api/release/ev3/v1.2.26/mac64");
window.open("https://makecode.com/api/release/ev3/v1.2.22/mac64");
tickEvent("offlineapp.download", { "target": "ev3", "platform": "mac64" });
}
</script>

View File

@@ -1,88 +1,9 @@
# Projects
```codecard
[
{
"name": "Getting Started",
"url": "/getting-started",
"imageUrl": "/static/lessons/firmware.png"
},
{
"name": "Brick Tutorials",
"url": "/tutorials/brick",
"imageUrl": "/static/tutorials/wake-up.png"
},
{
"name": "Motor Tutorials",
"url": "/tutorials/motors",
"imageUrl": "/static/tutorials/run-motors.png"
},
{
"name": "Touch Sensor Tutorials",
"url": "/tutorials/touch-sensor",
"imageUrl": "/static/tutorials/touch-to-run.png"
},
{
"name": "Color Sensor Tutorials",
"url": "/tutorials/color-sensor",
"imageUrl": "/static/tutorials/what-color.png"
},
{
"name": "Ultrasonic Sensor Tutorials",
"url": "/tutorials/ultrasonic-sensor",
"imageUrl": "/static/tutorials/object-near.png"
},
{
"name": "Gyro Tutorials",
"url": "/tutorials/gyro",
"imageUrl": "/static/tutorials/calibrate-gyro.png"
},
{
"name": "Infrared Sensor Tutorials",
"url": "/tutorials/infrared-sensor",
"imageUrl": "/static/tutorials/security-alert.png"
},
{
"name": "FLL / City Shaper",
"url": "/tutorials/city-shaper",
"imageUrl": "/static/tutorials/city-shaper/robot1.jpg"
},
{
"name": "Design Engineering",
"url": "/design-engineering",
"imageUrl": "/static/lessons/make-it-move-without-wheels.png"
},
{
"name": "Coding",
"url": "/coding",
"imageUrl": "/static/lessons/autonomous-parking.png"
},
{
"name": "Maker",
"url": "/maker",
"imageUrl": "/static/lessons/make-a-sound-machine.png"
},
{
"name": "Tutorial Videos",
"url": "/videos",
"imageUrl": "https://legoeducation.videomarketingplatform.co/27288170/35719444/5d009e5f93fbf479c2e5ed2bf87a7990/thumbnail.png"
}
]
```
Here are some cool projects that you can build with your @boardname@!
## See Also
## Basic
[Getting Started](/getting-started),
[Brick Tutorials](/tutorials/brick),
[Motor Tutorials](/tutorials/motors),
[Touch Sensor Tutorials](/tutorials/touch-sensor),
[Color Sensor Tutorials](/tutorials/color-sensor),
[Ultrasonic Sensor Tutorials](/tutorials/ultrasonic-sensor),
[Gyro Tutorials](/tutorials/gyro),
[Infrared Sensor Tutorials](/tutorials/infrared-sensor),
[FLL / City Shaper](/tutorials/city-shaper),
[Design Engineering](/design-engineering),
[Coding](/coding),
[Maker](/maker),
[Tutorial Videos](/videos)
Basic projects to build with your EV3 Brick.
Coming soon.

View File

@@ -1,69 +0,0 @@
# Projects
* [Getting Started](/getting-started)
* [Prepare](https://makecode.mindstorms.com/troubleshoot)
* [Try](/getting-started/try)
* [Use](/getting-started/use)
* [First LEGO League](/fll)
* [Brick Tutorials](/tutorials/brick)
* [Wake Up!](/tutorials/wake-up)
* [Make an Animation](/tutorials/make-an-animation)
* [What Animal Am I?](/tutorials/what-animal-am-i)
* [Music Brick](/tutorials/music-brick)
* [Pause On Start](/tutorials/pause-on-start)
* [Motor Tutorials](/tutorials/motors)
* [Run Motors](/tutorials/run-motors)
* [Spin Turn](/tutorials/spin-turn)
* [Pivot Turn](/tutorials/pivot-turn)
* [Smooth Turn](/tutorials/smooth-turn)
* [Tank ZigZag](/tutorials/tank-zigzag)
* [Coast Or Brake](/tutorials/coast-or-brake)
* [Turtle](/tutorials/turtle)
* [Touch Sensor Tutorials](/tutorials/touch-sensor)
* [Touch to Run](/tutorials/touch-to-run)
* [Sensor Values](/tutorials/touch-sensor-values)
* [Stop At Object](/tutorials/stop-at-object)
* [Color Sensor Tutorials](/tutorials/color-sensor)
* [What Color?](/tutorials/what-color)
* [Line Following](/tutorials/line-following)
* [Red Light, Green Light](/tutorials/redlight-greenlight)
* [Move To Color](/tutorials/move-to-color)
* [Reflected Light Measure](/tutorials/reflected-light-measure)
* [Reflected Light Calibration](/tutorials/reflected-light-calibration)
* [Ultrasonic Sensor Tutorials](/tutorials/ultrasonic-sensor)
* [Object Near](/tutorials/object-near)
* [Wall Follower](/tutorials/wall-follower)
* [Gyro Tutorials](/tutorials/gyro)
* [Calibrate](/tutorials/calibrate-gyro)
* [Turn](/tutorials/turn-with-gyro)
* [Move Straight](/tutorials/move-straight-with-gyro)
* [Drifter](/tutorials/drifter)
* [Infrared Sensor Tutorials](/tutorials/infrared-sensor)
* [Security Alert](/tutorials/security-alert)
* [FLL / City Shaper](/tutorials/city-shaper)
* [Crane Mission / Robot 1](/tutorials/city-shaper/robot-1)
* [Crane Mission / Robot 2](/tutorials/city-shaper/robot-2)
* [Crane Mission / Video 1](https://youtu.be/IqL0Pyeu5Ng)
* [Bluetooth download (beta)](https://youtu.be/VIq8-6Egtqs)
* [Turn with Gyro](https://youtu.be/I7ncuXAfBwk)
* [Moving with Gyro](https://youtu.be/ufiOPvW37xc)
* [Line following with 1 color sensor](https://youtu.be/_LeduyKQVjg)
* [Proportional line following with 1 color sensor](https://youtu.be/-AirqwC9DL4)
* [Proportional line following with 2 color sensors](https://youtu.be/QWOflBuu9Oo)
* [Design Engineering](/design-engineering)
* [Make It Move Without Wheels](/design-engineering/make-it-move)
* [Make It Smarter and Faster](/design-engineering/make-it-smarter)
* [Make a System that Communicates](/design-engineering/make-it-communicate)
* [Coding](/coding)
* [Autonomous Parking](/coding/autonomous-parking)
* [Object Detection](/coding/object-detection)
* [Line Detection](/coding/line-detection)
* [Maker](/maker)
* [Make A Sound Machine](/maker/sound-machine)
* [Make A Security Gadget](/maker/security-gadget)
* [Tutorial Videos](/videos)
* [undefined](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=5d009e5f93fbf479c2e5ed2bf87a7990&source=embed&photo%5fid=35719444)
* [undefined](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=2008a566f1fb034d58d5ebe19ba8621f&source=embed&photo%5fid=35719467)
* [undefined](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=629730c938e452f0fd7653fbc4708166&source=embed&photo%5fid=35719470)
* [undefined](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=3513a83b87fe536b2dc512237465fd1b&source=embed&photo%5fid=35719471)
* [undefined](https://legoeducation.videomarketingplatform.co/v.ihtml/player.html?token=5c594c2373367f7870196f519f3bfc7a&source=embed&photo%5fid=35719472)

View File

@@ -1,3 +0,0 @@
{
"appref": "v1.2"
}

View File

@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" id="svg4487" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.4" clip-rule="evenodd" version="1.1" width="32" height="32" viewBox="0 0 32 32">
<path id="path4485" fill="#696969" d="M16.1 2.7a13.8 13.8 0 00-4.4 27c.7 0 1-.4 1-.7v-2.4c-3.9.9-4.7-1.8-4.7-1.8-.6-1.6-1.5-2-1.5-2-1.3-1 0-.9 0-.9 1.4.1 2.2 1.4 2.2 1.4 1.2 2.1 3.2 1.5 4 1.2a3 3 0 01.9-1.9c-3.1-.3-6.3-1.5-6.3-6.8 0-1.5.5-2.7 1.4-3.7a5 5 0 01.1-3.7s1.2-.3 3.8 1.4a13.2 13.2 0 013.5-.4c1.2 0 2.4.1 3.5.4 2.6-1.7 3.8-1.4 3.8-1.4a5 5 0 01.1 3.7c.9 1 1.4 2.2 1.4 3.7 0 5.3-3.2 6.5-6.3 6.8.5.5 1 1.3 1 2.6V29c0 .4.2.8.9.6a13.8 13.8 0 00-4.4-27"/>
<g id="text4497" fill="#000" fill-opacity="1" stroke="none" stroke-width=".7" aria-label="v" font-family="consolas" font-size="26.6" font-stretch="normal" font-style="normal" font-variant="normal" font-weight="400" letter-spacing="0" style="line-height:1.25;-inkscape-font-specification:consolas" transform="rotate(14.1)" word-spacing="0">
<path id="path4499" stroke-width=".7" d="M21.4-1.7l2.9 8 3-8.3.8-1.5q.4-.4 1-.4.8 0 1.2.5.5.4.5 1v.6l-.3.5-.2.7-3.4 8.4-.4 1-.5 1-.7.5q-.4.2-1 .2-.8 0-1.2-.3-.5-.3-.7-.8l-.7-1.6-3.4-8.3q0-.4-.2-.7l-.2-.6-.1-.6.2-.7.6-.6q.4-.2.9-.2.9 0 1.2.5l.7 1.7z" font-family="Arial Rounded MT Bold" font-stretch="normal" font-style="normal" font-variant="normal" font-weight="400" style="-inkscape-font-specification:'Arial Rounded MT Bold, '"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,18 +1,16 @@
# Troubleshooting
## Download issues
# Troubleshooting download problems
If you're having trouble getting your code onto the EV3 Brick, try these steps to see if you can fix the problem.
### Check your **@drivename@** firmware
## Check your **@drivename@** firmware
MakeCode needs a firmware version of **1.10E** or higher installed on your brick.
#### ~hint
### ~hint
Firmware is the software that runs all the basic operations on your EV3 Brick. Your programs and the firmware work together to make the EV3 Brick do all things you want it to. Your EV3 Brick comes with firmware pre-installed, but it may need to be updated to work properly with MakeCode.
#### ~
### ~
To check the the firmware version on your EV3 Brick:
@@ -27,15 +25,15 @@ To check the the firmware version on your EV3 Brick:
If you can't find the **Brick Info** or you see that the version is less than **1.10E**, **you need to upgrade your firmware**.
### Upgrade your **@drivename@**
## Upgrade your **@drivename@**
If your a firmware version level is less than **1.10E**, you need to install an upgraded version. You can upgrade the firmware with the **EV3 Lab** or **EV3 Programming** software. Also, you can do a manual upgrade by downloading the firmware install file. See the [Firmware Update](https://education.lego.com/en-us/support/mindstorms-ev3/firmware-update) support page to learn about the upgrade process.
#### ~ hint
### ~ hint
**Recommended:** Upgrade with the **[EV3 Device Manager](https://ev3manager.education.lego.com/)**
#### ~
### ~
## Can I see the **@drivename@** drive on my computer?
@@ -47,24 +45,14 @@ On Windows, it looks like this in Explorer:
If you don't see the **@drivename@** drive, make sure your brick is powered on and check that your USB connection is good.
### The display on the EV3 Brick is blank
## The display on the EV3 Brick is blank
Make sure your EV3 Brick is charged and powered on. If your it doesn't turn on, find the charger and plug it into wall power, then connect it to your EV3 Brick. Does it turn on and start up?
### I still can't see my @drivename@ drive
## I still can't see my @drivename@ drive
Make sure that one end of your USB cable is firmly inserted into a USB port on the computer and the other end is connected to the EV3 Brick. If you still can't see the **@drivename@** drive, try a different port on the computer. If that doesn't work then maybe your cable is faulty or you need to reset the EV3 Brick.
## Why can't I delete my program (*.uf2) files from the Brick? #deletefiles
There's a bug in the firmware which prevents you from deleting the programs (``*.uf2`` files) from your EV3 Brick. There isn't a firmware update to fix this yet.
We have prepared a special program that lets you delete UF2 files from the brick.
Download [these PDF instructions](/file-manager.pdf) and drop the PDF on the brick drive.
This will present you with an menu for deleting files.
For other common questions, try the FAQ page https://makecode.mindstorms.com/faq.
## How do I reset my EV3 Brick?
If you think your USB connection is good and you still can't see your **@drivename@** drive, try giving the EV3 Brick a reset. You can follow these steps to reset:

View File

@@ -3,15 +3,20 @@
import UF2 = pxtc.UF2;
import { Ev3Wrapper } from "./wrap";
import { bluetoothTryAgainAsync } from "./dialogs";
export let ev3: Ev3Wrapper;
let confirmAsync: (options: any) => Promise<number>;
export function setConfirmAsync(cf: (options: any) => Promise<number>) {
confirmAsync = cf;
}
export function debug() {
return initHidAsync()
.then(w => w.downloadFileAsync("/tmp/dmesg.txt", v => console.log(pxt.Util.uint8ArrayToString(v))))
}
// Web Serial API https://wicg.github.io/serial/
// chromium bug https://bugs.chromium.org/p/chromium/issues/detail?id=884928
// Under experimental features in Chrome Desktop 77+
@@ -59,6 +64,7 @@ class WebSerialPackageIO implements pxt.HF2.PacketIO {
private _writer: any;
constructor(private port: SerialPort, private options: SerialOptions) {
console.log(`serial: new io`)
}
async readSerialAsync() {
@@ -85,17 +91,24 @@ class WebSerialPackageIO implements pxt.HF2.PacketIO {
return !!(<any>navigator).serial;
}
static portIos: WebSerialPackageIO[] = [];
static async mkPacketIOAsync(): Promise<pxt.HF2.PacketIO> {
const serial = (<any>navigator).serial;
if (serial) {
try {
const requestOptions: SerialPortRequestOptions = {};
const port = await serial.requestPort(requestOptions);
const options: SerialOptions = {
baudrate: 460800,
buffersize: 4096
};
return new WebSerialPackageIO(port, options);
let io = WebSerialPackageIO.portIos.filter(i => i.port == port)[0];
if (!io) {
const options: SerialOptions = {
baudrate: 460800,
buffersize: 4096
};
io = new WebSerialPackageIO(port, options);
WebSerialPackageIO.portIos.push(io);
}
return io;
} catch (e) {
console.log(`connection error`, e)
}
@@ -120,11 +133,8 @@ class WebSerialPackageIO implements pxt.HF2.PacketIO {
});
}
private closeAsync() {
console.log(`serial: closing port`);
this.port.close();
this._reader = undefined;
this._writer = undefined;
private async closeAsync() {
// don't close port
return Promise.delay(500);
}
@@ -162,7 +172,7 @@ export function initAsync(): Promise<void> {
useHID = false
} else {
const nodehid = /nodehid/i.test(window.location.href);
if (pxt.BrowserUtils.isLocalHost() && pxt.Cloud.localToken && nodehid)
if (pxt.Cloud.isLocalHost() && pxt.Cloud.localToken && nodehid)
useHID = true;
}
@@ -278,9 +288,22 @@ export function deployCoreAsync(resp: pxtc.CompileResult) {
return w.reconnectAsync(false)
.catch(e => {
// user easily forgets to stop robot
bluetoothTryAgainAsync().then(() => w.disconnectAsync())
.then(() => Promise.delay(1000))
.then(() => w.reconnectAsync());
if (confirmAsync)
return confirmAsync({
header: lf("Bluetooth download failed..."),
htmlBody:
`<ul>
<li>${lf("Make sure to stop your program or exit portview on the EV3.")}</li>
<li>${lf("Check your battery level.")}</li>
<li>${lf("Close EV3 LabView or other MakeCode editor tabs.")}
</ul>`,
hasCloseIcon: true,
hideCancel: true,
hideAgree: false,
agreeLbl: lf("Try again"),
}).then(() => w.disconnectAsync())
.then(() => Promise.delay(1000))
.then(() => w.reconnectAsync());
// nothing we can do
return Promise.reject(e);

View File

@@ -1,145 +0,0 @@
import * as React from "react";
import { canUseWebSerial, enableWebSerialAsync } from "./deploy";
import { projectView } from "./extension";
let confirmAsync: (options: any) => Promise<number>;
export function bluetoothTryAgainAsync(): Promise<void> {
return confirmAsync({
header: lf("Bluetooth download failed..."),
jsx: <ul>
<li>{lf("Make sure to stop your program or exit portview on the EV3.")}</li>
<li>{lf("Check your battery level.")}</li>
<li>{lf("Close EV3 LabView or other MakeCode editor tabs.")}</li>
</ul>,
hasCloseIcon: false,
hideCancel: true,
hideAgree: false,
agreeLbl: lf("Try again")
}).then(r => {});
}
function enableWebSerialAndCompileAsync() {
return enableWebSerialAsync()
.then(() => Promise.delay(500))
.then(() => projectView.compile());
}
let bluetoothDialogShown = false;
function explainWebSerialPairingAsync(): Promise<void> {
if (!confirmAsync || bluetoothDialogShown) return Promise.resolve();
bluetoothDialogShown = true;
return confirmAsync({
header: lf("Bluetooth pairing"),
hasCloseIcon: false,
hideCancel: true,
buttons: [{
label: lf("Help"),
icon: "question circle",
className: "lightgrey",
url: "/bluetooth"
}],
jsx: <p>
{lf("You will be prompted to select a serial port.")}
{pxt.BrowserUtils.isWindows()
? lf("Look for 'Standard Serial over Bluetooth link'.")
: lf("Loop for 'cu.EV3-SerialPort'.")}
{lf("If you have paired multiple EV3, you might have to try out multiple ports until you find the correct one.")}
</p>
}).then(() => { })
}
export function showUploadDialogAsync(fn: string, url: string, _confirmAsync: (options: any) => Promise<number>): Promise<void> {
confirmAsync = _confirmAsync;
// https://msdn.microsoft.com/en-us/library/cc848897.aspx
// "For security reasons, data URIs are restricted to downloaded resources.
// Data URIs cannot be used for navigation, for scripting, or to populate frame or iframe elements"
const downloadAgain = !pxt.BrowserUtils.isIE() && !pxt.BrowserUtils.isEdge();
const docUrl = pxt.appTarget.appTheme.usbDocs;
const jsx =
<div className="ui grid stackable">
<div className="column five wide" style={{ backgroundColor: "#E2E2E2" }}>
<div className="ui header">{lf("First time here?")}</div>
<strong style={{ fontSize: "small" }}>{lf("You must have version 1.10E or above of the firmware")}</strong>
<div style={{ justifyContent: "center", display: "flex", padding: "1rem" }}>
<img className="ui image" src="/static/download/firmware.png" style={{ height: "100px" }} />
</div>
<a href="/troubleshoot" target="_blank">{lf("Check your firmware version here and update if needed")}</a>
</div>
<div className="column eleven wide">
<div className="ui grid">
<div className="row">
<div className="column">
<div className="ui two column grid padded">
<div className="column">
<div className="ui">
<div className="image">
<img className="ui medium rounded image" src="/static/download/connect.svg" style={{ height: "109px", width: "261px", marginBottom: "1rem" }} />
</div>
<div className="content">
<div className="description">
<span className="ui yellow circular label">1</span>
<strong>{lf("Connect the EV3 to your computer with a USB cable")}</strong>
<br />
<span style={{ fontSize: "small" }}>{lf("Use the miniUSB port on the top of the EV3 Brick")}</span>
</div>
</div>
</div>
</div>
<div className="column">
<div className="ui">
<div className="image">
<img className="ui medium rounded image" src="/static/download/transfer.svg" style={{ height: "109px", width: "261px", marginBottom: "1rem" }} />
</div>
<div className="content">
<div className="description">
<span className="ui yellow circular label">2</span>
<strong>{lf("Move the .uf2 file to the EV3 Brick")}</strong>
<br />
<span style={{ fontSize: "small" }}>{lf("Locate the downloaded .uf2 file and drag it to the EV3 USB drive")}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>;
return confirmAsync({
header: lf("Download to your EV3"),
jsx,
hasCloseIcon: true,
hideCancel: true,
hideAgree: false,
agreeLbl: lf("I got it"),
className: 'downloaddialog',
buttons: [canUseWebSerial() ? {
label: lf("Bluetooth"),
icon: "bluetooth",
className: "bluetooth focused",
onclick: () => {
pxt.tickEvent("bluetooth.enable");
explainWebSerialPairingAsync()
.then(() => enableWebSerialAndCompileAsync())
.done();
}
} : undefined, downloadAgain ? {
label: fn,
icon: "download",
className: "lightgrey focused",
url,
fileName: fn
} : undefined, docUrl ? {
label: lf("Help"),
icon: "help",
className: "lightgrey",
url: docUrl
} : undefined]
//timeout: 20000
}).then(() => { });
}

View File

@@ -1,26 +1,144 @@
/// <reference path="../node_modules/pxt-core/localtypings/pxtarget.d.ts" />
/// <reference path="../node_modules/pxt-core/built/pxtblocks.d.ts" />
/// <reference path="../node_modules/pxt-core/built/pxtcompiler.d.ts" />
/// <reference path="../node_modules/pxt-core/built/pxtlib.d.ts" />
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
import { deployCoreAsync, initAsync } from "./deploy";
import { showUploadDialogAsync } from "./dialogs";
export let projectView: pxt.editor.IProjectView;
import { deployCoreAsync, initAsync, canUseWebSerial, enableWebSerialAsync, setConfirmAsync } from "./deploy";
let bluetoothDialogShown = false;
pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
const projectView = opts.projectView;
pxt.debug('loading pxt-ev3 target extensions...')
projectView = opts.projectView;
function enableWebSerialAndCompileAsync() {
return enableWebSerialAsync()
.then(() => Promise.delay(500))
.then(() => projectView.compile());
}
const res: pxt.editor.ExtensionResult = {
deployAsync: deployCoreAsync,
showUploadInstructionsAsync: showUploadDialogAsync
};
deployCoreAsync,
showUploadInstructionsAsync: (fn: string, url: string, confirmAsync: (options: any) => Promise<number>) => {
setConfirmAsync(confirmAsync);
// https://msdn.microsoft.com/en-us/library/cc848897.aspx
// "For security reasons, data URIs are restricted to downloaded resources.
// Data URIs cannot be used for navigation, for scripting, or to populate frame or iframe elements"
const downloadAgain = !pxt.BrowserUtils.isIE() && !pxt.BrowserUtils.isEdge();
const docUrl = pxt.appTarget.appTheme.usbDocs;
const htmlBody = `
<div class="ui grid stackable">
<div class="column five wide" style="background-color: #E2E2E2;">
<div class="ui header">${lf("First time here?")}</div>
<strong style="font-size:small">${lf("You must have version 1.10E or above of the firmware")}</strong>
<div style="justify-content: center;display: flex;padding: 1rem;">
<img class="ui image" src="/static/download/firmware.png" style="height:100px;" />
</div>
<a href="/troubleshoot" target="_blank">${lf("Check your firmware version here and update if needed")}</a>
</div>
<div class="column eleven wide">
<div class="ui grid">
<div class="row">
<div class="column">
<div class="ui two column grid padded">
<div class="column">
<div class="ui">
<div class="image">
<img class="ui medium rounded image" src="/static/download/connect.svg" style="height:109px;width:261px;margin-bottom:1rem;" />
</div>
<div class="content">
<div class="description">
<span class="ui yellow circular label">1</span>
<strong>${lf("Connect the EV3 to your computer with a USB cable")}</strong>
<br />
<span style="font-size:small">${lf("Use the miniUSB port on the top of the EV3 Brick")}</span>
</div>
</div>
</div>
</div>
<div class="column">
<div class="ui">
<div class="image">
<img class="ui medium rounded image" src="/static/download/transfer.svg" style="height:109px;width:261px;margin-bottom:1rem;" />
</div>
<div class="content">
<div class="description">
<span class="ui yellow circular label">2</span>
<strong>${lf("Move the .uf2 file to the EV3 Brick")}</strong>
<br />
<span style="font-size:small">${lf("Locate the downloaded .uf2 file and drag it to the EV3 USB drive")}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>`;
return confirmAsync({
header: lf("Download to your EV3"),
htmlBody,
hasCloseIcon: true,
hideCancel: true,
hideAgree: false,
agreeLbl: lf("I got it"),
className: 'downloaddialog',
buttons: [canUseWebSerial() ? {
label: lf("Bluetooth"),
icon: "bluetooth",
className: "bluetooth focused",
onclick: () => {
pxt.tickEvent("bluetooth.enable");
if (bluetoothDialogShown) {
enableWebSerialAndCompileAsync().done();
} else {
bluetoothDialogShown = true;
confirmAsync({
header: lf("Bluetooth pairing"),
hasCloseIcon: true,
hideCancel: true,
buttons: [{
label: lf("Help"),
icon: "question circle",
className: "lightgrey",
url: "/bluetooth"
}],
htmlBody: `<p>
${lf("You will be prompted to select a serial port.")}
${pxt.BrowserUtils.isWindows()
? lf("Look for 'Standard Serial over Bluetooth link'.")
: lf("Loop for 'cu.EV3-SerialPort'.")}
${lf("If you have paired multiple EV3, you might have to try out multiple ports until you find the correct one.")}
</p>
`
}).then(() => enableWebSerialAndCompileAsync())
}
}
} : undefined, downloadAgain ? {
label: fn,
icon: "download",
className: "lightgrey focused",
url,
fileName: fn
} : undefined, docUrl ? {
label: lf("Help"),
icon: "help",
className: "lightgrey",
url: docUrl
} : undefined]
//timeout: 20000
}).then(() => { });
}
};
initAsync().catch(e => {
// probably no HID - we'll try this again upon deployment
})
return Promise.resolve<pxt.editor.ExtensionResult>(res);
}
// When require()d from node, bind the global pxt namespace
// namespace pxt {
// export const dummyExport = 1;
// }
// eval("if (typeof process === 'object' && process + '' === '[object process]') pxt = global.pxt")

View File

@@ -1,17 +1,14 @@
{
"compilerOptions": {
"target": "es5",
"noImplicitAny": true,
"noImplicitAny": false,
"noImplicitReturns": true,
"noImplicitThis": true,
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"isolatedModules": false,
"outDir": "../built/editor",
"rootDir": ".",
"newLine": "LF",
"sourceMap": false,
"jsx": "react"
"allowSyntheticDefaultImports": true,
"declaration": true
}
}

View File

@@ -116,7 +116,7 @@ export class Ev3Wrapper {
if (this.dataDump)
log("TALK: " + U.toHex(buf))
return this.io.sendPacketAsync(buf)
.then(() => this.msgs.shiftAsync(1000))
.then(() => this.msgs.shiftAsync(5000))
.then(resp => {
if (resp[2] != buf[2] || resp[3] != buf[3])
U.userError("msg count de-sync")

View File

@@ -3,7 +3,9 @@
import { FieldPorts } from "./field_ports";
import { FieldMotors } from "./field_motors";
import { FieldSpeed } from "./field_speed";
import { FieldBrickButtons } from "./field_brickbuttons";
import { FieldTurnRatio } from "./field_turnratio";
import { FieldColorEnum } from "./field_color";
import { FieldMusic } from "./field_music";
@@ -17,9 +19,15 @@ pxt.editor.initFieldExtensionsAsync = function (opts: pxt.editor.FieldExtensionO
}, {
selector: "motors",
editor: FieldMotors
}, {
selector: "speed",
editor: FieldSpeed
}, {
selector: "brickbuttons",
editor: FieldBrickButtons
}, {
selector: "turnratio",
editor: FieldTurnRatio
}, {
selector: "colorenum",
editor: FieldColorEnum

View File

@@ -121,17 +121,17 @@ export class FieldBrickButtons extends Blockly.FieldDropdown implements Blockly.
Blockly.DropDownDiv.setColour('#ffffff', '#dddddd');
// Calculate positioning based on the field position.
let scale = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
let bBox = { width: this.size_.width, height: this.size_.height };
var scale = this.sourceBlock_.workspace.scale;
var bBox = { width: this.size_.width, height: this.size_.height };
bBox.width *= scale;
bBox.height *= scale;
let position = this.fieldGroup_.getBoundingClientRect();
let primaryX = position.left + bBox.width / 2;
let primaryY = position.top + bBox.height;
let secondaryX = primaryX;
let secondaryY = position.top;
var position = this.fieldGroup_.getBoundingClientRect();
var primaryX = position.left + bBox.width / 2;
var primaryY = position.top + bBox.height;
var secondaryX = primaryX;
var secondaryY = position.top;
// Set bounds to workspace; show the drop-down.
(Blockly.DropDownDiv as any).setBoundsElement((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
this.onHide_.bind(this));
}
@@ -152,10 +152,9 @@ export class FieldBrickButtons extends Blockly.FieldDropdown implements Blockly.
* Callback for when the drop-down is hidden.
*/
private onHide_ = function () {
const content = Blockly.DropDownDiv.getContentDiv();
content.removeAttribute('role');
content.removeAttribute('aria-haspopup');
content.removeAttribute('aria-activedescendant');
(content as HTMLElement).style.width = '';
Blockly.DropDownDiv.content_.removeAttribute('role');
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
Blockly.DropDownDiv.getContentDiv().style.width = '';
};
}

View File

@@ -44,7 +44,7 @@ export class FieldColorEnum extends pxtblockly.FieldColorNumber implements Block
* @return {string} Current colour in '#rrggbb' format.
*/
getValue(opt_asHex?: boolean) {
const colour = this.mapColour(this.value_);
var colour = this.mapColour(this.colour_);
if (!opt_asHex && colour.indexOf('#') > -1) {
return `0x${colour.replace(/^#/, '')}`;
}
@@ -56,13 +56,13 @@ export class FieldColorEnum extends pxtblockly.FieldColorNumber implements Block
* @param {string} colour The new colour in '#rrggbb' format.
*/
setValue(colorStr: string) {
let colour = this.mapEnum(colorStr);
var colour = this.mapEnum(colorStr);
if (this.sourceBlock_ && Blockly.Events.isEnabled() &&
this.value_ != colour) {
this.colour_ != colour) {
Blockly.Events.fire(new (Blockly as any).Events.BlockChange(
this.sourceBlock_, 'field', this.name, this.value_, colour));
this.sourceBlock_, 'field', this.name, this.colour_, colour));
}
this.value_ = colour;
this.colour_ = colour;
if (this.sourceBlock_) {
this.sourceBlock_.setColour(colour, colour, colour);
}

View File

@@ -46,31 +46,31 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
(this as any).arrowX_ = 0;
/** @type {Number} */
this.arrowY_ = 11;
this.arrow_ = Blockly.utils.dom.createSvgElement('image', {
this.arrow_ = Blockly.utils.createSvgElement('image', {
'height': (this as any).arrowSize_ + 'px',
'width': (this as any).arrowSize_ + 'px'
}, null);
});
this.arrow_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', (Blockly.FieldDropdown as any).DROPDOWN_SVG_DATAURI);
this.arrow2_ = <SVGImageElement>Blockly.utils.dom.createSvgElement('image', {
this.arrow2_ = Blockly.utils.createSvgElement('image', {
'height': (this as any).arrowSize_ + 'px',
'width': (this as any).arrowSize_ + 'px'
}, null);
});
this.arrow2_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', (Blockly.FieldDropdown as any).DROPDOWN_SVG_DATAURI);
(this as any).className_ += ' blocklyDropdownText';
// Build the DOM.
this.fieldGroup_ = Blockly.utils.dom.createSvgElement('g', {}, null);
this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null);
if (!this.visible_) {
(this.fieldGroup_ as any).style.display = 'none';
}
// Adjust X to be flipped for RTL. Position is relative to horizontal start of source block.
let size = this.getSize();
let fieldX = (this.sourceBlock_.RTL) ? -size.width / 2 : size.width / 2;
var size = this.getSize();
var fieldX = (this.sourceBlock_.RTL) ? -size.width / 2 : size.width / 2;
/** @type {!Element} */
this.textElement_ = Blockly.utils.dom.createSvgElement('text',
this.textElement_ = Blockly.utils.createSvgElement('text',
{
'class': (this as any).className_,
'x': fieldX,
@@ -79,7 +79,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
},
this.fieldGroup_);
fieldX += 10; // size of first group.
this.textElement2_ = <SVGTextElement>Blockly.utils.dom.createSvgElement('text',
this.textElement2_ = Blockly.utils.createSvgElement('text',
{
'class': (this as any).className_,
'x': fieldX,
@@ -89,17 +89,17 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
this.fieldGroup_);
this.updateEditable();
(this.sourceBlock_ as Blockly.BlockSvg).getSvgRoot().appendChild(this.fieldGroup_);
this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
// Force a render.
this.render_();
this.isDirty_ = true;
this.size_.width = 0;
(this as any).mouseDownWrapper_ =
Blockly.bindEventWithChecks_((this as any).getClickTarget_(), 'mousedown', this,
(this as any).onMouseDown_);
// Add second dropdown
if (this.shouldShowRect_()) {
this.box_ = <SVGRectElement>Blockly.utils.dom.createSvgElement('rect', {
this.box_ = Blockly.utils.createSvgElement('rect', {
'rx': (Blockly.BlockSvg as any).CORNER_RADIUS,
'ry': (Blockly.BlockSvg as any).CORNER_RADIUS,
'x': 0,
@@ -112,7 +112,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
'fill-opacity': 1
}, null);
this.fieldGroup_.insertBefore(this.box_, this.textElement_);
this.box2_ = <SVGRectElement>Blockly.utils.dom.createSvgElement('rect', {
this.box2_ = Blockly.utils.createSvgElement('rect', {
'rx': (Blockly.BlockSvg as any).CORNER_RADIUS,
'ry': (Blockly.BlockSvg as any).CORNER_RADIUS,
'x': 0,
@@ -128,7 +128,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
}
// Force a reset of the text to add the arrow.
let text = this.text_;
var text = this.text_;
this.text_ = null;
this.setText(text);
}
@@ -179,7 +179,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
// Not rendered yet.
return;
}
let text = this.text_;
var text = this.text_;
if (text.length > this.maxDisplayLength) {
// Truncate displayed string and add an ellipsis ('...').
text = text.substring(0, this.maxDisplayLength - 2) + '\u2026';
@@ -200,11 +200,11 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
// Prevent the field from disappearing if empty.
text = Blockly.Field.NBSP;
}
let textNode = document.createTextNode(text);
var textNode = document.createTextNode(text);
this.textElement2_.appendChild(textNode);
// Cached width is obsolete. Clear it.
this.isDirty_ = true;
this.size_.width = 0;
};
patchDualMotorText(text: string) {
@@ -233,8 +233,8 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
if (this.textElement2_) {
this.textElement2_.parentNode.appendChild(this.arrow2_);
}
if (this.sourceBlock_ && (<Blockly.BlockSvg>this.sourceBlock_).rendered) {
(<Blockly.BlockSvg>this.sourceBlock_).render();
if (this.sourceBlock_ && this.sourceBlock_.rendered) {
this.sourceBlock_.render();
this.sourceBlock_.bumpNeighbours_();
}
}
@@ -244,7 +244,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
return 0;
}
let addedWidth = 0;
var addedWidth = 0;
if (this.sourceBlock_.RTL) {
(this as any).arrow2X_ = (this as any).arrowSize_ - (Blockly.BlockSvg as any).DROPDOWN_ARROW_PADDING;
addedWidth = (this as any).arrowSize_ + (Blockly.BlockSvg as any).DROPDOWN_ARROW_PADDING;
@@ -263,10 +263,10 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
return addedWidth;
};
updateSize_() {
updateWidth() {
// Calculate width of field
let width = Blockly.Field.getCachedWidth(this.textElement_);
let width2 = Blockly.Field.getCachedWidth(this.textElement2_);
var width = Blockly.Field.getCachedWidth(this.textElement_);
var width2 = Blockly.Field.getCachedWidth(this.textElement2_);
// Add padding to left and right of text.
if (this.EDITABLE) {
@@ -311,15 +311,15 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
// First dropdown
// Use one of the following options, medium motor, large motor or large motors (translated)
const textNode1 = document.createTextNode(this.getFirstValueI11n(<string>this.value_));
const textNode1 = document.createTextNode(this.getFirstValueI11n(this.value_));
this.textElement_.appendChild(textNode1);
// Second dropdown, no need to translate. Only port numbers
if (this.textElement2_) {
const textNode2 = document.createTextNode(this.getSecondValue(<string>this.value_));
const textNode2 = document.createTextNode(this.getSecondValue(this.value_));
this.textElement2_.appendChild(textNode2);
}
this.updateSize_();
this.updateWidth();
// Update text centering, based on newly calculated width.
let centerTextX = ((this as any).width1 - this.arrowWidth_) / 2;
@@ -347,8 +347,8 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
}
// Apply new text element x position.
let width = Blockly.Field.getCachedWidth(this.textElement_);
let newX = centerTextX - width / 2;
var width = Blockly.Field.getCachedWidth(this.textElement_);
var newX = centerTextX - width / 2;
this.textElement_.setAttribute('x', `${newX}`);
// Update text centering, based on newly calculated width.
@@ -377,8 +377,8 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
}
// Apply new text element x position.
let width2 = Blockly.Field.getCachedWidth(this.textElement2_);
let newX2 = centerTextX2 - width2 / 2;
var width2 = Blockly.Field.getCachedWidth(this.textElement2_);
var newX2 = centerTextX2 - width2 / 2;
this.textElement2_.setAttribute('x', `${newX2 + (this as any).width1 + Blockly.BlockSvg.BOX_FIELD_PADDING}`);
}
@@ -401,7 +401,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
if (Blockly.DropDownDiv.hideIfOwner(this)) {
return;
}
this.isFirst_ = e.clientX - this.getScaledBBox_().left < ((this as any).width1 * (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale);
this.isFirst_ = e.clientX - this.getScaledBBox_().left < ((this as any).width1 * this.sourceBlock_.workspace.scale);
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
Blockly.DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.clearContent();
@@ -411,14 +411,14 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
// Accessibility properties
contentDiv.setAttribute('role', 'menu');
contentDiv.setAttribute('aria-haspopup', 'true');
const foptions = this.getOptions(); // [img info, text]
let options = this.getOptions();
let opts = {};
let conts = {};
let vals = {};
// Go through all option values and split them into groups
for (let opt = 0; opt < foptions.length; opt++) {
const value = foptions[opt][1];
for (let opt = 0; opt < options.length; opt++) {
const value = options[opt][1];
const motorValue = value.substring(value.indexOf('.') + 1);
const typeValue = motorValue.indexOf('large') == 0 ? 'large' : 'medium';
const portValue = motorValue.indexOf('large') == 0 ? motorValue.substring(5) : motorValue.substring(6);
@@ -429,14 +429,13 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
if (!opts[key]) opts[key] = [];
opts[key].push(portValue);
conts[text] = foptions[opt][0];
conts[text] = options[opt][0];
vals[text] = value;
}
const currentFirst = this.getFirstValue(<string>this.value_);
//const currentSecond = this.getSecondValue(<string>this.value_);
const currentFirst = this.getFirstValue(this.value_);
const currentSecond = this.getSecondValue(this.value_);
let options: string[];
if (!this.isFirst_) {
options = opts[currentFirst];
} else {
@@ -527,7 +526,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
Blockly.DropDownDiv.setColour(this.backgroundColour_, this.borderColour_);
// Calculate positioning based on the field position.
let scale = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
let scale = this.sourceBlock_.workspace.scale;
let width = this.isFirst_ ? (this as any).width1 : (this as any).width2;
let bBox = { width: this.size_.width, height: this.size_.height };
width *= scale;
@@ -539,7 +538,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
let secondaryX = primaryX;
let secondaryY = position.top;
// Set bounds to workspace; show the drop-down.
(Blockly.DropDownDiv as any).setBoundsElement((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
this.onHide_.bind(this));
@@ -562,11 +561,10 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
* Callback for when the drop-down is hidden.
*/
protected onHide_() {
const content = Blockly.DropDownDiv.getContentDiv();
content.removeAttribute('role');
content.removeAttribute('aria-haspopup');
content.removeAttribute('aria-activedescendant');
(content as HTMLElement).style.width = '';
Blockly.DropDownDiv.content_.removeAttribute('role');
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
Blockly.DropDownDiv.getContentDiv().style.width = '';
if (this.isFirst_ && this.box_) {
this.box_.setAttribute('fill', this.sourceBlock_.getColour());
} else if (!this.isFirst_ && this.box2_) {

View File

@@ -26,7 +26,7 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
this.width_ = parseInt(options.width) || 380;
this.setText = Blockly.FieldDropdown.prototype.setText;
this.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
this.updateWidth = (Blockly.Field as any).prototype.updateWidth;
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
if (!pxt.BrowserUtils.isIE() && !soundCache) {
@@ -78,14 +78,14 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
contentDiv.style.width = (this as any).width_ + 'px';
contentDiv.style.cssFloat = 'left';
(dropdownDiv as HTMLElement).style.maxHeight = `410px`;
dropdownDiv.style.maxHeight = `410px`;
dropdownDiv.appendChild(categoriesDiv);
dropdownDiv.appendChild(contentDiv);
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
// Calculate positioning based on the field position.
let scale = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
let scale = this.sourceBlock_.workspace.scale;
let bBox = { width: this.size_.width, height: this.size_.height };
bBox.width *= scale;
bBox.height *= scale;
@@ -95,7 +95,7 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
let secondaryX = primaryX;
let secondaryY = position.top;
// Set bounds to workspace; show the drop-down.
(Blockly.DropDownDiv as any).setBoundsElement((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
this.onHide_.bind(this));
@@ -236,7 +236,7 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
protected onHide_() {
super.onHide_();
(Blockly.DropDownDiv.getContentDiv() as HTMLElement).style.maxHeight = '';
Blockly.DropDownDiv.getContentDiv().style.maxHeight = '';
this.stopSounds();
}

View File

@@ -17,7 +17,7 @@ export class FieldPorts extends pxtblockly.FieldImages implements Blockly.FieldC
this.width_ = parseInt(options.width) || 300;
this.setText = Blockly.FieldDropdown.prototype.setText;
this.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
this.updateWidth = (Blockly.Field as any).prototype.updateWidth;
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
}

View File

@@ -0,0 +1,98 @@
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
export interface FieldSpeedOptions extends Blockly.FieldCustomOptions {
min?: string;
max?: string;
label?: string;
}
export class FieldSpeed extends Blockly.FieldSlider implements Blockly.FieldCustom {
public isFieldCustom_ = true;
private params: any;
private speedSVG: SVGElement;
private circleBar: SVGCircleElement;
private reporter: SVGTextElement;
/**
* Class for a color wheel field.
* @param {number|string} value The initial content of the field.
* @param {Function=} opt_validator An optional function that is called
* to validate any constraints on what the user entered. Takes the new
* text as an argument and returns either the accepted text, a replacement
* text, or null to abort the change.
* @extends {Blockly.FieldNumber}
* @constructor
*/
constructor(value_: any, params: FieldSpeedOptions, opt_validator?: Function) {
super(String(value_), '-100', '100', null, '10', 'Speed', opt_validator);
this.params = params;
if (this.params['min']) this.min_ = parseFloat(this.params.min);
if (this.params['max']) this.max_ = parseFloat(this.params.max);
if (this.params['label']) this.labelText_ = this.params.label;
(this as any).sliderColor_ = '#a8aaa8';
}
createLabelDom_(labelText: string) {
var labelContainer = document.createElement('div');
this.speedSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGGElement;
pxsim.svg.hydrate(this.speedSVG, {
viewBox: "0 0 200 100",
width: "170"
});
labelContainer.appendChild(this.speedSVG);
const outerCircle = pxsim.svg.child(this.speedSVG, "circle", {
'stroke-dasharray': '565.48', 'stroke-dashoffset': '0',
'cx': 100, 'cy': 100, 'r': '90', 'style': `fill:transparent; transition: stroke-dashoffset 0.1s linear;`,
'stroke': '#a8aaa8', 'stroke-width': '1rem'
}) as SVGCircleElement;
this.circleBar = pxsim.svg.child(this.speedSVG, "circle", {
'stroke-dasharray': '565.48', 'stroke-dashoffset': '0',
'cx': 100, 'cy': 100, 'r': '90', 'style': `fill:transparent; transition: stroke-dashoffset 0.1s linear;`,
'stroke': '#f12a21', 'stroke-width': '1rem'
}) as SVGCircleElement;
this.reporter = pxsim.svg.child(this.speedSVG, "text", {
'x': 100, 'y': 80,
'text-anchor': 'middle', 'dominant-baseline': 'middle',
'style': 'font-size: 50px',
'class': 'sim-text inverted number'
}) as SVGTextElement;
// labelContainer.setAttribute('class', 'blocklyFieldSliderLabel');
var readout = document.createElement('span');
readout.setAttribute('class', 'blocklyFieldSliderReadout');
// var label = document.createElement('span');
// label.setAttribute('class', 'blocklyFieldSliderLabelText');
// label.innerHTML = labelText;
// labelContainer.appendChild(label);
// labelContainer.appendChild(readout);
return [labelContainer, readout];
};
setReadout_(readout: Element, value: string) {
let x = parseFloat(value) || 0;
this.updateSpeed(x);
// Update reporter
this.reporter.textContent = `${x}%`;
}
private updateSpeed(speed: number) {
let sign = this.sign(speed);
speed = (Math.abs(speed) / 100 * 50) + 50;
if (sign == -1) speed = 50 - speed;
let c = Math.PI * (90 * 2);
let pct = ((100 - speed) / 100) * c;
this.circleBar.setAttribute('stroke-dashoffset', `${pct}`);
}
// A re-implementation of Math.sign (since IE11 doesn't support it)
private sign(num: number) {
return num ? num < 0 ? -1 : 1 : 0;
}
}

View File

@@ -0,0 +1,108 @@
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
export interface FieldTurnRatioOptions extends Blockly.FieldCustomOptions {
}
export class FieldTurnRatio extends Blockly.FieldSlider implements Blockly.FieldCustom {
public isFieldCustom_ = true;
private params: any;
private path_: SVGPathElement;
private reporter_: SVGTextElement;
/**
* Class for a color wheel field.
* @param {number|string} value The initial content of the field.
* @param {Function=} opt_validator An optional function that is called
* to validate any constraints on what the user entered. Takes the new
* text as an argument and returns either the accepted text, a replacement
* text, or null to abort the change.
* @extends {Blockly.FieldNumber}
* @constructor
*/
constructor(value_: any, params: FieldTurnRatioOptions, opt_validator?: Function) {
super(String(value_), '-200', '200', null, '10', 'TurnRatio', opt_validator);
this.params = params;
(this as any).sliderColor_ = '#a8aaa8';
}
static HALF = 80;
static HANDLE_RADIUS = 30;
static RADIUS = FieldTurnRatio.HALF - FieldTurnRatio.HANDLE_RADIUS - 1;
createLabelDom_(labelText: string) {
let labelContainer = document.createElement('div');
let svg = Blockly.utils.createSvgElement('svg', {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:html': 'http://www.w3.org/1999/xhtml',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'version': '1.1',
'height': (FieldTurnRatio.HALF + FieldTurnRatio.HANDLE_RADIUS + 10) + 'px',
'width': (FieldTurnRatio.HALF * 2) + 'px'
}, labelContainer);
let defs = Blockly.utils.createSvgElement('defs', {}, svg);
let marker = Blockly.utils.createSvgElement('marker', {
'id': 'head',
'orient': "auto",
'markerWidth': '2',
'markerHeight': '4',
'refX': '0.1', 'refY': '1.5'
}, defs);
let markerPath = Blockly.utils.createSvgElement('path', {
'd': 'M0,0 V3 L1.5,1.5 Z',
'fill': '#f12a21'
}, marker);
this.reporter_ = pxsim.svg.child(svg, "text", {
'x': FieldTurnRatio.HALF, 'y': 96,
'text-anchor': 'middle', 'dominant-baseline': 'middle',
'style': 'font-size: 50px',
'class': 'sim-text inverted number'
}) as SVGTextElement;
this.path_ = Blockly.utils.createSvgElement('path', {
'x1': FieldTurnRatio.HALF,
'y1': FieldTurnRatio.HALF,
'marker-end': 'url(#head)',
'style': 'fill: none; stroke: #f12a21; stroke-width: 10'
}, svg);
this.updateGraph_();
let readout = document.createElement('span');
readout.setAttribute('class', 'blocklyFieldSliderReadout');
return [labelContainer, readout];
};
updateGraph_() {
if (!this.path_) {
return;
}
let v = goog.math.clamp(parseFloat(this.getText()), -200, 200);
if (isNaN(v)) {
v = 0;
}
const x = v / 100;
const nx = Math.max(-1, Math.min(1, x));
const theta = Math.max(nx) * Math.PI / 2;
const r = FieldTurnRatio.RADIUS - 6;
let cx = FieldTurnRatio.HALF;
const cy = FieldTurnRatio.HALF - 22;
if (Math.abs(x) > 1) {
cx -= (x - (x > 0 ? 1 : -1)) * r / 2; // move center of circle
}
const alpha = 0.2 + Math.abs(nx) * 0.5;
const y1 = r * alpha;
const y2 = r * Math.sin(Math.PI / 2 - theta);
const x2 = r * Math.cos(Math.PI / 2 - theta);
const y3 = y2 - r * alpha * Math.cos(2 * theta);
const x3 = x2 - r * alpha * Math.sin(2 * theta);
const d = `M ${cx} ${cy} C ${cx} ${cy - y1} ${cx + x3} ${cy - y3} ${cx + x2} ${cy - y2}`;
this.path_.setAttribute('d', d);
this.reporter_.textContent = `${v}`;
}
setReadout_(readout: Element, value: string) {
this.updateGraph_();
}
}

View File

@@ -22,8 +22,13 @@
}
declare const enum PerfCounters {
GC = 0,
declare const enum ValType {
Undefined = 0,
Boolean = 1,
Number = 2,
String = 3,
Object = 4,
Function = 5,
}
// Auto-generated. Do not edit. Really.

76
libs/base/shims.d.ts vendored
View File

@@ -4,18 +4,6 @@
//% indexerGet=BufferMethods::getByte indexerSet=BufferMethods::setByte
declare interface Buffer {
/**
* Reads an unsigned byte at a particular location
*/
//% shim=BufferMethods::getUint8
getUint8(off: int32): int32;
/**
* Writes an unsigned byte at a particular location
*/
//% shim=BufferMethods::setUint8
setUint8(off: int32, v: int32): void;
/**
* Write a number in specified format in the buffer.
*/
@@ -54,12 +42,6 @@ declare interface Buffer {
//% start.defl=0 length.defl=-1 shim=BufferMethods::shift
shift(offset: int32, start?: int32, length?: int32): void;
/**
* Convert a buffer to string assuming UTF8 encoding
*/
//% shim=BufferMethods::toString
toString(): string;
/**
* Convert a buffer to its hexadecimal representation.
*/
@@ -90,13 +72,6 @@ declare namespace control {
*/
//% shim=control::createBuffer
function createBuffer(size: int32): Buffer;
/**
* Create a new buffer with UTF8-encoded string
* @param str the string to put in the buffer
*/
//% shim=control::createBufferFromUTF8
function createBufferFromUTF8(str: string): Buffer;
}
declare namespace loops {
@@ -126,12 +101,6 @@ declare namespace control {
//% blockId=control_running_time block="millis (ms)" shim=control::millis
function millis(): int32;
/**
* Gets current time in microseconds. Overflows every ~18 minutes.
*/
//% shim=control::micros
function micros(): int32;
/**
* Used internally
*/
@@ -174,54 +143,11 @@ declare namespace control {
//% help=control/device-serial-number shim=control::deviceSerialNumber
function deviceSerialNumber(): int32;
/**
* Derive a unique, consistent 64-bit serial number of this device from internal data.
*/
//% blockId="control_device_long_serial_number" block="device long serial number" weight=9
//% help=control/device-long-serial-number shim=control::deviceLongSerialNumber
function deviceLongSerialNumber(): Buffer;
/**
*
*/
//% shim=control::__log
function __log(prority: int32, text: string): void;
/**
* Dump internal information about a value.
*/
//% shim=control::dmesgValue
function dmesgValue(v: any): void;
/**
* Force GC and dump basic information about heap.
*/
//% shim=control::gc
function gc(): void;
/**
* Force GC and halt waiting for debugger to do a full heap dump.
*/
//% shim=control::heapDump
function heapDump(): void;
/**
* Set flags used when connecting an external debugger.
*/
//% shim=control::setDebugFlags
function setDebugFlags(flags: int32): void;
/**
* Record a heap snapshot to debug memory leaks.
*/
//% shim=control::heapSnapshot
function heapSnapshot(): void;
/**
* Return true if profiling is enabled in the current build.
*/
//% shim=control::profilingEnabled
function profilingEnabled(): boolean;
function __log(text: string): void;
}
// Auto-generated. Do not edit. Really.

40
libs/core/control.cpp Normal file
View File

@@ -0,0 +1,40 @@
#include "pxt.h"
namespace control {
/**
* Announce that an event happened to registered handlers.
* @param src ID of the Component that generated the event
* @param value Component specific code indicating the cause of the event.
* @param mode optional definition of how the event should be processed after construction.
*/
//% weight=21 blockGap=12 blockId="control_raise_event"
//% block="raise event|from %src|with value %value" blockExternalInputs=1
//% help=control/raise-event
void raiseEvent(int src, int value) {
pxt::raiseEvent(src, value);
}
/**
* Allocates the next user notification event
*/
//% help=control/allocate-notify-event
int allocateNotifyEvent() {
return pxt::allocateNotifyEvent();
}
/** Write data to DMESG debugging buffer. */
//%
void dmesg(String s) {
DMESG("# %s", s->data);
}
}
namespace serial {
/** Send DMESG debug buffer over serial. */
//%
void writeDmesg() {
pxt::dumpDmesg();
}
}

11
libs/core/cooperate.ts Normal file
View File

@@ -0,0 +1,11 @@
namespace control {
let lastPause = 0;
const COOPERATION_INTERVAL = 20
export function cooperate() {
const now = control.millis()
if (now - lastPause > COOPERATION_INTERVAL) {
lastPause = now
pause(1)
}
}
}

407
libs/core/dal.d.ts vendored
View File

@@ -1,305 +1,6 @@
// Auto-generated. Do not edit.
declare const enum DAL {
// /pxtapp/configkeys.h
CFG_PIN_NAME_MSK = 65535,
CFG_PIN_CONFIG_MSK = 4294901760,
CFG_PIN_CONFIG_ACTIVE_LO = 65536,
CFG_MAGIC0 = 513675505,
CFG_MAGIC1 = 539130489,
CFG_PIN_ACCELEROMETER_INT = 1,
CFG_PIN_ACCELEROMETER_SCL = 2,
CFG_PIN_ACCELEROMETER_SDA = 3,
CFG_PIN_BTN_A = 4,
CFG_PIN_BTN_B = 5,
CFG_PIN_BTN_SLIDE = 6,
CFG_PIN_DOTSTAR_CLOCK = 7,
CFG_PIN_DOTSTAR_DATA = 8,
CFG_PIN_FLASH_CS = 9,
CFG_PIN_FLASH_MISO = 10,
CFG_PIN_FLASH_MOSI = 11,
CFG_PIN_FLASH_SCK = 12,
CFG_PIN_LED = 13,
CFG_PIN_LIGHT = 14,
CFG_PIN_MICROPHONE = 15,
CFG_PIN_MIC_CLOCK = 16,
CFG_PIN_MIC_DATA = 17,
CFG_PIN_MISO = 18,
CFG_PIN_MOSI = 19,
CFG_PIN_NEOPIXEL = 20,
CFG_PIN_RX = 21,
CFG_PIN_RXLED = 22,
CFG_PIN_SCK = 23,
CFG_PIN_SCL = 24,
CFG_PIN_SDA = 25,
CFG_PIN_SPEAKER_AMP = 26,
CFG_PIN_TEMPERATURE = 27,
CFG_PIN_TX = 28,
CFG_PIN_TXLED = 29,
CFG_PIN_IR_OUT = 30,
CFG_PIN_IR_IN = 31,
CFG_PIN_DISPLAY_SCK = 32,
CFG_PIN_DISPLAY_MISO = 33,
CFG_PIN_DISPLAY_MOSI = 34,
CFG_PIN_DISPLAY_CS = 35,
CFG_PIN_DISPLAY_DC = 36,
CFG_DISPLAY_WIDTH = 37,
CFG_DISPLAY_HEIGHT = 38,
CFG_DISPLAY_CFG0 = 39,
CFG_DISPLAY_CFG1 = 40,
CFG_DISPLAY_CFG2 = 41,
CFG_DISPLAY_CFG3 = 42,
CFG_PIN_DISPLAY_RST = 43,
CFG_PIN_DISPLAY_BL = 44,
CFG_PIN_SERVO_1 = 45,
CFG_PIN_SERVO_2 = 46,
CFG_PIN_BTN_LEFT = 47,
CFG_PIN_BTN_RIGHT = 48,
CFG_PIN_BTN_UP = 49,
CFG_PIN_BTN_DOWN = 50,
CFG_PIN_BTN_MENU = 51,
CFG_PIN_LED_R = 52,
CFG_PIN_LED_G = 53,
CFG_PIN_LED_B = 54,
CFG_PIN_LED1 = 55,
CFG_PIN_LED2 = 56,
CFG_PIN_LED3 = 57,
CFG_PIN_LED4 = 58,
CFG_SPEAKER_VOLUME = 59,
CFG_PIN_JACK_TX = 60,
CFG_PIN_JACK_SENSE = 61,
CFG_PIN_JACK_HPEN = 62,
CFG_PIN_JACK_BZEN = 63,
CFG_PIN_JACK_PWREN = 64,
CFG_PIN_JACK_SND = 65,
CFG_PIN_JACK_BUSLED = 66,
CFG_PIN_JACK_COMMLED = 67,
CFG_PIN_BTN_SOFT_RESET = 69,
CFG_ACCELEROMETER_TYPE = 70,
CFG_PIN_BTNMX_LATCH = 71,
CFG_PIN_BTNMX_CLOCK = 72,
CFG_PIN_BTNMX_DATA = 73,
CFG_PIN_BTN_MENU2 = 74,
CFG_PIN_BATTSENSE = 75,
CFG_PIN_VIBRATION = 76,
CFG_PIN_PWREN = 77,
CFG_DISPLAY_TYPE = 78,
CFG_PIN_ROTARY_ENCODER_A = 79,
CFG_PIN_ROTARY_ENCODER_B = 80,
CFG_ACCELEROMETER_SPACE = 81,
CFG_PIN_WIFI_MOSI = 82,
CFG_PIN_WIFI_MISO = 83,
CFG_PIN_WIFI_SCK = 84,
CFG_PIN_WIFI_TX = 85,
CFG_PIN_WIFI_RX = 86,
CFG_PIN_WIFI_CS = 87,
CFG_PIN_WIFI_BUSY = 88,
CFG_PIN_WIFI_RESET = 89,
CFG_PIN_WIFI_GPIO0 = 90,
CFG_PIN_WIFI_AT_TX = 91,
CFG_PIN_WIFI_AT_RX = 92,
CFG_PIN_USB_POWER = 93,
ACCELEROMETER_TYPE_LIS3DH = 50,
ACCELEROMETER_TYPE_LIS3DH_ALT = 48,
ACCELEROMETER_TYPE_MMA8453 = 56,
ACCELEROMETER_TYPE_FXOS8700 = 60,
ACCELEROMETER_TYPE_MMA8653 = 58,
ACCELEROMETER_TYPE_MSA300 = 76,
ACCELEROMETER_TYPE_MPU6050 = 104,
DISPLAY_TYPE_ST7735 = 7735,
DISPLAY_TYPE_ILI9341 = 9341,
CFG_PIN_A0 = 100,
CFG_PIN_A1 = 101,
CFG_PIN_A2 = 102,
CFG_PIN_A3 = 103,
CFG_PIN_A4 = 104,
CFG_PIN_A5 = 105,
CFG_PIN_A6 = 106,
CFG_PIN_A7 = 107,
CFG_PIN_A8 = 108,
CFG_PIN_A9 = 109,
CFG_PIN_A10 = 110,
CFG_PIN_A11 = 111,
CFG_PIN_A12 = 112,
CFG_PIN_A13 = 113,
CFG_PIN_A14 = 114,
CFG_PIN_A15 = 115,
CFG_PIN_A16 = 116,
CFG_PIN_A17 = 117,
CFG_PIN_A18 = 118,
CFG_PIN_A19 = 119,
CFG_PIN_A20 = 120,
CFG_PIN_A21 = 121,
CFG_PIN_A22 = 122,
CFG_PIN_A23 = 123,
CFG_PIN_A24 = 124,
CFG_PIN_A25 = 125,
CFG_PIN_A26 = 126,
CFG_PIN_A27 = 127,
CFG_PIN_A28 = 128,
CFG_PIN_A29 = 129,
CFG_PIN_A30 = 130,
CFG_PIN_A31 = 131,
CFG_PIN_D0 = 150,
CFG_PIN_D1 = 151,
CFG_PIN_D2 = 152,
CFG_PIN_D3 = 153,
CFG_PIN_D4 = 154,
CFG_PIN_D5 = 155,
CFG_PIN_D6 = 156,
CFG_PIN_D7 = 157,
CFG_PIN_D8 = 158,
CFG_PIN_D9 = 159,
CFG_PIN_D10 = 160,
CFG_PIN_D11 = 161,
CFG_PIN_D12 = 162,
CFG_PIN_D13 = 163,
CFG_PIN_D14 = 164,
CFG_PIN_D15 = 165,
CFG_PIN_D16 = 166,
CFG_PIN_D17 = 167,
CFG_PIN_D18 = 168,
CFG_PIN_D19 = 169,
CFG_PIN_D20 = 170,
CFG_PIN_D21 = 171,
CFG_PIN_D22 = 172,
CFG_PIN_D23 = 173,
CFG_PIN_D24 = 174,
CFG_PIN_D25 = 175,
CFG_PIN_D26 = 176,
CFG_PIN_D27 = 177,
CFG_PIN_D28 = 178,
CFG_PIN_D29 = 179,
CFG_PIN_D30 = 180,
CFG_PIN_D31 = 181,
CFG_NUM_NEOPIXELS = 200,
CFG_NUM_DOTSTARS = 201,
CFG_DEFAULT_BUTTON_MODE = 202,
CFG_SWD_ENABLED = 203,
CFG_FLASH_BYTES = 204,
CFG_RAM_BYTES = 205,
CFG_SYSTEM_HEAP_BYTES = 206,
CFG_LOW_MEM_SIMULATION_KB = 207,
CFG_BOOTLOADER_BOARD_ID = 208,
CFG_UF2_FAMILY = 209,
CFG_PINS_PORT_SIZE = 210,
CFG_BOOTLOADER_PROTECTION = 211,
CFG_POWER_DEEPSLEEP_TIMEOUT = 212,
CFG_ANALOG_BUTTON_THRESHOLD = 213,
CFG_CPU_MHZ = 214,
CFG_CONTROLLER_LIGHT_MAX_BRIGHTNESS = 215,
CFG_PIN_B0 = 300,
CFG_PIN_B1 = 301,
CFG_PIN_B2 = 302,
CFG_PIN_B3 = 303,
CFG_PIN_B4 = 304,
CFG_PIN_B5 = 305,
CFG_PIN_B6 = 306,
CFG_PIN_B7 = 307,
CFG_PIN_B8 = 308,
CFG_PIN_B9 = 309,
CFG_PIN_B10 = 310,
CFG_PIN_B11 = 311,
CFG_PIN_B12 = 312,
CFG_PIN_B13 = 313,
CFG_PIN_B14 = 314,
CFG_PIN_B15 = 315,
CFG_PIN_B16 = 316,
CFG_PIN_B17 = 317,
CFG_PIN_B18 = 318,
CFG_PIN_B19 = 319,
CFG_PIN_B20 = 320,
CFG_PIN_B21 = 321,
CFG_PIN_B22 = 322,
CFG_PIN_B23 = 323,
CFG_PIN_B24 = 324,
CFG_PIN_B25 = 325,
CFG_PIN_B26 = 326,
CFG_PIN_B27 = 327,
CFG_PIN_B28 = 328,
CFG_PIN_B29 = 329,
CFG_PIN_B30 = 330,
CFG_PIN_B31 = 331,
CFG_PIN_C0 = 350,
CFG_PIN_C1 = 351,
CFG_PIN_C2 = 352,
CFG_PIN_C3 = 353,
CFG_PIN_C4 = 354,
CFG_PIN_C5 = 355,
CFG_PIN_C6 = 356,
CFG_PIN_C7 = 357,
CFG_PIN_C8 = 358,
CFG_PIN_C9 = 359,
CFG_PIN_C10 = 360,
CFG_PIN_C11 = 361,
CFG_PIN_C12 = 362,
CFG_PIN_C13 = 363,
CFG_PIN_C14 = 364,
CFG_PIN_C15 = 365,
CFG_PIN_C16 = 366,
CFG_PIN_C17 = 367,
CFG_PIN_C18 = 368,
CFG_PIN_C19 = 369,
CFG_PIN_C20 = 370,
CFG_PIN_C21 = 371,
CFG_PIN_C22 = 372,
CFG_PIN_C23 = 373,
CFG_PIN_C24 = 374,
CFG_PIN_C25 = 375,
CFG_PIN_C26 = 376,
CFG_PIN_C27 = 377,
CFG_PIN_C28 = 378,
CFG_PIN_C29 = 379,
CFG_PIN_C30 = 380,
CFG_PIN_C31 = 381,
CFG_PIN_P0 = 400,
CFG_PIN_P1 = 401,
CFG_PIN_P2 = 402,
CFG_PIN_P3 = 403,
CFG_PIN_P4 = 404,
CFG_PIN_P5 = 405,
CFG_PIN_P6 = 406,
CFG_PIN_P7 = 407,
CFG_PIN_P8 = 408,
CFG_PIN_P9 = 409,
CFG_PIN_P10 = 410,
CFG_PIN_P11 = 411,
CFG_PIN_P12 = 412,
CFG_PIN_P13 = 413,
CFG_PIN_P14 = 414,
CFG_PIN_P15 = 415,
CFG_PIN_P16 = 416,
CFG_PIN_P17 = 417,
CFG_PIN_P18 = 418,
CFG_PIN_P19 = 419,
CFG_PIN_P20 = 420,
CFG_PIN_P21 = 421,
CFG_PIN_P22 = 422,
CFG_PIN_P23 = 423,
CFG_PIN_P24 = 424,
CFG_PIN_P25 = 425,
CFG_PIN_P26 = 426,
CFG_PIN_P27 = 427,
CFG_PIN_P28 = 428,
CFG_PIN_P29 = 429,
CFG_PIN_P30 = 430,
CFG_PIN_P31 = 431,
CFG_PIN_LORA_MISO = 1001,
CFG_PIN_LORA_MOSI = 1002,
CFG_PIN_LORA_SCK = 1003,
CFG_PIN_LORA_CS = 1004,
CFG_PIN_LORA_BOOT = 1005,
CFG_PIN_LORA_RESET = 1006,
CFG_PIN_IRRXLED = 1007,
CFG_PIN_IRTXLED = 1008,
CFG_PIN_LCD_RESET = 1009,
CFG_PIN_LCD_ENABLE = 1010,
CFG_PIN_LCD_DATALINE4 = 1011,
CFG_PIN_LCD_DATALINE5 = 1012,
CFG_PIN_LCD_DATALINE6 = 1013,
CFG_PIN_LCD_DATALINE7 = 1014,
CFG_NUM_LCD_COLUMNS = 1015,
CFG_NUM_LCD_ROWS = 1016,
// /pxtapp/ev3const.h
// built/dockermake/pxtapp/ev3const.h
NUM_INPUTS = 4,
NUM_OUTPUTS = 4,
LCD_WIDTH = 178,
@@ -345,66 +46,44 @@ declare const enum DAL {
CONN_OUTPUT_TACHO = 125,
CONN_NONE = 126,
CONN_ERROR = 127,
opProgramStart = 3,
opOutputGetType = 160,
opOutputSetType = 161,
opOutputReset = 162,
opOutputStop = 163,
opOutputPower = 164,
opOutputSpeed = 165,
opOutputStart = 166,
opOutputPolarity = 167,
opOutputRead = 168,
opOutputTest = 169,
opOutputReady = 170,
opOutputPosition = 171,
opOutputStepPower = 172,
opOutputTimePower = 173,
opOutputStepSpeed = 174,
opOutputTimeSpeed = 175,
opOutputStepSync = 176,
opOutputTimeSync = 177,
opOutputClearCount = 178,
opOutputGetCount = 179,
opOutputProgramStop = 180,
BUTTON_ID_UP = 1,
BUTTON_ID_ENTER = 2,
BUTTON_ID_DOWN = 4,
BUTTON_ID_RIGHT = 8,
BUTTON_ID_LEFT = 16,
BUTTON_ID_ESCAPE = 32,
// /pxtapp/platform.h
PXT_GC_THREAD_LIST = 1,
// /pxtapp/pxt.h
opProgramStart = 0x03,
opOutputGetType = 0xA0,
opOutputSetType = 0xA1,
opOutputReset = 0xA2,
opOutputStop = 0xA3,
opOutputPower = 0xA4,
opOutputSpeed = 0xA5,
opOutputStart = 0xA6,
opOutputPolarity = 0xA7,
opOutputRead = 0xA8,
opOutputTest = 0xA9,
opOutputReady = 0xAA,
opOutputPosition = 0xAB,
opOutputStepPower = 0xAC,
opOutputTimePower = 0xAD,
opOutputStepSpeed = 0xAE,
opOutputTimeSpeed = 0xAF,
opOutputStepSync = 0xB0,
opOutputTimeSync = 0xB1,
opOutputClearCount = 0xB2,
opOutputGetCount = 0xB3,
opOutputProgramStop = 0xB4,
BUTTON_ID_UP = 0x01,
BUTTON_ID_ENTER = 0x02,
BUTTON_ID_DOWN = 0x04,
BUTTON_ID_RIGHT = 0x08,
BUTTON_ID_LEFT = 0x10,
BUTTON_ID_ESCAPE = 0x20,
// built/dockermake/pxtapp/pxt.h
DEVICE_EVT_ANY = 0,
DEVICE_ID_NOTIFY = 10000,
DEVICE_ID_NOTIFY_ONE = 10001,
IMAGE_BITS = 1,
// /pxtapp/pxtbase.h
PXT32 = 1,
PXT64 = 1,
PXT_VTABLE_SHIFT = 2,
PXT_REFCNT_FLASH = 65534,
VTABLE_MAGIC = 249,
Undefined = 0,
Boolean = 1,
Number = 2,
String = 3,
Object = 4,
Function = 5,
BoxedString = 1,
BoxedNumber = 2,
BoxedBuffer = 3,
RefAction = 4,
RefImage = 5,
RefCollection = 6,
RefRefLocal = 7,
RefMap = 8,
RefMImage = 9,
MMap = 10,
User0 = 16,
PXT_IOS_HEAP_ALLOC_BITS = 20,
IMAGE_HEADER_MAGIC = 135,
// built/dockermake/pxtapp/pxtbase.h
PXT_REF_TAG_STRING = 1,
PXT_REF_TAG_BUFFER = 2,
PXT_REF_TAG_IMAGE = 3,
PXT_REF_TAG_NUMBER = 32,
PXT_REF_TAG_ACTION = 33,
Int8LE = 1,
UInt8LE = 2,
Int16LE = 3,
@@ -421,10 +100,12 @@ declare const enum DAL {
Float64LE = 14,
Float32BE = 15,
Float64BE = 16,
NUM_TRY_FRAME_REGS = 3,
GC = 0,
// /pxtapp/pxtconfig.h
PXT_GC = 1,
// /pxtapp/pxtcore.h
PXT_HARD_FLOAT = 1,
Undefined = 0,
Boolean = 1,
Number = 2,
String = 3,
Object = 4,
Function = 5,
// built/dockermake/pxtapp/pxtconfig.h
// built/dockermake/pxtapp/pxtcore.h
}

View File

@@ -21,8 +21,7 @@ namespace sensors.internal {
const now = control.millis();
if (now - this.lastQuery >= this.interval * 2)
this.queryAndUpdate(); // sensor poller is not allowed to run
if (now - this.lastPause >= this.interval * 5)
pause(1); // allow events to trigger
control.cooperate(); // allow events to trigger
}
private queryAndUpdate() {
@@ -104,7 +103,7 @@ namespace sensors.internal {
}
}
function init() {
export function init() {
if (sensorInfos) return
sensorInfos = []
for (let i = 0; i < DAL.NUM_INPUTS; ++i) sensorInfos.push(new SensorInfo(i))
@@ -137,6 +136,7 @@ namespace sensors.internal {
buf[UartCtlOff.Port] = port
buf[UartCtlOff.Mode] = mode
uartMM.ioctl(IO.UART_READ_MODE_INFO, buf)
control.dmesg(`UART_READ_MODE_INFO ${buf.toHex()}`)
return buf
//let info = `t:${buf[TypesOff.Type]} c:${buf[TypesOff.Connection]} m:${buf[TypesOff.Mode]} n:${buf.slice(0, 12).toHex()}`
//serial.writeLine("UART " + port + " / " + mode + " - " + info)
@@ -208,14 +208,12 @@ namespace sensors.internal {
}
}
export interface BatteryInfo {
export function getBatteryInfo(): {
level: number;
Ibatt: number,
Vbatt: number,
Imotor: number
}
export function getBatteryInfo(): BatteryInfo {
} {
init();
if (!batteryInfo) updateBatteryInfo();
const CinCnt = batteryInfo.CinCnt;
@@ -294,8 +292,7 @@ void cUiUpdatePower(void)
if (newConn == sensorInfo.connType
&& sensorInfo.sensor
&& sensorInfo.sensor.isActive()) {
if (newConn == DAL.CONN_INPUT_UART)
uartSensors.push(sensorInfo);
// control.dmesg(`connection unchanged ${newConn} at ${sensorInfo.port}`)
continue;
}
numChanged++
@@ -326,8 +323,7 @@ void cUiUpdatePower(void)
for (const sensorInfo of uartSensors) {
let uinfo = readUartInfo(sensorInfo.port, 0)
sensorInfo.devType = uinfo[TypesOff.Type]
const mode = uinfo[TypesOff.Mode];
control.dmesg(`UART type ${sensorInfo.devType} mode ${mode}`)
control.dmesg(`UART type ${sensorInfo.devType}`)
}
}
@@ -431,11 +427,11 @@ void cUiUpdatePower(void)
constructor(port: number) {
super(port)
this.mode = 0
this.realmode = 0
this.realmode = -1
}
_activated() {
this.realmode = 0
this.realmode = -1
this._setMode(this.mode)
}
@@ -540,6 +536,8 @@ void cUiUpdatePower(void)
function uartClearChange(port: number) {
control.dmesg(`UART clear change`);
const UART_DATA_READY = 8
const UART_PORT_CHANGED = 1
while (true) {
let status = getUartStatus(port)
if (port < 0) break
@@ -560,7 +558,7 @@ void cUiUpdatePower(void)
}
function setUartModes() {
control.dmesg(`UART set modes 0x${devcon.toHex()}`)
control.dmesg(`UART set modes ${devcon.toHex()}`)
uartMM.ioctl(IO.UART_SET_CONN, devcon)
const ports: number[] = [];
for (let port = 0; port < DAL.NUM_INPUTS; ++port) {
@@ -573,25 +571,22 @@ void cUiUpdatePower(void)
const port = ports.pop();
const status = waitNonZeroUartStatus(port)
control.dmesg(`UART status ${status} at ${port}`);
if (!(status & UART_DATA_READY))
setUartMode(port, devcon[DevConOff.Mode + port]);
}
}
function updateUartMode(port: number, mode: number) {
control.dmesg(`UART update mode to ${mode} at ${port}`)
control.dmesg(`UART set mode to ${mode} at ${port}`)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_INPUT_UART)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, 33)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, mode)
}
const UART_PORT_CHANGED = 1
const UART_DATA_READY = 8
function setUartMode(port: number, mode: number) {
const UART_PORT_CHANGED = 1
while (true) {
if (port < 0) return
updateUartMode(port, mode);
control.dmesg(`UART set mode 0x${devcon.toHex()}`)
control.dmesg(`UART_SET_CONN ${devcon.toHex()}`)
uartMM.ioctl(IO.UART_SET_CONN, devcon)
let status = waitNonZeroUartStatus(port)
if (status & UART_PORT_CHANGED) {
@@ -599,8 +594,7 @@ void cUiUpdatePower(void)
uartClearChange(port)
} else {
control.dmesg(`UART status ${status}`);
if (status & UART_DATA_READY)
break;
break;
}
pause(10)
}

589
libs/core/linux.cpp Normal file
View File

@@ -0,0 +1,589 @@
#include "pxt.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <cstdarg>
#include <pthread.h>
#include <unistd.h>
#include <dirent.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include "ev3const.h"
#define THREAD_DBG(...)
#define MALLOC_LIMIT (8 * 1024 * 1024)
#define MALLOC_CHECK_PERIOD (1024 * 1024)
void *xmalloc(size_t sz) {
static size_t allocBytes = 0;
allocBytes += sz;
if (allocBytes >= MALLOC_CHECK_PERIOD) {
allocBytes = 0;
auto info = mallinfo();
DMESG("malloc used: %d kb", info.uordblks / 1024);
if (info.uordblks > MALLOC_LIMIT) {
target_panic(904);
}
}
auto r = malloc(sz);
if (r == NULL)
target_panic(905); // shouldn't happen
return r;
}
void *operator new(size_t size) {
return xmalloc(size);
}
void *operator new[](size_t size) {
return xmalloc(size);
}
void operator delete(void *p) {
free(p);
}
void operator delete[](void *p) {
free(p);
}
namespace pxt {
static int startTime;
static pthread_mutex_t execMutex;
static pthread_mutex_t eventMutex;
static pthread_cond_t newEventBroadcast;
struct Thread {
struct Thread *next;
Action act;
TValue arg0;
pthread_t pid;
pthread_cond_t waitCond;
int waitSource;
int waitValue;
TValue data0;
TValue data1;
};
static struct Thread *allThreads;
static struct Event *eventHead, *eventTail;
static int usbFD;
static int dmesgPtr;
static int dmesgSerialPtr;
static char dmesgBuf[4096];
struct Event {
struct Event *next;
int source;
int value;
};
Event lastEvent;
Event *mkEvent(int source, int value) {
auto res = new Event();
memset(res, 0, sizeof(Event));
res->source = source;
res->value = value;
return res;
}
#define USB_MAGIC 0x3d3f
#define USB_SERIAL 1
#define USB_RESTART 2
#define USB_DMESG 3
struct UsbPacket {
uint16_t size;
uint16_t msgcount;
uint16_t magic;
uint16_t code;
char buf[1024 - 8];
};
void *usbThread(void *) {
UsbPacket pkt;
UsbPacket resp;
while (true) {
int len = read(usbFD, &pkt, sizeof(pkt));
if (len <= 4) {
sleep_core_us(20000);
continue;
}
resp.msgcount = pkt.msgcount;
if (pkt.magic == USB_MAGIC) {
if (pkt.code == USB_RESTART) {
target_reset();
} else if (pkt.code == USB_DMESG) {
dumpDmesg();
}
/*
resp.magic = pkt.magic;
resp.code = pkt.code;
resp.size = 8;
write(usbFD, &resp, sizeof(resp));
*/
} else {
resp.magic = 0xffff;
resp.size = 4;
write(usbFD, &resp, sizeof(resp));
}
sleep_core_us(1000);
}
}
static void startUsb() {
usbFD = open("/dev/lms_usbdev", O_RDWR, 0666);
pthread_t pid;
pthread_create(&pid, NULL, usbThread, NULL);
pthread_detach(pid);
}
static void *exitThread(void *) {
int fd = open("/dev/lms_ui", O_RDWR, 0666);
if (fd < 0)
return 0;
uint8_t *data =
(uint8_t *)mmap(NULL, NUM_BUTTONS, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
return 0;
}
for (;;) {
if (data[5])
target_reset();
sleep_core_us(50000);
}
}
static void startExitThread() {
pthread_t pid;
pthread_create(&pid, NULL, exitThread, NULL);
pthread_detach(pid);
}
void sendUsb(uint16_t code, const char *data, int len) {
while (len > 0) {
int sz = len;
if (sz > 1000)
sz = 1000;
UsbPacket pkt = {(uint16_t)(6 + sz), 0, USB_MAGIC, code, {}};
memcpy(pkt.buf, data, sz);
write(usbFD, &pkt, sizeof(pkt));
len -= sz;
data += sz;
}
}
void sendSerial(const char *data, int len) {
sendUsb(USB_SERIAL, data, len);
}
volatile bool paniced;
extern "C" void drawPanic(int code);
extern "C" void target_panic(int error_code) {
char buf[50];
paniced = true;
pthread_mutex_trylock(&execMutex);
snprintf(buf, sizeof(buf), "\nPANIC %d\n", error_code);
drawPanic(error_code);
DMESG("PANIC %d", error_code);
for (int i = 0; i < 10; ++i) {
sendSerial(buf, strlen(buf));
sleep_core_us(500 * 1000);
}
target_reset();
}
void startUser() {
pthread_mutex_lock(&execMutex);
}
void stopUser() {
pthread_mutex_unlock(&execMutex);
}
void sleep_core_us(uint64_t us) {
struct timespec ts;
ts.tv_sec = us / 1000000;
ts.tv_nsec = (us % 1000000) * 1000;
while (nanosleep(&ts, &ts))
;
}
void sleep_ms(uint32_t ms) {
stopUser();
sleep_core_us(ms * 1000);
startUser();
}
void sleep_us(uint64_t us) {
if (us > 50000) {
sleep_ms(us / 1000);
}
sleep_core_us(us);
}
uint64_t currTime() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
int current_time_ms() {
return currTime() - startTime;
}
void disposeThread(Thread *t) {
if (allThreads == t) {
allThreads = t->next;
} else {
for (auto tt = allThreads; tt; tt = tt->next) {
if (tt->next == t) {
tt->next = t->next;
break;
}
}
}
decr(t->act);
decr(t->arg0);
decr(t->data0);
decr(t->data1);
pthread_cond_destroy(&t->waitCond);
delete t;
}
static void runAct(Thread *thr) {
startUser();
pxt::runAction1(thr->act, thr->arg0);
stopUser();
disposeThread(thr);
}
static void mainThread(Thread *) {}
void setupThread(Action a, TValue arg = 0, void (*runner)(Thread *) = NULL, TValue d0 = 0,
TValue d1 = 0) {
if (runner == NULL)
runner = runAct;
auto thr = new Thread();
memset(thr, 0, sizeof(Thread));
thr->next = allThreads;
allThreads = thr;
thr->act = incr(a);
thr->arg0 = incr(arg);
thr->data0 = incr(d0);
thr->data1 = incr(d1);
pthread_cond_init(&thr->waitCond, NULL);
if (runner == mainThread) {
thr->pid = pthread_self();
} else {
pthread_create(&thr->pid, NULL, (void *(*)(void *))runner, thr);
THREAD_DBG("setup thread: %p (pid %p)", thr, thr->pid);
pthread_detach(thr->pid);
}
}
void releaseFiber() {
stopUser();
pthread_exit(NULL);
}
void runInParallel(Action a) {
setupThread(a);
}
static void runFor(Thread *t) {
startUser();
while (true) {
pxt::runAction0(t->act);
sleep_ms(20);
}
}
void runForever(Action a) {
setupThread(a, 0, runFor);
}
void waitForEvent(int source, int value) {
THREAD_DBG("waitForEv: %d %d", source, value);
auto self = pthread_self();
for (auto t = allThreads; t; t = t->next) {
THREAD_DBG("t: %p", t);
if (t->pid == self) {
pthread_mutex_lock(&eventMutex);
t->waitSource = source;
t->waitValue = value;
stopUser();
// spourious wake ups may occur they say
while (t->waitSource) {
pthread_cond_wait(&t->waitCond, &eventMutex);
}
pthread_mutex_unlock(&eventMutex);
startUser();
return;
}
}
DMESG("current thread not registered!");
target_panic(901);
}
static void dispatchEvent(Event &e) {
lastEvent = e;
auto curr = findBinding(e.source, e.value);
if (curr)
setupThread(curr->action, fromInt(e.value));
curr = findBinding(e.source, DEVICE_EVT_ANY);
if (curr)
setupThread(curr->action, fromInt(e.value));
}
static void *evtDispatcher(void *dummy) {
pthread_mutex_lock(&eventMutex);
while (true) {
pthread_cond_wait(&newEventBroadcast, &eventMutex);
while (eventHead != NULL) {
if (paniced)
return 0;
Event *ev = eventHead;
eventHead = ev->next;
if (eventHead == NULL)
eventTail = NULL;
for (auto thr = allThreads; thr; thr = thr->next) {
if (paniced)
return 0;
if (thr->waitSource == 0)
continue;
if (thr->waitValue != ev->value && thr->waitValue != DEVICE_EVT_ANY)
continue;
if (thr->waitSource == ev->source) {
thr->waitSource = 0; // once!
pthread_cond_broadcast(&thr->waitCond);
} else if (thr->waitSource == DEVICE_ID_NOTIFY &&
ev->source == DEVICE_ID_NOTIFY_ONE) {
thr->waitSource = 0; // once!
pthread_cond_broadcast(&thr->waitCond);
break; // do not wake up any other threads
}
}
dispatchEvent(*ev);
delete ev;
}
}
}
int allocateNotifyEvent() {
static volatile int notifyId;
pthread_mutex_lock(&eventMutex);
int res = ++notifyId;
pthread_mutex_unlock(&eventMutex);
return res;
}
void raiseEvent(int id, int event) {
auto e = mkEvent(id, event);
pthread_mutex_lock(&eventMutex);
if (eventTail == NULL) {
if (eventHead != NULL)
target_panic(902);
eventHead = eventTail = e;
} else {
eventTail->next = e;
eventTail = e;
}
pthread_cond_broadcast(&newEventBroadcast);
pthread_mutex_unlock(&eventMutex);
}
void registerWithDal(int id, int event, Action a, int flags) {
// TODO support flags
setBinding(id, event, a);
}
static void runPoller(Thread *thr) {
Action query = thr->data0;
auto us = (uint64_t)toInt(thr->data1) * 1000;
// note that this is run without the user mutex held - it should not modify any state!
TValue prev = pxt::runAction0(query);
startUser();
pxt::runAction2(thr->act, prev, prev);
stopUser();
while (true) {
sleep_core_us(us);
if (paniced)
break;
TValue curr = pxt::runAction0(query);
if (curr != prev) {
startUser();
pxt::runAction2(thr->act, prev, curr);
stopUser();
if (paniced)
break;
decr(prev);
prev = curr;
}
}
// disposeThread(thr);
}
uint32_t afterProgramPage() {
return 0;
}
void dumpDmesg() {
auto len = dmesgPtr - dmesgSerialPtr;
if (len == 0)
return;
sendSerial(dmesgBuf + dmesgSerialPtr, len);
dmesgSerialPtr = dmesgPtr;
}
int lmsPid;
void stopLMS() {
struct dirent *ent;
DIR *dir;
dir = opendir("/proc");
if (dir == NULL)
return;
while ((ent = readdir(dir)) != NULL) {
int pid = atoi(ent->d_name);
if (!pid)
continue;
char namebuf[100];
snprintf(namebuf, 1000, "/proc/%d/cmdline", pid);
FILE *f = fopen(namebuf, "r");
if (f) {
fread(namebuf, 1, 99, f);
if (strcmp(namebuf, "./lms2012") == 0) {
lmsPid = pid;
}
fclose(f);
if (lmsPid)
break;
}
}
closedir(dir);
lmsPid = 0; // disable SIGSTOP for now - rethink if problems with I2C (runs on a thread)
if (lmsPid) {
DMESG("SIGSTOP to lmsPID=%d", lmsPid);
if (kill(lmsPid, SIGSTOP))
DMESG("SIGSTOP failed");
}
}
void runLMS() {
DMESG("re-starting LMS2012");
kill(lmsPid, SIGCONT);
sleep_core_us(200000);
exit(0);
/*
chdir("/home/root/lms2012/sys");
for (int fd = 3; fd < 9999; ++fd)
close(fd);
execl("lms2012", "./lms2012");
exit(100); // should not be reached
*/
}
void stopMotors() {
uint8_t cmd[3] = {opOutputStop, 0x0F, 0};
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 3);
close(fd);
}
void stopProgram() {
uint8_t cmd[1] = {opOutputProgramStop};
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 1);
close(fd);
}
extern "C" void target_reset() {
pthread_mutex_trylock(&execMutex);
stopMotors();
stopProgram();
if (lmsPid)
runLMS();
else
exit(0);
}
void screen_init();
void initRuntime() {
// daemon(1, 1);
startTime = currTime();
DMESG("runtime starting...");
stopLMS();
startUsb();
startExitThread();
pthread_t disp;
pthread_create(&disp, NULL, evtDispatcher, NULL);
pthread_detach(disp);
setupThread(0, 0, mainThread);
target_init();
screen_init();
startUser();
}
static FILE *dmesgFile;
void dmesgRaw(const char *buf, uint32_t len) {
if (!dmesgFile) {
dmesgFile = fopen("/tmp/dmesg.txt", "w");
if (!dmesgFile)
dmesgFile = stderr;
}
if (len > sizeof(dmesgBuf) / 2)
return;
if (dmesgPtr + len > sizeof(dmesgBuf)) {
dmesgPtr = 0;
dmesgSerialPtr = 0;
}
memcpy(dmesgBuf + dmesgPtr, buf, len);
dmesgPtr += len;
fwrite(buf, 1, len, dmesgFile);
}
void dmesg(const char *format, ...) {
char buf[500];
snprintf(buf, sizeof(buf), "[%8d] ", current_time_ms());
dmesgRaw(buf, strlen(buf));
va_list arg;
va_start(arg, format);
vsnprintf(buf, sizeof(buf), format, arg);
va_end(arg);
dmesgRaw(buf, strlen(buf));
dmesgRaw("\n", 1);
fflush(dmesgFile);
fdatasync(fileno(dmesgFile));
}
} // namespace pxt

View File

@@ -25,20 +25,13 @@ PXT_VTABLE_CTOR(MMap) {
}
void MMap::print() {
DMESG("MMap %p len=%d fd=%d data=%p", this, length, fd, data);
DMESG("MMap %p r=%d len=%d fd=%d data=%p", this, refcnt, length, fd, data);
}
void MMap::destroy() {
munmap(data, length);
close(fd);
}
void MMap::scan(MMap *) {}
unsigned MMap::gcsize(MMap *) {
return TOWORDS(sizeof(MMap));
}
}
namespace control {
@@ -46,8 +39,8 @@ namespace control {
/** Create new file mapping in memory */
//%
MMap *mmap(String filename, int size, int offset) {
DMESG("mmap %s len=%d off=%d", filename->getUTF8Data(), size, offset);
int fd = open(filename->getUTF8Data(), O_RDWR, 0);
DMESG("mmap %s len=%d off=%d", filename->data, size, offset);
int fd = open(filename->data, O_RDWR, 0);
if (fd < 0)
return 0;

View File

@@ -55,7 +55,7 @@ namespace motors {
Size = 12
}
function init() {
export function init() {
if (pwmMM) return
pwmMM = control.mmap("/dev/lms_pwm", 0, 0)
if (!pwmMM) control.fail("no PWM file")
@@ -124,7 +124,7 @@ namespace motors {
export function stopAll() {
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
writePWM(b);
pause(1);
control.cooperate();
}
/**
@@ -136,7 +136,7 @@ namespace motors {
//% help=motors/reset-all
export function resetAll() {
reset(Output.ALL)
pause(1);
control.cooperate();
}
interface MoveSchedule {
@@ -269,7 +269,7 @@ namespace motors {
if (this._brake && this._brakeSettleTime > 0)
pause(this._brakeSettleTime);
else {
pause(1);
control.cooperate();
}
}
@@ -280,7 +280,7 @@ namespace motors {
// allow robot to settle
this.settle();
} else {
pause(1);
control.cooperate();
}
}
@@ -357,7 +357,7 @@ namespace motors {
// special: 0 is infinity
if (schedule.steps[0] + schedule.steps[1] + schedule.steps[2] == 0) {
this._run(schedule.speed);
pause(1);
control.cooperate();
return;
}
@@ -924,3 +924,9 @@ namespace motors {
writePWM(b)
}
}
interface Buffer {
[index: number]: number;
// rest defined in buffer.cpp
}

View File

@@ -1,236 +0,0 @@
#include "pxt.h"
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <signal.h>
#include <dirent.h>
#include "ev3const.h"
namespace pxt {
static int usbFD;
#define USB_MAGIC 0x3d3f
#define USB_SERIAL 1
#define USB_RESTART 2
#define USB_DMESG 3
struct UsbPacket {
uint16_t size;
uint16_t msgcount;
uint16_t magic;
uint16_t code;
char buf[1024 - 8];
};
void *usbThread(void *) {
UsbPacket pkt;
UsbPacket resp;
while (true) {
int len = read(usbFD, &pkt, sizeof(pkt));
if (len <= 4) {
sleep_core_us(20000);
continue;
}
resp.msgcount = pkt.msgcount;
if (pkt.magic == USB_MAGIC) {
if (pkt.code == USB_RESTART) {
target_reset();
} else if (pkt.code == USB_DMESG) {
dumpDmesg();
}
/*
resp.magic = pkt.magic;
resp.code = pkt.code;
resp.size = 8;
write(usbFD, &resp, sizeof(resp));
*/
} else {
resp.magic = 0xffff;
resp.size = 4;
write(usbFD, &resp, sizeof(resp));
}
sleep_core_us(1000);
}
}
static void startUsb() {
usbFD = open("/dev/lms_usbdev", O_RDWR, 0666);
pthread_t pid;
pthread_create(&pid, NULL, usbThread, NULL);
pthread_detach(pid);
}
static void *exitThread(void *) {
int fd = open("/dev/lms_ui", O_RDWR, 0666);
if (fd < 0)
return 0;
uint8_t *data =
(uint8_t *)mmap(NULL, NUM_BUTTONS, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
return 0;
}
for (;;) {
if (data[5])
target_reset();
sleep_core_us(50000);
}
}
static void startExitThread() {
pthread_t pid;
pthread_create(&pid, NULL, exitThread, NULL);
pthread_detach(pid);
}
void sendUsb(uint16_t code, const char *data, int len) {
while (len > 0) {
int sz = len;
if (sz > 1000)
sz = 1000;
UsbPacket pkt = {(uint16_t)(6 + sz), 0, USB_MAGIC, code, {}};
memcpy(pkt.buf, data, sz);
write(usbFD, &pkt, sizeof(pkt));
len -= sz;
data += sz;
}
}
void sendSerial(const char *data, int len) {
sendUsb(USB_SERIAL, data, len);
}
int lmsPid;
void stopLMS() {
struct dirent *ent;
DIR *dir;
dir = opendir("/proc");
if (dir == NULL)
return;
while ((ent = readdir(dir)) != NULL) {
int pid = atoi(ent->d_name);
if (!pid)
continue;
char namebuf[100];
snprintf(namebuf, 100, "/proc/%d/cmdline", pid);
FILE *f = fopen(namebuf, "r");
if (f) {
fread(namebuf, 1, 99, f);
if (strcmp(namebuf, "./lms2012") == 0) {
lmsPid = pid;
}
fclose(f);
if (lmsPid)
break;
}
}
closedir(dir);
lmsPid = 0; // disable SIGSTOP for now - rethink if problems with I2C (runs on a thread)
if (lmsPid) {
DMESG("SIGSTOP to lmsPID=%d", lmsPid);
if (kill(lmsPid, SIGSTOP))
DMESG("SIGSTOP failed");
}
}
void runLMS() {
DMESG("re-starting LMS2012");
kill(lmsPid, SIGCONT);
sleep_core_us(200000);
exit(0);
/*
chdir("/home/root/lms2012/sys");
for (int fd = 3; fd < 9999; ++fd)
close(fd);
execl("lms2012", "./lms2012");
exit(100); // should not be reached
*/
}
void stopMotors() {
uint8_t cmd[3] = {opOutputStop, 0x0F, 0};
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 3);
close(fd);
}
void stopProgram() {
uint8_t cmd[1] = {opOutputProgramStop};
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 1);
close(fd);
}
extern "C" void target_reset() {
tryLockUser();
stopMotors();
stopProgram();
if (lmsPid)
runLMS();
else
exit(0);
}
void target_exit() {
target_reset();
}
void target_startup() {
stopLMS();
startUsb();
startExitThread();
}
void initKeys() {}
static const char *progPath = "/mnt/ramdisk/prjs/BrkProg_SAVE";
// These are disabled except when building File_manager.pdf
// %
void deletePrjFile(String filename) {
const char *d = filename->getUTF8Data();
if (strlen(d) > 500 || strchr(d, '/'))
return;
char buf[1024];
snprintf(buf, sizeof(buf), "%s/%s", progPath, d);
unlink(buf);
}
// %
RefCollection *listPrjFiles() {
auto res = Array_::mk();
registerGCObj(res);
auto dp = opendir(progPath);
for (;;) {
dirent *ep = dp ? readdir(dp) : NULL;
if (!ep)
break;
if (ep->d_name[0] == '.')
continue;
auto str = mkString(ep->d_name, -1);
registerGCObj(str);
res->head.push((TValue)str);
unregisterGCObj(str);
}
if (dp)
closedir(dp);
unregisterGCObj(res);
return res;
}
}

View File

@@ -1,3 +1 @@
#define PXT_GC_THREAD_LIST 1
#define PXT_IN_ISR() false
// leave empty

View File

@@ -11,7 +11,6 @@ int allocateNotifyEvent();
void sleep_core_us(uint64_t us);
void startUser();
void stopUser();
int tryLockUser();
class Button;
typedef Button *Button_;
@@ -28,13 +27,9 @@ class MMap : public RefObject {
MMap();
void destroy();
void print();
static void scan(MMap *);
static unsigned gcsize(MMap *);
};
extern volatile bool paniced;
void target_exit();
// Buffer, Sound, and Image share representation.
typedef Buffer Sound;

View File

@@ -26,19 +26,12 @@
"icons.jres",
"ns.ts",
"platform.h",
"platform.cpp",
"dmesg.cpp",
"integrator.ts"
"integrator.ts",
"cooperate.ts"
],
"testFiles": [
"test.ts"
],
"dalDTS": {
"includeDirs": [
"pxtapp"
]
},
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/core---linux",
"npmDependencies": {},
"public": true,
"dependencies": {

16
libs/core/pxtcore.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef __PXTCORE_H
#define __PXTCORE_H
#include <stdio.h>
namespace pxt {
void dmesg(const char *fmt, ...);
#define DMESG pxt::dmesg
}
static inline void itoa(int v, char *dst) {
snprintf(dst, 30, "%d", v);
}
#endif

View File

@@ -85,9 +85,10 @@ void updateScreen(Image_ img) {
lastImg = img;
}
if (lastImg && mappedFrameBuffer != MAP_FAILED) {
if (lastImg && lastImg->isDirty() && mappedFrameBuffer != MAP_FAILED) {
if (lastImg->bpp() != 1 || lastImg->width() != LCD_WIDTH || lastImg->height() != LCD_HEIGHT)
target_panic(906);
lastImg->clearDirty();
bitBufferToFrameBufferSwap(lastImg->pix(), mappedFrameBuffer);
}
}

View File

@@ -25,8 +25,8 @@ struct hci_dev_list_req {
hci_dev_req dev_req[2];
};
static uint64_t bt_addr() {
uint64_t res = -1;
static uint32_t bt_addr() {
uint32_t res = -1;
int fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
if (fd < 0) {
@@ -50,8 +50,11 @@ static uint64_t bt_addr() {
goto done;
}
res = 0;
memcpy(&res, di.bdaddr, 6);
memcpy(&res, di.bdaddr, 4);
res *= 0x1000193;
res += di.bdaddr[4];
res *= 0x1000193;
res += di.bdaddr[5];
done:
close(fd);
@@ -60,10 +63,14 @@ done:
namespace pxt {
uint64_t getLongSerialNumber() {
static uint64_t serial;
if (serial == 0)
serial = bt_addr();
int getSerialNumber() {
static int serial;
if (serial != 0)
return serial;
serial = bt_addr() & 0x7fffffff;
return serial;
}

View File

@@ -68,12 +68,6 @@ declare namespace control {
/** Write data to DMESG debugging buffer. */
//% shim=control::dmesg
function dmesg(s: string): void;
/**
* Determines if the USB has been enumerated.
*/
//% shim=control::isUSBInitialized
function isUSBInitialized(): boolean;
}
declare namespace serial {

View File

@@ -20,7 +20,7 @@ namespace console._screen {
lines = [];
console.addListener(log);
brick.buttonUp.onEvent(ButtonEvent.Bumped, () => scroll(-3))
brick.buttonDown.onEvent(ButtonEvent.Bumped, () => scroll(3))
brick.buttonDown.onEvent(ButtonEvent.Bumped, () => scroll(3))
}
}
@@ -44,7 +44,7 @@ namespace console._screen {
printLog();
}
function log(priority: ConsolePriority, msg: string): void {
function log(msg: string): void {
lines.push(msg);
if (lines.length + 5 > maxLines) {
lines.splice(0, maxLines - lines.length);

View File

@@ -1,6 +1,6 @@
// This is the last thing executed before user code
console.addListener(function(priority: ConsolePriority, msg: string) {
console.addListener(function(msg: string) {
control.dmesg(msg.substr(0, msg.length - 1))
})
// boot sequence
brick.showBoot();

View File

@@ -12,4 +12,5 @@ music.setTempo(120)
music.noteFrequency(Note.C)
music.beat()
music.setVolume(50)
```
music.volume()
```

View File

@@ -13,7 +13,22 @@
namespace music {
uint8_t currVolume = 50;
int _readSystemVolume() {
char ParBuf[8];
int volume;
int fd = open("../sys/settings/Volume.rtf", O_RDONLY);
read(fd, ParBuf, sizeof(ParBuf));
close(fd);
if (sscanf(ParBuf,"%d",&volume) > 0) {
if ((volume >= 0) && (volume <= 100)) {
return volume;
}
}
return 50;
}
uint8_t currVolume = _readSystemVolume();
uint8_t *lmsSoundMMap;
int writeDev(void *data, int size) {
@@ -37,6 +52,18 @@ void setVolume(int volume) {
currVolume = max(0, min(100, volume));
}
/**
* Return the output volume of the sound synthesizer.
*/
//% weight=96
//% blockId=synth_get_volume block="volume"
//% parts="speaker" blockGap=8
//% help=music/volume
//% weight=1
int volume() {
return currVolume;
}
#define SOUND_CMD_BREAK 0
#define SOUND_CMD_TONE 1
#define SOUND_CMD_PLAY 2
@@ -45,7 +72,7 @@ void setVolume(int volume) {
struct ToneCmd {
uint8_t cmd;
uint8_t vol;
uint8_t lvl;
uint16_t freq;
uint16_t duration;
};
@@ -55,10 +82,26 @@ static void _stopSound() {
writeDev(&cmd, sizeof(cmd));
}
static uint8_t _getVolumeLevel(uint8_t volume, uint8_t levels) {
uint8_t level;
uint8_t step = (uint8_t) (100 / (levels - 1));
if (volume < step) {
level = 0;
} else if (volume > step * (levels - 1)) {
level = levels;
} else {
level = (uint8_t) (volume / step);
}
return level;
}
static void _playTone(uint16_t frequency, uint16_t duration, uint8_t volume) {
// https://github.com/mindboards/ev3sources/blob/78ebaf5b6f8fe31cc17aa5dce0f8e4916a4fc072/lms2012/c_sound/source/c_sound.c#L471
uint8_t level = _getVolumeLevel(volume, 13);
ToneCmd cmd;
cmd.cmd = SOUND_CMD_TONE;
cmd.vol = volume;
cmd.lvl = level;
cmd.freq = frequency;
cmd.duration = duration;
// (*SoundInstance.pSound).Busy = TRUE;
@@ -122,7 +165,8 @@ void playSample(Buffer buf) {
stopUser();
pthread_mutex_lock(&pumpMutex);
*lmsSoundMMap = 1; // BUSY
uint8_t cmd[] = {SOUND_CMD_PLAY, (uint8_t)((currVolume / 33) + 1)};
// https://github.com/mindboards/ev3sources/blob/78ebaf5b6f8fe31cc17aa5dce0f8e4916a4fc072/lms2012/c_sound/source/c_sound.c#L605
uint8_t cmd[] = {SOUND_CMD_PLAY, _getVolumeLevel(currVolume, 8)};
writeDev(cmd, 2);
decrRC(currentSample);
currentSample = buf;
@@ -201,4 +245,4 @@ Buffer buffer(Sound snd) {
void play(Sound snd) {
music::playSample(snd);
}
}
}

10
libs/music/shims.d.ts vendored
View File

@@ -13,6 +13,16 @@ declare namespace music {
//% weight=1 shim=music::setVolume
function setVolume(volume: int32): void;
/**
* Return the output volume of the sound synthesizer.
*/
//% weight=96
//% blockId=synth_get_volume block="volume"
//% parts="speaker" blockGap=8
//% help=music/volume
//% weight=1 shim=music::volume
function volume(): int32;
/**
* Play a tone through the speaker for some amount of time.
* @param frequency pitch of the tone to play in Hertz (Hz), eg: Note.C

View File

@@ -5,3 +5,4 @@ music.playTone(1440, 500)
pause(500)
music.playTone(2440, 500)
pause(500)
music.volume()

View File

@@ -1,10 +0,0 @@
namespace image {
/**
* Get the screen image
*/
//%
export function screenImage(): Image {
return screen;
}
}

View File

@@ -15,7 +15,7 @@ declare interface Image {
height: int32;
/**
* True if the image is monochromatic (black and white)
* True iff the image is monochromatic (black and white)
*/
//% property shim=ImageMethods::isMono
isMono: boolean;
@@ -45,18 +45,6 @@ declare interface Image {
//% shim=ImageMethods::fill
fill(c: int32): void;
/**
* Copy row(s) of pixel from image to buffer (8 bit per pixel).
*/
//% shim=ImageMethods::getRows
getRows(x: int32, dst: Buffer): void;
/**
* Copy row(s) of pixel from buffer to image.
*/
//% shim=ImageMethods::setRows
setRows(x: int32, src: Buffer): void;
/**
* Return a copy of the current image
*/

View File

@@ -141,6 +141,8 @@ namespace brick {
const col = 44;
const lineHeight8 = image.font8.charHeight + 2;
const h = screen.height;
const w = screen.width;
const blink = (control.millis() >> 5) % (h - 1);
clearScreen();
@@ -180,21 +182,23 @@ namespace brick {
if (inf)
screen.print(inf, x, h - 2 * lineHeight8, 1, inf.length > 4 ? image.font5 : image.font8);
}
// alive dot
screen.setPixel(w - 1, blink, 1);
screen.setPixel(w - 1, blink - 1, 1);
screen.setPixel(w - 2, blink - 1, 1);
screen.setPixel(w - 2, blink, 1);
}
export function showBoot() {
// pulse green, play startup sound, turn off light
brick.setStatusLight(StatusLight.GreenPulse);
// We pause for 100ms to give time to read sensor values, so they work in on_start block
pause(400)
sensors.internal.init();
motors.init();
pause(800);
// and we're ready
brick.setStatusLight(StatusLight.Off);
// always show port by default if no UI is set
control.runInParallel(function () {
// show ports if nothing is has been shown
if (screenMode != ScreenMode.None) return;
showPorts();
})
}
/**

5105
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,12 @@
{
"name": "pxt-ev3",
"version": "1.4.17",
"version": "1.2.30",
"description": "LEGO MINDSTORMS EV3 for Microsoft MakeCode",
"private": false,
"keywords": [
"JavaScript",
"education",
"LEGO",
"EV3",
"lego",
"pxt",
"MakeCode",
"Microsoft"
@@ -33,20 +32,15 @@
],
"devDependencies": {
"typescript": "2.6.1",
"react": "16.8.3",
"semantic-ui-less": "2.2.14",
"@types/bluebird": "2.0.33",
"@types/marked": "0.3.0",
"@types/node": "8.0.53",
"webfonts-generator": "^0.4.0",
"@types/jquery": "3.2.16",
"@types/react": "16.0.25",
"@types/react-dom": "16.0.3",
"@types/web-bluetooth": "0.0.4"
"webfonts-generator": "^0.4.0"
},
"dependencies": {
"pxt-common-packages": "6.16.37",
"pxt-core": "5.30.19"
"pxt-common-packages": "0.23.61",
"pxt-core": "4.0.11"
},
"scripts": {
"test": "node node_modules/pxt-core/built/pxt.js travis"

View File

@@ -22,7 +22,7 @@
],
"simulator": {
"autoRun": true,
"autoRunLight": false,
"streams": true,
"aspectRatio": 0.5,
"parts": false,
"enableTrace": true,
@@ -37,10 +37,7 @@
"publishing": true,
"importing": true,
"preferredPackages": [],
"githubPackages": true,
"cloudProviders": {
"github": {}
}
"githubPackages": true
},
"compile": {
"isNative": true,
@@ -53,16 +50,15 @@
"flashCodeAlign": 256,
"floatingPoint": true,
"taggedInts": true,
"stackAlign": 2,
"gc": true
"stackAlign": 2
},
"serial": {
"vendorId": "0x0694",
"productId": "0x0005",
"rawHID": true,
"useEditor": true,
"log": true
},
"vendorId": "0x0694",
"productId": "0x0005",
"rawHID": true,
"useEditor": true,
"log": true
},
"runtime": {
"mathBlocks": true,
"loopsBlocks": true,
@@ -76,9 +72,7 @@
"onStartColor": "#58AB41",
"pauseUntilBlock": {
"category": "loops"
},
"bannedCategories": [
]
}
},
"compileService": {
"buildEngine": "dockermake",
@@ -88,7 +82,7 @@
"appTheme": {
"accentColor": "#0089BF",
"logoWide": true,
"logoUrl": "https://education.lego.com/",
"logoUrl": "https://education.lego.com/",
"logo": "./static/lego_education_logo.png",
"docsLogo": "./static/lego_education_logo.png",
"portraitLogo": "./static/lego_education_logo.png",
@@ -157,12 +151,6 @@
"usbHelp": [],
"extendEditor": true,
"extendFieldEditors": true,
"scriptManager": true,
"importExtensionFiles": true,
"experiments": [
"python",
"alwaysGithubItemBlocks"
],
"disableBlockIcons": true,
"blocklyOptions": {
"grid": {
@@ -198,12 +186,7 @@
"editor.background": "#f9f9f9"
},
"fileNameExclusiveFilter": "[^a-zA-Z0-9]",
"qrCode": true,
"shareFinishedTutorials": true,
"nameProjectFirst": true,
"alwaysGithubItem": true,
"enableTrace": true
},
"ignoreDocsErrors": true,
"uploadDocs": true
}
"ignoreDocsErrors": true
}

View File

@@ -1,97 +0,0 @@
//% shim=pxt::listPrjFiles
function getPrjs() {
let programs = [
"pxt",
"my amazing robot",
]
for (let i = 1; i < 6; ++i)
programs.push("Untitled-" + i)
return programs
}
//% shim=pxt::deletePrjFile
function delPrj(fn: string) {
return
}
const programs = getPrjs()
.filter(s => s.substr(s.length - 4, 4) == ".rbf")
.map(s => s.substr(0, s.length - 4))
.filter(s => s != "File_manager")
programs.push("")
programs.push("Cancel")
programs.push("Delete 0 files")
let todel: boolean[] = []
let scrollTop = 0
let cursor = 0
let confirm = false
function showMenu() {
if (cursor < scrollTop + 2)
scrollTop = cursor - 2
else if (cursor > scrollTop + 11)
scrollTop = cursor - 11
if (scrollTop < 0)
scrollTop = 0
let num = 0
for (let i = 0; i < todel.length; ++i)
if (todel[i]) num++
programs[programs.length - 1] =
confirm ? "Enter to confirm" : "Delete " + num + " file(s)"
brick.clearScreen()
const h = brick.lineHeight()
for (let i = 0; i < 13; ++i) {
const y = i * h
const idx = scrollTop + i
const fg = idx == cursor ? 0 : 1
const bg = idx == cursor ? 1 : 0
// screen.fillRect(0, y, screen.width, h, bg);
let text = (idx == cursor ? ">" : " ")
+ (todel[idx] ? "*" : " ")
+ " "
+ (programs[scrollTop + i] || "")
screen.print(text, 0, y, fg, brick.font);
}
}
function move(d: number) {
confirm = false
const nc = cursor + d
if (0 <= nc && nc < programs.length)
cursor = nc
showMenu()
}
brick.buttonDown.onEvent(ButtonEvent.Pressed, () => move(1))
brick.buttonUp.onEvent(ButtonEvent.Pressed, () => move(-1))
brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
if (cursor < programs.length - 3) {
todel[cursor] = !todel[cursor]
move(1)
} else if (cursor == programs.length - 3) {
// nothing
} else if (cursor == programs.length - 2) {
control.reset()
} else if (cursor == programs.length - 1) {
if (todel.every(x => !x))
return
if (confirm) {
brick.clearScreen()
brick.showString("deleting...", 6)
for (let i = 0; i < todel.length; ++i) {
if (todel[i]) {
delPrj(programs[i] + ".elf")
delPrj(programs[i] + ".rbf")
}
}
pause(1000)
control.reset()
} else {
confirm = true
showMenu()
}
}
})
showMenu()

View File

@@ -5,7 +5,6 @@
namespace pxsim {
export class EV3Board extends CoreBoard {
viewHost: visuals.BoardHost;
view: SVGSVGElement;
outputState: EV3OutputState;
@@ -84,8 +83,7 @@ namespace pxsim {
highContrast: msg.highContrast,
light: msg.light
};
this.viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
boardDef,
const viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
visual: boardDef.visual,
highContrast: msg.highContrast,
light: msg.light
@@ -93,7 +91,7 @@ namespace pxsim {
document.body.innerHTML = ""; // clear children
document.body.className = msg.light ? "light" : "";
document.body.appendChild(this.view = this.viewHost.getView() as SVGSVGElement);
document.body.appendChild(this.view = viewHost.getView() as SVGSVGElement);
this.inputNodes = [];
this.outputNodes = [];
@@ -104,8 +102,8 @@ namespace pxsim {
return Promise.resolve();
}
screenshotAsync(width?: number): Promise<ImageData> {
return this.viewHost.screenshotAsync(width);
screenshot(): string {
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
}
getBrickNode() {

View File

@@ -1,19 +1,27 @@
namespace pxsim.music {
export function fromWAV(buf: RefBuffer) {
return buf
return incr(buf)
}
export function stopAllSounds() {
SoundMethods.stop()
}
pxsim.music.setVolume = (volume: number): void => {
pxsim.getAudioState().volume = volume;
};
export function volume() {
return pxsim.getAudioState().volume;
}
}
namespace pxsim.SoundMethods {
let audio: HTMLAudioElement;
export function buffer(buf: RefBuffer) {
return buf
return incr(buf)
}
export function play(buf: RefBuffer) {

View File

@@ -176,7 +176,7 @@ namespace pxsim.visuals {
const dalBoard = board();
dalBoard.updateSubscribers.push(() => this.updateState());
if (props && props.wireframe)
U.addClass(this.element, "sim-wireframe");
svg.addClass(this.element, "sim-wireframe");
if (props && props.theme)
this.updateTheme();

View File

@@ -24,7 +24,7 @@ namespace pxsim.visuals {
protected buildDomCore() {
// Setup buttons
this.buttons = this.btnids.map(n => this.content.getElementById(this.normalizeId(n)) as SVGElement);
this.buttons.forEach(b => U.addClass(b, "sim-button"));
this.buttons.forEach(b => svg.addClass(b, "sim-button"));
this.light = this.content.getElementById(this.normalizeId(BrickView.EV3_LIGHT_ID)) as SVGElement;
}

View File

@@ -24,9 +24,9 @@
"Design Engineering": "design-engineering",
"Coding": "coding",
"Maker": "maker",
"Tutorial Videos": "videos"
"Videos": "videos"
},
"electronManifest": {
"latest": "v1.2.26"
"latest": "v1.2.22"
}
}

View File

@@ -16,10 +16,6 @@
/*******************************
Add your custom CSS here
*******************************/
.simframe.ui.embed {
background: transparent;
}
/* Open Sans font */
@font-face {