diff --git a/src/adafruit_circuitplayground/constants.py b/src/adafruit_circuitplayground/constants.py index 5cb698560..4e6100d97 100644 --- a/src/adafruit_circuitplayground/constants.py +++ b/src/adafruit_circuitplayground/constants.py @@ -47,3 +47,16 @@ EVENTS_BUTTON_PRESS = ["button_a", "button_b", "switch"] EVENTS_SENSOR_CHANGED = ["temperature", "light", "motion_x", "motion_y", "motion_z"] + +ALL_EXPECTED_INPUT_EVENTS = [ + "button_a", + "button_b", + "switch", + "temperature", + "light", + "shake", + "motion_x", + "motion_y", + "motion_z", + "touch", +] diff --git a/src/adafruit_circuitplayground/express.py b/src/adafruit_circuitplayground/express.py index 16b6807a6..9e20af4b2 100644 --- a/src/adafruit_circuitplayground/express.py +++ b/src/adafruit_circuitplayground/express.py @@ -194,5 +194,11 @@ def stop_tone(self): telemetry_py.send_telemetry("STOP_TONE") raise NotImplementedError(CONSTANTS.NOT_IMPLEMENTED_ERROR) + def update_state(self, new_state): + for event in CONSTANTS.ALL_EXPECTED_INPUT_EVENTS: + self._Express__state[event] = new_state.get( + event, self._Express__state[event] + ) + cpx = Express() diff --git a/src/extension.ts b/src/extension.ts index 60144e10a..b65a648e1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -154,7 +154,10 @@ export async function activate(context: vscode.ExtensionContext) { // Handle messages from webview messageListener = currentPanel.webview.onDidReceiveMessage( message => { - const messageJson = JSON.stringify(message.text); + const messageJson = JSON.stringify({ + active_device: currentActiveDevice, + state: message.text, + }); switch (message.command) { case WEBVIEW_MESSAGES.BUTTON_PRESS: // Send input to the Python process diff --git a/src/microbit/__model/constants.py b/src/microbit/__model/constants.py index 7f566ea90..a93250140 100644 --- a/src/microbit/__model/constants.py +++ b/src/microbit/__model/constants.py @@ -122,3 +122,8 @@ SAME_SIZE_ERR = "images must be the same size" TIME_DELAY = 0.03 + +EXPECTED_INPUT_BUTTONS = [ + "button_a", + "button_b", +] diff --git a/src/microbit/__model/microbit_model.py b/src/microbit/__model/microbit_model.py index a1de5045d..a92763e34 100644 --- a/src/microbit/__model/microbit_model.py +++ b/src/microbit/__model/microbit_model.py @@ -2,6 +2,7 @@ from .button import Button from .display import Display +from . import constants as CONSTANTS class MicrobitModel: @@ -12,6 +13,11 @@ def __init__(self): self.__start_time = time.time() self.display = Display() + self.microbit_button_dict = { + "button_a": self.button_a, + "button_b": self.button_b, + } + def sleep(self, n): time.sleep(n / 1000) @@ -19,5 +25,18 @@ def running_time(self): print(f"time. time: {time.time()}") return time.time() - self.__start_time + def update_state(self, new_state): + for button_name in CONSTANTS.EXPECTED_INPUT_BUTTONS: + button = self.microbit_button_dict[button_name] + + previous_pressed = button.is_pressed() + button_pressed = new_state.get(button_name, previous_pressed) + + if button_pressed != previous_pressed: + if button_pressed: + button._Button__press_down() + else: + button._Button__release() + __mb = MicrobitModel() diff --git a/src/process_user_code.py b/src/process_user_code.py index fbcf08ff8..486551e2b 100644 --- a/src/process_user_code.py +++ b/src/process_user_code.py @@ -30,7 +30,10 @@ # This import must happen after the sys.path is modified from adafruit_circuitplayground.express import cpx from adafruit_circuitplayground.telemetry import telemetry_py +from adafruit_circuitplayground.constants import CPX + from microbit.__model.microbit_model import __mb as mb +from microbit.__model.constants import MICROBIT # Handle User Inputs Thread @@ -39,15 +42,19 @@ def __init__(self): threading.Thread.__init__(self) def run(self): + device_dict = {CPX: cpx, MICROBIT: mb} while True: read_val = sys.stdin.readline() sys.stdin.flush() try: - new_state = json.loads(read_val) - for event in CONSTANTS.EXPECTED_INPUT_EVENTS_CPX: - cpx._Express__state[event] = new_state.get( - event, cpx._Express__state[event] - ) + new_state_message = json.loads(read_val) + device = new_state_message.get(CONSTANTS.ACTIVE_DEVICE_FIELD) + new_state = new_state_message.get(CONSTANTS.STATE_FIELD, {}) + + if device in device_dict: + device_dict[device].update_state(new_state) + else: + raise Exception(CONSTANTS.DEVICE_NOT_IMPLEMENTED_ERROR) except Exception as e: print(CONSTANTS.ERROR_SENDING_EVENT, e, file=sys.stderr, flush=True) diff --git a/src/python_constants.py b/src/python_constants.py index 4b9d0338f..03b5b6b7a 100644 --- a/src/python_constants.py +++ b/src/python_constants.py @@ -1,21 +1,13 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. +ACTIVE_DEVICE_FIELD = "active_device" + CPX_DRIVE_NAME = "CIRCUITPY" +DEVICE_NOT_IMPLEMENTED_ERROR = "Device not implemented." + ENABLE_TELEMETRY = "enable_telemetry" -EXPECTED_INPUT_EVENTS_CPX = [ - "button_a", - "button_b", - "switch", - "temperature", - "light", - "shake", - "motion_x", - "motion_y", - "motion_z", - "touch", -] EXEC_COMMAND = "exec" ERROR_SENDING_EVENT = "Error trying to send event to the process : " @@ -37,6 +29,8 @@ PYTHON_LIBS_DIR = "python_libs" +STATE_FIELD = "state" + UTF_FORMAT = "utf-8" WINDOWS_OS = "win32" diff --git a/src/view/components/cpx/CpxSimulator.tsx b/src/view/components/cpx/CpxSimulator.tsx index fb3b23e09..d69c50abf 100644 --- a/src/view/components/cpx/CpxSimulator.tsx +++ b/src/view/components/cpx/CpxSimulator.tsx @@ -172,7 +172,6 @@ class Simulator extends React.Component<{}, IState> { protected togglePlayClick() { sendMessage(WEBVIEW_MESSAGES.TOGGLE_PLAY_STOP, { - active_device: CONSTANTS.DEVICE_NAME.CPX, selected_file: this.state.selected_file, state: !this.state.play_button, }); diff --git a/src/view/components/microbit/MicrobitImage.tsx b/src/view/components/microbit/MicrobitImage.tsx index 98c554607..08d1d83b3 100644 --- a/src/view/components/microbit/MicrobitImage.tsx +++ b/src/view/components/microbit/MicrobitImage.tsx @@ -6,13 +6,9 @@ import "../../styles/Microbit.css"; import { MicrobitSvg } from "./Microbit_svg"; interface EventTriggers { - onMouseUp: (button: HTMLElement, event: Event, buttonKey: string) => void; - onMouseDown: (button: HTMLElement, event: Event, buttonKey: string) => void; - onMouseLeave: ( - button: HTMLElement, - event: Event, - buttonKey: string - ) => void; + onMouseUp: (event: Event, buttonKey: string) => void; + onMouseDown: (event: Event, buttonKey: string) => void; + onMouseLeave: (event: Event, buttonKey: string) => void; } interface IProps { eventTriggers: EventTriggers; @@ -47,13 +43,13 @@ const setupButton = ( key: string ) => { buttonElement.onmousedown = e => { - eventTriggers.onMouseDown(buttonElement, e, key); + eventTriggers.onMouseDown(e, key); }; buttonElement.onmouseup = e => { - eventTriggers.onMouseUp(buttonElement, e, key); + eventTriggers.onMouseUp(e, key); }; buttonElement.onmouseleave = e => { - eventTriggers.onMouseLeave(buttonElement, e, key); + eventTriggers.onMouseLeave(e, key); }; }; const setupAllButtons = (eventTriggers: EventTriggers, buttonRefs: Object) => { diff --git a/src/view/components/microbit/MicrobitSimulator.tsx b/src/view/components/microbit/MicrobitSimulator.tsx index 5899334d7..a31d0e469 100644 --- a/src/view/components/microbit/MicrobitSimulator.tsx +++ b/src/view/components/microbit/MicrobitSimulator.tsx @@ -1,5 +1,9 @@ import * as React from "react"; -import CONSTANTS, { WEBVIEW_MESSAGES, DEVICE_LIST_KEY } from "../../constants"; +import { + WEBVIEW_MESSAGES, + MICROBIT_BUTTONS_KEYS, + DEVICE_LIST_KEY, +} from "../../constants"; import PlayLogo from "../../svgs/play_svg"; import StopLogo from "../../svgs/stop_svg"; import { sendMessage } from "../../utils/MessageUtils"; @@ -7,26 +11,34 @@ import Dropdown from "../Dropdown"; import ActionBar from "../simulator/ActionBar"; import { MicrobitImage } from "./MicrobitImage"; -const initialLedState = [ - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], -]; +const DEFAULT_MICROBIT_STATE: IMicrobitState = { + leds: [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ], + buttons: { button_a: false, button_b: false }, +}; interface IState { active_editors: string[]; running_file: string; - leds: number[][]; play_button: boolean; selected_file: string; + microbit: IMicrobitState; +} + +interface IMicrobitState { + leds: number[][]; + buttons: { button_a: boolean; button_b: boolean }; } export class MicrobitSimulator extends React.Component { constructor() { super({}); this.state = { - leds: initialLedState, + microbit: DEFAULT_MICROBIT_STATE, play_button: false, selected_file: "", active_editors: [], @@ -43,13 +55,16 @@ export class MicrobitSimulator extends React.Component { switch (message.command) { case "reset-state": this.setState({ - leds: initialLedState, + microbit: DEFAULT_MICROBIT_STATE, play_button: false, }); break; case "set-state": this.setState({ - leds: message.state.leds, + microbit: { + ...this.state.microbit, + leds: message.state.leds, + }, }); break; case "activate-play": @@ -101,7 +116,7 @@ export class MicrobitSimulator extends React.Component { onMouseUp: this.onMouseUp, onMouseLeave: this.onMouseLeave, }} - leds={this.state.leds} + leds={this.state.microbit.leds} /> { } protected togglePlayClick = () => { sendMessage(WEBVIEW_MESSAGES.TOGGLE_PLAY_STOP, { - active_device: CONSTANTS.DEVICE_NAME.MICROBIT, selected_file: this.state.selected_file, state: !this.state.play_button, }); @@ -127,16 +141,41 @@ export class MicrobitSimulator extends React.Component { protected refreshSimulatorClick = () => { sendMessage(WEBVIEW_MESSAGES.REFRESH_SIMULATOR, true); }; - protected onMouseUp(button: HTMLElement, event: Event, key: string) { + protected handleButtonClick = (key: string, isActive: boolean) => { + let newButtonState = this.state.microbit.buttons; + switch (key) { + case MICROBIT_BUTTONS_KEYS.BTN_A: + newButtonState.button_a = isActive; + break; + case MICROBIT_BUTTONS_KEYS.BTN_B: + newButtonState.button_b = isActive; + break; + case MICROBIT_BUTTONS_KEYS.BTN_AB: + newButtonState = { + button_a: isActive, + button_b: isActive, + }; + break; + } + sendMessage(WEBVIEW_MESSAGES.BUTTON_PRESS, newButtonState); + this.setState({ + microbit: { + ...this.state.microbit, + buttons: newButtonState, + }, + }); + }; + protected onMouseUp = (event: Event, key: string) => { event.preventDefault(); - console.log(`To implement onMouseUp on ${key}`); - } - protected onMouseDown(button: HTMLElement, event: Event, key: string) { + this.handleButtonClick(key, false); + }; + protected onMouseDown = (event: Event, key: string) => { event.preventDefault(); - console.log(`To implement onMouseDown ${key}`); - } - protected onMouseLeave(button: HTMLElement, event: Event, key: string) { + this.handleButtonClick(key, true); + }; + + protected onMouseLeave = (event: Event, key: string) => { event.preventDefault(); console.log(`To implement onMouseLeave ${key}`); - } + }; } diff --git a/src/view/constants.ts b/src/view/constants.ts index fc1f53631..2fde0d112 100644 --- a/src/view/constants.ts +++ b/src/view/constants.ts @@ -45,6 +45,11 @@ export const CONSTANTS = { SIMULATOR_BUTTON_WIDTH: 60, TOOLBAR_INFO: `Explore what's on the board:`, }; +export const MICROBIT_BUTTONS_KEYS = { + BTN_A: "BTN_A", + BTN_B: "BTN_B", + BTN_AB: "BTN_AB", +}; export enum DEVICE_LIST_KEY { CPX = "CPX", MICROBIT = "micro:bit",