Compare commits

..

15 Commits

Author SHA1 Message Date
dc6ce0efc7 0.0.53 2018-01-05 08:39:30 -08:00
4039a85bc9 Make sure the ESC button always stops the program 2018-01-05 16:17:33 +00:00
7bd6280292 Disable HID deploy from command line (seems broken; was crashing until my previous checkin) 2018-01-05 16:17:20 +00:00
2ebe96e563 Fix command line deploy 2018-01-05 15:33:09 +00:00
a9be582f90 gyro calibration done right (#186) 2018-01-04 23:21:19 -08:00
de91dc6ab7 Handle the case where multiple motors or multiple sensors are connected to the same port. (#181) 2018-01-04 21:57:01 -08:00
1e460eef9e basic reading of battery level (#182) 2018-01-04 21:50:13 -08:00
0db6987ee5 Add loader and animation (#180) 2018-01-04 16:17:14 -08:00
148657908c Merge pull request #179 from Microsoft/syncedmotorui
Synced motors label (in simulator)
2018-01-04 14:38:29 -08:00
f3f87331c8 nit 2018-01-04 14:09:10 -08:00
5aef77ccc6 Generalizing motorView for both medium and large motor views. Initial work towards synced motor views. Adding sync label for controller motor. 2018-01-04 14:03:50 -08:00
59ca9cf463 0.0.52 2018-01-04 13:04:23 -08:00
7da811246c Behaviors Driven Robotics (#178)
* adding avoid crash behavior

* more implementation

* add another simple behavior

* fixed synched motors

* bump common packages
2018-01-04 12:55:30 -08:00
69f8453947 Merge pull request #177 from Microsoft/sounds_bug_fix
Fix stopAllSounds simulator bug
2018-01-04 12:52:17 -08:00
39ba9b81af Initial work 2018-01-04 12:20:31 -08:00
35 changed files with 391 additions and 104 deletions

View File

@ -2,14 +2,10 @@
import * as fs from 'fs';
require("./editor/deploy")
declare namespace pxt.editor {
function deployCoreAsync(resp: pxtc.CompileResult, disconnect?: boolean): Promise<void>;
}
const deploy = require("./editor/deploy")
export function deployCoreAsync(resp: pxtc.CompileResult) {
return pxt.editor.deployCoreAsync(resp, process.env["PXT_SERIAL"] ? false : true)
return deploy.deployCoreAsync(resp, process.env["PXT_SERIAL"] ? false : true)
.then(() => {
fs.writeFileSync("built/full-" + pxtc.BINARY_UF2, resp.outfiles[pxtc.BINARY_UF2], {
encoding: "base64"

1
docs/static/loader_back.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="301.499" height="179.742" viewBox="0 0 282.655 168.508"><g transform="translate(-41.005 -446.364)"><rect ry="30" rx="30" y="458.77" x="53.41" height="143.698" width="105.312" stroke="#000" stroke-width="17.724" stroke-linecap="round" stroke-linejoin="round"/><rect width="105.668" height="144.183" x="53.232" y="458.527" rx="30.101" ry="30.101" fill="#fff"/></g><g transform="translate(111.528 -446.364)"><rect width="105.312" height="143.698" x="53.41" y="458.77" rx="30" ry="30" stroke="#000" stroke-width="17.724" stroke-linecap="round" stroke-linejoin="round"/><rect ry="30.101" rx="30.101" y="458.527" x="53.232" height="144.183" width="105.668" fill="#fff"/></g></svg>

After

Width:  |  Height:  |  Size: 721 B

1
docs/static/loader_front.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="301.499" height="179.742" viewBox="0 0 282.655 168.508"><g stroke="#000" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(124.66 -243.829)"><circle r="25.968" cy="356.872" cx="-38.891" fill="none"/><circle cx="-38.891" cy="356.872" r="21.013" stroke-width=".809"/><path d="M-30.581 341.741a13.835 13.835 0 0 1 5.76 4.504l-11.099 8.26z" fill="#fff" stroke-width=".533"/></g><g transform="translate(277.193 -243.829)"><circle cx="-38.891" cy="356.872" r="25.968" fill="none"/><circle r="21.013" cy="356.872" cx="-38.891" stroke-width=".809"/><path d="M-30.581 341.741a13.835 13.835 0 0 1 5.76 4.504l-11.099 8.26z" fill="#fff" stroke-width=".533"/></g></g></svg>

After

Width:  |  Height:  |  Size: 728 B

1
docs/static/loader_full.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="301.499" height="179.742" viewBox="0 0 282.655 168.508"><g transform="translate(-41.005 -446.364)"><rect ry="30" rx="30" y="458.77" x="53.41" height="143.698" width="105.312" stroke="#000" stroke-width="17.724" stroke-linecap="round" stroke-linejoin="round"/><rect width="105.668" height="144.183" x="53.232" y="458.527" rx="30.101" ry="30.101" fill="#fff"/></g><g transform="translate(124.66 -243.829)" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><circle r="25.968" cy="356.872" cx="-38.891" fill="none"/><circle cx="-38.891" cy="356.872" r="21.013" stroke-width=".809"/><path d="M-30.581 341.741a13.835 13.835 0 0 1 5.76 4.504l-11.099 8.26z" fill="#fff" stroke-width=".533"/></g><g transform="translate(111.528 -446.364)"><rect width="105.312" height="143.698" x="53.41" y="458.77" rx="30" ry="30" stroke="#000" stroke-width="17.724" stroke-linecap="round" stroke-linejoin="round"/><rect ry="30.101" rx="30.101" y="458.527" x="53.232" height="144.183" width="105.668" fill="#fff"/></g><g transform="translate(277.193 -243.829)" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><circle cx="-38.891" cy="356.872" r="25.968" fill="none"/><circle r="21.013" cy="356.872" cx="-38.891" stroke-width=".809"/><path d="M-30.581 341.741a13.835 13.835 0 0 1 5.76 4.504l-11.099 8.26z" fill="#fff" stroke-width=".533"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -29,7 +29,8 @@ export function initAsync() {
let canHID = false
if (pxt.U.isNodeJS) {
canHID = true
// doesn't seem to work ATM
canHID = false
} else {
const forceHexDownload = /forceHexDownload/i.test(window.location.href);
if (pxt.Cloud.isLocalHost() && pxt.Cloud.localToken && !forceHexDownload)

View File

@ -0,0 +1,14 @@
{
"behaviors": "Behavior drive blocks",
"behaviors.Behavior": "A behavior",
"behaviors.BehaviorManager": "A manager for behaviors",
"behaviors.BehaviorManager.add": "Adds a new behavior to the behavior manager",
"behaviors.BehaviorManager.add|param|behavior": "the behavior to add",
"behaviors.BehaviorManager.start": "Starts the behavior control loop",
"behaviors.BehaviorManager.stop": "Stops the execution loop",
"behaviors.addBehavior": "Adds the behavior and starts it",
"behaviors.addBehavior|param|behavior": "a behavior",
"behaviors.avoidCrash": "A behavior that stops all motors if the sensor distance get too short",
"behaviors.driveForward": "A behavior that turns on the motors to the specified speed",
"behaviors.driveForward|param|motors": "@param speed the desired speed, eg: 50"
}

View File

@ -0,0 +1,7 @@
{
"behaviors.addBehavior|block": "add behavior %behavior",
"behaviors.avoidCrash|block": "avoid crash using %ultrasonic",
"behaviors.driveForward|block": "drive %motors|forward at %speed|%",
"behaviors|block": "behaviors",
"{id:category}Behaviors": "Behaviors"
}

6
libs/behaviors/pxt.json Normal file
View File

@ -0,0 +1,6 @@
{
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/behaviors",
"dependencies": {
"core": "file:../ev3"
}
}

View File

@ -0,0 +1,56 @@
namespace behaviors {
class AvoidCrashBehavior extends behaviors.Behavior {
private ultrasonic: sensors.UltraSonicSensor;
constructor(ultrasonic: sensors.UltraSonicSensor) {
super();
this.ultrasonic = ultrasonic;
}
shouldRun(): boolean {
return this.ultrasonic.distance() < 5;
}
run(): void {
motors.stopAllMotors();
this.active = false;
}
}
/**
* A behavior that stops all motors if the sensor distance get too short
*/
//% blockId=behaviorsAvoidCrash block="avoid crash using %ultrasonic"
export function avoidCrash(ultrasonic: sensors.UltraSonicSensor) : behaviors.Behavior {
return new AvoidCrashBehavior(ultrasonic);
}
class DriveForwardBehavior extends behaviors.Behavior {
private motors: motors.MotorBase;
private speed: number;
constructor(motors: motors.MotorBase, speed: number) {
super();
this.motors = motors;
this.speed = speed;
}
shouldRun(): boolean {
return true;
}
run(): void {
this.motors.setSpeed(this.speed);
pauseUntil(() => !this.active);
this.motors.setSpeed(0);
}
}
/**
* A behavior that turns on the motors to the specified speed
* @param motors
* @param speed the desired speed, eg: 50
*/
//% blockId=behaviorsDriveForward block="drive %motors|forward at %speed|%"
export function driveForward(motors: motors.MotorBase, speed: number): behaviors.Behavior {
return new DriveForwardBehavior(motors, speed);
}
}

View File

@ -21,6 +21,7 @@
"brick.Button.pauseUntil": "Waits until the event is raised",
"brick.Button.pauseUntil|param|ev": "the event to wait for",
"brick.Button.wasPressed": "See if the button was pressed again since the last time you checked.",
"brick.batteryLevel": "Returns the current battery level",
"brick.buttonDown": "Down button on the EV3 Brick.",
"brick.buttonEnter": "Enter button on the EV3 Brick.",
"brick.buttonLeft": "Left button on the EV3 Brick.",

View File

@ -28,6 +28,7 @@
"brick.Button.onEvent|block": "on %button|%event",
"brick.Button.pauseUntil|block": "pause until %button|%event",
"brick.Button.wasPressed|block": "%button|was pressed",
"brick.batteryLevel|block": "battery level",
"brick.buttonDown|block": "down",
"brick.buttonEnter|block": "enter",
"brick.buttonLeft|block": "left",
@ -89,6 +90,7 @@
"{id:group}Buttons": "Buttons",
"{id:group}Chassis": "Chassis",
"{id:group}Light": "Light",
"{id:group}More": "More",
"{id:group}Motion": "Motion",
"{id:group}Screen": "Screen",
"{id:group}Sensors": "Sensors"

12
libs/core/battery.ts Normal file
View File

@ -0,0 +1,12 @@
namespace brick {
/**
* Returns the current battery level
*/
//% blockId=brickBatteryLevel block="battery level"
//% group="More"
export function batteryLevel(): number {
const info = sensors.internal.getBatteryInfo();
return info.current;
}
}

View File

@ -163,6 +163,10 @@ namespace brick {
if (sl[i])
ret |= 1 << i
}
// this needs to be done in query(), which is run without the main JS execution mutex
// otherwise, while(true){} will lock the device
if (ret & DAL.BUTTON_ID_ESCAPE)
control.reset()
return ret
}
@ -172,8 +176,6 @@ namespace brick {
if (!btnsMM) control.fail("no buttons?")
buttons = []
sensors.internal.unsafePollForChanges(50, readButtons, (prev, curr) => {
if (curr & DAL.BUTTON_ID_ESCAPE)
control.reset()
for (let b of buttons)
b._update(!!(curr & b.mask))
})

View File

@ -89,6 +89,14 @@ namespace sensors.internal {
//serial.writeLine("UART " + port + " / " + mode + " - " + info)
}
export function getBatteryInfo(): { temp: number; current: number } {
init();
return {
temp: analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.BatteryTemp),
current: analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.BatteryCurrent)
}
}
function detectDevices() {
let conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
let numChanged = 0
@ -301,6 +309,10 @@ namespace sensors.internal {
return 0
return getUartNumber(fmt, off, this._port)
}
protected reset() {
if (this.isActive()) uartReset(this._port);
}
}
function uartReset(port: number) {

View File

@ -343,7 +343,7 @@ namespace motors {
* Gets motor angle.
* @param motor the port which connects to the motor
*/
//% blockId=motorTachoCount block="%motor|angle"
//% blockId=motorAngle block="%motor|angle"
//% weight=70
//% group="Sensors"
angle(): number {
@ -445,7 +445,7 @@ namespace motors {
private __setSpeed(speed: number) {
syncMotors(this._port, {
speed: speed,
turnRatio: 0,
turnRatio: 100, // same speed
useBrake: !!this._brake
})
}

View File

@ -16,6 +16,7 @@
"png.cpp",
"screen.cpp",
"screen.ts",
"battery.ts",
"output.cpp",
"output.ts",
"core.ts",

View File

@ -19,6 +19,10 @@ namespace sensors {
namespace motors {
}
//% labelLineWidth=0
namespace behaviors {
}
//% color="#D67923" weight=80 icon="\uf10e"
namespace music {

View File

@ -1,4 +1,5 @@
{
"sensors.GyroSensor.angle": "Get the current angle from the gyroscope.",
"sensors.GyroSensor.rate": "Get the current rotation rate from the gyroscope."
"sensors.GyroSensor.calibrate": "Forces a calibration of the gyro. Must be called when the sensor is completely still.",
"sensors.GyroSensor.rotationRate": "Get the current rotation rate from the gyroscope."
}

View File

@ -1,6 +1,7 @@
{
"sensors.GyroSensor.angle|block": "%sensor|angle",
"sensors.GyroSensor.rate|block": "%sensor|rotation rate",
"sensors.GyroSensor.calibrate|block": "%sensor|calibrate",
"sensors.GyroSensor.rotationRate|block": "%sensor|rotation rate",
"sensors.gyro1|block": "gyro 1",
"sensors.gyro2|block": "gyro 2",
"sensors.gyro3|block": "gyro 3",

View File

@ -7,8 +7,10 @@ const enum GyroSensorMode {
namespace sensors {
//% fixedInstances
export class GyroSensor extends internal.UartSensor {
private calibrating: boolean;
constructor(port: number) {
super(port)
this.calibrating = false;
}
_deviceType() {
@ -32,6 +34,9 @@ namespace sensors {
//% weight=65 blockGap=8
//% group="Gyro Sensor"
angle(): number {
if (this.calibrating)
pauseUntil(() => !this.calibrating, 2000);
this.setMode(GyroSensorMode.Angle)
return this.getNumber(NumberFormat.Int16LE, 0)
}
@ -40,7 +45,7 @@ namespace sensors {
* Get the current rotation rate from the gyroscope.
* @param sensor the gyroscope to query the request
*/
//% help=input/gyro/rate
//% help=input/gyro/rotation-rate
//% block="%sensor|rotation rate"
//% blockId=gyroGetRate
//% parts="gyroscope"
@ -48,18 +53,53 @@ namespace sensors {
//% sensor.fieldEditor="ports"
//% weight=65 blockGap=8
//% group="Gyro Sensor"
rate(): number {
rotationRate(): number {
if (this.calibrating)
pauseUntil(() => !this.calibrating, 2000);
this.setMode(GyroSensorMode.Rate)
return this.getNumber(NumberFormat.Int16LE, 0)
}
}
//% fixedInstance whenUsed block="gyro 1" jres=icons.port1
export const gyro1: GyroSensor = new GyroSensor(1)
/**
* Forces a calibration of the gyro. Must be called when the sensor is completely still.
*/
//% help=input/gyro/calibrate
//% block="%sensor|calibrate"
//% blockId=gyroCalibrate
//% parts="gyroscope"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=65 blockGap=8
//% group="Gyro Sensor"
calibrate(): void {
if (this.calibrating) return; // already in calibration mode
this.calibrating = true;
// may be triggered by a button click, give time to settle
loops.pause(500);
// send a reset command
this.reset();
// we need to switch mode twice to perform a calibration
if (this.mode == GyroSensorMode.Rate)
this.setMode(GyroSensorMode.Angle);
else
this.setMode(GyroSensorMode.Rate);
// switch back and wait
if (this.mode == GyroSensorMode.Rate)
this.setMode(GyroSensorMode.Angle);
else
this.setMode(GyroSensorMode.Rate);
this.calibrating = false;
}
}
//% fixedInstance whenUsed block="gyro 2" weight=95 jres=icons.port2
export const gyro2: GyroSensor = new GyroSensor(2)
//% fixedInstance whenUsed block="gyro 1" jres=icons.port1
export const gyro1: GyroSensor = new GyroSensor(1)
//% fixedInstance whenUsed block="gyro 3" jres=icons.port3
export const gyro3: GyroSensor = new GyroSensor(3)

View File

@ -266,8 +266,10 @@ namespace music {
//% blockId=music_play_sound_effect_until_done block="play sound effect %sound|until done"
//% weight=98 blockGap=8
export function playSoundEffectUntilDone(sound: Sound) {
if (!sound) return;
if (!sound || numSoundsPlaying >= soundsLimit) return;
numSoundsPlaying++;
sound.play();
numSoundsPlaying--;
}
/**

View File

@ -1,16 +1,6 @@
{
"name": "tests",
"description": "A unit test library",
"files": [
"README.md",
"tests.ts",
"platformoverrides.ts"
],
"testFiles": [
],
"public": true,
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/tests",
"dependencies": {
"core": "file:../core"
"core": "file:../ev3"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.51",
"version": "0.0.53",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true,
"keywords": [
@ -44,7 +44,7 @@
"webfonts-generator": "^0.4.0"
},
"dependencies": {
"pxt-common-packages": "0.15.1",
"pxt-common-packages": "0.15.3",
"pxt-core": "3.0.5"
},
"scripts": {

View File

@ -16,7 +16,8 @@
"libs/infrared-sensor",
"libs/gyro-sensor",
"libs/ev3",
"libs/tests"
"libs/tests",
"libs/behaviors"
],
"simulator": {
"autoRun": true,

View File

@ -3,24 +3,6 @@
/// <reference path="../built/common-sim.d.ts"/>
namespace pxsim {
export enum CPlayPinName {
A0,
A1,
A2,
A3,
A4,
A5,
A6,
A7,
A8,
A9,
D4,
D5,
D6,
D7,
D8,
D13
}
export class EV3Board extends CoreBoard {
view: SVGSVGElement;
@ -36,7 +18,7 @@ namespace pxsim {
brickNode: BrickNode;
outputNodes: MotorNode[] = [];
private motorMap: pxt.Map<number> = {
public motorMap: pxt.Map<number> = {
0x01: 0,
0x02: 1,
0x04: 2,
@ -115,27 +97,40 @@ namespace pxsim {
return this.brickNode;
}
motorUsed(port:number, large: boolean) {
for(let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
motorUsed(port: number, large: boolean) {
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
const p = 1 << i;
if (port & p) {
const motorPort = this.motorMap[p];
if (!this.outputNodes[motorPort])
this.outputNodes[motorPort] = new MotorNode(motorPort, large);
}
}
}
}
}
hasMotor(port: number) {
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
const p = 1 << i;
if (port & p) {
const motorPort = this.motorMap[p];
const outputNode = this.outputNodes[motorPort];
if (outputNode)
return true;
}
}
return false;
}
getMotor(port: number, large?: boolean): MotorNode[] {
const r = [];
for(let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
const p = 1 << i;
if (port & p) {
const motorPort = this.motorMap[p];
const outputNode = this.outputNodes[motorPort];
if (outputNode)
r.push(outputNode);
}
}
}
return r;
}
@ -144,6 +139,10 @@ namespace pxsim {
return this.outputNodes;
}
hasSensor(port: number) {
return !!this.inputNodes[port];
}
getSensor(port: number, type: number): SensorNode {
if (!this.inputNodes[port]) {
switch (type) {
@ -168,6 +167,7 @@ namespace pxsim {
runtime.postError = (e) => {
// TODO
runtime.updateDisplay();
console.log('runtime error: ' + e);
}
}

View File

@ -24,13 +24,14 @@ namespace pxsim {
}
export class EV3AnalogState {
constructor() {
let data = new Uint8Array(5172)
MMapMethods.register("/dev/lms_analog", {
data,
beforeMemRead: () => {
//console.log("analog before read");
data[AnalogOff.BatteryTemp] = 21; // TODO simulate this
data[AnalogOff.BatteryCurrent] = 100; // TODO simulate this
const inputNodes = ev3board().getInputNodes();
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
const node = inputNodes[port];

View File

@ -1,10 +1,17 @@
import lf = pxsim.localization.lf;
namespace pxsim.motors {
export function __motorUsed(port: number, large: boolean) {
//console.log("MOTOR INIT " + port);
ev3board().motorUsed(port, large);
runtime.queueDisplayUpdate();
if (!ev3board().hasMotor(port)) {
ev3board().motorUsed(port, large);
runtime.queueDisplayUpdate();
} else {
U.userError(`${lf("Multiple motors are connected to Port")} ${String.fromCharCode('A'.charCodeAt(0) + ev3board().motorMap[port])}`);
}
}
}
@ -12,7 +19,11 @@ namespace pxsim.sensors {
export function __sensorUsed(port: number, type: number) {
//console.log("SENSOR INIT " + port + ", type: " + type);
const sensor = ev3board().getSensor(port, type);
runtime.queueDisplayUpdate();
if (!ev3board().hasSensor(port)) {
const sensor = ev3board().getSensor(port, type);
runtime.queueDisplayUpdate();
} else {
U.userError(`${lf("Multiple sensors are connected to Port")} ${port + 1}`);
}
}
}

View File

@ -88,7 +88,7 @@ namespace pxsim {
}
updateState(elapsed: number) {
console.log(`motor: ${elapsed}ms - ${this.speed}% - ${this.angle}> - ${this.tacho}|`)
//console.log(`motor: ${elapsed}ms - ${this.speed}% - ${this.angle}> - ${this.tacho}|`)
const interval = Math.min(20, elapsed);
let t = 0;
while (t < elapsed) {

View File

@ -10,8 +10,6 @@ namespace pxsim.music {
}
namespace pxsim.SoundMethods {
let numSoundsPlaying = 0;
const soundsLimit = 1;
let audio: HTMLAudioElement;
export function buffer(buf: RefBuffer) {
@ -27,18 +25,15 @@ namespace pxsim.SoundMethods {
}
export function play(buf: RefBuffer, volume: number) {
if (!buf || numSoundsPlaying >= soundsLimit) {
if (!buf) {
return Promise.resolve();
}
return new Promise<void>(resolve => {
let url = "data:audio/wav;base64," + btoa(uint8ArrayToString(buf.data))
audio = new Audio(url)
audio.onended = () => {
resolve();
numSoundsPlaying--;
}
numSoundsPlaying++;
audio.play()
audio.onended = () => resolve();
audio.onpause = () => resolve();
audio.play();
})
}
@ -46,8 +41,8 @@ namespace pxsim.SoundMethods {
return new Promise<void>(resolve => {
if (audio) {
audio.pause();
numSoundsPlaying--;
}
resolve();
})
}

View File

@ -1,26 +1,47 @@
/// <reference path="./moduleView.ts" />
/// <reference path="./motorView.ts" />
namespace pxsim.visuals {
export class LargeMotorView extends ModuleView implements LayoutElement {
private static ROTATING_ECLIPSE_ID = "hole";
export class LargeMotorView extends MotorView implements LayoutElement {
constructor(port: number) {
super(LARGE_MOTOR_SVG, "large-motor", NodeType.LargeMotor, port);
super(LARGE_MOTOR_SVG, "large-motor", NodeType.LargeMotor, port, "hole");
}
private syncedMotor: MotorNode;
private syncedLabelG: SVGGElement;
updateState() {
super.updateState();
const motorState = ev3board().getMotors()[this.port];
if (!motorState) return;
const speed = motorState.getSpeed();
if (!speed) return;
this.setMotorAngle(motorState.getAngle());
const syncedMotor = motorState.getSynchedMotor();
if ((syncedMotor || this.syncedMotor) && syncedMotor != this.syncedMotor) {
this.syncedMotor = syncedMotor;
this.showSyncedLabel(motorState, syncedMotor);
}
}
private setMotorAngle(angle: number) {
const holeEl = this.content.getElementById(this.normalizeId(LargeMotorView.ROTATING_ECLIPSE_ID))
private showSyncedLabel(motorNode: MotorNode, syncedMotor: MotorNode) {
const a = String.fromCharCode('A'.charCodeAt(0) + motorNode.port);
const b = String.fromCharCode('A'.charCodeAt(0) + syncedMotor.port);
this.syncedLabelG = pxsim.svg.child(this.element, 'g', {'transform': 'scale(0.5)'}) as SVGGElement;
pxsim.svg.child(this.syncedLabelG, 'rect', {'rx': 15, 'ry': 15, 'x': 0, 'y': 0, 'width': 84, 'height': 34, 'fill': '#A8A9A8'});
pxsim.svg.child(this.syncedLabelG, 'circle', {'cx': 17, 'cy': 17, 'r': 15, 'fill': 'white'});
const leftLabel = pxsim.svg.child(this.syncedLabelG, 'text', {'transform': 'translate(11, 22)', 'style': 'isolation: isolate;font-size: 16px;fill: #A8A9A8;font-family: ArialMT, Arial'});
leftLabel.textContent = a;
pxsim.svg.child(this.syncedLabelG, 'rect', {'rx': 0, 'ry': 0, 'x': 37, 'y': 12, 'width': 10, 'height': 3, 'fill': '#ffffff'});
pxsim.svg.child(this.syncedLabelG, 'rect', {'rx': 0, 'ry': 0, 'x': 37, 'y': 18, 'width': 10, 'height': 3, 'fill': '#ffffff'});
pxsim.svg.child(this.syncedLabelG, 'circle', {'cx': 67, 'cy': 17, 'r': 15, 'fill': 'white'});
const rightLabel = pxsim.svg.child(this.syncedLabelG, 'text', {'transform': 'translate(61, 22)', 'style': 'isolation: isolate;font-size: 16px;fill: #A8A9A8;font-family: ArialMT, Arial'});
rightLabel.textContent = b;
}
protected renderMotorAngle(holeEl: Element, angle: number) {
const width = 125.92;
const height = 37.9;
const transform = `rotate(${angle} ${width / 2} ${height / 2})`;

View File

@ -2,35 +2,17 @@
namespace pxsim.visuals {
export const MOTOR_ROTATION_FPS = 32;
export class MediumMotorView extends ModuleView implements LayoutElement {
private static ROTATING_ECLIPSE_ID = "medmotor_Hole";
private hasPreviousAngle: boolean;
private previousAngle: number;
export class MediumMotorView extends MotorView implements LayoutElement {
constructor(port: number) {
super(MEDIUM_MOTOR_SVG, "medium-motor", NodeType.MediumMotor, port);
super(MEDIUM_MOTOR_SVG, "medium-motor", NodeType.MediumMotor, port, "medmotor_Hole");
}
public getPaddingRatio() {
return 1 / 5;
}
updateState() {
super.updateState();
const motorState = ev3board().getMotors()[this.port];
if (!motorState) return;
const speed = motorState.getSpeed();
if (!speed) return;
this.setMotorAngle(motorState.getAngle());
}
private setMotorAngle(angle: number) {
const holeEl = this.content.getElementById(this.normalizeId(MediumMotorView.ROTATING_ECLIPSE_ID))
protected renderMotorAngle(holeEl: Element, angle: number) {
const width = 44.45;
const height = 44.45;
const transform = `translate(2 1.84) rotate(${angle} ${width / 2} ${height / 2})`;

View File

@ -0,0 +1,33 @@
/// <reference path="./moduleView.ts" />
namespace pxsim.visuals {
export abstract class MotorView extends ModuleView implements LayoutElement {
constructor(xml: string, prefix: string, id: NodeType, port: NodeType,
protected rotating_hole_id: string) {
super(xml, prefix, id, port);
}
updateState() {
super.updateState();
const motorState = ev3board().getMotors()[this.port];
if (!motorState) return;
const speed = motorState.getSpeed();
if (!speed) return;
this.setMotorAngle(motorState.getAngle());
}
private setMotorAngle(angle: number) {
const holeEl = this.content.getElementById(this.normalizeId(this.rotating_hole_id))
this.renderMotorAngle(holeEl, angle);
}
protected abstract renderMotorAngle(holeEl: Element, angle: number): void;
getWiringRatio() {
return 0.37;
}
}
}

View File

@ -1,3 +1,79 @@
/*******************************
Site Overrides
*******************************/
.ui.loader:before {
border: none;
border-radius: 0px;
box-shadow: none;
background: transparent @loaderBackImage no-repeat center center;
background-size: 100%;
}
.ui.loader:after {
border: none;
box-shadow: none;
border-radius: 0px;
margin: 40px auto;
background: transparent @loaderImage no-repeat center center;
background-size: 100%;
-webkit-animation: @loaderAnimation @loaderSpeed infinite linear;
animation: @loaderAnimation @loaderSpeed infinite linear;
}
.ui.loader.avatar:after {
border: none;
box-shadow: none;
border-radius: 0px;
margin: 40px auto;
background: transparent @avatarImage no-repeat center center;
background-size: 100%;
-webkit-animation: @loaderAnimation @loaderSpeed infinite linear;
animation: @loaderAnimation @loaderSpeed infinite linear;
}
@-webkit-keyframes loader-pxt-ev3 {
0% {
-webkit-transform: translateX(@loaderStartPoint);
transform: translateX(@loaderStartPoint);
}
20% {
-webkit-transform: translateX(-@loaderMiddlePoint);
transform: translateX(-@loaderMiddlePoint);
}
50% {
-webkit-transform: translateX(-@loaderEndPoint);
transform: translateX(-@loaderEndPoint);
}
75% {
-webkit-transform: translateX(-@loaderMiddlePoint);
transform: translateX(-@loaderMiddlePoint);
}
100% {
-webkit-transform: translateX(@loaderStartPoint);
transform: translateX(@loaderStartPoint);
}
}
@keyframes loader-pxt-ev3 {
0% {
-webkit-transform: translateX(@loaderStartPoint);
transform: translateX(@loaderStartPoint);
}
20% {
-webkit-transform: translateX(-@loaderMiddlePoint);
transform: translateX(-@loaderMiddlePoint);
}
50% {
-webkit-transform: translateX(-@loaderEndPoint);
transform: translateX(-@loaderEndPoint);
}
75% {
-webkit-transform: translateX(-@loaderMiddlePoint);
transform: translateX(-@loaderMiddlePoint);
}
100% {
-webkit-transform: translateX(@loaderStartPoint);
transform: translateX(@loaderStartPoint);
}
}

View File

@ -1,3 +1,19 @@
/*******************************
User Variable Overrides
*******************************/
@loaderBackImageUrl: "../docs/static/loader_back.svg";
@loaderBackImage: data-uri(@loaderBackImageUrl);
@loaderImageUrl: "../docs/static/loader_front.svg";
@loaderImage: data-uri(@loaderImageUrl);
@loaderSpeed: 2s;
@loaderStartPoint: 0px;
@loaderMiddlePoint: 14px;
@loaderEndPoint: 28px;
@loaderAnimation: loader-pxt-ev3;
@large : 200px;