Skip to content

Commit 1374bde

Browse files
authored
Update the Robot -> Methods category on the toolbox when the user is editing an opmode. (#157)
1 parent 6cc482b commit 1374bde

File tree

7 files changed

+225
-227
lines changed

7 files changed

+225
-227
lines changed

src/blocks/mrc_call_python_function.ts

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export enum FunctionKind {
4848
INSTANCE = 'instance',
4949
INSTANCE_WITHIN = 'instance_within',
5050
INSTANCE_COMPONENT = 'instance_component',
51+
INSTANCE_ROBOT = 'instance_robot',
5152
EVENT = 'event',
5253
}
5354

@@ -220,7 +221,7 @@ function createInstanceMethodBlock(
220221

221222
export function getInstanceComponentBlocks(
222223
componentType: string,
223-
componentName: string) {
224+
componentName: string): ToolboxItems.ContentsType[] {
224225
const contents: ToolboxItems.ContentsType[] = [];
225226

226227
const classData = getClassData(componentType);
@@ -267,7 +268,7 @@ function createInstanceComponentBlock(
267268
// We don't include the arg or input for self.
268269
for (let i = 1; i < functionData.args.length; i++) {
269270
const argData = functionData.args[i];
270-
let argName = argData.name;
271+
const argName = argData.name;
271272
extraState.args.push({
272273
'name': argName,
273274
'type': argData.type,
@@ -289,6 +290,51 @@ function createInstanceComponentBlock(
289290
return block;
290291
}
291292

293+
export function getInstanceRobotBlocks(methods: CommonStorage.Method[]): ToolboxItems.ContentsType[] {
294+
const contents: ToolboxItems.ContentsType[] = [];
295+
296+
for (const method of methods) {
297+
const block = createInstanceRobotBlock(method);
298+
contents.push(block);
299+
}
300+
301+
return contents;
302+
}
303+
304+
function createInstanceRobotBlock(method: CommonStorage.Method): ToolboxItems.Block {
305+
const extraState: CallPythonFunctionExtraState = {
306+
functionKind: FunctionKind.INSTANCE_ROBOT,
307+
returnType: method.returnType,
308+
actualFunctionName: method.pythonName,
309+
args: [],
310+
};
311+
const fields: {[key: string]: any} = {};
312+
fields[FIELD_FUNCTION_NAME] = method.visibleName;
313+
const inputs: {[key: string]: any} = {};
314+
// We don't include the arg or input for the self argument.
315+
for (let i = 1; i < method.args.length; i++) {
316+
const arg = method.args[i];
317+
extraState.args.push({
318+
'name': arg.name,
319+
'type': arg.type,
320+
});
321+
// Check if we should plug a variable getter block into the argument input socket.
322+
const input = Value.valueForFunctionArgInput(arg.type, '');
323+
if (input) {
324+
// Because we skipped the self argument, use i - 1 when filling the inputs array.
325+
inputs['ARG' + (i - 1)] = input;
326+
}
327+
}
328+
let block = new ToolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null);
329+
if (method.returnType && method.returnType != 'None') {
330+
const varName = Variable.varNameForType(method.returnType);
331+
if (varName) {
332+
block = Variable.createVariableSetterBlock(varName, block);
333+
}
334+
}
335+
return block;
336+
}
337+
292338
//..............................................................................
293339

294340
export type CallPythonFunctionBlock = Blockly.Block & CallPythonFunctionMixin;
@@ -401,6 +447,11 @@ const CALL_PYTHON_FUNCTION = {
401447
' on the component named ' + this.getComponentName() + '.';
402448
break;
403449
}
450+
case FunctionKind.INSTANCE_ROBOT: {
451+
const functionName = this.getFieldValue(FIELD_FUNCTION_NAME);
452+
tooltip = 'Calls the robot method ' + functionName + '.';
453+
break;
454+
}
404455
default:
405456
throw new Error('mrcFunctionKind has unexpected value: ' + this.mrcFunctionKind)
406457
}
@@ -568,6 +619,14 @@ const CALL_PYTHON_FUNCTION = {
568619
.appendField(createFieldNonEditableText(''), FIELD_FUNCTION_NAME);
569620
break;
570621
}
622+
case FunctionKind.INSTANCE_ROBOT: {
623+
this.appendDummyInput('TITLE')
624+
.appendField('call')
625+
.appendField(createFieldNonEditableText('robot'))
626+
.appendField('.')
627+
.appendField(createFieldNonEditableText(''), FIELD_FUNCTION_NAME);
628+
break;
629+
}
571630
default:
572631
throw new Error('mrcFunctionKind has unexpected value: ' + this.mrcFunctionKind)
573632
}
@@ -707,6 +766,13 @@ export const pythonFromBlock = function(
707766
code += componentName + '.' + functionName;
708767
break;
709768
}
769+
case FunctionKind.INSTANCE_ROBOT: {
770+
const functionName = callPythonFunctionBlock.mrcActualFunctionName
771+
? callPythonFunctionBlock.mrcActualFunctionName
772+
: block.getFieldValue(FIELD_FUNCTION_NAME);
773+
code = 'self.robot.' + functionName;
774+
break;
775+
}
710776
default:
711777
throw new Error('mrcFunctionKind has unexpected value: ' + callPythonFunctionBlock.mrcFunctionKind)
712778
}

src/blocks/mrc_class_method_def.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
/**
19-
* @fileoverview Blocks for class method defintion
19+
* @fileoverview Blocks for class method definition
2020
* @author [email protected] (Alan Smith)
2121
*/
2222
import * as Blockly from 'blockly';
@@ -25,6 +25,7 @@ import { createFieldNonEditableText } from '../fields/FieldNonEditableText'
2525
import { createFieldFlydown } from '../fields/field_flydown';
2626
import { Order } from 'blockly/python';
2727
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
28+
import * as commonStorage from '../storage/common_storage';
2829
import { renameMethodCallers, mutateMethodCallers } from './mrc_call_python_function'
2930
import { findConnectedBlocksOfType } from './utils/find_connected_blocks';
3031
import { BLOCK_NAME as MRC_GET_PARAMETER_BLOCK_NAME } from './mrc_get_parameter';
@@ -45,6 +46,7 @@ interface ClassMethodDefMixin extends ClassMethodDefMixinType {
4546
mrcReturnType: string,
4647
mrcParameters: Parameter[],
4748
mrcPythonMethodName: string,
49+
mrcMethod: commonStorage.Method | null,
4850
}
4951
type ClassMethodDefMixinType = typeof CLASS_METHOD_DEF;
5052

@@ -131,6 +133,7 @@ const CLASS_METHOD_DEF = {
131133
this.mrcPythonMethodName = extraState.pythonMethodName ? extraState.pythonMethodName : '';
132134
this.mrcReturnType = extraState.returnType;
133135
this.mrcParameters = [];
136+
this.mrcMethod = null;
134137

135138
extraState.params.forEach((param) => {
136139
this.mrcParameters.push({
@@ -254,6 +257,9 @@ const CLASS_METHOD_DEF = {
254257
}
255258
return legalName;
256259
},
260+
getMethod: function (this: ClassMethodDefBlock): commonStorage.Method | null {
261+
return this.mrcMethod;
262+
}
257263
};
258264

259265
/**
@@ -395,5 +401,24 @@ export const pythonFromBlock = function (
395401
code = generator.scrub_(block, code);
396402
generator.addClassMethodDefinition(funcName, code);
397403

404+
if (block.mrcCanBeCalledOutsideClass) {
405+
// Update the mrcMethod.
406+
block.mrcMethod = {
407+
visibleName: block.getFieldValue('NAME'),
408+
pythonName: funcName,
409+
returnType: block.mrcReturnType,
410+
args: [{
411+
name: 'self',
412+
type: '',
413+
}],
414+
};
415+
block.mrcParameters.forEach(param => {
416+
block.mrcMethod!.args.push({
417+
name: param.name,
418+
type: param.type ? param.type : '',
419+
});
420+
});
421+
}
422+
398423
return '';
399424
}

src/editor/editor.ts

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { extendedPythonGenerator } from './extended_python_generator';
2525
import { GeneratorContext } from './generator_context';
2626
import * as commonStorage from '../storage/common_storage';
2727
import * as mechanismComponentHolder from '../blocks/mrc_mechanism_component_holder';
28+
import * as classMethodDef from '../blocks/mrc_class_method_def';
2829
//import { testAllBlocksInToolbox } from '../toolbox/toolbox_tests';
2930
import { MethodsCategory } from '../toolbox/methods_category';
3031
import { EventsCategory } from '../toolbox/event_category';
@@ -71,38 +72,15 @@ export class Editor {
7172
return;
7273
}
7374

74-
// TODO(lizlooney): As blocks are loaded, determine whether any blocks
75-
// are accessing variable or calling functions thar are defined in another
76-
// blocks file (like the Robot) and check whether the variable or function
77-
// definition has changed. This might happen if the user defines a variable
78-
// or function in the Robot, uses the variable or function in the
79-
// OpMode, and then removes or changes the variable or function in the
80-
// Robot.
75+
// TODO(lizlooney): Look at blocks with type 'mrc_call_python_function' that
76+
// are calling methods defined in the Robot and check that the Robot method
77+
// still exists and hasn't been changed. If it has changed, update the block
78+
// if possible or put a visible warning on it.
8179

82-
// TODO(lizlooney): We will need a way to identify which variable or
83-
// function, other than by the variable name or function name, because the
84-
// user might change the name. This will take some thought and I should
85-
// write up a design doc and discuss it with others to make sure we have a
86-
// good solution.
87-
88-
// TODO(lizlooney): Look at blocks with type 'mrc_get_python_variable' or
89-
// 'mrc_set_python_variable', and where block.mrcExportedVariable === true.
90-
// Look at block.mrcImportModule and get the exported blocks for that module.
91-
// It could be from the Robot (or a Mechanism?) and we already have the Robot content.
92-
// Check whether block.mrcActualVariableName matches any exportedBlock's
93-
// extraState.actualVariableName. If there is no match, put a warning on the
94-
// block.
95-
96-
// TODO(lizlooney): Look at blocks with type 'mrc_call_python_function' and
97-
// where block.mrcExportedFunction === true.
98-
// Look at block.mrcImportModule and get the exported blocks for that module.
99-
// It could be from the Robot (or a Mechanism?) and we already have the Robot content.
100-
// Check whether block.mrcActualFunctionName matches any exportedBlock's
101-
// extraState.actualFunctionName. If there is no match, put a warning on the block.
102-
// If there is a match, check whether
103-
// block.mrcArgs.length === exportedBlock.extraState.args.length and
104-
// block.mrcArgs[i].name === exportedBlock.extraState.args[i].name for all args.
105-
// If there is any differences, put a warning on the block.
80+
// TODO(lizlooney): Look at blocks with type 'mrc_call_python_function' that
81+
// are calling methods on components defined in the Robot and check that the
82+
// component and the method still exists and hasn't been changed. If it has
83+
// changed, update the block if possible or put a visible warning on it.
10684
}
10785

10886
private onChangeAfterLoading(event: Blockly.Events.Abstract) {
@@ -221,12 +199,12 @@ export class Editor {
221199
}
222200
const pythonCode = extendedPythonGenerator.mrcWorkspaceToCode(
223201
this.blocklyWorkspace, this.generatorContext);
224-
const exportedBlocks = JSON.stringify(this.generatorContext.getExportedBlocks());
225202
const blocksContent = JSON.stringify(
226203
Blockly.serialization.workspaces.save(this.blocklyWorkspace));
204+
const methodsContent = JSON.stringify(this.getMethodsFromWorkspace());
227205
const componentsContent = JSON.stringify(this.getComponentsFromWorkspace());
228206
return commonStorage.makeModuleContent(
229-
this.currentModule, pythonCode, blocksContent, exportedBlocks, componentsContent);
207+
this.currentModule, pythonCode, blocksContent, methodsContent, componentsContent);
230208
}
231209

232210
public getComponentsFromWorkspace(): commonStorage.Component[] {
@@ -246,6 +224,22 @@ export class Editor {
246224
return components;
247225
}
248226

227+
public getMethodsFromWorkspace(): commonStorage.Method[] {
228+
const methods: commonStorage.Method[] = [];
229+
if (this.currentModule?.moduleType === commonStorage.MODULE_TYPE_ROBOT ||
230+
this.currentModule?.moduleType === commonStorage.MODULE_TYPE_MECHANISM) {
231+
// Get the class method definition blocks.
232+
const methodDefBlocks = this.blocklyWorkspace.getBlocksByType(classMethodDef.BLOCK_NAME);
233+
methodDefBlocks.forEach(methodDefBlock => {
234+
const method = (methodDefBlock as classMethodDef.ClassMethodDefBlock).getMethod();
235+
if (method) {
236+
methods.push(method);
237+
}
238+
});
239+
}
240+
return methods;
241+
}
242+
249243
public async saveBlocks() {
250244
const moduleContent = this.getModuleContent();
251245
try {
@@ -260,8 +254,6 @@ export class Editor {
260254
* Returns the components defined in the robot.
261255
*/
262256
public getComponentsFromRobot(): commonStorage.Component[] {
263-
let components: commonStorage.Component[];
264-
265257
if (this.currentModule?.moduleType === commonStorage.MODULE_TYPE_ROBOT) {
266258
return this.getComponentsFromWorkspace();
267259
}
@@ -271,6 +263,19 @@ export class Editor {
271263
return commonStorage.extractComponents(this.robotContent);
272264
}
273265

266+
/**
267+
* Returns the methods defined in the robot.
268+
*/
269+
public getMethodsFromRobot(): commonStorage.Method[] {
270+
if (this.currentModule?.moduleType === commonStorage.MODULE_TYPE_ROBOT) {
271+
return this.getMethodsFromWorkspace();
272+
}
273+
if (!this.robotContent) {
274+
throw new Error('getMethodsFromRobot: this.robotContent is null.');
275+
}
276+
return commonStorage.extractMethods(this.robotContent);
277+
}
278+
274279
public static getEditorForBlocklyWorkspace(workspace: Blockly.Workspace): Editor | null {
275280
if (workspace.id in Editor.workspaceIdToEditor) {
276281
return Editor.workspaceIdToEditor[workspace.id];

0 commit comments

Comments
 (0)