diff --git a/src/blocks/mrc_call_python_function.ts b/src/blocks/mrc_call_python_function.ts index 496aa3e5..08876d1c 100644 --- a/src/blocks/mrc_call_python_function.ts +++ b/src/blocks/mrc_call_python_function.ts @@ -545,12 +545,17 @@ const CALL_PYTHON_FUNCTION = { break; } case FunctionKind.INSTANCE_COMPONENT: { - let componentNames: string[] = []; + const componentNames: string[] = []; // Get the list of component names whose type matches this.mrcComponentClassName so we can // create a dropdown that has the appropriate component names. const editor = Editor.getEditorForBlocklyWorkspace(this.workspace); if (editor) { - componentNames = editor.getComponentNames(this.mrcComponentClassName); + const components = editor.getComponentsFromRobot(); + components.forEach(component => { + if (component.className === this.mrcComponentClassName) { + componentNames.push(component.name); + } + }); } const componentName = this.getComponentName(); if (!componentNames.includes(componentName)) { diff --git a/src/editor/editor.ts b/src/editor/editor.ts index 176b9a72..67aa746b 100644 --- a/src/editor/editor.ts +++ b/src/editor/editor.ts @@ -139,7 +139,7 @@ export class Editor { const promises: { [key: string]: Promise } = {}; // key is module path, value is promise of module content. promises[this.modulePath] = this.storage.fetchModuleContent(this.modulePath); if (this.robotPath !== this.modulePath) { - // Also fetch the robot module content. It contains components, etc, that can be used. + // Also fetch the robot module content. It contains components, etc, that can be used in OpModes. promises[this.robotPath] = this.storage.fetchModuleContent(this.robotPath) } @@ -224,12 +224,12 @@ 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()); + const componentsContent = JSON.stringify(this.getComponentsFromWorkspace()); return commonStorage.makeModuleContent( this.currentModule, pythonCode, blocksContent, exportedBlocks, componentsContent); } - private getComponents(): commonStorage.Component[] { + public getComponentsFromWorkspace(): commonStorage.Component[] { const components: commonStorage.Component[] = []; if (this.currentModule?.moduleType === commonStorage.MODULE_TYPE_ROBOT || this.currentModule?.moduleType === commonStorage.MODULE_TYPE_MECHANISM) { @@ -257,28 +257,18 @@ export class Editor { } /** - * Returns the names of components defined in the robot that have the given component class name. + * Returns the components defined in the robot. */ - // TODO: what about components defined in a mechanism? - public getComponentNames(componentClassName: string): string[] { + public getComponentsFromRobot(): commonStorage.Component[] { let components: commonStorage.Component[]; if (this.currentModule?.moduleType === commonStorage.MODULE_TYPE_ROBOT) { - components = this.getComponents(); - } else { - if (!this.robotContent) { - throw new Error('getComponentNames: this.robotContent is null.'); - } - components = commonStorage.extractComponents(this.robotContent); + return this.getComponentsFromWorkspace(); } - - const componentNames: string[] = []; - components.forEach((component) => { - if (component.className === componentClassName) { - componentNames.push(component.name); - } - }); - return componentNames; + if (!this.robotContent) { + throw new Error('getComponentsFromRobot: this.robotContent is null.'); + } + return commonStorage.extractComponents(this.robotContent); } public static getEditorForBlocklyWorkspace(workspace: Blockly.Workspace): Editor | null { diff --git a/src/toolbox/blocks_mechanisms.ts b/src/toolbox/blocks_mechanisms.ts index 249dc454..494cc595 100644 --- a/src/toolbox/blocks_mechanisms.ts +++ b/src/toolbox/blocks_mechanisms.ts @@ -20,102 +20,105 @@ */ import * as ToolboxItems from './items'; -/** - * TODO: This is all fake right now, it will be generated dynamically - * based on reading things from mechanisms. - */ - export function getAllPossibleMechanisms(): ToolboxItems.ContentsType[] { - return [ + const contents: ToolboxItems.ContentsType[] = [] + + // TODO: Get the blocks for adding mechanisms. - { - kind: 'block', - type: 'mrc_mechanism', + /* // Uncomment this fake code for testing purposes only. + contents.push( + { + kind: 'block', + type: 'mrc_mechanism', + fields: { + NAME: 'claw', + TYPE: 'Claw' + }, + extraState: { + importModule: 'claw', + params: [ + { name: 'gripper_port', type: 'int' }, + { name: 'piece_sensor_port', type: 'int' }, + ] + }, + inputs: { + ARG0: { + shadow: { + type: 'mrc_port', + fields: { + TYPE: 'SmartMotor', + PORT_NUM: 1 + }, + }, + }, + ARG1: { + shadow: { + type: 'mrc_port', + fields: { + TYPE: 'SmartIO', + PORT_NUM: 1 + }, + }, + }, + } + }, + { + kind: 'block', + type: 'mrc_mechanism', + fields: { + NAME: 'drive', + TYPE: 'DriveMecanum' + }, + extraState: { + importModule: 'drive_mecanum', + params: [ + { name: 'front_left_drive_port', type: 'int' }, + { name: 'front_right_drive_port', type: 'int' }, + { name: 'back_left_drive_port', type: 'int' }, + { name: 'back_right_drive_port', type: 'int' }, + ] + }, + inputs: { + ARG0: { + shadow: { + type: 'mrc_port', fields: { - NAME: 'claw', - TYPE: 'Claw' + TYPE: 'SmartMotor', + PORT_NUM: 1 }, - extraState: { - importModule: 'claw', - params: [{ name: 'gripper_port', type: 'int' }, - { name: 'piece_sensor_port', type: 'int' }, - ] + }, + }, + ARG1: { + shadow: { + type: 'mrc_port', + fields: { + TYPE: 'SmartMotor', + PORT_NUM: 2 }, - inputs: { - ARG0: { - shadow: { - type: 'mrc_port', - fields: { - TYPE: 'SmartMotor', - PORT_NUM: 1 - }, - }, - }, - ARG1: { - shadow: { - type: 'mrc_port', - fields: { - TYPE: 'SmartIO', - PORT_NUM: 1 - }, - }, - }, - } + }, }, - { - kind: 'block', - type: 'mrc_mechanism', + ARG2: { + shadow: { + type: 'mrc_port', fields: { - NAME: 'drive', - TYPE: 'DriveMecanum' + TYPE: 'SmartMotor', + PORT_NUM: 3 }, - extraState: { - importModule: 'DriveMecanum', - params: [{ name: 'front_left_drive_port', type: 'int' }, - { name: 'front_right_drive_port', type: 'int' }, - { name: 'back_left_drive_port', type: 'int' }, - { name: 'back_right_drive_port', type: 'int' }, - ] + }, + }, + ARG3: { + shadow: { + type: 'mrc_port', + fields: { + TYPE: 'SmartMotor', + PORT_NUM: 4 }, - inputs: { - ARG0: { - shadow: { - type: 'mrc_port', - fields: { - TYPE: 'SmartMotor', - PORT_NUM: 1 - }, - }, - }, - ARG1: { - shadow: { - type: 'mrc_port', - fields: { - TYPE: 'SmartMotor', - PORT_NUM: 2 - }, - }, - }, - ARG2: { - shadow: { - type: 'mrc_port', - fields: { - TYPE: 'SmartMotor', - PORT_NUM: 3 - }, - }, - }, - ARG3: { - shadow: { - type: 'mrc_port', - fields: { - TYPE: 'SmartMotor', - PORT_NUM: 4 - }, - }, - }, - } + }, }, - ]; -} + } + } + ); + */ + return contents; +} diff --git a/src/toolbox/hardware_category.ts b/src/toolbox/hardware_category.ts index 37aaff6a..a7c73b91 100644 --- a/src/toolbox/hardware_category.ts +++ b/src/toolbox/hardware_category.ts @@ -19,60 +19,52 @@ * @author alan@porpoiseful.com (Alan Smith) */ -/** - * TODO: This is all fake right now, it will be generated dynamically - * based on the components that are available in the current module. - */ - import * as Blockly from 'blockly/core'; - import * as commonStorage from '../storage/common_storage'; import { getAllPossibleMechanisms } from './blocks_mechanisms'; import * as Component from '../blocks/mrc_component'; import { getInstanceComponentBlocks } from '../blocks/mrc_call_python_function'; -import * as MechanismComponentHolder from '../blocks/mrc_mechanism_component_holder'; +import { Editor } from '../editor/editor'; export function getHardwareCategory(currentModule: commonStorage.Module) { - if (currentModule.moduleType === commonStorage.MODULE_TYPE_OPMODE) { + if (currentModule.moduleType === commonStorage.MODULE_TYPE_ROBOT) { return { kind: 'category', - name: 'Robot', + name: 'Hardware', contents: [ getRobotMechanismsBlocks(currentModule), - getRobotComponentsBlocks(currentModule), - getRobotMethodsBlocks(currentModule), + getComponentsBlocks(currentModule, false), ] }; } - if (currentModule.moduleType === commonStorage.MODULE_TYPE_ROBOT) { + if (currentModule.moduleType === commonStorage.MODULE_TYPE_MECHANISM) { + return getComponentsBlocks(currentModule, true); + } + if (currentModule.moduleType === commonStorage.MODULE_TYPE_OPMODE) { return { kind: 'category', - name: 'Hardware', + name: 'Robot', contents: [ getRobotMechanismsBlocks(currentModule), - getComponentsBlocks(currentModule, false), + getRobotComponentsBlocks(currentModule), + getRobotMethodsBlocks(currentModule), ] }; } - if (currentModule.moduleType === commonStorage.MODULE_TYPE_MECHANISM) { - return getComponentsBlocks(currentModule, true); - } - // Return default empty category if module type doesn't match - return { - kind: 'category', - name: 'Hardware', - contents: [] - }; + throw new Error('currentModule.moduleType has unexpected value: ' + currentModule.moduleType) } -// TODO: This needs to load the robot file and get the list of mechanisms function getRobotMechanismsBlocks(currentModule: commonStorage.Module) { - const includeAdd = currentModule.moduleType !== commonStorage.MODULE_TYPE_OPMODE; + // getRobotMechanismsBlocks is called when the user is editing the robot or an opmode. + // If the user is editing the robot, it allows the user to add a mechanism to + // the robot or use an existing mechanism. + // If the user is editing an opmode, it allows the user to use a mechanism that + // was previously added to the Robot. const contents = []; - // Only include the "+ Mechanism" category if includeAdd is true - if (includeAdd) { + // Include the "+ Mechanism" category if the user it editing the robot. + if (currentModule.moduleType === commonStorage.MODULE_TYPE_ROBOT) { contents.push({ kind: 'category', name: '+ Mechanism', @@ -80,7 +72,10 @@ function getRobotMechanismsBlocks(currentModule: commonStorage.Module) { }); } - // Add the existing mechanisms + // TODO: Get the list of mechanisms from the robot and add the blocks for + // calling the mechanism functions to the toolbox. + + /* // Uncomment this fake code for testing purposes only. contents.push( { kind: 'category', @@ -204,6 +199,7 @@ function getRobotMechanismsBlocks(currentModule: commonStorage.Module) { contents: [], } ); + */ return { kind: 'category', @@ -212,17 +208,27 @@ function getRobotMechanismsBlocks(currentModule: commonStorage.Module) { }; } -// TODO: This needs to load the robot file and get the list of components function getRobotComponentsBlocks(currentModule: commonStorage.Module) { - const includeAdd = currentModule.moduleType !== commonStorage.MODULE_TYPE_OPMODE; + // getRobotComponentsBlocks is called when the user is editing an opmode. + // It allows the user to use a component that was previously added to the Robot. + const contents = []; - if (includeAdd) { - contents.push({ - kind: 'category', - name: '+ Component', - contents: Component.getAllPossibleComponents(false) - }); + // Get the list of components from the roobt. + const workspace = Blockly.getMainWorkspace(); + if (workspace) { + const editor = Editor.getEditorForBlocklyWorkspace(workspace); + if (editor) { + const componentsFromRobot = editor.getComponentsFromRobot(); + componentsFromRobot.forEach(component => { + // Get the blocks for this specific component. + contents.push({ + kind: 'category', + name: component.name, + contents: getInstanceComponentBlocks(component.className, component.name), + }); + }); + } } return { @@ -233,16 +239,22 @@ function getRobotComponentsBlocks(currentModule: commonStorage.Module) { } function getRobotMethodsBlocks(currentModule: commonStorage.Module) { + const contents = []; + + // TODO: Get the list of public methods from the robot and add the blocks for + // calling the robot functions to the toolbox. + return { kind: 'category', name: 'Methods', - contents: [] + contents, }; } -// This is called when the user is editing a mechanism or the robot and allows -// the user to add a component or use an existing component. function getComponentsBlocks(currentModule: commonStorage.Module, hideParams : boolean) { + // getComponentsBlocks is called when the user is editing the robot or a + // mechanism. It allows the user to add a component or use an existing component. + const contents = []; // Add the "+ Component" category @@ -252,16 +264,13 @@ function getComponentsBlocks(currentModule: commonStorage.Module, hideParams : b contents: Component.getAllPossibleComponents(hideParams) }); - // Get components from the current workspace + // Get components from the current workspace. const workspace = Blockly.getMainWorkspace(); if (workspace) { - // Get the holder block and ask it for the components. - const holderBlocks = workspace.getBlocksByType(MechanismComponentHolder.BLOCK_NAME); - - holderBlocks.forEach(holderBlock => { - const componentsFromHolder: commonStorage.Component[] = - (holderBlock as MechanismComponentHolder.MechanismComponentHolderBlock).getComponents(); - componentsFromHolder.forEach(component => { + const editor = Editor.getEditorForBlocklyWorkspace(workspace); + if (editor) { + const componentsFromWorkspace = editor.getComponentsFromWorkspace(); + componentsFromWorkspace.forEach(component => { // Get the blocks for this specific component contents.push({ kind: 'category', @@ -269,8 +278,9 @@ function getComponentsBlocks(currentModule: commonStorage.Module, hideParams : b contents: getInstanceComponentBlocks(component.className, component.name), }); }); - }); + } } + return { kind: 'category', name: 'Components',