diff --git a/external_samples/docs/BlockWithComponentDropdown.png b/external_samples/docs/BlockWithComponentDropdown.png
new file mode 100644
index 00000000..097d6d76
Binary files /dev/null and b/external_samples/docs/BlockWithComponentDropdown.png differ
diff --git a/external_samples/docs/BlockWithTooltip.png b/external_samples/docs/BlockWithTooltip.png
new file mode 100644
index 00000000..9bbb6cf7
Binary files /dev/null and b/external_samples/docs/BlockWithTooltip.png differ
diff --git a/external_samples/docs/REV_ColorRangeSensor.png b/external_samples/docs/REV_ColorRangeSensor.png
new file mode 100644
index 00000000..facf0da8
Binary files /dev/null and b/external_samples/docs/REV_ColorRangeSensor.png differ
diff --git a/external_samples/docs/REV_Servo.png b/external_samples/docs/REV_Servo.png
new file mode 100644
index 00000000..11f80372
Binary files /dev/null and b/external_samples/docs/REV_Servo.png differ
diff --git a/external_samples/docs/REV_SmartMotor.png b/external_samples/docs/REV_SmartMotor.png
new file mode 100644
index 00000000..50450e45
Binary files /dev/null and b/external_samples/docs/REV_SmartMotor.png differ
diff --git a/external_samples/docs/REV_TouchSensor.png b/external_samples/docs/REV_TouchSensor.png
new file mode 100644
index 00000000..f27afdc3
Binary files /dev/null and b/external_samples/docs/REV_TouchSensor.png differ
diff --git a/external_samples/docs/SparkFun_LEDStick.png b/external_samples/docs/SparkFun_LEDStick.png
new file mode 100644
index 00000000..eacffffd
Binary files /dev/null and b/external_samples/docs/SparkFun_LEDStick.png differ
diff --git a/external_samples/docs/index.md b/external_samples/docs/index.md
new file mode 100644
index 00000000..79c41bf9
--- /dev/null
+++ b/external_samples/docs/index.md
@@ -0,0 +1,33 @@
+# Component Blocks
+
+## Block Details
+
+### Dropdown shows components of the specific type.
+
+
+
+### Tooltip shows that the block calls a method on a component, followed by details about what that method does.
+
+
+
+## Component Toolbox Categories
+
+### REV ColorRangeSensor
+
+
+
+### REV Servo
+
+
+
+### REV SmartMotor
+
+
+
+### REV TouchSensor
+
+
+
+### SparkFun LEDStick
+
+
diff --git a/external_samples/sparkfun_led_stick.py b/external_samples/sparkfun_led_stick.py
new file mode 100644
index 00000000..3b00ec1e
--- /dev/null
+++ b/external_samples/sparkfun_led_stick.py
@@ -0,0 +1,65 @@
+from component import Component, PortType, InvalidPortException
+from enum import Enum
+import wpilib
+
+class SparkFunLEDStick(Component):
+ def __init__(self, ports : list[tuple[PortType, int]]):
+ portType, port = ports[0]
+ if portType != PortType.I2C_PORT:
+ raise InvalidPortException
+ self.port = port
+
+ # Component methods
+
+ def get_manufacturer(self) -> str:
+ return "SparkFun"
+
+ def get_name(self) -> str:
+ return "SparkFun Qwiic LED Strip"
+
+ def get_part_number(self) -> str:
+ return "COM-18354"
+
+ def get_url(self) -> str:
+ return "https://www.sparkfun.com/sparkfun-qwiic-led-stick-apa102c.html"
+
+ def get_version(self) -> tuple[int, int, str]:
+ return (1, 0, "")
+
+ def stop(self) -> None:
+ self.turn_all_off()
+
+ def reset(self) -> None:
+ pass
+
+ def get_connection_port_type(self) -> list[PortType]:
+ return [PortType.I2C_PORT]
+
+ def periodic(self) -> None:
+ pass
+
+ # SparkFunLEDStick methods
+
+ def set_color(self, position: int, color: wpilib.Color) -> None:
+ '''Change the color of an individual LED.'''
+ pass # TODO: implement
+
+ def set_color(self, color: wpilib.Color) -> None:
+ '''Change the color of all LEDs to a single color.'''
+ pass # TODO: implement
+
+ def set_colors(self, colors: list[int]) -> None:
+ '''Change the color of all LEDs using a list.'''
+ pass # TODO: implement
+
+ def set_brightness(self, position: int, brightness: int) -> None:
+ '''Set the brightness of an individual LED.'''
+ pass # TODO: implement
+
+ def set_brightness(self, brightness: int) -> None:
+ '''Set the brightness of all LEDs.'''
+ pass # TODO: implement
+
+ def turn_all_off(self) -> None:
+ '''Turn all LEDs off.'''
+ pass # TODO: implement
diff --git a/src/blocks/mrc_call_python_function.ts b/src/blocks/mrc_call_python_function.ts
index 3d7b3fba..227204d8 100644
--- a/src/blocks/mrc_call_python_function.ts
+++ b/src/blocks/mrc_call_python_function.ts
@@ -24,11 +24,13 @@ import * as Blockly from 'blockly';
import { Order } from 'blockly/python';
import * as pythonUtils from './utils/generated/python';
+import { createFieldDropdown } from '../fields/FieldDropdown';
import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
import { getAllowedTypesForSetCheck, getOutputCheck } from './utils/python';
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
import { MRC_STYLE_FUNCTIONS } from '../themes/styles'
import { ClassMethodDefExtraState } from './mrc_class_method_def'
+import { Editor } from '../editor/editor';
// A block to call a python function.
@@ -40,10 +42,13 @@ enum FunctionKind {
CONSTRUCTOR = 'constructor',
INSTANCE = 'instance',
INSTANCE_WITHIN = 'instance_within',
+ INSTANCE_COMPONENT = 'instance_component'
}
const RETURN_TYPE_NONE = 'None';
+const FIELD_COMPONENT_NAME = 'COMPONENT_NAME';
+
export type FunctionArg = {
name: string,
type: string,
@@ -58,6 +63,8 @@ interface CallPythonFunctionMixin extends CallPythonFunctionMixinType {
mrcImportModule: string,
mrcActualFunctionName: string,
mrcExportedFunction: boolean,
+ mrcComponentClassName: string,
+ mrcComponentName: string, // Do not access directly. Call getComponentName.
renameMethod(this: CallPythonFunctionBlock, newName: string): void;
mutateMethod(this: CallPythonFunctionBlock, defBlockExtraState: ClassMethodDefExtraState): void;
}
@@ -99,6 +106,14 @@ type CallPythonFunctionExtraState = {
* user's Project).
*/
exportedFunction: boolean,
+ /**
+ * The component name. Specified only if the function kind is INSTANCE_COMPONENT.
+ */
+ componentName?: string,
+ /**
+ * The component class name. Specified only if the function kind is INSTANCE_COMPONENT.
+ */
+ componentClassName?: string,
};
const CALL_PYTHON_FUNCTION = {
@@ -138,6 +153,13 @@ const CALL_PYTHON_FUNCTION = {
tooltip = 'Calls the method ' + functionName + '.';
break;
}
+ case FunctionKind.INSTANCE_COMPONENT: {
+ const className = this.mrcComponentClassName;
+ const functionName = this.getFieldValue(pythonUtils.FIELD_FUNCTION_NAME);
+ tooltip = 'Calls the method ' + className + '.' + functionName +
+ ' on the component named ' + this.getComponentName() + '.';
+ break;
+ }
default:
throw new Error('mrcFunctionKind has unexpected value: ' + this.mrcFunctionKind)
}
@@ -174,6 +196,12 @@ const CALL_PYTHON_FUNCTION = {
if (this.mrcActualFunctionName) {
extraState.actualFunctionName = this.mrcActualFunctionName;
}
+ if (this.mrcComponentClassName) {
+ extraState.componentClassName = this.mrcComponentClassName;
+ }
+ if (this.getField(FIELD_COMPONENT_NAME)) {
+ extraState.componentName = this.getComponentName();
+ }
return extraState;
},
/**
@@ -199,6 +227,10 @@ const CALL_PYTHON_FUNCTION = {
? extraState.actualFunctionName : '';
this.mrcExportedFunction = extraState.exportedFunction
? extraState.exportedFunction : false;
+ this.mrcComponentClassName = extraState.componentClassName
+ ? extraState.componentClassName : '';
+ this.mrcComponentName = extraState.componentName
+ ? extraState.componentName : '';
this.updateBlock_();
},
/**
@@ -260,6 +292,19 @@ const CALL_PYTHON_FUNCTION = {
}
break;
}
+ case FunctionKind.INSTANCE_COMPONENT: {
+ const componentNames = Editor.getComponentNames(this.workspace, this.mrcComponentClassName);
+ const componentName = this.getComponentName();
+ if (!componentNames.includes(componentName)) {
+ componentNames.push(componentName);
+ }
+ this.appendDummyInput('TITLE')
+ .appendField('call')
+ .appendField(createFieldDropdown(componentNames), FIELD_COMPONENT_NAME)
+ .appendField('.')
+ .appendField(createFieldNonEditableText(''), pythonUtils.FIELD_FUNCTION_NAME);
+ break;
+ }
default:
throw new Error('mrcFunctionKind has unexpected value: ' + this.mrcFunctionKind)
}
@@ -293,6 +338,15 @@ const CALL_PYTHON_FUNCTION = {
this.removeInput('ARG' + i);
}
},
+ getComponentName(this: CallPythonFunctionBlock): string {
+ // If the COMPONENT_NAME field has been created, get the field value, which the user may have changed.
+ // If the COMPONENT_NAME field has not been created, get the component name from this.mrcComponentName.
+ return (this.getField(FIELD_COMPONENT_NAME))
+ ? this.getFieldValue(FIELD_COMPONENT_NAME) : this.mrcComponentName;
+ },
+ getComponentClassName(this: CallPythonFunctionBlock): string {
+ return this.mrcComponentClassName;
+ },
renameMethod: function(this: CallPythonFunctionBlock, newName: string): void {
this.setFieldValue(newName, pythonUtils.FIELD_FUNCTION_NAME);
},
@@ -365,6 +419,14 @@ export const pythonFromBlock = function(
code = 'self.' + functionName;
break;
}
+ case FunctionKind.INSTANCE_COMPONENT: {
+ const componentName = callPythonFunctionBlock.getComponentName();
+ const functionName = callPythonFunctionBlock.mrcActualFunctionName
+ ? callPythonFunctionBlock.mrcActualFunctionName
+ : block.getFieldValue(pythonUtils.FIELD_FUNCTION_NAME);
+ code = 'self.robot.' + componentName + '.' + functionName;
+ break;
+ }
default:
throw new Error('mrcFunctionKind has unexpected value: ' + callPythonFunctionBlock.mrcFunctionKind)
}
diff --git a/src/blocks/mrc_class_method_def.ts b/src/blocks/mrc_class_method_def.ts
index e1a43d08..9b081f96 100644
--- a/src/blocks/mrc_class_method_def.ts
+++ b/src/blocks/mrc_class_method_def.ts
@@ -458,7 +458,8 @@ export const pythonFromBlock = function (
xfix2 = xfix1;
}
if(block.mrcPythonMethodName == '__init__'){
- branch = generator.defineClassVariables() + branch;
+ branch = generator.INDENT + 'super().__init__(robot)\n' +
+ generator.defineClassVariables() + branch;
}
if (returnValue) {
returnValue = generator.INDENT + 'return ' + returnValue + '\n';
diff --git a/src/editor/editor.ts b/src/editor/editor.ts
index 6cc9994b..b9e9c728 100644
--- a/src/editor/editor.ts
+++ b/src/editor/editor.ts
@@ -34,6 +34,8 @@ const EMPTY_TOOLBOX: Blockly.utils.toolbox.ToolboxDefinition = {
};
export class Editor {
+ private static workspaceIdToEditor: {[key: string]: Editor} = {};
+
private blocklyWorkspace: Blockly.WorkspaceSvg;
private generatorContext: GeneratorContext;
private storage: commonStorage.Storage;
@@ -47,6 +49,7 @@ export class Editor {
private toolbox: Blockly.utils.toolbox.ToolboxDefinition = EMPTY_TOOLBOX;
constructor(blocklyWorkspace: Blockly.WorkspaceSvg, generatorContext: GeneratorContext, storage: commonStorage.Storage) {
+ Editor.workspaceIdToEditor[blocklyWorkspace.id] = this;
this.blocklyWorkspace = blocklyWorkspace;
this.generatorContext = generatorContext;
this.storage = storage;
@@ -219,8 +222,17 @@ export class Editor {
const exportedBlocks = JSON.stringify(this.generatorContext.getExportedBlocks());
const blocksContent = JSON.stringify(
Blockly.serialization.workspaces.save(this.blocklyWorkspace));
+ const componentsContent = JSON.stringify(this.getComponents());
return commonStorage.makeModuleContent(
- this.currentModule, pythonCode, exportedBlocks, blocksContent);
+ this.currentModule, pythonCode, blocksContent, exportedBlocks, componentsContent);
+ }
+
+ private getComponents(): commonStorage.Component[] {
+ const components: commonStorage.Component[] = [];
+ if (this.currentModule?.moduleType === commonStorage.MODULE_TYPE_PROJECT) {
+ // TODO(lizlooney): Fill the components array.
+ }
+ return components;
}
public async saveBlocks() {
@@ -233,4 +245,45 @@ export class Editor {
}
}
+ private getComponentNamesImpl(componentClassName: string): string[] {
+ if (!this.projectContent) {
+ throw new Error('getComponentNames: this.projectContent is null.');
+ }
+ const components = commonStorage.extractComponents(this.projectContent);
+
+ // TODO(lizlooney): Remove this fake code after getComponents (above) has been implemented.
+ components.push({name: 'frontTouch', className: 'rev.TouchSensor'});
+ components.push({name: 'backTouch', className: 'rev.TouchSensor'});
+ components.push({name: 'leftMotor', className: 'rev.SmartMotor'});
+ components.push({name: 'rightMotor', className: 'rev.SmartMotor'});
+ components.push({name: 'clawServo', className: 'rev.Servo'});
+ components.push({name: 'colorSensor', className: 'rev.ColorRangeSensor'});
+ components.push({name: 'ledStick', className: 'sparkfun.LEDStick'});
+ // End of fake code
+
+ const componentNames: string[] = [];
+ components.forEach((component) => {
+ if (component.className === componentClassName) {
+ componentNames.push(component.name);
+ }
+ });
+ return componentNames;
+ }
+
+ public static getComponentNames(
+ workspace: Blockly.Workspace, componentClassName: string): string[] {
+
+ let editor: Editor | null = null;
+ if (workspace.id in Editor.workspaceIdToEditor) {
+ editor = Editor.workspaceIdToEditor[workspace.id];
+ } else {
+ // If the workspace id was not found, it might be because the workspace is associated with the
+ // toolbox flyout, not a real workspace. In that case, use the first editor.
+ const allEditors = Object.values(Editor.workspaceIdToEditor);
+ if (allEditors.length) {
+ editor = allEditors[0];
+ }
+ }
+ return editor ? editor.getComponentNamesImpl(componentClassName) : [];
+ }
}
diff --git a/src/editor/extended_python_generator.ts b/src/editor/extended_python_generator.ts
index 3780a7bc..cba4c9d9 100644
--- a/src/editor/extended_python_generator.ts
+++ b/src/editor/extended_python_generator.ts
@@ -23,7 +23,7 @@ import * as Blockly from 'blockly/core';
import { PythonGenerator } from 'blockly/python';
import { GeneratorContext } from './generator_context';
import { Block } from '../toolbox/items';
-import { FunctionArg } from '../blocks/mrc_call_python_function';
+import { CallPythonFunctionBlock, FunctionArg } from '../blocks/mrc_call_python_function';
import * as commonStorage from '../storage/common_storage';
// Extends the python generator to collect some information about functions and
@@ -41,9 +41,10 @@ export class ExtendedPythonGenerator extends PythonGenerator {
init(workspace: Blockly.Workspace){
super.init(workspace);
- // This will have all variables in the definition 'variables' so we will need to make it contain only the developer variables
- delete this.definitions_['variables'];
+ // super.init will have put all variables in this.definitions_['variables'] but we need to make
+ // it contain only the developer variables.
+ delete this.definitions_['variables'];
const defvars = [];
// Add developer variables (not created or named by the user).
const devVarList = Blockly.Variables.allDeveloperVariables(workspace);
@@ -52,20 +53,21 @@ export class ExtendedPythonGenerator extends PythonGenerator {
this.nameDB_!.getName(devVarList[i], Blockly.Names.DEVELOPER_VARIABLE_TYPE) + ' = None',
);
}
- this.definitions_['variables'] = defvars.join('\n');
+ this.definitions_['variables'] = defvars.join('\n');
}
- /*
+ /*
* This is called from the python generator for the mrc_class_method_def for the
* init method
*/
defineClassVariables() : string {
- let variableDefinitions = '';
+ let variableDefinitions = '';
- if (this.context?.getHasMechanisms()) {
- variableDefinitions += this.INDENT + "self.mechanisms = []\n";
- }
- return variableDefinitions;
+ if (this.context?.getHasMechanisms()) {
+ variableDefinitions += this.INDENT + "self.mechanisms = []\n";
+ }
+
+ return variableDefinitions;
}
getVariableName(nameOrId: string): string {
const varName = super.getVariableName(nameOrId);
@@ -74,7 +76,7 @@ export class ExtendedPythonGenerator extends PythonGenerator {
setHasMechanism() : void{
this.context?.setHasMechanism();
}
-
+
mrcWorkspaceToCode(workspace: Blockly.Workspace, context: GeneratorContext): string {
this.workspace = workspace;
this.context = context;
@@ -108,7 +110,7 @@ export class ExtendedPythonGenerator extends PythonGenerator {
this.addImport(classParent);
const classDef = 'class ' + className + '(' + classParent + '):\n';
const classMethods = [];
- for (let name in this.classMethods) {
+ for (const name in this.classMethods) {
classMethods.push(this.classMethods[name])
}
this.classMethods = Object.create(null);
diff --git a/src/modules/opmode_start.json b/src/modules/opmode_start.json
index bd763f17..55b5f11b 100644
--- a/src/modules/opmode_start.json
+++ b/src/modules/opmode_start.json
@@ -13,7 +13,12 @@
"canBeCalledWithinClass": false,
"canBeCalledOutsideClass": false,
"returnType": "None",
- "params": [],
+ "params": [
+ {
+ "name": "robot",
+ "type": "Robot"
+ }
+ ],
"pythonMethodName": "__init__"
},
"fields": {
diff --git a/src/storage/client_side_storage.ts b/src/storage/client_side_storage.ts
index d24a189a..9540fb6f 100644
--- a/src/storage/client_side_storage.ts
+++ b/src/storage/client_side_storage.ts
@@ -574,7 +574,7 @@ class ClientSideStorage implements commonStorage.Storage {
};
const modulesObjectStore = transaction.objectStore('modules');
- for (let moduleName in moduleTypes) {
+ for (const moduleName in moduleTypes) {
const moduleType = moduleTypes[moduleName];
const moduleContent = moduleContents[moduleName];
const modulePath = commonStorage.makeModulePath(projectName, moduleName);
diff --git a/src/storage/common_storage.ts b/src/storage/common_storage.ts
index f3acdfda..c67f9f41 100644
--- a/src/storage/common_storage.ts
+++ b/src/storage/common_storage.ts
@@ -50,6 +50,11 @@ export type Project = Module & {
opModes: OpMode[],
};
+export type Component = {
+ name: string,
+ className: string,
+}
+
export const MODULE_TYPE_UNKNOWN = 'unknown';
export const MODULE_TYPE_PROJECT = 'project';
export const MODULE_TYPE_MECHANISM = 'mechanism';
@@ -58,13 +63,15 @@ export const MODULE_TYPE_OPMODE = 'opmode';
export const MODULE_NAME_PLACEHOLDER = '%module_name%';
const DELIMITER_PREFIX = 'BlocksContent';
-const MARKER_MODULE_TYPE = 'moduleType: ';
-const MARKER_EXPORTED_BLOCKS = 'exportedBlocks: ';
const MARKER_BLOCKS_CONTENT = 'blocksContent: ';
+const MARKER_EXPORTED_BLOCKS = 'exportedBlocks: ';
+const MARKER_MODULE_TYPE = 'moduleType: ';
+const MARKER_COMPONENTS = 'components: ';
const PARTS_INDEX_BLOCKS_CONTENT = 0;
const PARTS_INDEX_EXPORTED_BLOCKS = 1;
const PARTS_INDEX_MODULE_TYPE = 2;
-const NUMBER_OF_PARTS = 3;
+const PARTS_INDEX_COMPONENTS = 3;
+const NUMBER_OF_PARTS = 4;
export const UPLOAD_DOWNLOAD_FILE_EXTENSION = '.blocks';
@@ -246,7 +253,9 @@ function startingBlocksToModuleContent(
const exportedBlocks = JSON.stringify(generatorContext.getExportedBlocks());
const blocksContent = JSON.stringify(
Blockly.serialization.workspaces.save(headlessBlocklyWorkspace));
- return makeModuleContent(module, pythonCode, exportedBlocks, blocksContent);
+ const components = '[]';
+ return makeModuleContent(
+ module, pythonCode, blocksContent, exportedBlocks, components);
}
/**
@@ -300,7 +309,12 @@ export function newOpModeContent(projectName: string, opModeName: string): strin
/**
* Make the module content from the given python code and blocks content.
*/
-export function makeModuleContent(module: Module, pythonCode: string, exportedBlocks: string, blocksContent: string): string {
+export function makeModuleContent(
+ module: Module,
+ pythonCode: string,
+ blocksContent: string,
+ exportedBlocks: string,
+ components: string): string {
let delimiter = DELIMITER_PREFIX;
while (blocksContent.includes(delimiter) || exportedBlocks.includes(delimiter) || module.moduleType.includes(delimiter)) {
delimiter += '.';
@@ -310,6 +324,8 @@ export function makeModuleContent(module: Module, pythonCode: string, exportedBl
pythonCode + '\n\n\n' +
'"""\n' +
delimiter + '\n' +
+ MARKER_COMPONENTS + components + '\n' +
+ delimiter + '\n' +
MARKER_MODULE_TYPE + module.moduleType + '\n' +
delimiter + '\n' +
MARKER_EXPORTED_BLOCKS + exportedBlocks + '\n' +
@@ -346,14 +362,18 @@ function getParts(moduleContent: string): string[] {
parts.push(s.trim());
}
}
- if (parts.length < 2) {
- throw new Error('Unable to parse the module content.');
+ if (parts.length <= PARTS_INDEX_EXPORTED_BLOCKS) {
+ parts.push('[]');
}
- if (parts.length == 2) {
- // This module was saved without the module type, which was added to the module content when we introduced mechanisms.
+ if (parts.length <= PARTS_INDEX_MODULE_TYPE) {
+ // This module was saved without the module type.
// This module is either a Project or an OpMode, but we don't know which from just the content.
parts.push(MODULE_TYPE_UNKNOWN);
}
+ if (parts.length <= PARTS_INDEX_COMPONENTS) {
+ // This module was saved without components.
+ parts.push('');
+ }
return parts;
}
@@ -403,13 +423,26 @@ export function extractModuleType(moduleContent: string): string {
return moduleType;
}
+/**
+ * Extract the components from the given module content.
+ */
+export function extractComponents(moduleContent: string): Component[] {
+ const parts = getParts(moduleContent);
+ let componentsContent = parts[PARTS_INDEX_COMPONENTS];
+ if (componentsContent.startsWith(MARKER_COMPONENTS)) {
+ componentsContent = componentsContent.substring(MARKER_COMPONENTS.length);
+ }
+ const components: Component[] = JSON.parse(componentsContent);
+ return components;
+}
+
/**
* Produce the blob for downloading a project.
*/
export async function produceDownloadProjectBlob(
projectName: string, moduleContents: {[key: string]: string}): Promise {
const zip = new JSZip();
- for (let moduleName in moduleContents) {
+ for (const moduleName in moduleContents) {
const moduleContent = moduleContents[moduleName];
const moduleContentForDownload = _processModuleContentForDownload(
projectName, moduleName, moduleContent);
@@ -447,7 +480,9 @@ function _processModuleContentForDownload(
// Clear out the python content and exported blocks.
const pythonCode = '';
const exportedBlocks = '[]';
- return makeModuleContent(module, pythonCode, exportedBlocks, blocksContent);
+ const components = '[]';
+ return makeModuleContent(
+ module, pythonCode, blocksContent, exportedBlocks, components);
}
/**
@@ -491,7 +526,6 @@ export async function processUploadedBlob(
const promises: {[key: string]: Promise} = {};
zip.forEach((moduleName, zipEntry) => {
promises[moduleName] = zipEntry.async('text');
- console.log("HeyLiz - promises[moduleName] is ..."); console.log(promises[moduleName]);
});
// Wait for all promises to resolve.
@@ -505,7 +539,7 @@ export async function processUploadedBlob(
// Process each module's content.
const moduleTypes: {[key: string]: string} = {}; // key is module name, value is module type
const moduleContents: {[key: string]: string} = {}; // key is module name, value is module content
- for (let filename in files) {
+ for (const filename in files) {
const uploadedContent = files[filename];
let [moduleName, moduleType, moduleContent] = _processUploadedModule(
projectName, filename, uploadedContent);
@@ -558,7 +592,8 @@ export function _processUploadedModule(
const pythonCode = extendedPythonGenerator.mrcWorkspaceToCode(
headlessBlocklyWorkspace, generatorContext);
const exportedBlocks = JSON.stringify(generatorContext.getExportedBlocks());
+ const components = '[]';
const moduleContent = makeModuleContent(
- module, pythonCode, exportedBlocks, blocksContent);
+ module, pythonCode, blocksContent, exportedBlocks, components);
return [moduleName, moduleType, moduleContent];
}
diff --git a/src/toolbox/component_samples_category.ts b/src/toolbox/component_samples_category.ts
new file mode 100644
index 00000000..68008610
--- /dev/null
+++ b/src/toolbox/component_samples_category.ts
@@ -0,0 +1,526 @@
+export const category =
+{
+ kind: 'category',
+ name: 'Components',
+ contents: [
+ {
+ kind: 'category',
+ name: 'REV Color Range Sensor',
+ contents: [
+ // def get_color_rgb(self) -> tuple[int, int, int]:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'tuple[int, int, int]',
+ args: [],
+ tooltip: 'Get the color in rgb (red, green, blue).',
+ importModule: '',
+ componentClassName: 'rev.ColorRangeSensor',
+ componentName: 'colorSensor',
+ },
+ fields: {
+ COMPONENT_NAME: 'colorSensor',
+ FUNC: 'get_color_rgb',
+ },
+ inputs: {},
+ },
+ // def get_color_hsv(self) -> tuple[int, int, int]:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'tuple[int, int, int]',
+ args: [],
+ tooltip: 'Get the color in hsv (hue, saturation, value).',
+ importModule: '',
+ componentClassName: 'rev.ColorRangeSensor',
+ componentName: 'colorSensor',
+ },
+ fields: {
+ COMPONENT_NAME: 'colorSensor',
+ FUNC: 'get_color_hsv',
+ },
+ inputs: {},
+ },
+ // def get_distance_mm(self) -> float:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'float',
+ args: [],
+ tooltip: 'Get the distance of the object seen.',
+ importModule: '',
+ componentClassName: 'rev.ColorRangeSensor',
+ componentName: 'colorSensor',
+ },
+ fields: {
+ COMPONENT_NAME: 'colorSensor',
+ FUNC: 'get_distance_mm',
+ },
+ inputs: {},
+ },
+ ],
+ },
+ {
+ kind: 'category',
+ name: 'REV Servo',
+ contents: [
+ // def set_position(self, pos: float) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [
+ {
+ name: 'pos',
+ type: 'float',
+ },
+ ],
+ tooltip: 'Set the servo to a position between 0 and 1.',
+ importModule: '',
+ componentClassName: 'rev.Servo',
+ componentName: 'clawServo',
+ },
+ fields: {
+ COMPONENT_NAME: 'clawServo',
+ FUNC: 'set_position',
+ },
+ inputs: {
+ ARG0: {
+ block: {
+ type: 'math_number',
+ fields: {
+ NUM: 0.5,
+ },
+ },
+ },
+ },
+ },
+ // def set_angle_degrees(self, angle: float) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [
+ {
+ name: 'angle',
+ type: 'float',
+ },
+ ],
+ tooltip: 'Set the servo to an angle between 0 and 270.',
+ importModule: '',
+ componentClassName: 'rev.Servo',
+ componentName: 'clawServo',
+ },
+ fields: {
+ COMPONENT_NAME: 'clawServo',
+ FUNC: 'set_angle_degrees',
+ },
+ inputs: {
+ ARG0: {
+ block: {
+ type: 'math_number',
+ fields: {
+ NUM: 180,
+ },
+ },
+ },
+ },
+ },
+ ],
+ },
+ {
+ kind: 'category',
+ name: 'REV Smart Motor',
+ contents: [
+ // def set_speed(self, speed: float) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [
+ {
+ name: 'speed',
+ type: 'float',
+ },
+ ],
+ tooltip: 'Set the motor to a speed between -1 and 1.',
+ importModule: '',
+ componentClassName: 'rev.SmartMotor',
+ componentName: 'leftMotor',
+ },
+ fields: {
+ COMPONENT_NAME: 'leftMotor',
+ FUNC: 'set_speed',
+ },
+ inputs: {
+ ARG0: {
+ block: {
+ type: 'math_number',
+ fields: {
+ NUM: 0.8,
+ },
+ },
+ },
+ },
+ },
+ // def set_angle_degrees(self, angle: float) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [
+ {
+ name: 'angle',
+ type: 'float',
+ },
+ ],
+ tooltip: 'Set the motor to an angle between 0 and 360.',
+ importModule: '',
+ componentClassName: 'rev.SmartMotor',
+ componentName: 'leftMotor',
+ },
+ fields: {
+ COMPONENT_NAME: 'leftMotor',
+ FUNC: 'set_angle_degrees',
+ },
+ inputs: {
+ ARG0: {
+ block: {
+ type: 'math_number',
+ fields: {
+ NUM: 180,
+ },
+ },
+ },
+ },
+ },
+ // def get_num_relative_encoder_ticks(self) -> int:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'int',
+ args: [],
+ tooltip: 'Get the number of relative motor ticks since reset of encoder.',
+ importModule: '',
+ componentClassName: 'rev.SmartMotor',
+ componentName: 'leftMotor',
+ },
+ fields: {
+ COMPONENT_NAME: 'leftMotor',
+ FUNC: 'get_num_relative_encoder_ticks',
+ },
+ inputs: {},
+ },
+ // def get_angle_degrees(self) -> float:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'float',
+ args: [],
+ tooltip: 'Get the angle position of the motor.',
+ importModule: '',
+ componentClassName: 'rev.SmartMotor',
+ componentName: 'leftMotor',
+ },
+ fields: {
+ COMPONENT_NAME: 'leftMotor',
+ FUNC: 'get_angle_degrees',
+ },
+ inputs: {},
+ },
+ // def reset_relative_encoder(self) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [],
+ tooltip: 'Reset the relative encoder value to 0.',
+ importModule: '',
+ componentClassName: 'rev.SmartMotor',
+ componentName: 'leftMotor',
+ },
+ fields: {
+ COMPONENT_NAME: 'leftMotor',
+ FUNC: 'reset_relative_encoder',
+ },
+ inputs: {},
+ },
+ ]
+ },
+ {
+ kind: 'category',
+ name: 'REV Touch Sensor',
+ contents: [
+ // def is_pressed(self) -> bool:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'bool',
+ args: [],
+ tooltip: 'Return whether the touch sensor is pressed.',
+ importModule: '',
+ componentClassName: 'rev.TouchSensor',
+ componentName: 'frontTouch',
+ },
+ fields: {
+ COMPONENT_NAME: 'frontTouch',
+ FUNC: 'is_pressed',
+ },
+ inputs: {},
+ },
+ ],
+ },
+ {
+ kind: 'category',
+ name: 'SparkFun LED Stick',
+ contents: [
+ // def set_color(self, position: int, color: wpilib.Color) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [
+ {
+ name: 'position',
+ type: 'int',
+ },
+ {
+ name: 'color',
+ type: 'wpilib.Color',
+ },
+ ],
+ tooltip: 'Change the color of an individual LED.',
+ importModule: '',
+ componentClassName: 'sparkfun.LEDStick',
+ componentName: 'ledStick',
+ },
+ fields: {
+ COMPONENT_NAME: 'ledStick',
+ FUNC: 'set_color',
+ },
+ inputs: {
+ ARG0: {
+ block: {
+ type: 'math_number',
+ fields: {
+ NUM: 1,
+ },
+ },
+ },
+ ARG1: {
+ block: {
+ type: 'mrc_get_python_variable',
+ extraState: {
+ varKind: 'class',
+ moduleOrClassName: 'wpilib.Color',
+ varType: 'wpilib.Color',
+ importModule: 'wpilib',
+ },
+ fields: {
+ MODULE_OR_CLASS: 'wpilib.Color',
+ VAR: 'kWhite',
+ },
+ },
+ },
+ },
+ },
+ // def set_color(self, color: wpilib.Color) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [
+ {
+ name: 'color',
+ type: 'wpilib.Color',
+ },
+ ],
+ tooltip: 'Change the color of all LEDs to a single color.',
+ importModule: '',
+ componentClassName: 'sparkfun.LEDStick',
+ componentName: 'ledStick',
+ },
+ fields: {
+ COMPONENT_NAME: 'ledStick',
+ FUNC: 'set_color',
+ },
+ inputs: {
+ ARG0: {
+ block: {
+ type: 'mrc_get_python_variable',
+ extraState: {
+ varKind: 'class',
+ moduleOrClassName: 'wpilib.Color',
+ varType: 'wpilib.Color',
+ importModule: 'wpilib',
+ },
+ fields: {
+ MODULE_OR_CLASS: 'wpilib.Color',
+ VAR: 'kWhite',
+ },
+ },
+ },
+ },
+ },
+ // def set_colors(self, colors: list[int]) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [
+ {
+ name: 'colors',
+ type: 'list[int]',
+ },
+ ],
+ tooltip: 'Change the color of all LEDs using a list.',
+ importModule: '',
+ componentClassName: 'sparkfun.LEDStick',
+ componentName: 'ledStick',
+ },
+ fields: {
+ COMPONENT_NAME: 'ledStick',
+ FUNC: 'set_colors',
+ },
+ inputs: {
+ ARG0: {
+ block: {
+ type: 'variables_get',
+ fields: {
+ VAR: {
+ name: 'myListOfColors',
+ },
+ },
+ },
+ },
+ },
+ },
+ // def set_brightness(self, position: int, brightness: int) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [
+ {
+ name: 'position',
+ type: 'int',
+ },
+ {
+ name: 'brightness',
+ type: 'int',
+ },
+ ],
+ tooltip: 'Set the brightness of an individual LED.',
+ importModule: '',
+ componentClassName: 'sparkfun.LEDStick',
+ componentName: 'ledStick',
+ },
+ fields: {
+ COMPONENT_NAME: 'ledStick',
+ FUNC: 'set_brightness',
+ },
+ inputs: {
+ ARG0: {
+ block: {
+ type: 'math_number',
+ fields: {
+ NUM: 1,
+ },
+ },
+ },
+ ARG1: {
+ block: {
+ type: 'math_number',
+ fields: {
+ NUM: 16,
+ },
+ },
+ },
+ },
+ },
+ // def set_brightness(self, brightness: int) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [
+ {
+ name: 'brightness',
+ type: 'int',
+ }
+ ],
+ tooltip: 'Set the brightness of all LEDs.',
+ importModule: '',
+ componentClassName: 'sparkfun.LEDStick',
+ componentName: 'ledStick',
+ },
+ fields: {
+ COMPONENT_NAME: 'ledStick',
+ FUNC: 'set_brightness',
+ },
+ inputs: {
+ ARG0: {
+ block: {
+ type: 'math_number',
+ fields: {
+ NUM: 16,
+ },
+ },
+ },
+ },
+ },
+ // def turn_all_off(self) -> None:
+ {
+ kind: 'block',
+ type: 'mrc_call_python_function',
+ extraState: {
+ functionKind: 'instance_component',
+ returnType: 'None',
+ args: [],
+ tooltip: 'Turn all LEDs off.',
+ importModule: '',
+ componentClassName: 'sparkfun.LEDStick',
+ componentName: 'ledStick',
+ },
+ fields: {
+ COMPONENT_NAME: 'ledStick',
+ FUNC: 'turn_all_off',
+ },
+ inputs: {},
+ },
+ ],
+ }
+ ],
+}
\ No newline at end of file
diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts
index ce8c361a..ed17a591 100644
--- a/src/toolbox/toolbox.ts
+++ b/src/toolbox/toolbox.ts
@@ -28,6 +28,7 @@ import {category as textCategory} from './text_category';
import {category as listsCategory} from './lists_category';
import {category as miscCategory} from './misc_category';
import {category as methodsCategory} from './methods_category';
+import {category as componentSampleCategory} from './component_samples_category';
export function getToolboxJSON(
opt_includeExportedBlocksFromProject: toolboxItems.ContentsType[],
@@ -72,6 +73,7 @@ export function getToolboxJSON(
custom: 'VARIABLE',
},
methodsCategory,
+ componentSampleCategory,
]);
return {