diff --git a/src/blocks/mrc_mechanism.ts b/src/blocks/mrc_mechanism.ts index 964aa5bf..63a3c010 100644 --- a/src/blocks/mrc_mechanism.ts +++ b/src/blocks/mrc_mechanism.ts @@ -46,6 +46,7 @@ type Parameter = { }; type MechanismExtraState = { + mechanismModuleId?: string, importModule?: string, parameters?: Parameter[], } @@ -54,6 +55,7 @@ const WARNING_ID_MECHANISM_CHANGED = 'mechanism changed'; export type MechanismBlock = Blockly.Block & MechanismMixin & Blockly.BlockSvg; interface MechanismMixin extends MechanismMixinType { + mrcMechanismModuleId: string mrcImportModule: string, mrcParameters: Parameter[], } @@ -80,6 +82,7 @@ const MECHANISM = { */ saveExtraState: function (this: MechanismBlock): MechanismExtraState { const extraState: MechanismExtraState = { + mechanismModuleId: this.mrcMechanismModuleId, }; extraState.parameters = []; this.mrcParameters.forEach((arg) => { @@ -97,6 +100,7 @@ const MECHANISM = { * Applies the given state to this block. */ loadExtraState: function (this: MechanismBlock, extraState: MechanismExtraState): void { + this.mrcMechanismModuleId = extraState.mechanismModuleId ? extraState.mechanismModuleId : ''; this.mrcImportModule = extraState.importModule ? extraState.importModule : ''; this.mrcParameters = []; if (extraState.parameters) { @@ -157,6 +161,7 @@ const MECHANISM = { const mechanismName = this.getFieldValue(FIELD_NAME); const mechanismType = this.mrcImportModule + '.' + this.getFieldValue(FIELD_TYPE); return { + moduleId: this.mrcMechanismModuleId, blockId: this.id, name: mechanismName, className: mechanismType, @@ -169,21 +174,33 @@ const MECHANISM = { const editor = Editor.getEditorForBlocklyWorkspace(this.workspace); if (editor) { // Find the mechanism. - // TODO(lizlooney): The user can rename the mechanism. We need to store a UUID in - // each module JSON file so we can track mechanisms, etc, even if the name changes. - // Then here, we'd look for the mechanism with the marching UUID, and we'd update the - // FIELD_TYPE value if the mechanism's class name had changed. let foundMechanism: storageModule.Mechanism | null = null; - const components: storageModuleContent.Component[] = [] - for (const mechanism of editor.getMechanisms()) { - if (mechanism.className === this.getFieldValue(FIELD_TYPE)) { - foundMechanism = mechanism; - components.push(...editor.getComponentsFromMechanism(mechanism)); - break; + + if (this.mrcMechanismModuleId) { + // Find the mechanism by module id. + for (const mechanism of editor.getMechanisms()) { + if (mechanism.moduleId === this.mrcMechanismModuleId) { + foundMechanism = mechanism; + break; + } + } + } else { + // Find the mechanism by class name. + const className = this.getFieldValue(FIELD_TYPE); + for (const mechanism of editor.getMechanisms()) { + if (mechanism.className === className) { + // Grap the mechanism module id, so we have it for next time. + this.mrcMechanismModuleId = mechanism.moduleId; + foundMechanism = mechanism; + break; + } } } if (foundMechanism) { + const components: storageModuleContent.Component[] = []; + components.push(...editor.getComponentsFromMechanism(foundMechanism)); + // If the mechanism class name has changed, update this blcok. if (this.getFieldValue(FIELD_TYPE) !== foundMechanism.className) { this.setFieldValue(foundMechanism.className, FIELD_TYPE); @@ -251,6 +268,7 @@ export function createMechanismBlock( const snakeCaseName = storageNames.pascalCaseToSnakeCase(mechanism.className); const mechanismName = 'my_' + snakeCaseName; const extraState: MechanismExtraState = { + mechanismModuleId: mechanism.moduleId, importModule: snakeCaseName, parameters: [], }; diff --git a/src/storage/module.ts b/src/storage/module.ts index 08f8d355..97e31888 100644 --- a/src/storage/module.ts +++ b/src/storage/module.ts @@ -28,9 +28,9 @@ export const MODULE_TYPE_MECHANISM = 'mechanism'; export const MODULE_TYPE_OPMODE = 'opmode'; export type Module = { - // TODO(lizlooney): Add a uuid so we can keep track of mechanisms in the robot even if the user renames the mechamism modulePath: string, moduleType: string, + moduleId: string, projectName: string, // For example, WackyWheelerRobot className: string, // For example, GamePieceShooter. dateModifiedMillis: number, diff --git a/src/storage/module_content.ts b/src/storage/module_content.ts index e6f8be30..472a6c32 100644 --- a/src/storage/module_content.ts +++ b/src/storage/module_content.ts @@ -19,6 +19,7 @@ * @author lizlooney@google.com (Liz Looney) */ +import * as Blockly from 'blockly/core'; import * as storageModule from './module'; import * as storageNames from './names'; import startingOpModeBlocks from '../modules/opmode_start.json'; @@ -39,6 +40,7 @@ export type Method = { }; export type MechanismInRobot = { + moduleId: string // ID of the mechanism module. blockId: string, // ID of the mrc_mechanism block that adds the mechanism to the robot. name: string, className: string, // Includes the module name, for example 'game_piece_shooter.GamePieceShooter'. @@ -79,6 +81,7 @@ export function newRobotContent(projectName: string): string { const module: storageModule.Robot = { modulePath: storageNames.makeRobotPath(projectName), moduleType: storageModule.MODULE_TYPE_ROBOT, + moduleId: Blockly.utils.idGenerator.genUid(), projectName: projectName, className: storageNames.CLASS_NAME_ROBOT, dateModifiedMillis: 0, @@ -94,6 +97,7 @@ export function newMechanismContent(projectName: string, mechanismClassName: str const module: storageModule.Mechanism = { modulePath: storageNames.makeModulePath(projectName, mechanismClassName), moduleType: storageModule.MODULE_TYPE_MECHANISM, + moduleId: Blockly.utils.idGenerator.genUid(), projectName: projectName, className: mechanismClassName, dateModifiedMillis: 0, @@ -109,6 +113,7 @@ export function newOpModeContent(projectName: string, opModeClassName: string): const module: storageModule.OpMode = { modulePath: storageNames.makeModulePath(projectName, opModeClassName), moduleType: storageModule.MODULE_TYPE_OPMODE, + moduleId: Blockly.utils.idGenerator.genUid(), projectName: projectName, className: opModeClassName, dateModifiedMillis: 0, @@ -119,6 +124,7 @@ export function newOpModeContent(projectName: string, opModeClassName: string): /** * Make the module content from the given python code and blocks content. + * If the given module has an empty moduleId field, it will be set to a valid id. */ export function makeModuleContentText( module: storageModule.Module, @@ -127,8 +133,12 @@ export function makeModuleContentText( components: Component[], events: Event[], methods: Method[]): string { + if (!module.moduleId) { + module.moduleId = Blockly.utils.idGenerator.genUid(); + } const moduleContent = new ModuleContent( module.moduleType, + module.moduleId, blocks, mechanisms, components, @@ -139,8 +149,12 @@ export function makeModuleContentText( export function parseModuleContentText(moduleContentText: string): ModuleContent { const parsedContent = JSON.parse(moduleContentText); + if (!('moduleId' in parsedContent)) { + parsedContent.moduleId = ''; + } return new ModuleContent( parsedContent.moduleType, + parsedContent.moduleId, parsedContent.blocks, parsedContent.mechanisms, parsedContent.components, @@ -151,6 +165,7 @@ export function parseModuleContentText(moduleContentText: string): ModuleContent export class ModuleContent { constructor( private moduleType: string, + private moduleId: string, private blocks : { [key: string]: any }, private mechanisms: MechanismInRobot[], private components: Component[], @@ -166,6 +181,10 @@ export class ModuleContent { return this.moduleType; } + getModuleId(): string { + return this.moduleId; + } + getBlocks(): { [key: string]: any } { return this.blocks; } diff --git a/src/storage/project.ts b/src/storage/project.ts index 82c84277..08f4d619 100644 --- a/src/storage/project.ts +++ b/src/storage/project.ts @@ -51,6 +51,7 @@ export async function listProjects(storage: commonStorage.Storage): Promise