Skip to content

Commit 7df5010

Browse files
authored
Make fire event blocks update if the event name or parameters are changed. (#169)
* In common_storage, added Event type. In editor, added getEventsFromWorkspace function. In mrc_call_python_funcction: Added addFireEventBlocks and createFireEventBlock functions. Replace classMethodDefBlockId and componentBlockId with otherBlockId. Replace mrcClassMethodDefBlockId and componentBlockId with otherBlockId. Make renameMethodCallers and mutateMethodCallers work for fire event blocks. Added function getComponentsFromRobot. Removed mrcComponents. Make fire event block use a field called 'EVENT' instead of 'FUNC'. In mrc_component, added getComponent function. In mrc_class_method_def: Remove mrcMethod. Added function getMethod. Added mrcFuncName which is set during python code generation. Pass commonStorage.Method (instead of ClassMethodDefExtraState) to mutateMethodCallers. In mrc_event: Added mrcNameFieldValidator and getEvent functions. Call mutateMethodCallers and renameMethodCallers user changes parameters or name. In mrc_mechanism_component_holder, added getEvents function. In event_category, call editor.getEventsFromWorkspace and addFireEventBlocks instead of using the mrc_event and mrc_call_python_function implementation details here. * Fixed typescript problems.
1 parent b5ab654 commit 7df5010

File tree

10 files changed

+348
-219
lines changed

10 files changed

+348
-219
lines changed

src/blocks/mrc_call_python_function.ts

Lines changed: 187 additions & 124 deletions
Large diffs are not rendered by default.

src/blocks/mrc_class_method_def.ts

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const BLOCK_NAME = 'mrc_class_method_def';
3838

3939
const FIELD_METHOD_NAME = 'NAME';
4040

41-
export type Parameter = {
41+
type Parameter = {
4242
name: string,
4343
type?: string,
4444
};
@@ -51,12 +51,12 @@ interface ClassMethodDefMixin extends ClassMethodDefMixinType {
5151
mrcReturnType: string,
5252
mrcParameters: Parameter[],
5353
mrcPythonMethodName: string,
54-
mrcMethod: commonStorage.Method | null,
54+
mrcFuncName: string | null,
5555
}
5656
type ClassMethodDefMixinType = typeof CLASS_METHOD_DEF;
5757

5858
/** Extra state for serialising call_python_* blocks. */
59-
export type ClassMethodDefExtraState = {
59+
type ClassMethodDefExtraState = {
6060
/**
6161
* Can change name and parameters and return type
6262
*/
@@ -137,18 +137,16 @@ const CLASS_METHOD_DEF = {
137137
this.mrcCanBeCalledWithinClass = extraState.canBeCalledWithinClass;
138138
this.mrcCanBeCalledOutsideClass = extraState.canBeCalledOutsideClass;
139139
this.mrcPythonMethodName = extraState.pythonMethodName ? extraState.pythonMethodName : '';
140+
this.mrcFuncName = null; // Set during python code generation.
140141
this.mrcReturnType = extraState.returnType;
141142
this.mrcParameters = [];
142-
this.mrcMethod = null;
143-
144143
extraState.params.forEach((param) => {
145144
this.mrcParameters.push({
146145
'name': param.name,
147146
'type': param.type,
148147
});
149148
});
150149
this.updateBlock_();
151-
mutateMethodCallers(this.workspace, this.getFieldValue(FIELD_METHOD_NAME), this.saveExtraState());
152150
},
153151
/**
154152
* Update the block to reflect the newly loaded extra state.
@@ -194,7 +192,12 @@ const CLASS_METHOD_DEF = {
194192
paramBlock.nextConnection && paramBlock.nextConnection.targetBlock();
195193
}
196194
this.mrcUpdateParams();
197-
mutateMethodCallers(this.workspace, this.getFieldValue(FIELD_METHOD_NAME), this.saveExtraState());
195+
if (this.mrcCanBeCalledWithinClass) {
196+
const methodForWithin = this.getMethodForWithin();
197+
if (methodForWithin) {
198+
mutateMethodCallers(this.workspace, this.id, methodForWithin);
199+
}
200+
}
198201
},
199202
decompose: function (this: ClassMethodDefBlock, workspace: Blockly.Workspace) {
200203
// This is a special sub-block that only gets created in the mutator UI.
@@ -259,15 +262,43 @@ const CLASS_METHOD_DEF = {
259262
const oldName = nameField.getValue();
260263
if (oldName && oldName !== name && oldName !== legalName) {
261264
// Rename any callers.
262-
renameMethodCallers(this.workspace, oldName, legalName);
265+
renameMethodCallers(this.workspace, this.id, legalName);
263266
}
264267
return legalName;
265268
},
269+
getMethod: function (this: ClassMethodDefBlock): commonStorage.Method | null {
270+
const method: commonStorage.Method = {
271+
blockId: this.id,
272+
visibleName: this.getFieldValue(FIELD_METHOD_NAME),
273+
pythonName: this.mrcFuncName ? this.mrcFuncName : '',
274+
returnType: this.mrcReturnType,
275+
args: [{
276+
name: 'self',
277+
type: '',
278+
}],
279+
};
280+
if (!method.pythonName) {
281+
method.pythonName = method.visibleName;
282+
}
283+
this.mrcParameters.forEach(param => {
284+
method.args.push({
285+
name: param.name,
286+
type: param.type ? param.type : '',
287+
});
288+
});
289+
return method;
290+
},
266291
getMethodForWithin: function (this: ClassMethodDefBlock): commonStorage.Method | null {
267-
return this.mrcCanBeCalledWithinClass ? this.mrcMethod : null;
292+
if (this.mrcCanBeCalledWithinClass) {
293+
return this.getMethod();
294+
}
295+
return null;
268296
},
269297
getMethodForOutside: function (this: ClassMethodDefBlock): commonStorage.Method | null {
270-
return this.mrcCanBeCalledOutsideClass ? this.mrcMethod : null;
298+
if (this.mrcCanBeCalledOutsideClass) {
299+
return this.getMethod();
300+
}
301+
return null;
271302
},
272303
canChangeSignature: function (this: ClassMethodDefBlock): boolean {
273304
return this.mrcCanChangeSignature;
@@ -342,6 +373,7 @@ export const pythonFromBlock = function (
342373
) {
343374
const blocklyName = block.mrcPythonMethodName ? block.mrcPythonMethodName : block.getFieldValue(FIELD_METHOD_NAME);
344375

376+
// Call generator.getProcedureName so our function name is not a reserved word such as "while".
345377
const funcName = generator.getProcedureName(blocklyName);
346378

347379
let xfix1 = '';
@@ -380,7 +412,8 @@ export const pythonFromBlock = function (
380412
generator.defineClassVariables() + branch;
381413
}
382414
else if (generator.inBaseClassMethod(blocklyName)){
383-
// Special case for update, to also call the update method of the base class
415+
// Special case for methods inherited from the based class: generate the
416+
// call to the method in the base class.
384417
branch = generator.INDENT + 'super().' + blocklyName + '()\n' + branch;
385418
}
386419
if (returnValue) {
@@ -416,25 +449,9 @@ export const pythonFromBlock = function (
416449
code = generator.scrub_(block, code);
417450
generator.addClassMethodDefinition(funcName, code);
418451

419-
if (block.mrcCanBeCalledWithinClass || block.mrcCanBeCalledOutsideClass) {
420-
// Update the mrcMethod.
421-
block.mrcMethod = {
422-
blockId: block.id,
423-
visibleName: block.getFieldValue(FIELD_METHOD_NAME),
424-
pythonName: funcName,
425-
returnType: block.mrcReturnType,
426-
args: [{
427-
name: 'self',
428-
type: '',
429-
}],
430-
};
431-
block.mrcParameters.forEach(param => {
432-
block.mrcMethod!.args.push({
433-
name: param.name,
434-
type: param.type ? param.type : '',
435-
});
436-
});
437-
}
452+
// Save the name of the function we just generated so we can use it to create the commonStorage.Method.
453+
// in the getMethod function.
454+
block.mrcFuncName = funcName;
438455

439456
return '';
440457
}

src/blocks/mrc_component.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ import { MRC_STYLE_COMPONENTS } from '../themes/styles'
2626
import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
2727
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
2828
import { getAllowedTypesForSetCheck, getClassData, getModuleData, getSubclassNames } from './utils/python';
29-
import * as ToolboxItems from '../toolbox/items';
30-
import * as CommonStorage from '../storage/common_storage';
29+
import * as toolboxItems from '../toolbox/items';
30+
import * as commonStorage from '../storage/common_storage';
3131
import { createPortShadow } from './mrc_port';
3232
import { createNumberShadowValue } from './utils/value';
3333
import { ClassData, FunctionData } from './utils/python_json_types';
@@ -52,7 +52,7 @@ type ComponentExtraState = {
5252
params?: ConstructorArg[],
5353
}
5454

55-
type ComponentBlock = Blockly.Block & ComponentMixin;
55+
export type ComponentBlock = Blockly.Block & ComponentMixin;
5656
interface ComponentMixin extends ComponentMixinType {
5757
mrcArgs: ConstructorArg[],
5858
hideParams: boolean,
@@ -136,7 +136,16 @@ const COMPONENT = {
136136
}
137137
}
138138
}
139-
}
139+
},
140+
getComponent: function (this: ComponentBlock): commonStorage.Component | null {
141+
const componentName = this.getFieldValue(FIELD_NAME);
142+
const componentType = this.getFieldValue(FIELD_TYPE);
143+
return {
144+
blockId: this.id,
145+
name: componentName,
146+
className: componentType,
147+
};
148+
},
140149
}
141150

142151
export const setup = function () {
@@ -177,8 +186,8 @@ export const pythonFromBlock = function (
177186
return code;
178187
}
179188

180-
export function getAllPossibleComponents(hideParams: boolean): ToolboxItems.ContentsType[] {
181-
const contents: ToolboxItems.ContentsType[] = [];
189+
export function getAllPossibleComponents(hideParams: boolean): toolboxItems.ContentsType[] {
190+
const contents: toolboxItems.ContentsType[] = [];
182191
// Iterate through all the components subclasses and add definition blocks.
183192
const componentTypes = getSubclassNames('component.Component');
184193

@@ -190,7 +199,7 @@ export function getAllPossibleComponents(hideParams: boolean): ToolboxItems.Cont
190199

191200
const componentName = (
192201
'my_' +
193-
CommonStorage.pascalCaseToSnakeCase(
202+
commonStorage.pascalCaseToSnakeCase(
194203
componentType.substring(componentType.lastIndexOf('.') + 1)));
195204

196205
classData.staticMethods.forEach(staticFunctionData => {
@@ -204,7 +213,7 @@ export function getAllPossibleComponents(hideParams: boolean): ToolboxItems.Cont
204213
}
205214

206215
function createComponentBlock(
207-
componentName: string, classData: ClassData, staticFunctionData: FunctionData, hideParams: boolean): ToolboxItems.Block {
216+
componentName: string, classData: ClassData, staticFunctionData: FunctionData, hideParams: boolean): toolboxItems.Block {
208217
const extraState: ComponentExtraState = {
209218
importModule: classData.moduleName,
210219
staticFunctionName: staticFunctionData.functionName,
@@ -232,7 +241,7 @@ function createComponentBlock(
232241
}
233242
}
234243
}
235-
return new ToolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null);
244+
return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null);
236245
}
237246

238247
function getPortTypeForArgument(argName: string): string | null {

src/blocks/mrc_event.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,22 @@
2020
* @author [email protected] (Alan Smith)
2121
*/
2222
import * as Blockly from 'blockly';
23-
import { Order } from 'blockly/python';
2423

2524
import { MRC_STYLE_EVENTS } from '../themes/styles'
2625
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
2726
import { MUTATOR_BLOCK_NAME, PARAM_CONTAINER_BLOCK_NAME, MethodMutatorArgBlock } from './mrc_param_container'
2827
import * as ChangeFramework from './utils/change_framework';
2928
import { BLOCK_NAME as MRC_MECHANISM_COMPONENT_HOLDER } from './mrc_mechanism_component_holder';
3029
import * as toolboxItems from '../toolbox/items';
30+
import * as commonStorage from '../storage/common_storage';
31+
import { renameMethodCallers, mutateMethodCallers } from './mrc_call_python_function'
3132

3233
export const BLOCK_NAME = 'mrc_event';
3334
export const OUTPUT_NAME = 'mrc_event';
3435

3536
const FIELD_EVENT_NAME = 'NAME';
3637

37-
export type Parameter = {
38+
type Parameter = {
3839
name: string,
3940
type?: string,
4041
};
@@ -112,7 +113,7 @@ const EVENT = {
112113
const nameField = new Blockly.FieldTextInput(name);
113114
input.insertFieldAt(0, nameField, FIELD_EVENT_NAME);
114115
this.setMutator(new Blockly.icons.MutatorIcon([MUTATOR_BLOCK_NAME], this));
115-
// nameField.setValidator(this.mrcNameFieldValidator.bind(this, nameField));
116+
nameField.setValidator(this.mrcNameFieldValidator.bind(this, nameField));
116117

117118
this.mrcUpdateParams();
118119
},
@@ -135,7 +136,7 @@ const EVENT = {
135136
paramBlock.nextConnection && paramBlock.nextConnection.targetBlock();
136137
}
137138
this.mrcUpdateParams();
138-
//mutateMethodCallers(this.workspace, this.getFieldValue(FIELD_EVENT_NAME), this.saveExtraState());
139+
mutateMethodCallers(this.workspace, this.id, this.getEvent());
139140
},
140141
decompose: function (this: EventBlock, workspace: Blockly.Workspace) {
141142
// This is a special sub-block that only gets created in the mutator UI.
@@ -180,6 +181,18 @@ const EVENT = {
180181
input.removeField(fieldName);
181182
});
182183
},
184+
mrcNameFieldValidator(this: EventBlock, nameField: Blockly.FieldTextInput, name: string): string {
185+
// Strip leading and trailing whitespace.
186+
name = name.trim();
187+
188+
const legalName = name;
189+
const oldName = nameField.getValue();
190+
if (oldName && oldName !== name && oldName !== legalName) {
191+
// Rename any callers.
192+
renameMethodCallers(this.workspace, this.id, legalName);
193+
}
194+
return legalName;
195+
},
183196
onBlockChanged(block: Blockly.BlockSvg, blockEvent: Blockly.Events.BlockBase): void {
184197
const blockBlock = block as Blockly.Block;
185198

@@ -193,9 +206,23 @@ const EVENT = {
193206
}
194207
// If we end up here it shouldn't be allowed
195208
block.unplug(true);
196-
blockBlock.setWarningText('Events can only go in the events block');
209+
blockBlock.setWarningText('Events can only go in the events section of the robot or mechanism');
197210
}
198211
},
212+
getEvent: function (this: EventBlock): commonStorage.Event {
213+
const event: commonStorage.Event = {
214+
blockId: this.id,
215+
name: this.getFieldValue(FIELD_EVENT_NAME),
216+
args: [],
217+
};
218+
this.mrcParameters.forEach(param => {
219+
event.args.push({
220+
name: param.name,
221+
type: param.type ? param.type : '',
222+
});
223+
});
224+
return event;
225+
},
199226
}
200227

201228
export const setup = function () {
@@ -204,9 +231,8 @@ export const setup = function () {
204231

205232
export const pythonFromBlock = function (
206233
block: EventBlock,
207-
generator: ExtendedPythonGenerator,
208-
) {
209-
//TODO (Alan): What should this do here??
234+
generator: ExtendedPythonGenerator) {
235+
// TODO (Alan): What should this do here??
210236
return '';
211237
}
212238

src/blocks/mrc_mechanism_component_holder.ts

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ import * as commonStorage from '../storage/common_storage';
2929
import { OUTPUT_NAME as MECHANISM_OUTPUT } from './mrc_mechanism';
3030
import { BLOCK_NAME as MRC_MECHANISM_NAME } from './mrc_mechanism';
3131
import { BLOCK_NAME as MRC_COMPONENT_NAME } from './mrc_component';
32-
import * as Component from './mrc_component';
3332
import { OUTPUT_NAME as COMPONENT_OUTPUT } from './mrc_component';
33+
import { ComponentBlock } from './mrc_component';
3434
import { BLOCK_NAME as MRC_EVENT_NAME } from './mrc_event';
3535
import { OUTPUT_NAME as EVENT_OUTPUT } from './mrc_event';
36+
import { EventBlock } from './mrc_event';
3637

3738
export const BLOCK_NAME = 'mrc_mechanism_component_holder';
3839

@@ -139,15 +140,9 @@ const MECHANISM_COMPONENT_HOLDER = {
139140
let componentBlock = componentsInput.connection.targetBlock();
140141
while (componentBlock) {
141142
if (componentBlock.type === MRC_COMPONENT_NAME) {
142-
const componentName = componentBlock.getFieldValue(Component.FIELD_NAME);
143-
const componentType = componentBlock.getFieldValue(Component.FIELD_TYPE);
144-
145-
if (componentName && componentType) {
146-
components.push({
147-
blockId: componentBlock.id,
148-
name: componentName,
149-
className: componentType,
150-
});
143+
const component = (componentBlock as ComponentBlock).getComponent();
144+
if (component) {
145+
components.push(component);
151146
}
152147
}
153148
// Move to the next block in the chain
@@ -157,6 +152,28 @@ const MECHANISM_COMPONENT_HOLDER = {
157152

158153
return components;
159154
},
155+
getEvents: function (this: MechanismComponentHolderBlock): commonStorage.Event[] {
156+
const events: commonStorage.Event[] = []
157+
158+
// Get event blocks from the EVENTS input
159+
const eventsInput = this.getInput('EVENTS');
160+
if (eventsInput && eventsInput.connection) {
161+
// Walk through all connected event blocks.
162+
let eventBlock = eventsInput.connection.targetBlock();
163+
while (eventBlock) {
164+
if (eventBlock.type === MRC_EVENT_NAME) {
165+
const event = (eventBlock as EventBlock).getEvent();
166+
if (event) {
167+
events.push(event);
168+
}
169+
}
170+
// Move to the next block in the chain
171+
eventBlock = eventBlock.getNextBlock();
172+
}
173+
}
174+
175+
return events;
176+
},
160177
}
161178

162179
let toolboxUpdateTimeout: NodeJS.Timeout | null = null;

0 commit comments

Comments
 (0)