Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 52 additions & 25 deletions src/blocks/mrc_call_python_function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,9 @@ const WARNING_ID_FUNCTION_CHANGED = 'function changed';
export function addBuiltInFunctionBlocks(
functions: FunctionData[],
contents: ToolboxItems.ContentsType[]) {
for (const functionData of functions) {
const block = createBuiltInMethodBlock(functionData);
contents.push(block);
}
functions.forEach(functionData => {
contents.push(createBuiltInMethodBlock(functionData));
});
}

function createBuiltInMethodBlock(
Expand Down Expand Up @@ -132,24 +131,24 @@ export function addModuleFunctionBlocks(
moduleName: string,
functions: FunctionData[],
contents: ToolboxItems.ContentsType[]) {
for (const functionData of functions) {
functions.forEach(functionData => {
const block = createModuleFunctionOrStaticMethodBlock(
FunctionKind.MODULE, moduleName, moduleName, functionData);
contents.push(block);
}
});
}

export function addStaticMethodBlocks(
importModule: string,
functions: FunctionData[],
contents: ToolboxItems.ContentsType[]) {
for (const functionData of functions) {
functions.forEach(functionData => {
if (functionData.declaringClassName) {
const block = createModuleFunctionOrStaticMethodBlock(
FunctionKind.STATIC, importModule, functionData.declaringClassName, functionData);
contents.push(block);
}
}
});
}

function createModuleFunctionOrStaticMethodBlock(
Expand All @@ -176,10 +175,9 @@ export function addConstructorBlocks(
importModule: string,
functions: FunctionData[],
contents: ToolboxItems.ContentsType[]) {
for (const functionData of functions) {
const block = createConstructorBlock(importModule, functionData);
contents.push(block);
}
functions.forEach(functionData => {
contents.push(createConstructorBlock(importModule, functionData));
});
}

function createConstructorBlock(
Expand All @@ -202,10 +200,9 @@ function createConstructorBlock(
export function addInstanceMethodBlocks(
functions: FunctionData[],
contents: ToolboxItems.ContentsType[]) {
for (const functionData of functions) {
const block = createInstanceMethodBlock(functionData);
contents.push(block);
}
functions.forEach(functionData => {
contents.push(createInstanceMethodBlock(functionData));
});
}

function createInstanceMethodBlock(
Expand All @@ -224,6 +221,39 @@ function createInstanceMethodBlock(
return createBlock(extraState, fields, inputs);
}

export function addInstanceWithinBlocks(
methods: CommonStorage.Method[],
contents: ToolboxItems.ContentsType[]) {
methods.forEach(method => {
contents.push(createInstanceWithinBlock(method));
});
}

function createInstanceWithinBlock(method: CommonStorage.Method): ToolboxItems.Block {
const extraState: CallPythonFunctionExtraState = {
functionKind: FunctionKind.INSTANCE_WITHIN,
returnType: method.returnType,
actualFunctionName: method.pythonName,
args: [],
classMethodDefBlockId: method.blockId,
};
const fields: {[key: string]: any} = {};
fields[FIELD_FUNCTION_NAME] = method.visibleName;
const inputs: {[key: string]: any} = {};
// Convert method.args from CommonStorage.MethodArg[] to ArgData[].
const args: ArgData[] = [];
// We don't include the arg for the self argument.
for (let i = 1; i < method.args.length; i++) {
args.push({
name: method.args[i].name,
type: method.args[i].type,
defaultValue: '',
});
}
processArgs(args, extraState, inputs);
return createBlock(extraState, fields, inputs);
}

export function getInstanceComponentBlocks(
component: CommonStorage.Component): ToolboxItems.ContentsType[] {
const contents: ToolboxItems.ContentsType[] = [];
Expand Down Expand Up @@ -276,15 +306,12 @@ function createInstanceComponentBlock(
return createBlock(extraState, fields, inputs);
}

export function getInstanceRobotBlocks(methods: CommonStorage.Method[]): ToolboxItems.ContentsType[] {
const contents: ToolboxItems.ContentsType[] = [];

for (const method of methods) {
const block = createInstanceRobotBlock(method);
contents.push(block);
}

return contents;
export function addInstanceRobotBlocks(
methods: CommonStorage.Method[],
contents: ToolboxItems.ContentsType[]) {
methods.forEach(method => {
contents.push(createInstanceRobotBlock(method));
});
}

function createInstanceRobotBlock(method: CommonStorage.Method): ToolboxItems.Block {
Expand Down
17 changes: 13 additions & 4 deletions src/blocks/mrc_class_method_def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,18 @@ const CLASS_METHOD_DEF = {
}
return legalName;
},
getMethod: function (this: ClassMethodDefBlock): commonStorage.Method | null {
return this.mrcMethod;
}
getMethodForWithin: function (this: ClassMethodDefBlock): commonStorage.Method | null {
return this.mrcCanBeCalledWithinClass ? this.mrcMethod : null;
},
getMethodForOutside: function (this: ClassMethodDefBlock): commonStorage.Method | null {
return this.mrcCanBeCalledOutsideClass ? this.mrcMethod : null;
},
canChangeSignature: function (this: ClassMethodDefBlock): boolean {
return this.mrcCanChangeSignature;
},
getMethodName: function (this: ClassMethodDefBlock): string {
return this.getFieldValue('NAME');
},
};

/**
Expand Down Expand Up @@ -402,7 +411,7 @@ export const pythonFromBlock = function (
code = generator.scrub_(block, code);
generator.addClassMethodDefinition(funcName, code);

if (block.mrcCanBeCalledOutsideClass) {
if (block.mrcCanBeCalledWithinClass || block.mrcCanBeCalledOutsideClass) {
// Update the mrcMethod.
block.mrcMethod = {
blockId: block.id,
Expand Down
63 changes: 49 additions & 14 deletions src/editor/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,11 @@ export class Editor {
this.blocklyWorkspace, this.generatorContext);
const blocksContent = JSON.stringify(
Blockly.serialization.workspaces.save(this.blocklyWorkspace));
const methodsContent = JSON.stringify(this.getMethodsFromWorkspace());
const methodsContent = (
this.currentModule?.moduleType === commonStorage.MODULE_TYPE_ROBOT ||
this.currentModule?.moduleType === commonStorage.MODULE_TYPE_MECHANISM)
? JSON.stringify(this.getMethodsForOutsideFromWorkspace())
: '[]';
const componentsContent = JSON.stringify(this.getComponentsFromWorkspace());
return commonStorage.makeModuleContent(
this.currentModule, pythonCode, blocksContent, methodsContent, componentsContent);
Expand All @@ -229,22 +233,53 @@ export class Editor {
return components;
}

public getMethodsFromWorkspace(): commonStorage.Method[] {
public getMethodsForWithinFromWorkspace(): commonStorage.Method[] {
const methods: commonStorage.Method[] = [];
if (this.currentModule?.moduleType === commonStorage.MODULE_TYPE_ROBOT ||
this.currentModule?.moduleType === commonStorage.MODULE_TYPE_MECHANISM) {
// Get the class method definition blocks.
const methodDefBlocks = this.blocklyWorkspace.getBlocksByType(classMethodDef.BLOCK_NAME);
methodDefBlocks.forEach(methodDefBlock => {
const method = (methodDefBlock as classMethodDef.ClassMethodDefBlock).getMethod();
if (method) {
methods.push(method);
}
});
}

// Get the class method definition blocks.
const methodDefBlocks = this.blocklyWorkspace.getBlocksByType(classMethodDef.BLOCK_NAME);
methodDefBlocks.forEach(methodDefBlock => {
const method = (methodDefBlock as classMethodDef.ClassMethodDefBlock).getMethodForWithin();
if (method) {
methods.push(method);
}
});

return methods;
}

public getMethodsForOutsideFromWorkspace(): commonStorage.Method[] {
const methods: commonStorage.Method[] = [];

// Get the class method definition blocks.
const methodDefBlocks = this.blocklyWorkspace.getBlocksByType(classMethodDef.BLOCK_NAME);
methodDefBlocks.forEach(methodDefBlock => {
const method = (methodDefBlock as classMethodDef.ClassMethodDefBlock).getMethodForOutside();
if (method) {
methods.push(method);
}
});

return methods;
}

public getMethodNamesAlreadyOverriddenInWorkspace(): string[] {
const methodNamesAlreadyOverridden: string[] = [];

// Get the class method definition blocks.
const methodDefBlocks = this.blocklyWorkspace.getBlocksByType(classMethodDef.BLOCK_NAME);
methodDefBlocks.forEach(block => {
const methodDefBlock = block as classMethodDef.ClassMethodDefBlock;
// If the user cannot change the signature, it means the block defines a method that overrides a baseclass method.
// That's what we are looking for here.
if (!methodDefBlock.canChangeSignature()) {
methodNamesAlreadyOverridden.push(methodDefBlock.getMethodName());
}
});

return methodNamesAlreadyOverridden;
}

public async saveBlocks() {
const moduleContent = this.getModuleContent();
try {
Expand Down Expand Up @@ -273,7 +308,7 @@ export class Editor {
*/
public getMethodsFromRobot(): commonStorage.Method[] {
if (this.currentModule?.moduleType === commonStorage.MODULE_TYPE_ROBOT) {
return this.getMethodsFromWorkspace();
return this.getMethodsForWithinFromWorkspace();
}
if (!this.robotContent) {
throw new Error('getMethodsFromRobot: this.robotContent is null.');
Expand Down
4 changes: 2 additions & 2 deletions src/toolbox/hardware_category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import * as commonStorage from '../storage/common_storage';
import * as toolboxItems from './items';
import { getAllPossibleMechanisms } from './blocks_mechanisms';
import * as Component from '../blocks/mrc_component';
import { getInstanceComponentBlocks, getInstanceRobotBlocks } from '../blocks/mrc_call_python_function';
import { getInstanceComponentBlocks, addInstanceRobotBlocks } from '../blocks/mrc_call_python_function';
import { Editor } from '../editor/editor';

export function getHardwareCategory(currentModule: commonStorage.Module): toolboxItems.Category {
Expand Down Expand Up @@ -253,7 +253,7 @@ function getRobotMethodsBlocks(): toolboxItems.Category {
const editor = Editor.getEditorForBlocklyWorkspace(workspace);
if (editor) {
const methodsFromRobot = editor.getMethodsFromRobot();
contents.push(...getInstanceRobotBlocks(methodsFromRobot));
addInstanceRobotBlocks(methodsFromRobot, contents);
}
}

Expand Down
92 changes: 33 additions & 59 deletions src/toolbox/methods_category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { mechanism_class_blocks } from './mechanism_class_methods';
import { opmode_class_blocks } from './opmode_class_methods';
import { robot_class_blocks } from './robot_class_methods';
import { ClassMethodDefBlock } from '../blocks/mrc_class_method_def'
import { addInstanceWithinBlocks } from '../blocks/mrc_call_python_function'
import { Editor } from '../editor/editor';


const CUSTOM_CATEGORY_METHODS = 'METHODS';
Expand Down Expand Up @@ -56,32 +58,29 @@ export class MethodsCategory {
// Add blocks for defining any methods that can be defined in the current
// module. For example, if the current module is an OpMode, add blocks to
// define the methods declared in the OpMode class.
if (this.currentModule) {
// Collect the method names for mrc_class_method_def blocks that are
// already in the blockly workspace.
const methodNamesAlreadyUsed: string[] = [];
workspace.getBlocksByType('mrc_class_method_def', false).forEach((block) => {
const classMethodDefBlock = block as ClassMethodDefBlock;
if (!classMethodDefBlock.mrcCanChangeSignature) {
methodNamesAlreadyUsed.push(classMethodDefBlock.getFieldValue('NAME'));

const editor = Editor.getEditorForBlocklyWorkspace(workspace);
if (editor) {
// Collect the method names that are already overridden in the blockly workspace.
const methodNamesAlreadyOverridden = editor.getMethodNamesAlreadyOverriddenInWorkspace();

if (this.currentModule) {
if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_ROBOT) {
// Add the methods for a Robot.
this.addClassBlocksForCurrentModule(
'More Robot Methods', robot_class_blocks,
methodNamesAlreadyOverridden, contents);
} else if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_MECHANISM) {
// Add the methods for a Mechanism.
this.addClassBlocksForCurrentModule(
'More Mechanism Methods', mechanism_class_blocks,
methodNamesAlreadyOverridden, contents);
} else if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_OPMODE) {
// Add the methods for an OpMode.
this.addClassBlocksForCurrentModule(
'More OpMode Methods', opmode_class_blocks,
methodNamesAlreadyOverridden, contents);
}
});

if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_ROBOT) {
// Add the methods for a Robot.
this.addClassBlocksForCurrentModule(
'More Robot Methods', robot_class_blocks,
methodNamesAlreadyUsed, contents);
} else if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_MECHANISM) {
// Add the methods for a Mechanism.
this.addClassBlocksForCurrentModule(
'More Mechanism Methods', mechanism_class_blocks,
methodNamesAlreadyUsed, contents);
} else if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_OPMODE) {
// Add the methods for an OpMode.
this.addClassBlocksForCurrentModule(
'More OpMode Methods', opmode_class_blocks,
methodNamesAlreadyUsed, contents);
}
}

Expand All @@ -103,39 +102,14 @@ export class MethodsCategory {
returnType: 'None',
params: [],
},
});

// For each mrc_class_method_def block in the blockly workspace, check if it
// can be called from within the class, and if so, add a
// mrc_call_python_function block.
workspace.getBlocksByType('mrc_class_method_def', false).forEach((block) => {
const classMethodDefBlock = block as ClassMethodDefBlock;
if (classMethodDefBlock.mrcCanBeCalledWithinClass) {
const callPythonFunctionBlock: toolboxItems.Block = {
kind: 'block',
type: 'mrc_call_python_function',
extraState: {
functionKind: 'instance_within',
returnType: classMethodDefBlock.mrcReturnType,
args: [],
importModule: '',
},
fields: {
FUNC: classMethodDefBlock.getFieldValue('NAME'),
},
};
classMethodDefBlock.mrcParameters.forEach((param) => {
if (callPythonFunctionBlock.extraState) {
callPythonFunctionBlock.extraState.args.push(
{
name: param.name,
type: param.type ?? '',
});
}
});
contents.push(callPythonFunctionBlock);
}
});
);

// Get blocks for calling methods defined in the current workspace.
if (editor) {
const methodsFromWorkspace = editor.getMethodsForWithinFromWorkspace();
addInstanceWithinBlocks(methodsFromWorkspace, contents);
}

const toolboxInfo = {
contents: contents,
Expand All @@ -146,12 +120,12 @@ export class MethodsCategory {

private addClassBlocksForCurrentModule(
label: string, class_blocks: toolboxItems.Block[],
methodNamesAlreadyUsed: string[], contents: toolboxItems.ContentsType[]) {
methodNamesAlreadyOverridden: string[], contents: toolboxItems.ContentsType[]) {
let labelAdded = false;
class_blocks.forEach((blockInfo) => {
if (blockInfo.fields) {
const methodName = blockInfo.fields['NAME'];
if (!methodNamesAlreadyUsed.includes(methodName)) {
if (!methodNamesAlreadyOverridden.includes(methodName)) {
if (!labelAdded) {
contents.push(
{
Expand Down