Compare commits
38 Commits
upgradetsv
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
6d222c50cb | ||
|
af263c8183 | ||
|
dd18007035 | ||
|
fba924d125 | ||
|
76257775c3 | ||
|
9be35a1034 | ||
|
2ca706df70 | ||
|
dd415019c4 | ||
|
20581513b1 | ||
|
1652fc5ffa | ||
|
8a1b6452bd | ||
|
a6712960db | ||
|
bac70eb177 | ||
|
830f5ffd77 | ||
|
aac5edd64b | ||
|
a54007d9ae | ||
|
3952cb4858 | ||
|
0226888441 | ||
|
1373e9c0bc | ||
|
cabcb048e3 | ||
|
512a441bca | ||
|
b4a18f260d | ||
|
6b3f2da72c | ||
|
b9b21328b1 | ||
|
bef4ebac43 | ||
|
617fdeb747 | ||
|
c1aead1aa9 | ||
|
bd0cf05693 | ||
|
faae7133f5 | ||
|
5289850351 | ||
|
ba6e2f7174 | ||
|
7c5dfc474f | ||
|
5ab3a6ae96 | ||
|
ed2e1f23e9 | ||
|
a157943bf7 | ||
|
40aaf0fb18 | ||
|
b26cf289c3 | ||
|
f0821f8d6c |
40
.github/workflows/pxt-buildmain.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
name: pxt-buildmain
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'main'
|
||||||
|
create:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [14.x]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: npm install
|
||||||
|
run: |
|
||||||
|
sudo apt-get install xvfb
|
||||||
|
sudo npm install -g pxt
|
||||||
|
npm install
|
||||||
|
- name: pxt ci
|
||||||
|
run: |
|
||||||
|
pxt ci
|
||||||
|
env:
|
||||||
|
CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
|
||||||
|
PXT_ACCESS_TOKEN: ${{ secrets.PXT_ACCESS_TOKEN }}
|
||||||
|
PXT_RELEASE_REPO: ${{ secrets.PXT_RELEASE_REPO }}
|
||||||
|
NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||||
|
CHROME_BIN: chromium-browser
|
||||||
|
DISPLAY: :99.0
|
||||||
|
CI: true
|
31
.github/workflows/pxt-buildpr.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
name: pxt-buildpr
|
||||||
|
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [14.x]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: npm install
|
||||||
|
run: |
|
||||||
|
sudo apt-get install xvfb
|
||||||
|
sudo npm install -g pxt
|
||||||
|
npm install
|
||||||
|
- name: pxt ci
|
||||||
|
run: |
|
||||||
|
pxt ci
|
||||||
|
env:
|
||||||
|
CHROME_BIN: chromium-browser
|
||||||
|
DISPLAY: :99.0
|
||||||
|
CI: true
|
39
.github/workflows/pxt-buildpush.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
name: pxt-buildpush
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# main/master has its own build that includes the crowdin key
|
||||||
|
branches-ignore:
|
||||||
|
- 'main'
|
||||||
|
- 'master'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [14.x]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: npm install
|
||||||
|
run: |
|
||||||
|
sudo apt-get install xvfb
|
||||||
|
sudo npm install -g pxt
|
||||||
|
npm install
|
||||||
|
- name: pxt ci
|
||||||
|
run: |
|
||||||
|
pxt ci
|
||||||
|
env:
|
||||||
|
PXT_ACCESS_TOKEN: ${{ secrets.PXT_ACCESS_TOKEN }}
|
||||||
|
PXT_RELEASE_REPO: ${{ secrets.PXT_RELEASE_REPO }}
|
||||||
|
NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||||
|
CHROME_BIN: chromium-browser
|
||||||
|
DISPLAY: :99.0
|
||||||
|
CI: true
|
@ -1,9 +0,0 @@
|
|||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- "8.9.0"
|
|
||||||
script:
|
|
||||||
- "node node_modules/pxt-core/built/pxt.js travis"
|
|
||||||
sudo: false
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- node_modules
|
|
41
SECURITY.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
|
||||||
|
|
||||||
|
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
|
||||||
|
|
||||||
|
## Reporting Security Issues
|
||||||
|
|
||||||
|
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||||
|
|
||||||
|
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
|
||||||
|
|
||||||
|
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
|
||||||
|
|
||||||
|
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
|
||||||
|
|
||||||
|
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||||
|
|
||||||
|
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||||
|
* Full paths of source file(s) related to the manifestation of the issue
|
||||||
|
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||||
|
* Any special configuration required to reproduce the issue
|
||||||
|
* Step-by-step instructions to reproduce the issue
|
||||||
|
* Proof-of-concept or exploit code (if possible)
|
||||||
|
* Impact of the issue, including how an attacker might exploit the issue
|
||||||
|
|
||||||
|
This information will help us triage your report more quickly.
|
||||||
|
|
||||||
|
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
|
||||||
|
|
||||||
|
## Preferred Languages
|
||||||
|
|
||||||
|
We prefer all communications to be in English.
|
||||||
|
|
||||||
|
## Policy
|
||||||
|
|
||||||
|
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
|
||||||
|
|
||||||
|
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"appref": "v"
|
"appref": "v1.4"
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ while (true) {
|
|||||||
music.playSoundEffectUntilDone(sounds.mechanicalMotorStart)
|
music.playSoundEffectUntilDone(sounds.mechanicalMotorStart)
|
||||||
music.playSoundEffectUntilDone(sounds.mechanicalMotorIdle);
|
music.playSoundEffectUntilDone(sounds.mechanicalMotorIdle);
|
||||||
}
|
}
|
||||||
pause(1);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -43,6 +42,5 @@ while (true) {
|
|||||||
music.playSoundEffectUntilDone(sounds.mechanicalMotorStart)
|
music.playSoundEffectUntilDone(sounds.mechanicalMotorStart)
|
||||||
music.playSoundEffectUntilDone(sounds.mechanicalMotorIdle);
|
music.playSoundEffectUntilDone(sounds.mechanicalMotorIdle);
|
||||||
}
|
}
|
||||||
pause(1);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"appref": "v1.2.27"
|
"appref": "v1.2.31"
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
## Example #example
|
## Example #example
|
||||||
|
|
||||||
Use the a wait and the timer to generate a crazy number.
|
Use a wait and the timer to generate a number.
|
||||||
|
|
||||||
```blocks
|
```blocks
|
||||||
let crazy = 0
|
let something = 0
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
control.waitMicros(100)
|
control.waitMicros(100)
|
||||||
crazy = control.millis()
|
something = control.millis()
|
||||||
crazy += control.deviceSerialNumber()
|
something += control.deviceSerialNumber()
|
||||||
if (crazy != 0) {
|
if (something != 0) {
|
||||||
crazy = crazy / 1000000
|
something = something / 1000000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
@ -49,7 +49,7 @@ declare interface Serial extends EventTarget {
|
|||||||
requestPort(options: SerialPortRequestOptions): Promise<SerialPort>;
|
requestPort(options: SerialPortRequestOptions): Promise<SerialPort>;
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebSerialPackageIO implements pxt.HF2.PacketIO {
|
class WebSerialPackageIO implements pxt.packetio.PacketIO {
|
||||||
onData: (v: Uint8Array) => void;
|
onData: (v: Uint8Array) => void;
|
||||||
onError: (e: Error) => void;
|
onError: (e: Error) => void;
|
||||||
onEvent: (v: Uint8Array) => void;
|
onEvent: (v: Uint8Array) => void;
|
||||||
@ -87,7 +87,7 @@ class WebSerialPackageIO implements pxt.HF2.PacketIO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static portIos: WebSerialPackageIO[] = [];
|
static portIos: WebSerialPackageIO[] = [];
|
||||||
static async mkPacketIOAsync(): Promise<pxt.HF2.PacketIO> {
|
static async mkPacketIOAsync(): Promise<pxt.packetio.PacketIO> {
|
||||||
const serial = (<any>navigator).serial;
|
const serial = (<any>navigator).serial;
|
||||||
if (serial) {
|
if (serial) {
|
||||||
try {
|
try {
|
||||||
@ -130,7 +130,7 @@ class WebSerialPackageIO implements pxt.HF2.PacketIO {
|
|||||||
|
|
||||||
private async closeAsync() {
|
private async closeAsync() {
|
||||||
// don't close port
|
// don't close port
|
||||||
return Promise.delay(500);
|
return pxt.U.delay(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
reconnectAsync(): Promise<void> {
|
reconnectAsync(): Promise<void> {
|
||||||
@ -146,11 +146,33 @@ class WebSerialPackageIO implements pxt.HF2.PacketIO {
|
|||||||
this._writer = this.port.writable.getWriter();
|
this._writer = this.port.writable.getWriter();
|
||||||
return this._writer.write(pkt);
|
return this._writer.write(pkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDeviceConnectionChanged(connect: boolean) {
|
||||||
|
throw new Error("onDeviceConnectionChanged not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
onConnectionChanged() {
|
||||||
|
throw new Error("onConnectionChanged not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
isConnecting() {
|
||||||
|
throw new Error("isConnecting not implemented");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isConnected() {
|
||||||
|
throw new Error("isConnected not implemented");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
disposeAsync() {
|
||||||
|
return Promise.reject("disposeAsync not implemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hf2Async() {
|
function hf2Async() {
|
||||||
const pktIOAsync: Promise<pxt.HF2.PacketIO> = useWebSerial
|
const pktIOAsync: Promise<pxt.packetio.PacketIO> = useWebSerial
|
||||||
? WebSerialPackageIO.mkPacketIOAsync() : pxt.HF2.mkPacketIOAsync()
|
? WebSerialPackageIO.mkPacketIOAsync() : pxt.packetio.mkPacketIOAsync()
|
||||||
return pktIOAsync.then(h => {
|
return pktIOAsync.then(h => {
|
||||||
let w = new Ev3Wrapper(h)
|
let w = new Ev3Wrapper(h)
|
||||||
ev3 = w
|
ev3 = w
|
||||||
@ -190,14 +212,19 @@ export function enableWebSerialAsync() {
|
|||||||
else return Promise.resolve();
|
else return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupAsync() {
|
async function cleanupAsync() {
|
||||||
if (ev3) {
|
if (ev3) {
|
||||||
console.log('cleanup previous port')
|
console.log('cleanup previous port')
|
||||||
return ev3.disconnectAsync()
|
try {
|
||||||
.catch(e => { })
|
await ev3.disconnectAsync()
|
||||||
.finally(() => { ev3 = undefined; });
|
}
|
||||||
|
catch (e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ev3 = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let initPromise: Promise<Ev3Wrapper>
|
let initPromise: Promise<Ev3Wrapper>
|
||||||
@ -207,7 +234,7 @@ function initHidAsync() { // needs to run within a click handler
|
|||||||
if (useHID) {
|
if (useHID) {
|
||||||
initPromise = cleanupAsync()
|
initPromise = cleanupAsync()
|
||||||
.then(() => hf2Async())
|
.then(() => hf2Async())
|
||||||
.catch(err => {
|
.catch((err: any) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
initPromise = null
|
initPromise = null
|
||||||
useHID = false;
|
useHID = false;
|
||||||
@ -284,7 +311,7 @@ export function deployCoreAsync(resp: pxtc.CompileResult) {
|
|||||||
.catch(e => {
|
.catch(e => {
|
||||||
// user easily forgets to stop robot
|
// user easily forgets to stop robot
|
||||||
bluetoothTryAgainAsync().then(() => w.disconnectAsync())
|
bluetoothTryAgainAsync().then(() => w.disconnectAsync())
|
||||||
.then(() => Promise.delay(1000))
|
.then(() => pxt.U.delay(1000))
|
||||||
.then(() => w.reconnectAsync());
|
.then(() => w.reconnectAsync());
|
||||||
|
|
||||||
// nothing we can do
|
// nothing we can do
|
||||||
@ -296,7 +323,7 @@ export function deployCoreAsync(resp: pxtc.CompileResult) {
|
|||||||
.then(() => w.flashAsync(elfPath, UF2.readBytes(origElfUF2, 0, origElfUF2.length * 256)))
|
.then(() => w.flashAsync(elfPath, UF2.readBytes(origElfUF2, 0, origElfUF2.length * 256)))
|
||||||
.then(() => w.flashAsync(rbfPath, rbfBIN))
|
.then(() => w.flashAsync(rbfPath, rbfBIN))
|
||||||
.then(() => w.runAsync(rbfPath))
|
.then(() => w.runAsync(rbfPath))
|
||||||
.then(() => Promise.delay(500))
|
.then(() => pxt.U.delay(500))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
pxt.tickEvent("webserial.success");
|
pxt.tickEvent("webserial.success");
|
||||||
return w.disconnectAsync()
|
return w.disconnectAsync()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { canUseWebSerial, enableWebSerialAsync } from "./deploy";
|
import { canUseWebSerial, enableWebSerialAsync } from "./deploy";
|
||||||
import { projectView } from "./extension";
|
import { projectView } from "./extension";
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ export function bluetoothTryAgainAsync(): Promise<void> {
|
|||||||
|
|
||||||
function enableWebSerialAndCompileAsync() {
|
function enableWebSerialAndCompileAsync() {
|
||||||
return enableWebSerialAsync()
|
return enableWebSerialAsync()
|
||||||
.then(() => Promise.delay(500))
|
.then(() => pxt.U.delay(500))
|
||||||
.then(() => projectView.compile());
|
.then(() => projectView.compile());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,11 +53,12 @@ function explainWebSerialPairingAsync(): Promise<void> {
|
|||||||
|
|
||||||
export function showUploadDialogAsync(fn: string, url: string, _confirmAsync: (options: any) => Promise<number>): Promise<void> {
|
export function showUploadDialogAsync(fn: string, url: string, _confirmAsync: (options: any) => Promise<number>): Promise<void> {
|
||||||
confirmAsync = _confirmAsync;
|
confirmAsync = _confirmAsync;
|
||||||
|
|
||||||
// https://msdn.microsoft.com/en-us/library/cc848897.aspx
|
// https://msdn.microsoft.com/en-us/library/cc848897.aspx
|
||||||
// "For security reasons, data URIs are restricted to downloaded resources.
|
// "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"
|
// 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 downloadAgain = !pxt.BrowserUtils.isIE() && !pxt.BrowserUtils.isEdge();
|
||||||
const docUrl = pxt.appTarget.appTheme.usbDocs;
|
const docUrl = (pxt.appTarget.appTheme.usbDocs ? pxt.appTarget.appTheme.usbDocs : false);
|
||||||
|
|
||||||
const jsx =
|
const jsx =
|
||||||
<div className="ui grid stackable">
|
<div className="ui grid stackable">
|
||||||
@ -118,7 +120,7 @@ export function showUploadDialogAsync(fn: string, url: string, _confirmAsync: (o
|
|||||||
hideAgree: false,
|
hideAgree: false,
|
||||||
agreeLbl: lf("I got it"),
|
agreeLbl: lf("I got it"),
|
||||||
className: 'downloaddialog',
|
className: 'downloaddialog',
|
||||||
buttons: [canUseWebSerial() ? {
|
buttons: [canUseWebSerial() && {
|
||||||
label: lf("Bluetooth"),
|
label: lf("Bluetooth"),
|
||||||
icon: "bluetooth",
|
icon: "bluetooth",
|
||||||
className: "bluetooth focused",
|
className: "bluetooth focused",
|
||||||
@ -126,20 +128,19 @@ export function showUploadDialogAsync(fn: string, url: string, _confirmAsync: (o
|
|||||||
pxt.tickEvent("bluetooth.enable");
|
pxt.tickEvent("bluetooth.enable");
|
||||||
explainWebSerialPairingAsync()
|
explainWebSerialPairingAsync()
|
||||||
.then(() => enableWebSerialAndCompileAsync())
|
.then(() => enableWebSerialAndCompileAsync())
|
||||||
.done();
|
|
||||||
}
|
}
|
||||||
} : undefined, downloadAgain ? {
|
}, downloadAgain && {
|
||||||
label: fn,
|
label: fn,
|
||||||
icon: "download",
|
icon: "download",
|
||||||
className: "lightgrey focused",
|
className: "lightgrey focused",
|
||||||
url,
|
url,
|
||||||
fileName: fn
|
fileName: fn
|
||||||
} : undefined, docUrl ? {
|
}, docUrl && {
|
||||||
label: lf("Help"),
|
label: lf("Help"),
|
||||||
icon: "help",
|
icon: "help",
|
||||||
className: "lightgrey",
|
className: "lightgrey",
|
||||||
url: docUrl
|
url: docUrl
|
||||||
} : undefined]
|
}]
|
||||||
//timeout: 20000
|
//timeout: 20000
|
||||||
}).then(() => { });
|
}).then(() => { });
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es2017",
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
|
@ -26,7 +26,7 @@ export class Ev3Wrapper {
|
|||||||
isStreaming = false;
|
isStreaming = false;
|
||||||
dataDump = /talkdbg=1/.test(window.location.href);
|
dataDump = /talkdbg=1/.test(window.location.href);
|
||||||
|
|
||||||
constructor(public io: pxt.HF2.PacketIO) {
|
constructor(public io: pxt.packetio.PacketIO) {
|
||||||
io.onData = buf => {
|
io.onData = buf => {
|
||||||
buf = buf.slice(0, HF2.read16(buf, 0) + 2)
|
buf = buf.slice(0, HF2.read16(buf, 0) + 2)
|
||||||
if (HF2.read16(buf, 4) == usbMagic) {
|
if (HF2.read16(buf, 4) == usbMagic) {
|
||||||
@ -81,7 +81,7 @@ export class Ev3Wrapper {
|
|||||||
log(`stopping PXT app`)
|
log(`stopping PXT app`)
|
||||||
let buf = this.allocCustom(2)
|
let buf = this.allocCustom(2)
|
||||||
return this.justSendAsync(buf)
|
return this.justSendAsync(buf)
|
||||||
.then(() => Promise.delay(500))
|
.then(() => pxt.U.delay(500))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ export class Ev3Wrapper {
|
|||||||
if (this.dataDump)
|
if (this.dataDump)
|
||||||
log("TALK: " + U.toHex(buf))
|
log("TALK: " + U.toHex(buf))
|
||||||
return this.io.sendPacketAsync(buf)
|
return this.io.sendPacketAsync(buf)
|
||||||
.then(() => this.msgs.shiftAsync(1000))
|
.then(() => this.msgs.shiftAsync(5000))
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
if (resp[2] != buf[2] || resp[3] != buf[3])
|
if (resp[2] != buf[2] || resp[3] != buf[3])
|
||||||
U.userError("msg count de-sync")
|
U.userError("msg count de-sync")
|
||||||
@ -236,7 +236,7 @@ export class Ev3Wrapper {
|
|||||||
let contFileReq = this.allocSystem(1 + 2, 0x97)
|
let contFileReq = this.allocSystem(1 + 2, 0x97)
|
||||||
HF2.write16(contFileReq, 7, 1000) // maxRead
|
HF2.write16(contFileReq, 7, 1000) // maxRead
|
||||||
contFileReq[6] = handle
|
contFileReq[6] = handle
|
||||||
return Promise.delay(data.length > 0 ? 0 : 500)
|
return pxt.U.delay(data.length > 0 ? 0 : 500)
|
||||||
.then(() => this.talkAsync(contFileReq, -1))
|
.then(() => this.talkAsync(contFileReq, -1))
|
||||||
.then(resp)
|
.then(resp)
|
||||||
}
|
}
|
||||||
@ -251,7 +251,7 @@ export class Ev3Wrapper {
|
|||||||
let loop = (): Promise<void> =>
|
let loop = (): Promise<void> =>
|
||||||
this.lock.enqueue("file", () =>
|
this.lock.enqueue("file", () =>
|
||||||
this.streamFileOnceAsync(path, cb))
|
this.streamFileOnceAsync(path, cb))
|
||||||
.then(() => Promise.delay(500))
|
.then(() => pxt.U.delay(500))
|
||||||
.then(loop)
|
.then(loop)
|
||||||
return loop()
|
return loop()
|
||||||
}
|
}
|
||||||
|
@ -120,20 +120,17 @@ export class FieldBrickButtons extends Blockly.FieldDropdown implements Blockly.
|
|||||||
|
|
||||||
Blockly.DropDownDiv.setColour('#ffffff', '#dddddd');
|
Blockly.DropDownDiv.setColour('#ffffff', '#dddddd');
|
||||||
|
|
||||||
// Calculate positioning based on the field position.
|
// Position based on the field position.
|
||||||
let scale = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
|
Blockly.DropDownDiv.showPositionedByField(this, this.onHide_.bind(this));
|
||||||
let bBox = { width: this.size_.width, height: this.size_.height };
|
|
||||||
bBox.width *= scale;
|
// Update colour to look selected.
|
||||||
bBox.height *= scale;
|
let source = this.sourceBlock_ as Blockly.BlockSvg;
|
||||||
let position = this.fieldGroup_.getBoundingClientRect();
|
this.savedPrimary_ = source?.getColour();
|
||||||
let primaryX = position.left + bBox.width / 2;
|
if (source?.isShadow()) {
|
||||||
let primaryY = position.top + bBox.height;
|
source.setColour(source.getColourTertiary());
|
||||||
let secondaryX = primaryX;
|
} else if (this.borderRect_) {
|
||||||
let secondaryY = position.top;
|
this.borderRect_.setAttribute('fill', (this.sourceBlock_ as Blockly.BlockSvg).getColourTertiary());
|
||||||
// Set bounds to workspace; show the drop-down.
|
}
|
||||||
(Blockly.DropDownDiv as any).setBoundsElement((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
|
|
||||||
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
|
|
||||||
this.onHide_.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,5 +154,12 @@ export class FieldBrickButtons extends Blockly.FieldDropdown implements Blockly.
|
|||||||
content.removeAttribute('aria-haspopup');
|
content.removeAttribute('aria-haspopup');
|
||||||
content.removeAttribute('aria-activedescendant');
|
content.removeAttribute('aria-activedescendant');
|
||||||
(content as HTMLElement).style.width = '';
|
(content as HTMLElement).style.width = '';
|
||||||
|
// Update color (deselect) on dropdown hide
|
||||||
|
let source = this.sourceBlock_ as Blockly.BlockSvg;
|
||||||
|
if (source?.isShadow()) {
|
||||||
|
source.setColour(this.savedPrimary_);
|
||||||
|
} else if (this.borderRect_) {
|
||||||
|
this.borderRect_.setAttribute('fill', this.savedPrimary_);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -5,10 +5,14 @@ export interface FieldColorEnumOptions extends pxtblockly.FieldColourNumberOptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class FieldColorEnum extends pxtblockly.FieldColorNumber implements Blockly.FieldCustom {
|
export class FieldColorEnum extends pxtblockly.FieldColorNumber implements Blockly.FieldCustom {
|
||||||
|
|
||||||
public isFieldCustom_ = true;
|
public isFieldCustom_ = true;
|
||||||
|
private paramsData: any[];
|
||||||
|
|
||||||
constructor(text: string, params: FieldColorEnumOptions, opt_validator?: Function) {
|
constructor(text: string, params: FieldColorEnumOptions, opt_validator?: Function) {
|
||||||
super(text, params, opt_validator);
|
super(text, params, opt_validator);
|
||||||
|
|
||||||
|
this.paramsData = params["data"];
|
||||||
}
|
}
|
||||||
|
|
||||||
mapColour(enumString: string) {
|
mapColour(enumString: string) {
|
||||||
@ -33,11 +37,21 @@ export class FieldColorEnum extends pxtblockly.FieldColorNumber implements Block
|
|||||||
case 'ColorSensorColor.Red': return '#f12a21';
|
case 'ColorSensorColor.Red': return '#f12a21';
|
||||||
case 'ColorSensorColor.White': return '#ffffff';
|
case 'ColorSensorColor.White': return '#ffffff';
|
||||||
case 'ColorSensorColor.Brown': return '#6c2d00';
|
case 'ColorSensorColor.Brown': return '#6c2d00';
|
||||||
case 'ColorSensorColor.None': return '#dfe6e9'; // Grey
|
case 'ColorSensorColor.None': return '#dfe6e9';
|
||||||
default: return colorString;
|
default: return colorString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showEditor_() {
|
||||||
|
super.showEditor_();
|
||||||
|
const colorCells = document.querySelectorAll('.legoColorPicker td');
|
||||||
|
colorCells.forEach((cell) => {
|
||||||
|
const titleName = this.mapColour(cell.getAttribute("title"));
|
||||||
|
const index = this.paramsData.findIndex(item => item[1] === titleName);
|
||||||
|
cell.setAttribute("title", this.paramsData[index][0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current colour.
|
* Return the current colour.
|
||||||
* @param {boolean} opt_asHex optional field if the returned value should be a hex
|
* @param {boolean} opt_asHex optional field if the returned value should be a hex
|
||||||
@ -64,7 +78,7 @@ export class FieldColorEnum extends pxtblockly.FieldColorNumber implements Block
|
|||||||
}
|
}
|
||||||
this.value_ = colour;
|
this.value_ = colour;
|
||||||
if (this.sourceBlock_) {
|
if (this.sourceBlock_) {
|
||||||
this.sourceBlock_.setColour(colour, colour, colour);
|
this.sourceBlock_.setColour(colour);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,6 +11,7 @@ declare const pxtTargetBundle: any;
|
|||||||
|
|
||||||
let soundCache: any;
|
let soundCache: any;
|
||||||
let soundIconCache: any;
|
let soundIconCache: any;
|
||||||
|
let soundIconCacheArray: any;
|
||||||
|
|
||||||
export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldCustom {
|
export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldCustom {
|
||||||
public isFieldCustom_ = true;
|
public isFieldCustom_ = true;
|
||||||
@ -23,17 +24,17 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
super(text, { blocksInfo: options.blocksInfo, sort: true, data: options.data }, validator);
|
super(text, { blocksInfo: options.blocksInfo, sort: true, data: options.data }, validator);
|
||||||
|
|
||||||
this.columns_ = parseInt(options.columns) || 4;
|
this.columns_ = parseInt(options.columns) || 4;
|
||||||
this.width_ = parseInt(options.width) || 380;
|
this.width_ = parseInt(options.width) || 450;
|
||||||
|
|
||||||
this.setText = Blockly.FieldDropdown.prototype.setText;
|
this.setText = Blockly.FieldDropdown.prototype.setText;
|
||||||
this.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
|
this.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
|
||||||
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
|
|
||||||
|
|
||||||
if (!pxt.BrowserUtils.isIE() && !soundCache) {
|
if (!pxt.BrowserUtils.isIE() && !soundCache) {
|
||||||
soundCache = JSON.parse(pxtTargetBundle.bundledpkgs['music']['sounds.jres']);
|
soundCache = JSON.parse(pxtTargetBundle.bundledpkgs['music']['sounds.jres']);
|
||||||
}
|
}
|
||||||
if (!soundIconCache) {
|
if (!soundIconCache) {
|
||||||
soundIconCache = JSON.parse(pxtTargetBundle.bundledpkgs['music']['icons.jres']);
|
soundIconCache = JSON.parse(pxtTargetBundle.bundledpkgs['music']['icons.jres']);
|
||||||
|
soundIconCacheArray = Object.entries(soundIconCache).filter(el => el[0] !== "*");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,14 +51,17 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||||
Blockly.DropDownDiv.clearContent();
|
Blockly.DropDownDiv.clearContent();
|
||||||
// Populate the drop-down with the icons for this field.
|
// Populate the drop-down with the icons for this field.
|
||||||
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
|
let dropdownDiv = Blockly.DropDownDiv.getContentDiv() as HTMLElement;
|
||||||
let contentDiv = document.createElement('div');
|
let contentDiv = document.createElement('div');
|
||||||
// Accessibility properties
|
// Accessibility properties
|
||||||
contentDiv.setAttribute('role', 'menu');
|
contentDiv.setAttribute('role', 'menu');
|
||||||
contentDiv.setAttribute('aria-haspopup', 'true');
|
contentDiv.setAttribute('aria-haspopup', 'true');
|
||||||
contentDiv.className = 'blocklyMusicFieldOptions';
|
contentDiv.className = 'blocklyMusicFieldOptions';
|
||||||
|
contentDiv.style.display = "flex";
|
||||||
|
contentDiv.style.flexWrap = "wrap";
|
||||||
|
contentDiv.style.float = "none";
|
||||||
const options = this.getOptions();
|
const options = this.getOptions();
|
||||||
options.sort();
|
//options.sort(); // Do not need to use to not apply sorting in different languages
|
||||||
|
|
||||||
// Create categoies
|
// Create categoies
|
||||||
const categories = this.getCategories(options);
|
const categories = this.getCategories(options);
|
||||||
@ -68,7 +72,7 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
// Accessibility properties
|
// Accessibility properties
|
||||||
categoriesDiv.setAttribute('role', 'menu');
|
categoriesDiv.setAttribute('role', 'menu');
|
||||||
categoriesDiv.setAttribute('aria-haspopup', 'true');
|
categoriesDiv.setAttribute('aria-haspopup', 'true');
|
||||||
categoriesDiv.style.backgroundColor = this.sourceBlock_.getColourTertiary();
|
categoriesDiv.style.backgroundColor = (this.sourceBlock_ as Blockly.BlockSvg).getColourTertiary();
|
||||||
categoriesDiv.className = 'blocklyMusicFieldCategories';
|
categoriesDiv.className = 'blocklyMusicFieldCategories';
|
||||||
|
|
||||||
this.refreshCategories(categoriesDiv, categories);
|
this.refreshCategories(categoriesDiv, categories);
|
||||||
@ -82,30 +86,18 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
dropdownDiv.appendChild(categoriesDiv);
|
dropdownDiv.appendChild(categoriesDiv);
|
||||||
dropdownDiv.appendChild(contentDiv);
|
dropdownDiv.appendChild(contentDiv);
|
||||||
|
|
||||||
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
|
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), (this.sourceBlock_ as Blockly.BlockSvg).getColourTertiary());
|
||||||
|
|
||||||
// Calculate positioning based on the field position.
|
// Position based on the field position.
|
||||||
let scale = (<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).scale;
|
Blockly.DropDownDiv.showPositionedByField(this, this.onHide_.bind(this));
|
||||||
let 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;
|
|
||||||
// Set bounds to workspace; show the drop-down.
|
|
||||||
(Blockly.DropDownDiv as any).setBoundsElement((<Blockly.WorkspaceSvg>this.sourceBlock_.workspace).getParentSvg().parentNode);
|
|
||||||
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
|
|
||||||
this.onHide_.bind(this));
|
|
||||||
|
|
||||||
// Update colour to look selected.
|
// Update colour to look selected.
|
||||||
if (this.sourceBlock_.isShadow()) {
|
let source = this.sourceBlock_ as Blockly.BlockSvg;
|
||||||
this.savedPrimary_ = this.sourceBlock_.getColour();
|
this.savedPrimary_ = source?.getColour();
|
||||||
this.sourceBlock_.setColour(this.sourceBlock_.getColourTertiary(),
|
if (source?.isShadow()) {
|
||||||
this.sourceBlock_.getColourSecondary(), this.sourceBlock_.getColourTertiary());
|
source.setColour(source.getColourTertiary());
|
||||||
} else if (this.box_) {
|
} else if (this.borderRect_) {
|
||||||
this.box_.setAttribute('fill', this.sourceBlock_.getColourTertiary());
|
this.borderRect_.setAttribute('fill', (this.sourceBlock_ as Blockly.BlockSvg).getColourTertiary());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,6 +130,7 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
backgroundColor = '#0c4e5e';
|
backgroundColor = '#0c4e5e';
|
||||||
button.setAttribute('aria-selected', 'true');
|
button.setAttribute('aria-selected', 'true');
|
||||||
}
|
}
|
||||||
|
button.style.padding = "2px 6px";
|
||||||
button.style.backgroundColor = backgroundColor;
|
button.style.backgroundColor = backgroundColor;
|
||||||
button.style.borderColor = backgroundColor;
|
button.style.borderColor = backgroundColor;
|
||||||
Blockly.bindEvent_(button, 'click', this, this.categoryClick_);
|
Blockly.bindEvent_(button, 'click', this, this.categoryClick_);
|
||||||
@ -151,7 +144,7 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshOptions(contentDiv: Element, options: any) {
|
refreshOptions(contentDiv: Element, options: any) {
|
||||||
|
const categories = this.getCategories(options);
|
||||||
// Show options
|
// Show options
|
||||||
for (let i = 0, option: any; option = options[i]; i++) {
|
for (let i = 0, option: any; option = options[i]; i++) {
|
||||||
let content = (options[i] as any)[0]; // Human-readable text or image.
|
let content = (options[i] as any)[0]; // Human-readable text or image.
|
||||||
@ -186,11 +179,11 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
let backgroundColor = this.savedPrimary_ || this.sourceBlock_.getColour();
|
let backgroundColor = this.savedPrimary_ || this.sourceBlock_.getColour();
|
||||||
if (value == this.getValue()) {
|
if (value == this.getValue()) {
|
||||||
// This icon is selected, show it in a different colour
|
// This icon is selected, show it in a different colour
|
||||||
backgroundColor = this.sourceBlock_.getColourTertiary();
|
backgroundColor = (this.sourceBlock_ as Blockly.BlockSvg).getColourTertiary();
|
||||||
button.setAttribute('aria-selected', 'true');
|
button.setAttribute('aria-selected', 'true');
|
||||||
}
|
}
|
||||||
button.style.backgroundColor = backgroundColor;
|
button.style.backgroundColor = backgroundColor;
|
||||||
button.style.borderColor = this.sourceBlock_.getColourTertiary();
|
button.style.borderColor = (this.sourceBlock_ as Blockly.BlockSvg).getColourTertiary();
|
||||||
Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
|
Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
|
||||||
Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
|
Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
|
||||||
// These are applied manually instead of using the :hover pseudoclass
|
// These are applied manually instead of using the :hover pseudoclass
|
||||||
@ -215,15 +208,27 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
this.setAttribute('class', 'blocklyDropDownButton');
|
this.setAttribute('class', 'blocklyDropDownButton');
|
||||||
contentDiv.removeAttribute('aria-activedescendant');
|
contentDiv.removeAttribute('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Find index in array by category name
|
||||||
|
const categoryIndex = categories.indexOf(category);
|
||||||
|
|
||||||
let buttonImg = document.createElement('img');
|
let buttonImg = document.createElement('img');
|
||||||
buttonImg.src = this.getSoundIcon(category);
|
buttonImg.src = this.getSoundIcon(categoryIndex);
|
||||||
//buttonImg.alt = icon.alt;
|
|
||||||
// Upon click/touch, we will be able to get the clicked element as e.target
|
// Upon click/touch, we will be able to get the clicked element as e.target
|
||||||
// Store a data attribute on all possible click targets so we can match it to the icon.
|
// Store a data attribute on all possible click targets so we can match it to the icon.
|
||||||
const textNode = this.createTextNode_(content);
|
const textNode = this.createTextNode_(content);
|
||||||
button.setAttribute('data-value', value);
|
button.setAttribute('data-value', value);
|
||||||
buttonImg.setAttribute('data-value', value);
|
buttonImg.setAttribute('data-value', value);
|
||||||
|
buttonImg.style.height = "auto";
|
||||||
textNode.setAttribute('data-value', value);
|
textNode.setAttribute('data-value', value);
|
||||||
|
if (pxt.Util.userLanguage() !== "en") textNode.setAttribute('lang', pxt.Util.userLanguage()); // for hyphens, here you need to set the correct abbreviation of the selected language
|
||||||
|
textNode.style.display = "block";
|
||||||
|
textNode.style.lineHeight = "1rem";
|
||||||
|
textNode.style.marginBottom = "5%";
|
||||||
|
textNode.style.padding = "0px 8px";
|
||||||
|
textNode.style.wordBreak = "break-word";
|
||||||
|
textNode.style.hyphens = "auto";
|
||||||
|
|
||||||
button.appendChild(buttonImg);
|
button.appendChild(buttonImg);
|
||||||
button.appendChild(textNode);
|
button.appendChild(textNode);
|
||||||
@ -238,12 +243,19 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
super.onHide_();
|
super.onHide_();
|
||||||
(Blockly.DropDownDiv.getContentDiv() as HTMLElement).style.maxHeight = '';
|
(Blockly.DropDownDiv.getContentDiv() as HTMLElement).style.maxHeight = '';
|
||||||
this.stopSounds();
|
this.stopSounds();
|
||||||
|
// Update color (deselect) on dropdown hide
|
||||||
|
let source = this.sourceBlock_ as Blockly.BlockSvg;
|
||||||
|
if (source?.isShadow()) {
|
||||||
|
source.setColour(this.savedPrimary_);
|
||||||
|
} else if (this.borderRect_) {
|
||||||
|
this.borderRect_.setAttribute('fill', this.savedPrimary_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createTextNode_(content: string) {
|
protected createTextNode_(content: string) {
|
||||||
const category = this.parseCategory(content);
|
const category = this.parseCategory(content);
|
||||||
let text = content.substr(content.indexOf(' ') + 1);
|
let text = content.substr(content.indexOf(' ') + 1);
|
||||||
text = text.length > 15 ? text.substr(0, 12) + "..." : text;
|
|
||||||
const textSpan = document.createElement('span');
|
const textSpan = document.createElement('span');
|
||||||
textSpan.setAttribute('class', 'blocklyDropdownText');
|
textSpan.setAttribute('class', 'blocklyDropdownText');
|
||||||
textSpan.textContent = text;
|
textSpan.textContent = text;
|
||||||
@ -311,9 +323,9 @@ export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
pxsim.AudioContextManager.stop();
|
pxsim.AudioContextManager.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSoundIcon(category: string) {
|
private getSoundIcon(indexCategory: number) {
|
||||||
if (soundIconCache && soundIconCache[category]) {
|
if (soundIconCacheArray && soundIconCacheArray[indexCategory]) {
|
||||||
return soundIconCache[category].icon;
|
return soundIconCacheArray[indexCategory][1].icon;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ export class FieldPorts extends pxtblockly.FieldImages implements Blockly.FieldC
|
|||||||
|
|
||||||
this.setText = Blockly.FieldDropdown.prototype.setText;
|
this.setText = Blockly.FieldDropdown.prototype.setText;
|
||||||
this.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
|
this.updateSize_ = (Blockly.Field as any).prototype.updateSize_;
|
||||||
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trimOptions_() {
|
trimOptions_() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es2017",
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
|
2
libs/base/shims.d.ts
vendored
@ -204,6 +204,8 @@ declare namespace control {
|
|||||||
*/
|
*/
|
||||||
//% shim=control::dmesgValue
|
//% shim=control::dmesgValue
|
||||||
function dmesgValue(v: any): void;
|
function dmesgValue(v: any): void;
|
||||||
|
}
|
||||||
|
declare namespace control {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force GC and dump basic information about heap.
|
* Force GC and dump basic information about heap.
|
||||||
|
@ -250,7 +250,7 @@ namespace sensors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Measure the ambient or reflected light value from 0 (darkest) to 100 (brightest).
|
* Measure the ambient or reflected light value from 0 (darkest) to 100 (brightest). In raw reflection light mode, the range will be different.
|
||||||
* @param sensor the color sensor port
|
* @param sensor the color sensor port
|
||||||
*/
|
*/
|
||||||
//% help=sensors/color-sensor/light
|
//% help=sensors/color-sensor/light
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
namespace sensors {
|
namespace sensors {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -9,7 +7,7 @@ namespace sensors {
|
|||||||
//% blockId=colorEnumPicker block="%color" shim=TD_ID
|
//% blockId=colorEnumPicker block="%color" shim=TD_ID
|
||||||
//% weight=0 blockHidden=1 turnRatio.fieldOptions.decompileLiterals=1
|
//% weight=0 blockHidden=1 turnRatio.fieldOptions.decompileLiterals=1
|
||||||
//% color.fieldEditor="colorenum"
|
//% color.fieldEditor="colorenum"
|
||||||
//% color.fieldOptions.colours='["#f12a21", "#ffd01b", "#006db3", "#00934b", "#000000", "#6c2d00", "#ffffff"]'
|
//% color.fieldOptions.colours='["#f12a21", "#ffd01b", "#006db3", "#00934b", "#000000", "#6c2d00", "#ffffff", "#dfe6e9"]'
|
||||||
//% color.fieldOptions.columns=2 color.fieldOptions.className='legoColorPicker'
|
//% color.fieldOptions.columns=2 color.fieldOptions.className='legoColorPicker'
|
||||||
export function __colorEnumPicker(color: ColorSensorColor): number {
|
export function __colorEnumPicker(color: ColorSensorColor): number {
|
||||||
return color;
|
return color;
|
||||||
|
11
libs/core/cooperate.ts
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,8 +21,7 @@ namespace sensors.internal {
|
|||||||
const now = control.millis();
|
const now = control.millis();
|
||||||
if (now - this.lastQuery >= this.interval * 2)
|
if (now - this.lastQuery >= this.interval * 2)
|
||||||
this.queryAndUpdate(); // sensor poller is not allowed to run
|
this.queryAndUpdate(); // sensor poller is not allowed to run
|
||||||
if (now - this.lastPause >= this.interval * 5)
|
control.cooperate(); // allow events to trigger
|
||||||
pause(1); // allow events to trigger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private queryAndUpdate() {
|
private queryAndUpdate() {
|
||||||
|
BIN
libs/core/jres/icons/dualMotorLarge-portAB-icon.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
libs/core/jres/icons/dualMotorLarge-portAD-icon.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
libs/core/jres/icons/dualMotorLarge-portBC-icon.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
libs/core/jres/icons/dualMotorLarge-portCD-icon.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
libs/core/jres/icons/motorLarge-portA-icon.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
libs/core/jres/icons/motorLarge-portB-icon.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
libs/core/jres/icons/motorLarge-portC-icon.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
libs/core/jres/icons/motorLarge-portD-icon.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
libs/core/jres/icons/motorMedium-portA-icon.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
libs/core/jres/icons/motorMedium-portB-icon.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
libs/core/jres/icons/motorMedium-portC-icon.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
libs/core/jres/icons/motorMedium-portD-icon.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
@ -124,7 +124,7 @@ namespace motors {
|
|||||||
export function stopAll() {
|
export function stopAll() {
|
||||||
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
|
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
|
||||||
writePWM(b);
|
writePWM(b);
|
||||||
pause(1);
|
control.cooperate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,7 +136,7 @@ namespace motors {
|
|||||||
//% help=motors/reset-all
|
//% help=motors/reset-all
|
||||||
export function resetAll() {
|
export function resetAll() {
|
||||||
reset(Output.ALL)
|
reset(Output.ALL)
|
||||||
pause(1);
|
control.cooperate();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MoveSchedule {
|
interface MoveSchedule {
|
||||||
@ -196,6 +196,7 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=outputMotorSetBrakeMode block="set %motor|brake %brake=toggleOnOff"
|
//% blockId=outputMotorSetBrakeMode block="set %motor|brake %brake=toggleOnOff"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=60 blockGap=8
|
//% weight=60 blockGap=8
|
||||||
//% group="Properties"
|
//% group="Properties"
|
||||||
//% help=motors/motor/set-brake
|
//% help=motors/motor/set-brake
|
||||||
@ -210,6 +211,7 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=outputMotorSetPauseMode block="set %motor|pause on run %brake=toggleOnOff"
|
//% blockId=outputMotorSetPauseMode block="set %motor|pause on run %brake=toggleOnOff"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=60 blockGap=8
|
//% weight=60 blockGap=8
|
||||||
//% group="Properties"
|
//% group="Properties"
|
||||||
setPauseOnRun(value: boolean) {
|
setPauseOnRun(value: boolean) {
|
||||||
@ -222,6 +224,7 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=motorSetInverted block="set %motor|inverted %reversed=toggleOnOff"
|
//% blockId=motorSetInverted block="set %motor|inverted %reversed=toggleOnOff"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=59 blockGap=8
|
//% weight=59 blockGap=8
|
||||||
//% group="Properties"
|
//% group="Properties"
|
||||||
//% help=motors/motor/set-inverted
|
//% help=motors/motor/set-inverted
|
||||||
@ -239,6 +242,7 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=motorSetBrakeSettleTime block="set %motor|brake settle time %millis|ms"
|
//% blockId=motorSetBrakeSettleTime block="set %motor|brake settle time %millis|ms"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=1 blockGap=8
|
//% weight=1 blockGap=8
|
||||||
//% group="Properties"
|
//% group="Properties"
|
||||||
//% millis.defl=200 millis.min=0 millis.max=500
|
//% millis.defl=200 millis.min=0 millis.max=500
|
||||||
@ -269,7 +273,7 @@ namespace motors {
|
|||||||
if (this._brake && this._brakeSettleTime > 0)
|
if (this._brake && this._brakeSettleTime > 0)
|
||||||
pause(this._brakeSettleTime);
|
pause(this._brakeSettleTime);
|
||||||
else {
|
else {
|
||||||
pause(1);
|
control.cooperate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +284,7 @@ namespace motors {
|
|||||||
// allow robot to settle
|
// allow robot to settle
|
||||||
this.settle();
|
this.settle();
|
||||||
} else {
|
} else {
|
||||||
pause(1);
|
control.cooperate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,6 +348,7 @@ namespace motors {
|
|||||||
//% weight=100 blockGap=8
|
//% weight=100 blockGap=8
|
||||||
//% group="Move"
|
//% group="Move"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% expandableArgumentMode=toggle
|
//% expandableArgumentMode=toggle
|
||||||
//% help=motors/motor/run
|
//% help=motors/motor/run
|
||||||
run(speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
|
run(speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
|
||||||
@ -357,7 +362,7 @@ namespace motors {
|
|||||||
// special: 0 is infinity
|
// special: 0 is infinity
|
||||||
if (schedule.steps[0] + schedule.steps[1] + schedule.steps[2] == 0) {
|
if (schedule.steps[0] + schedule.steps[1] + schedule.steps[2] == 0) {
|
||||||
this._run(schedule.speed);
|
this._run(schedule.speed);
|
||||||
pause(1);
|
control.cooperate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,6 +398,7 @@ namespace motors {
|
|||||||
//% weight=99 blockGap=8
|
//% weight=99 blockGap=8
|
||||||
//% group="Move"
|
//% group="Move"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% help=motors/motor/ramp
|
//% help=motors/motor/ramp
|
||||||
//% inlineInputMode=inline
|
//% inlineInputMode=inline
|
||||||
//% expandableArgumentMode=toggle
|
//% expandableArgumentMode=toggle
|
||||||
@ -422,6 +428,7 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=outputMotorsetRunRamp block="set %motor|run %ramp to $value||$unit"
|
//% blockId=outputMotorsetRunRamp block="set %motor|run %ramp to $value||$unit"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=21 blockGap=8
|
//% weight=21 blockGap=8
|
||||||
//% group="Properties"
|
//% group="Properties"
|
||||||
//% help=motors/motor/set-run-phase
|
//% help=motors/motor/set-run-phase
|
||||||
@ -493,6 +500,7 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=outputMotorSetRegulated block="set %motor|regulated %value=toggleOnOff"
|
//% blockId=outputMotorSetRegulated block="set %motor|regulated %value=toggleOnOff"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=58 blockGap=8
|
//% weight=58 blockGap=8
|
||||||
//% group="Properties"
|
//% group="Properties"
|
||||||
//% help=motors/motor/set-regulated
|
//% help=motors/motor/set-regulated
|
||||||
@ -518,6 +526,7 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=motorPauseUntilRead block="pause until %motor|ready"
|
//% blockId=motorPauseUntilRead block="pause until %motor|ready"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=90 blockGap=8
|
//% weight=90 blockGap=8
|
||||||
//% group="Move"
|
//% group="Move"
|
||||||
pauseUntilReady(timeOut?: number) {
|
pauseUntilReady(timeOut?: number) {
|
||||||
@ -576,6 +585,7 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=motorSpeed block="%motor|speed"
|
//% blockId=motorSpeed block="%motor|speed"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=72
|
//% weight=72
|
||||||
//% blockGap=8
|
//% blockGap=8
|
||||||
//% group="Counters"
|
//% group="Counters"
|
||||||
@ -591,13 +601,14 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=motorAngle block="%motor|angle"
|
//% blockId=motorAngle block="%motor|angle"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=70
|
//% weight=70
|
||||||
//% blockGap=8
|
//% blockGap=8
|
||||||
//% group="Counters"
|
//% group="Counters"
|
||||||
//% help=motors/motor/angle
|
//% help=motors/motor/angle
|
||||||
angle(): number {
|
angle(): number {
|
||||||
this.init();
|
this.init();
|
||||||
return getMotorData(this._port).count;
|
return getMotorData(this._port).count * this.invertedFactor();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -605,6 +616,7 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=motorClearCount block="clear %motor|counters"
|
//% blockId=motorClearCount block="clear %motor|counters"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=68
|
//% weight=68
|
||||||
//% blockGap=8
|
//% blockGap=8
|
||||||
//% group="Counters"
|
//% group="Counters"
|
||||||
@ -633,6 +645,7 @@ namespace motors {
|
|||||||
*/
|
*/
|
||||||
//% blockId=motorPauseUntilStall block="pause until %motor|stalled"
|
//% blockId=motorPauseUntilStall block="pause until %motor|stalled"
|
||||||
//% motor.fieldEditor="motors"
|
//% motor.fieldEditor="motors"
|
||||||
|
//% motor.fieldOptions.decompileLiterals=1
|
||||||
//% weight=89
|
//% weight=89
|
||||||
//% group="Move"
|
//% group="Move"
|
||||||
//% help=motors/motor/pause-until-stalled
|
//% help=motors/motor/pause-until-stalled
|
||||||
@ -656,28 +669,28 @@ namespace motors {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="large motor A" jres=icons.portA
|
//% whenUsed fixedInstance block="large motor A" jres=icons.motorLargePortA
|
||||||
export const largeA = new Motor(Output.A, true);
|
export const largeA = new Motor(Output.A, true);
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="large motor B" jres=icons.portB
|
//% whenUsed fixedInstance block="large motor B" jres=icons.motorLargePortB
|
||||||
export const largeB = new Motor(Output.B, true);
|
export const largeB = new Motor(Output.B, true);
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="large motor C" jres=icons.portC
|
//% whenUsed fixedInstance block="large motor C" jres=icons.motorLargePortC
|
||||||
export const largeC = new Motor(Output.C, true);
|
export const largeC = new Motor(Output.C, true);
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="large motor D" jres=icons.portD
|
//% whenUsed fixedInstance block="large motor D" jres=icons.motorLargePortD
|
||||||
export const largeD = new Motor(Output.D, true);
|
export const largeD = new Motor(Output.D, true);
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="medium motor A" jres=icons.portA
|
//% whenUsed fixedInstance block="medium motor A" jres=icons.motorMeduimPortA
|
||||||
export const mediumA = new Motor(Output.A, false);
|
export const mediumA = new Motor(Output.A, false);
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="medium motor B" jres=icons.portB
|
//% whenUsed fixedInstance block="medium motor B" jres=icons.motorMeduimPortB
|
||||||
export const mediumB = new Motor(Output.B, false);
|
export const mediumB = new Motor(Output.B, false);
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="medium motor C" jres=icons.portC
|
//% whenUsed fixedInstance block="medium motor C" jres=icons.motorMeduimPortC
|
||||||
export const mediumC = new Motor(Output.C, false);
|
export const mediumC = new Motor(Output.C, false);
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="medium motor D" jres=icons.portD
|
//% whenUsed fixedInstance block="medium motor D" jres=icons.motorMeduimPortD
|
||||||
export const mediumD = new Motor(Output.D, false);
|
export const mediumD = new Motor(Output.D, false);
|
||||||
|
|
||||||
//% fixedInstances
|
//% fixedInstances
|
||||||
@ -708,7 +721,7 @@ namespace motors {
|
|||||||
* @param unit (optional) unit of the value
|
* @param unit (optional) unit of the value
|
||||||
*/
|
*/
|
||||||
//% blockId=motorPairTank block="tank **motors** %motors %speedLeft=motorSpeedPicker|\\% %speedRight=motorSpeedPicker|\\%||for %value %unit"
|
//% blockId=motorPairTank block="tank **motors** %motors %speedLeft=motorSpeedPicker|\\% %speedRight=motorSpeedPicker|\\%||for %value %unit"
|
||||||
//% motors.fieldEditor="ports"
|
//% motors.fieldEditor="motors"
|
||||||
//% weight=96 blockGap=8
|
//% weight=96 blockGap=8
|
||||||
//% inlineInputMode=inline
|
//% inlineInputMode=inline
|
||||||
//% group="Move"
|
//% group="Move"
|
||||||
@ -738,7 +751,7 @@ namespace motors {
|
|||||||
* @param unit (optional) unit of the value
|
* @param unit (optional) unit of the value
|
||||||
*/
|
*/
|
||||||
//% blockId=motorPairSteer block="steer **motors** %chassis turn ratio %turnRatio=motorTurnRatioPicker speed %speed=motorSpeedPicker|\\%||for %value %unit"
|
//% blockId=motorPairSteer block="steer **motors** %chassis turn ratio %turnRatio=motorTurnRatioPicker speed %speed=motorSpeedPicker|\\%||for %value %unit"
|
||||||
//% chassis.fieldEditor="ports"
|
//% chassis.fieldEditor="motors"
|
||||||
//% weight=95
|
//% weight=95
|
||||||
//% turnRatio.min=-200 turnRatio=200
|
//% turnRatio.min=-200 turnRatio=200
|
||||||
//% inlineInputMode=inline
|
//% inlineInputMode=inline
|
||||||
@ -811,16 +824,16 @@ namespace motors {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="B+C" jres=icons.portBC
|
//% whenUsed fixedInstance block="large motors B+C" jres=icons.dualMotorLargePortBC
|
||||||
export const largeBC = new SynchedMotorPair(Output.BC);
|
export const largeBC = new SynchedMotorPair(Output.BC);
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="A+D" jres=icons.portAD
|
//% whenUsed fixedInstance block="large motors A+D" jres=icons.dualMotorLargePortAD
|
||||||
export const largeAD = new SynchedMotorPair(Output.AD);
|
export const largeAD = new SynchedMotorPair(Output.AD);
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="A+B" jres=icons.portAB
|
//% whenUsed fixedInstance block="large motors A+B" jres=icons.dualMotorLargePortAB
|
||||||
export const largeAB = new SynchedMotorPair(Output.AB);
|
export const largeAB = new SynchedMotorPair(Output.AB);
|
||||||
|
|
||||||
//% whenUsed fixedInstance block="C+D" jres=icons.portCD
|
//% whenUsed fixedInstance block="large motors C+D" jres=icons.dualMotorLargePortCD
|
||||||
export const largeCD = new SynchedMotorPair(Output.CD);
|
export const largeCD = new SynchedMotorPair(Output.CD);
|
||||||
|
|
||||||
function reset(out: Output) {
|
function reset(out: Output) {
|
||||||
|
@ -28,7 +28,8 @@
|
|||||||
"platform.h",
|
"platform.h",
|
||||||
"platform.cpp",
|
"platform.cpp",
|
||||||
"dmesg.cpp",
|
"dmesg.cpp",
|
||||||
"integrator.ts"
|
"integrator.ts",
|
||||||
|
"cooperate.ts"
|
||||||
],
|
],
|
||||||
"testFiles": [
|
"testFiles": [
|
||||||
"test.ts"
|
"test.ts"
|
||||||
|
@ -12,4 +12,5 @@ music.setTempo(120)
|
|||||||
music.noteFrequency(Note.C)
|
music.noteFrequency(Note.C)
|
||||||
music.beat()
|
music.beat()
|
||||||
music.setVolume(50)
|
music.setVolume(50)
|
||||||
|
music.volume()
|
||||||
```
|
```
|
@ -13,7 +13,22 @@
|
|||||||
|
|
||||||
namespace music {
|
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;
|
uint8_t *lmsSoundMMap;
|
||||||
|
|
||||||
int writeDev(void *data, int size) {
|
int writeDev(void *data, int size) {
|
||||||
@ -37,6 +52,18 @@ void setVolume(int volume) {
|
|||||||
currVolume = max(0, min(100, 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_BREAK 0
|
||||||
#define SOUND_CMD_TONE 1
|
#define SOUND_CMD_TONE 1
|
||||||
#define SOUND_CMD_PLAY 2
|
#define SOUND_CMD_PLAY 2
|
||||||
@ -45,7 +72,7 @@ void setVolume(int volume) {
|
|||||||
|
|
||||||
struct ToneCmd {
|
struct ToneCmd {
|
||||||
uint8_t cmd;
|
uint8_t cmd;
|
||||||
uint8_t vol;
|
uint8_t lvl;
|
||||||
uint16_t freq;
|
uint16_t freq;
|
||||||
uint16_t duration;
|
uint16_t duration;
|
||||||
};
|
};
|
||||||
@ -55,10 +82,26 @@ static void _stopSound() {
|
|||||||
writeDev(&cmd, sizeof(cmd));
|
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) {
|
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;
|
ToneCmd cmd;
|
||||||
cmd.cmd = SOUND_CMD_TONE;
|
cmd.cmd = SOUND_CMD_TONE;
|
||||||
cmd.vol = volume;
|
cmd.lvl = level;
|
||||||
cmd.freq = frequency;
|
cmd.freq = frequency;
|
||||||
cmd.duration = duration;
|
cmd.duration = duration;
|
||||||
// (*SoundInstance.pSound).Busy = TRUE;
|
// (*SoundInstance.pSound).Busy = TRUE;
|
||||||
@ -122,7 +165,8 @@ void playSample(Buffer buf) {
|
|||||||
stopUser();
|
stopUser();
|
||||||
pthread_mutex_lock(&pumpMutex);
|
pthread_mutex_lock(&pumpMutex);
|
||||||
*lmsSoundMMap = 1; // BUSY
|
*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);
|
writeDev(cmd, 2);
|
||||||
decrRC(currentSample);
|
decrRC(currentSample);
|
||||||
currentSample = buf;
|
currentSample = buf;
|
||||||
|
10
libs/music/shims.d.ts
vendored
@ -13,6 +13,16 @@ declare namespace music {
|
|||||||
//% weight=1 shim=music::setVolume
|
//% weight=1 shim=music::setVolume
|
||||||
function setVolume(volume: int32): void;
|
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.
|
* 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
|
* @param frequency pitch of the tone to play in Hertz (Hz), eg: Note.C
|
||||||
|
@ -5,3 +5,4 @@ music.playTone(1440, 500)
|
|||||||
pause(500)
|
pause(500)
|
||||||
music.playTone(2440, 500)
|
music.playTone(2440, 500)
|
||||||
pause(500)
|
pause(500)
|
||||||
|
music.volume()
|
||||||
|
@ -22,6 +22,7 @@ namespace _screen_internal {
|
|||||||
namespace brick {
|
namespace brick {
|
||||||
const textOffset = 4;
|
const textOffset = 4;
|
||||||
const lineOffset = 2;
|
const lineOffset = 2;
|
||||||
|
|
||||||
enum ScreenMode {
|
enum ScreenMode {
|
||||||
None,
|
None,
|
||||||
ShowLines,
|
ShowLines,
|
||||||
@ -29,6 +30,14 @@ namespace brick {
|
|||||||
Ports,
|
Ports,
|
||||||
Custom
|
Custom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum PrintStyle {
|
||||||
|
//% block="black on white"
|
||||||
|
BlackOnWhite,
|
||||||
|
//% block="white on black"
|
||||||
|
WhiteOnBlack
|
||||||
|
}
|
||||||
|
|
||||||
let screenMode = ScreenMode.None;
|
let screenMode = ScreenMode.None;
|
||||||
export let font = image.font8;
|
export let font = image.font8;
|
||||||
|
|
||||||
@ -48,6 +57,48 @@ namespace brick {
|
|||||||
return ((screen.height - textOffset) / lineHeight()) >> 0
|
return ((screen.height - textOffset) / lineHeight()) >> 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of columns
|
||||||
|
*/
|
||||||
|
//%
|
||||||
|
export function columnCount(): number {
|
||||||
|
return ((screen.width - textOffset) / font.charWidth) >> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show text on the screen on a specific line and starting at a column and the selected print style.
|
||||||
|
* @param text the text to print on the screen, eg: "Hello world"
|
||||||
|
* @param line the line number to print the text at (starting at 1), eg: 1
|
||||||
|
* @param column the column number to print the text at (starting at 1), eg: 1
|
||||||
|
* @param printStyle print style black on white or white on black
|
||||||
|
*/
|
||||||
|
//% blockId=screenPrintString block="show string $text|at line $line||column $column|style $printStyle"
|
||||||
|
//% weight=98 group="Screen" inlineInputMode="inline" blockGap=8
|
||||||
|
//% expandableArgumentMode="enabled"
|
||||||
|
//% help=brick/show-string
|
||||||
|
//% line.min=1 line.max=12
|
||||||
|
//% column.min=1 column.max=29
|
||||||
|
export function printString(text: string, line: number, column: number = 1, printStyle: PrintStyle = PrintStyle.BlackOnWhite) {
|
||||||
|
if (screenMode != ScreenMode.ShowLines) {
|
||||||
|
screenMode = ScreenMode.ShowLines;
|
||||||
|
screen.fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
line = (line - 1) >> 0; // line indexing starts at 1
|
||||||
|
column = (column - 1) >> 0; // column indexing starts at 1
|
||||||
|
const nlines = lineCount();
|
||||||
|
const nColumn = columnCount();
|
||||||
|
if (line < 0 || line > nlines) return; // out of screen by line
|
||||||
|
if (column < 0 || column > nColumn) return; // out of screen by column
|
||||||
|
|
||||||
|
const w = font.charWidth;
|
||||||
|
const h = lineHeight();
|
||||||
|
const x = textOffset + w * column;
|
||||||
|
const y = textOffset + h * line;
|
||||||
|
screen.fillRect(x, y, text.length * font.charWidth, h, (printStyle == PrintStyle.BlackOnWhite ? 0 : 255)); // clear background
|
||||||
|
screen.print(text, x, y, (printStyle == PrintStyle.BlackOnWhite ? 1 : 2), font);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show text on the screen at a specific line.
|
* Show text on the screen at a specific line.
|
||||||
* @param text the text to print on the screen, eg: "Hello world"
|
* @param text the text to print on the screen, eg: "Hello world"
|
||||||
@ -56,7 +107,8 @@ namespace brick {
|
|||||||
//% blockId=screen_print block="show string %text|at line %line"
|
//% blockId=screen_print block="show string %text|at line %line"
|
||||||
//% weight=98 group="Screen" inlineInputMode="inline" blockGap=8
|
//% weight=98 group="Screen" inlineInputMode="inline" blockGap=8
|
||||||
//% help=brick/show-string
|
//% help=brick/show-string
|
||||||
//% line.min=1 line.max=10
|
//% line.min=1 line.max=12
|
||||||
|
//% deprecated=true
|
||||||
export function showString(text: string, line: number) {
|
export function showString(text: string, line: number) {
|
||||||
if (screenMode != ScreenMode.ShowLines) {
|
if (screenMode != ScreenMode.ShowLines) {
|
||||||
screenMode = ScreenMode.ShowLines;
|
screenMode = ScreenMode.ShowLines;
|
||||||
@ -74,19 +126,56 @@ namespace brick {
|
|||||||
screen.print(text, textOffset, y, 1, font);
|
screen.print(text, textOffset, y, 1, font);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a number on the screen on a specific line and starting at a column and the selected print style.
|
||||||
|
* @param value the numeric value
|
||||||
|
* @param line the line number to print the text at, eg: 1
|
||||||
|
* @param column the column number to print the text at (starting at 1), eg: 1
|
||||||
|
* @param printStyle print style black on white or white on black
|
||||||
|
*/
|
||||||
|
//% blockId=screenPrintNumber block="show number $value|at line $line||column $column|style $printStyle"
|
||||||
|
//% weight=97 group="Screen" inlineInputMode="inline" blockGap=8
|
||||||
|
//% expandableArgumentMode="enabled"
|
||||||
|
//% help=brick/show-number
|
||||||
|
//% line.min=1 line.max=12
|
||||||
|
//% column.min=1 column.max=29
|
||||||
|
export function printNumber(value: number, line: number, column: number = 1, printStyle: PrintStyle = PrintStyle.BlackOnWhite) {
|
||||||
|
printString("" + value, line, column, printStyle);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a number on the screen
|
* Show a number on the screen
|
||||||
* @param value the numeric value
|
* @param value the numeric value
|
||||||
* @param line the line number to print the text at, eg: 1
|
* @param line the line number to print the text at, eg: 1
|
||||||
*/
|
*/
|
||||||
//% blockId=screenShowNumber block="show number %name|at line %line"
|
//% blockId=screenShowNumber block="show number %name|at line %line"
|
||||||
//% weight=96 group="Screen" inlineInputMode="inline" blockGap=8
|
//% weight=97 group="Screen" inlineInputMode="inline" blockGap=8
|
||||||
//% help=brick/show-number
|
//% help=brick/show-number
|
||||||
//% line.min=1 line.max=10
|
//% line.min=1 line.max=12
|
||||||
|
//% deprecated=true
|
||||||
export function showNumber(value: number, line: number) {
|
export function showNumber(value: number, line: number) {
|
||||||
showString("" + value, line);
|
showString("" + value, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a name, value pair on the screen on a specific line and starting at a column and the selected print style.
|
||||||
|
* @param name the value name
|
||||||
|
* @param value the numeric value
|
||||||
|
* @param line the line number to print the text at, eg: 1
|
||||||
|
* @param column the column number to print the text at (starting at 1), eg: 1
|
||||||
|
* @param printStyle print style black on white or white on black
|
||||||
|
*/
|
||||||
|
//% blockId=screenPrintValue block="show value $name|= $value|at line $line||column $column|style $printStyle"
|
||||||
|
//% weight=96 group="Screen" inlineInputMode="inline" blockGap=8
|
||||||
|
//% expandableArgumentMode="enabled"
|
||||||
|
//% help=brick/show-value
|
||||||
|
//% line.min=1 line.max=12
|
||||||
|
//% column.min=1 column.max=29
|
||||||
|
export function printValue(name: string, value: number, line: number, column: number = 1, printStyle: PrintStyle = PrintStyle.BlackOnWhite) {
|
||||||
|
value = Math.round(value * 1000) / 1000;
|
||||||
|
printString((name ? name + ": " : "") + value, line, column, printStyle);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a name, value pair on the screen
|
* Show a name, value pair on the screen
|
||||||
* @param value the numeric value
|
* @param value the numeric value
|
||||||
@ -95,7 +184,8 @@ namespace brick {
|
|||||||
//% blockId=screenShowValue block="show value %name|= %text|at line %line"
|
//% blockId=screenShowValue block="show value %name|= %text|at line %line"
|
||||||
//% weight=96 group="Screen" inlineInputMode="inline" blockGap=8
|
//% weight=96 group="Screen" inlineInputMode="inline" blockGap=8
|
||||||
//% help=brick/show-value
|
//% help=brick/show-value
|
||||||
//% line.min=1 line.max=10
|
//% line.min=1 line.max=12
|
||||||
|
//% deprecated=true
|
||||||
export function showValue(name: string, value: number, line: number) {
|
export function showValue(name: string, value: number, line: number) {
|
||||||
value = Math.round(value * 1000) / 1000;
|
value = Math.round(value * 1000) / 1000;
|
||||||
showString((name ? name + ": " : "") + value, line);
|
showString((name ? name + ": " : "") + value, line);
|
||||||
@ -123,11 +213,11 @@ namespace brick {
|
|||||||
*/
|
*/
|
||||||
//% blockId=brickShowPorts block="show ports"
|
//% blockId=brickShowPorts block="show ports"
|
||||||
//% help=brick/show-ports blockGap=8
|
//% help=brick/show-ports blockGap=8
|
||||||
//% weight=10 group="Screen"
|
//% weight=95 group="Screen"
|
||||||
export function showPorts() {
|
export function showPorts() {
|
||||||
if (screenMode == ScreenMode.Ports) return;
|
if (screenMode == ScreenMode.Ports) return;
|
||||||
|
|
||||||
screenMode = ScreenMode.Ports;
|
screenMode = ScreenMode.Ports;
|
||||||
|
|
||||||
renderPorts();
|
renderPorts();
|
||||||
control.runInParallel(function () {
|
control.runInParallel(function () {
|
||||||
while (screenMode == ScreenMode.Ports) {
|
while (screenMode == ScreenMode.Ports) {
|
||||||
@ -146,11 +236,14 @@ namespace brick {
|
|||||||
|
|
||||||
for (let i = 0; i < 4; ++i) {
|
for (let i = 0; i < 4; ++i) {
|
||||||
const x = i * col + 2;
|
const x = i * col + 2;
|
||||||
screen.print("ABCD"[i], x, 1 * lineHeight8, 1, image.font8)
|
if (screenMode != ScreenMode.Ports) return;
|
||||||
screen.print((i + 1).toString(), x, h - lineHeight8, 1, image.font8)
|
screen.print("ABCD"[i], x, 1 * lineHeight8, 1, image.font8);
|
||||||
|
screen.print((i + 1).toString(), x, h - lineHeight8, 1, image.font8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (screenMode != ScreenMode.Ports) return;
|
||||||
screen.drawLine(0, 5 * lineHeight8, screen.width, 5 * lineHeight8, 1);
|
screen.drawLine(0, 5 * lineHeight8, screen.width, 5 * lineHeight8, 1);
|
||||||
screen.drawLine(0, h - 5 * lineHeight8, screen.width, h - 5 * lineHeight8, 1)
|
screen.drawLine(0, h - 5 * lineHeight8, screen.width, h - 5 * lineHeight8, 1);
|
||||||
|
|
||||||
function scale(x: number) {
|
function scale(x: number) {
|
||||||
if (Math.abs(x) >= 5000) {
|
if (Math.abs(x) >= 5000) {
|
||||||
@ -167,8 +260,9 @@ namespace brick {
|
|||||||
const data = datas[i];
|
const data = datas[i];
|
||||||
const x = i * col + 2;
|
const x = i * col + 2;
|
||||||
if (!data.actualSpeed && !data.count) continue;
|
if (!data.actualSpeed && !data.count) continue;
|
||||||
screen.print(`${scale(data.actualSpeed)}%`, x, 3 * lineHeight8, 1, image.font8)
|
if (screenMode != ScreenMode.Ports) return;
|
||||||
screen.print(`${scale(data.count)}>`, x, 4 * lineHeight8, 1, image.font8)
|
screen.print(`${scale(data.actualSpeed)}%`, x, 3 * lineHeight8, 1, image.font8);
|
||||||
|
screen.print(`${scale(data.count)}>`, x, 4 * lineHeight8, 1, image.font8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sensors
|
// sensors
|
||||||
@ -177,16 +271,16 @@ namespace brick {
|
|||||||
const si = sis[i];
|
const si = sis[i];
|
||||||
const x = (si.port() - 1) * col + 2;
|
const x = (si.port() - 1) * col + 2;
|
||||||
const inf = si._info();
|
const inf = si._info();
|
||||||
if (inf)
|
if (screenMode != ScreenMode.Ports) return;
|
||||||
screen.print(inf, x, h - 2 * lineHeight8, 1, inf.length > 4 ? image.font5 : image.font8);
|
if (inf) screen.print(inf, x, h - 2 * lineHeight8, 1, inf.length > 4 ? image.font5 : image.font8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showBoot() {
|
export function showBoot() {
|
||||||
// pulse green, play startup sound, turn off light
|
// pulse green, play startup sound, turn off light
|
||||||
brick.setStatusLight(StatusLight.GreenPulse);
|
brick.setStatusLight(StatusLight.GreenPulse);
|
||||||
// We pause for 100ms to give time to read sensor values, so they work in on_start block
|
// We pause to give time to read sensor values, so they work in on_start block
|
||||||
pause(400)
|
pause(400); // It turns out that this time is not enough for the simulator to display the LED change
|
||||||
// and we're ready
|
// and we're ready
|
||||||
brick.setStatusLight(StatusLight.Off);
|
brick.setStatusLight(StatusLight.Off);
|
||||||
// always show port by default if no UI is set
|
// always show port by default if no UI is set
|
||||||
@ -210,11 +304,33 @@ namespace brick {
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear on the screen at a specific line.
|
||||||
|
* @param line the line number to clear at (starting at 1), eg: 1
|
||||||
|
*/
|
||||||
|
//% blockId=clearLine block="clear line $line"
|
||||||
|
//% weight=94 group="Screen" inlineInputMode="inline" blockGap=8
|
||||||
|
//% line.min=1 line.max=12
|
||||||
|
export function clearLine(line: number) {
|
||||||
|
if (screenMode != ScreenMode.ShowLines) {
|
||||||
|
screenMode = ScreenMode.ShowLines;
|
||||||
|
screen.fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
line = (line - 1) >> 0; // line indexing starts at 1
|
||||||
|
const nlines = lineCount();
|
||||||
|
if (line < 0 || line > nlines) return; // out of screen by line
|
||||||
|
|
||||||
|
const h = lineHeight();
|
||||||
|
const y = textOffset + h * line;
|
||||||
|
screen.fillRect(0, y, screen.width, h, 0); // clear background
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the screen
|
* Clear the screen
|
||||||
*/
|
*/
|
||||||
//% blockId=screen_clear_screen block="clear screen"
|
//% blockId=screen_clear_screen block="clear screen"
|
||||||
//% weight=90 group="Screen"
|
//% weight=93 group="Screen"
|
||||||
//% help=brick/clear-screen weight=1
|
//% help=brick/clear-screen weight=1
|
||||||
export function clearScreen() {
|
export function clearScreen() {
|
||||||
screen.fill(0)
|
screen.fill(0)
|
||||||
|
23
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pxt-ev3",
|
"name": "pxt-ev3",
|
||||||
"version": "1.4.21",
|
"version": "1.4.32",
|
||||||
"description": "LEGO MINDSTORMS EV3 for Microsoft MakeCode",
|
"description": "LEGO MINDSTORMS EV3 for Microsoft MakeCode",
|
||||||
"private": false,
|
"private": false,
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@ -32,21 +32,20 @@
|
|||||||
"docs/*/*/*.md"
|
"docs/*/*/*.md"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"react": "16.8.3",
|
|
||||||
"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",
|
|
||||||
"@types/bluebird": "^2.0.33",
|
|
||||||
"@types/marked": "^0.3.0",
|
"@types/marked": "^0.3.0",
|
||||||
"@types/node": "^9.3.0",
|
"@types/node": "^9.3.0",
|
||||||
"semantic-ui-less": "2.2.14",
|
"@types/react": "16.8.25",
|
||||||
"typescript": "^3.7.5"
|
"@types/react-dom": "16.0.3",
|
||||||
|
"@types/web-bluetooth": "0.0.4",
|
||||||
|
"@vusion/webfonts-generator": "^0.7.1",
|
||||||
|
"react": "16.8.3",
|
||||||
|
"react-dom": "16.11.0",
|
||||||
|
"semantic-ui-less": "2.4.1",
|
||||||
|
"typescript": "^4.2.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pxt-common-packages": "8.1.3",
|
"pxt-common-packages": "9.2.7",
|
||||||
"pxt-core": "6.2.8"
|
"pxt-core": "7.2.27"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node node_modules/pxt-core/built/pxt.js travis"
|
"test": "node node_modules/pxt-core/built/pxt.js travis"
|
||||||
|
@ -77,8 +77,9 @@
|
|||||||
"pauseUntilBlock": {
|
"pauseUntilBlock": {
|
||||||
"category": "loops"
|
"category": "loops"
|
||||||
},
|
},
|
||||||
"bannedCategories": [
|
"breakBlock": true,
|
||||||
]
|
"continueBlock": true,
|
||||||
|
"bannedCategories": []
|
||||||
},
|
},
|
||||||
"compileService": {
|
"compileService": {
|
||||||
"buildEngine": "dockermake",
|
"buildEngine": "dockermake",
|
||||||
@ -106,20 +107,21 @@
|
|||||||
"driveDisplayName": "EV3",
|
"driveDisplayName": "EV3",
|
||||||
"boardName": "LEGO® MINDSTORMS® Education EV3",
|
"boardName": "LEGO® MINDSTORMS® Education EV3",
|
||||||
"copyrightText": "LEGO, the LEGO logo, MINDSTORMS and the MINDSTORMS EV3 logo are trademarks and/ or copyrights of the LEGO Group. ©2018 The LEGO Group. All rights reserved.",
|
"copyrightText": "LEGO, the LEGO logo, MINDSTORMS and the MINDSTORMS EV3 logo are trademarks and/ or copyrights of the LEGO Group. ©2018 The LEGO Group. All rights reserved.",
|
||||||
"crowdinProject": "kindscript",
|
"crowdinProject": "makecode",
|
||||||
"selectLanguage": true,
|
"selectLanguage": true,
|
||||||
|
"blocksCollapsing": true,
|
||||||
|
"highContrast": true,
|
||||||
"greenScreen": true,
|
"greenScreen": true,
|
||||||
"availableLocales": [
|
"availableLocales": [
|
||||||
"en",
|
"en",
|
||||||
"de",
|
"de",
|
||||||
"ja",
|
"ja",
|
||||||
"ru",
|
"ru",
|
||||||
"zh-CN"
|
"zh-CN",
|
||||||
|
"fr"
|
||||||
],
|
],
|
||||||
"highContrast": true,
|
|
||||||
"lightToc": true,
|
"lightToc": true,
|
||||||
"docMenu": [
|
"docMenu": [{
|
||||||
{
|
|
||||||
"name": "Support",
|
"name": "Support",
|
||||||
"path": "https://forum.makecode.com/"
|
"path": "https://forum.makecode.com/"
|
||||||
},
|
},
|
||||||
@ -158,7 +160,10 @@
|
|||||||
"extendEditor": true,
|
"extendEditor": true,
|
||||||
"extendFieldEditors": true,
|
"extendFieldEditors": true,
|
||||||
"scriptManager": true,
|
"scriptManager": true,
|
||||||
|
"debugger": true,
|
||||||
|
"errorList": true,
|
||||||
"importExtensionFiles": true,
|
"importExtensionFiles": true,
|
||||||
|
"addNewTypeScriptFile": true,
|
||||||
"experiments": [
|
"experiments": [
|
||||||
"python",
|
"python",
|
||||||
"alwaysGithubItemBlocks"
|
"alwaysGithubItemBlocks"
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
CACHE MANIFEST
|
CACHE MANIFEST
|
||||||
|
|
||||||
CACHE:
|
CACHE:
|
||||||
/cdn/bluebird.min.js
|
|
||||||
/cdn/pxtsim.js
|
/cdn/pxtsim.js
|
||||||
/sim/common-sim.js
|
/sim/common-sim.js
|
||||||
/sim/sim.js
|
/sim/sim.js
|
||||||
|
@ -23,7 +23,6 @@ body {
|
|||||||
transition: none !important;
|
transition: none !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="/cdn/bluebird.min.js"></script>
|
|
||||||
<script src="/cdn/pxtsim.js"></script>
|
<script src="/cdn/pxtsim.js"></script>
|
||||||
<script src="/sim/common-sim.js"></script>
|
<script src="/sim/common-sim.js"></script>
|
||||||
<script src="/sim/sim.js"></script>
|
<script src="/sim/sim.js"></script>
|
||||||
|
@ -21,7 +21,7 @@ namespace pxsim {
|
|||||||
export class ColorSensorNode extends UartSensorNode {
|
export class ColorSensorNode extends UartSensorNode {
|
||||||
id = NodeType.ColorSensor;
|
id = NodeType.ColorSensor;
|
||||||
|
|
||||||
private color: number = 50;
|
private color: number = 0;
|
||||||
|
|
||||||
constructor(port: number) {
|
constructor(port: number) {
|
||||||
super(port);
|
super(port);
|
||||||
@ -40,5 +40,13 @@ namespace pxsim {
|
|||||||
getValue() {
|
getValue() {
|
||||||
return this.color;
|
return this.color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMode(mode: number) {
|
||||||
|
this.mode = mode;
|
||||||
|
if (this.mode == ColorSensorMode.RefRaw) this.color = 512;
|
||||||
|
else this.color = 50;
|
||||||
|
this.changed = true;
|
||||||
|
this.modeChanged = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -37,7 +37,7 @@ namespace pxsim {
|
|||||||
return Math.round(this.angle);
|
return Math.round(this.angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the slave motor if any
|
// returns the secondary motor if any
|
||||||
getSynchedMotor() {
|
getSynchedMotor() {
|
||||||
return this._synchedMotor;
|
return this._synchedMotor;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,14 @@ namespace pxsim.music {
|
|||||||
export function stopAllSounds() {
|
export function stopAllSounds() {
|
||||||
SoundMethods.stop()
|
SoundMethods.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pxsim.music.setVolume = (volume: number): void => {
|
||||||
|
pxsim.getAudioState().volume = volume;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function volume() {
|
||||||
|
return pxsim.getAudioState().volume;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace pxsim.SoundMethods {
|
namespace pxsim.SoundMethods {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es2017",
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
@ -8,8 +8,8 @@
|
|||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"newLine": "LF",
|
"newLine": "LF",
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"lib": ["dom", "dom.iterable", "scripthost", "es6"],
|
"lib": ["dom", "dom.iterable", "scripthost", "es2017"],
|
||||||
"types": ["bluebird"],
|
"types": [],
|
||||||
"typeRoots": ["../node_modules/@types"]
|
"typeRoots": ["../node_modules/@types"]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -241,6 +241,8 @@ namespace pxsim.visuals {
|
|||||||
view = new ColorGridControl(this.element, this.defs, state, port);
|
view = new ColorGridControl(this.element, this.defs, state, port);
|
||||||
} else if (state.getMode() == ColorSensorMode.Reflected) {
|
} else if (state.getMode() == ColorSensorMode.Reflected) {
|
||||||
view = new ColorWheelControl(this.element, this.defs, state, port);
|
view = new ColorWheelControl(this.element, this.defs, state, port);
|
||||||
|
} else if (state.getMode() == ColorSensorMode.RefRaw) {
|
||||||
|
view = new ColorWheelControl(this.element, this.defs, state, port);
|
||||||
} else if (state.getMode() == ColorSensorMode.Ambient) {
|
} else if (state.getMode() == ColorSensorMode.Ambient) {
|
||||||
view = new ColorWheelControl(this.element, this.defs, state, port);
|
view = new ColorWheelControl(this.element, this.defs, state, port);
|
||||||
}
|
}
|
||||||
@ -381,7 +383,7 @@ namespace pxsim.visuals {
|
|||||||
this.screenCanvas = document.createElement("canvas");
|
this.screenCanvas = document.createElement("canvas");
|
||||||
this.screenCanvas.id = "board-screen-canvas";
|
this.screenCanvas.id = "board-screen-canvas";
|
||||||
this.screenCanvas.style.userSelect = "none";
|
this.screenCanvas.style.userSelect = "none";
|
||||||
this.screenCanvas.style.msUserSelect = "none";
|
(this.screenCanvas.style as any).msUserSelect = "none";
|
||||||
this.screenCanvas.style.webkitUserSelect = "none";
|
this.screenCanvas.style.webkitUserSelect = "none";
|
||||||
(this.screenCanvas.style as any).MozUserSelect = "none";
|
(this.screenCanvas.style as any).MozUserSelect = "none";
|
||||||
this.screenCanvas.style.position = "absolute";
|
this.screenCanvas.style.position = "absolute";
|
||||||
|
@ -5,8 +5,8 @@ namespace pxsim.visuals {
|
|||||||
export class ColorGridControl extends ControlView<ColorSensorNode> {
|
export class ColorGridControl extends ControlView<ColorSensorNode> {
|
||||||
private group: SVGGElement;
|
private group: SVGGElement;
|
||||||
|
|
||||||
private static colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey', 'white'];
|
private static colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'brown', 'white', 'none'];
|
||||||
private static colorValue = [5, 4, 2, 3, 1, 7, 6];
|
private static colorValue = [5, 4, 2, 3, 1, 7, 6, 0];
|
||||||
|
|
||||||
private colorDivs: Element[] = [];
|
private colorDivs: Element[] = [];
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ namespace pxsim.visuals {
|
|||||||
this.group.setAttribute("transform", `translate(2, 2.5) scale(0.6)`)
|
this.group.setAttribute("transform", `translate(2, 2.5) scale(0.6)`)
|
||||||
|
|
||||||
const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#6c2d00'];
|
const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#6c2d00'];
|
||||||
const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey'];
|
const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'brown'];
|
||||||
|
|
||||||
let cy = -4;
|
let cy = -4;
|
||||||
for (let c = 0; c < colorIds.length; c++) {
|
for (let c = 0; c < colorIds.length; c++) {
|
||||||
@ -33,12 +33,19 @@ namespace pxsim.visuals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const whiteCircleWrapper = pxsim.svg.child(this.group, "g", { 'id': 'white-cirlce-wrapper' });
|
const whiteCircleWrapper = pxsim.svg.child(this.group, "g", { 'id': 'white-cirlce-wrapper' });
|
||||||
const circle = pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle sim-color-grid-white', 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: #fff` });
|
const noneCircleWrapper = pxsim.svg.child(this.group, "g", { 'id': 'nothing-circle-wrapper' });
|
||||||
this.colorDivs.push(circle);
|
const whiteCircle = pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle sim-color-grid-white', 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: #fff` });
|
||||||
pxsim.svg.child(whiteCircleWrapper, "circle", { 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: none;stroke: #94989b;stroke-width: 0.1px` });
|
const noneCircle = pxsim.svg.child(noneCircleWrapper, "circle", { 'class': 'sim-color-grid-circle sim-color-grid-none', 'cx': 7.5, 'cy': '16', 'r': '2', 'style': `fill: #fff; fill-opacity: 0%;` });
|
||||||
|
this.colorDivs.push(whiteCircle);
|
||||||
|
this.colorDivs.push(noneCircle);
|
||||||
|
pxsim.svg.child(whiteCircleWrapper, "circle", { 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: none; stroke: #94989b; stroke-width: 0.1px` });
|
||||||
|
pxsim.svg.child(noneCircleWrapper, "circle", { 'cx': 7.5, 'cy': '16', 'r': '2', 'style': `fill: none; stroke: #94989b; stroke-width: 0.1px` });
|
||||||
pointerEvents.down.forEach(evid => whiteCircleWrapper.addEventListener(evid, ev => {
|
pointerEvents.down.forEach(evid => whiteCircleWrapper.addEventListener(evid, ev => {
|
||||||
this.setColor(6);
|
this.setColor(6);
|
||||||
}));
|
}));
|
||||||
|
pointerEvents.down.forEach(evid => noneCircleWrapper.addEventListener(evid, ev => {
|
||||||
|
this.setColor(0);
|
||||||
|
}));
|
||||||
return this.group;
|
return this.group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,8 +26,12 @@ namespace pxsim.visuals {
|
|||||||
return 131;
|
return 131;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMaxValue() {
|
private getMaxValue(state: ColorSensorMode) {
|
||||||
return 100;
|
return (state == ColorSensorMode.RefRaw ? 1023 : 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapValue(x: number, inMin: number, inMax: number, outMin: number, outMax: number) {
|
||||||
|
return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState() {
|
updateState() {
|
||||||
@ -35,10 +39,12 @@ namespace pxsim.visuals {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const node = this.state;
|
const node = this.state;
|
||||||
const percentage = node.getValue();
|
const value = node.getValue();
|
||||||
const inversePercentage = this.getMaxValue() - percentage;
|
let inverseValue = this.getMaxValue(node.getMode()) - value;
|
||||||
svg.setGradientValue(this.colorGradient, inversePercentage + "%");
|
if (node.getMode() == ColorSensorMode.RefRaw) inverseValue = this.mapValue(inverseValue, 0, 1023, 0, 100);
|
||||||
this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}%`;
|
svg.setGradientValue(this.colorGradient, inverseValue + "%");
|
||||||
|
this.reporter.textContent = `${parseFloat((value).toString()).toFixed(0)}`;
|
||||||
|
if (node.getMode() != ColorSensorMode.RefRaw) this.reporter.textContent += `%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateColorLevel(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
|
updateColorLevel(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
|
||||||
@ -47,7 +53,7 @@ namespace pxsim.visuals {
|
|||||||
const height = bBox.height;
|
const height = bBox.height;
|
||||||
let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height));
|
let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height));
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
state.setColor(t * this.getMaxValue());
|
state.setColor(t * this.getMaxValue(state.getMode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
|
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
|
||||||
|
@ -29,6 +29,7 @@ namespace pxsim.visuals {
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case ColorSensorMode.Colors: this.updateSensorLightVisual('#0062DD'); return; // blue
|
case ColorSensorMode.Colors: this.updateSensorLightVisual('#0062DD'); return; // blue
|
||||||
case ColorSensorMode.Reflected: this.updateSensorLightVisual('#F86262'); return; // red
|
case ColorSensorMode.Reflected: this.updateSensorLightVisual('#F86262'); return; // red
|
||||||
|
case ColorSensorMode.RefRaw: this.updateSensorLightVisual('#F86262'); return; // red
|
||||||
case ColorSensorMode.Ambient: this.updateSensorLightVisual('#67C3E2'); return; // light blue
|
case ColorSensorMode.Ambient: this.updateSensorLightVisual('#67C3E2'); return; // light blue
|
||||||
}
|
}
|
||||||
this.updateSensorLightVisual('#ffffff');
|
this.updateSensorLightVisual('#ffffff');
|
||||||
|
@ -92,6 +92,11 @@
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* active toggle */
|
||||||
|
.ui.item.editor-menuitem .active~.item.toggle {
|
||||||
|
background: @editorToggleColor!important;
|
||||||
|
}
|
||||||
|
|
||||||
.menubar .ui.item.editor-menuitem .item.toggle {
|
.menubar .ui.item.editor-menuitem .item.toggle {
|
||||||
height: 45px;
|
height: 45px;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
specify theme name below
|
specify theme name below
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@placeholder: 'default';
|
||||||
|
|
||||||
/* Global */
|
/* Global */
|
||||||
@site : 'pxt';
|
@site : 'pxt';
|
||||||
@reset : 'default';
|
@reset : 'default';
|
||||||
@ -87,7 +89,7 @@
|
|||||||
Import Theme
|
Import Theme
|
||||||
*******************************/
|
*******************************/
|
||||||
|
|
||||||
@import "theme.less";
|
@import (multiple) "theme.less";
|
||||||
|
|
||||||
@fontPath : 'fonts';
|
@fontPath : 'fonts';
|
||||||
|
|
||||||
|