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 {