Skip to content

Commit a23ce38

Browse files
authored
Add support for mechanism event handlers (#199)
* Make contents optional in Category class because custom categories don't have contents. * Convert getCategory (in event_category.ts and methods_category.ts) from arrow functions to function declarations. See https://google.github.io/styleguide/tsguide.html#function-declarations In mrc_call_python_function.ts: Removed unnecessary semicolon. Changed "...method on a mechanism..." to "...method in a mechanism...". * Renamed getRobotEventsCategory to getRobotEventHandlersCategory. Renamed RobotEventsCategory to RobotEventHandlersCategory. In editor.ts, updated code that parses the module content text for the robot and mechanisms to use ? : instead of if/else. * Updated mrc_event_handler.ts to support mechanism events. * Moved CUSTOM_CATEGORY_EVENT_HANDLERS_ROBOT, getRobotEventHandlersCategory, and RobotEventHandlersCategory from hardware_category.ts to event_handlers_category.ts. * Modified event_handlers_category.ts to work for robot events and mechanism events. Use the block id of the mrc_event blocks to identify which event handlers are already on the workspace. * Update event handler blocks if the user renames a mechanism. * Update python generator for registerering event handlers. * Create functions for registering custom categories. Changed custom values to begin with MRC_.
1 parent 1113eab commit a23ce38

12 files changed

+439
-134
lines changed

src/blocks/mrc_call_python_function.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ type CallPythonFunctionExtraState = {
141141
* Specified only if the function kind is INSTANCE_MECHANISM.
142142
*/
143143
mechanismBlockId?: string,
144-
};
144+
}
145145

146146
const CALL_PYTHON_FUNCTION = {
147147
/**
@@ -697,7 +697,7 @@ const CALL_PYTHON_FUNCTION = {
697697
}
698698
}
699699
if (!foundMechanism) {
700-
warnings.push('This block calls a method on a mechanism that no longer exists.');
700+
warnings.push('This block calls a method in a mechanism that no longer exists.');
701701
}
702702
}
703703
}

src/blocks/mrc_event_handler.ts

Lines changed: 185 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { createFieldFlydown } from '../fields/field_flydown';
2929
import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
3030
import { MRC_STYLE_EVENT_HANDLER } from '../themes/styles';
3131
import * as toolboxItems from '../toolbox/items';
32+
import * as storageModule from '../storage/module';
3233
import * as storageModuleContent from '../storage/module_content';
3334

3435
export const BLOCK_NAME = 'mrc_event_handler';
@@ -52,22 +53,26 @@ const WARNING_ID_EVENT_CHANGED = 'event changed';
5253
export type EventHandlerBlock = Blockly.Block & EventHandlerMixin & Blockly.BlockSvg;
5354

5455
interface EventHandlerMixin extends EventHandlerMixinType {
55-
mrcPathOfSender: string;
5656
mrcSenderType: SenderType;
5757
mrcParameters: Parameter[];
5858
mrcOtherBlockId: string,
59+
mrcMechanismBlockId: string,
5960
}
6061

6162
type EventHandlerMixinType = typeof EVENT_HANDLER;
6263

6364
/** Extra state for serialising event handler blocks. */
6465
export interface EventHandlerExtraState {
65-
pathOfSender: string;
6666
senderType: SenderType;
6767
/** The parameters of the event handler. */
6868
params: Parameter[];
6969
/** The id of the mrc_event block that defines the event. */
7070
otherBlockId: string,
71+
/**
72+
* The id of the mrc_mechanism block that adds the mechanism to the robot.
73+
* Specified only if the sender type is MECHANISM.
74+
*/
75+
mechanismBlockId?: string,
7176
}
7277

7378
const EVENT_HANDLER = {
@@ -77,8 +82,8 @@ const EVENT_HANDLER = {
7782
init(this: EventHandlerBlock): void {
7883
this.appendDummyInput('TITLE')
7984
.appendField(Blockly.Msg.WHEN)
80-
.appendField(createFieldNonEditableText('sender'), FIELD_SENDER)
81-
.appendField(createFieldNonEditableText('eventName'), FIELD_EVENT_NAME);
85+
.appendField(createFieldNonEditableText(''), FIELD_SENDER)
86+
.appendField(createFieldNonEditableText(''), FIELD_EVENT_NAME);
8287
this.appendDummyInput('PARAMS')
8388
.appendField(Blockly.Msg.WITH);
8489
this.setOutput(false);
@@ -94,11 +99,13 @@ const EVENT_HANDLER = {
9499
*/
95100
saveExtraState(this: EventHandlerBlock): EventHandlerExtraState {
96101
const extraState: EventHandlerExtraState = {
97-
pathOfSender: this.mrcPathOfSender,
98102
senderType: this.mrcSenderType,
99103
params: [],
100104
otherBlockId: this.mrcOtherBlockId,
101105
};
106+
if (this.mrcMechanismBlockId) {
107+
extraState.mechanismBlockId = this.mrcMechanismBlockId;
108+
}
102109

103110
this.mrcParameters.forEach((param) => {
104111
extraState.params.push({
@@ -114,10 +121,11 @@ const EVENT_HANDLER = {
114121
* Applies the given state to this block.
115122
*/
116123
loadExtraState(this: EventHandlerBlock, extraState: EventHandlerExtraState): void {
117-
this.mrcPathOfSender = extraState.pathOfSender;
118124
this.mrcSenderType = extraState.senderType;
119125
this.mrcParameters = [];
120126
this.mrcOtherBlockId = extraState.otherBlockId;
127+
this.mrcMechanismBlockId = extraState.mechanismBlockId
128+
? extraState.mechanismBlockId : '';
121129

122130
extraState.params.forEach((param) => {
123131
this.mrcParameters.push({
@@ -158,29 +166,30 @@ const EVENT_HANDLER = {
158166
input.removeField(fieldName);
159167
});
160168
},
169+
170+
/**
171+
* mrcOnLoad is called for each EventHandlerBlock when the blocks are loaded in the blockly
172+
* workspace.
173+
*/
161174
mrcOnLoad: function(this: EventHandlerBlock): void {
162-
// mrcOnLoad is called for each EventHandlerBlock when the blocks are loaded in the blockly workspace.
163175
const warnings: string[] = [];
164176

165-
// If this block is an event handler for a robot event, check that the robot event
166-
// still exists and hasn't been changed.
167-
// If the robot event doesn't exist, put a visible warning on this block.
168-
// If the robot event has changed, update the block if possible or put a
169-
// visible warning on it.
170-
if (this.mrcSenderType === SenderType.ROBOT) {
171-
let foundRobotEvent = false;
172-
const editor = Editor.getEditorForBlocklyWorkspace(this.workspace);
173-
if (editor) {
177+
const editor = Editor.getEditorForBlocklyWorkspace(this.workspace);
178+
if (editor) {
179+
if (this.mrcSenderType === SenderType.ROBOT) {
180+
// This block is an event handler for a robot event.
181+
// Check whether the robot event still exists and whether it has been changed.
182+
// If the robot event doesn't exist, put a visible warning on this block.
183+
// If the robot event has changed, update the block if possible or put a
184+
// visible warning on it.
185+
let foundRobotEvent = false;
174186
const robotEvents = editor.getEventsFromRobot();
175187
for (const robotEvent of robotEvents) {
176188
if (robotEvent.blockId === this.mrcOtherBlockId) {
177189
foundRobotEvent = true;
178-
179-
// If the event name has changed, we can fix this block.
180190
if (this.getFieldValue(FIELD_EVENT_NAME) !== robotEvent.name) {
181191
this.setFieldValue(robotEvent.name, FIELD_EVENT_NAME);
182192
}
183-
184193
this.mrcParameters = [];
185194
robotEvent.args.forEach(arg => {
186195
this.mrcParameters.push({
@@ -198,6 +207,65 @@ const EVENT_HANDLER = {
198207
warnings.push('This block is an event handler for an event that no longer exists.');
199208
}
200209
}
210+
211+
if (this.mrcSenderType === SenderType.MECHANISM) {
212+
// This block is an event handler for a mechanism event.
213+
// Check whether the mechanism still exists, whether it has been
214+
// changed, whether the event still exists, and whether the event has
215+
// been changed.
216+
// If the mechanism doesn't exist, put a visible warning on this block.
217+
// If the mechanism has changed, update the block if possible or put a
218+
// visible warning on it.
219+
// If the event doesn't exist, put a visible warning on this block.
220+
// If the event has changed, update the block if possible or put a
221+
// visible warning on it.
222+
let foundMechanism = false;
223+
const mechanismsInRobot = editor.getMechanismsFromRobot();
224+
for (const mechanismInRobot of mechanismsInRobot) {
225+
if (mechanismInRobot.blockId === this.mrcMechanismBlockId) {
226+
foundMechanism = true;
227+
228+
// If the mechanism name has changed, we can handle that.
229+
if (this.getFieldValue(FIELD_SENDER) !== mechanismInRobot.name) {
230+
this.setFieldValue(mechanismInRobot.name, FIELD_SENDER);
231+
}
232+
233+
let foundMechanismEvent = false;
234+
const mechanism = editor.getMechanism(mechanismInRobot);
235+
const mechanismEvents: storageModuleContent.Event[] = mechanism
236+
? editor.getEventsFromMechanism(mechanism) : [];
237+
for (const mechanismEvent of mechanismEvents) {
238+
if (mechanismEvent.blockId === this.mrcOtherBlockId) {
239+
foundMechanismEvent = true;
240+
if (this.getFieldValue(FIELD_EVENT_NAME) !== mechanismEvent.name) {
241+
this.setFieldValue(mechanismEvent.name, FIELD_EVENT_NAME);
242+
}
243+
244+
this.mrcParameters = [];
245+
mechanismEvent.args.forEach(arg => {
246+
this.mrcParameters.push({
247+
name: arg.name,
248+
type: arg.type,
249+
});
250+
});
251+
this.mrcUpdateParams();
252+
253+
// Since we found the mechanism event, we can break out of the loop.
254+
break;
255+
}
256+
}
257+
if (!foundMechanismEvent) {
258+
warnings.push('This block is an event handler for an event that no longer exists.');
259+
}
260+
261+
// Since we found the mechanism, we can break out of the loop.
262+
break;
263+
}
264+
}
265+
if (!foundMechanism) {
266+
warnings.push('This block is an event handler for an event in a mechanism that no longer exists.');
267+
}
268+
}
201269
}
202270

203271
if (warnings.length) {
@@ -211,6 +279,16 @@ const EVENT_HANDLER = {
211279
this.setWarningText(null, WARNING_ID_EVENT_CHANGED);
212280
}
213281
},
282+
getEventBlockId: function(this: EventHandlerBlock): string {
283+
return this.mrcOtherBlockId;
284+
},
285+
renameMechanismName: function(this: EventHandlerBlock, mechanismBlockId: string, newName: string): void {
286+
// renameMechanismName is called when a mechanism block in the same module is modified.
287+
if (this.mrcSenderType === SenderType.MECHANISM &&
288+
mechanismBlockId === this.mrcMechanismBlockId) {
289+
this.setFieldValue(newName, FIELD_SENDER);
290+
}
291+
},
214292
};
215293

216294
export function setup(): void {
@@ -282,11 +360,37 @@ export function pythonFromBlock(
282360
code = generator.scrub_(block, code);
283361

284362
generator.addClassMethodDefinition(funcName, code);
285-
generator.addEventHandler(sender, eventName, funcName);
363+
generateRegisterEventHandler(block, generator, sender, eventName, funcName);
286364

287365
return '';
288366
}
289367

368+
function generateRegisterEventHandler(
369+
block: EventHandlerBlock,
370+
generator: ExtendedPythonGenerator,
371+
sender: string,
372+
eventName: string,
373+
funcName: string) {
374+
// Create the line of code that will register this event handler.
375+
let fullSender = '';
376+
if (block.mrcSenderType === SenderType.ROBOT) {
377+
fullSender = 'self.' + sender;
378+
} else if (block.mrcSenderType === SenderType.MECHANISM) {
379+
switch (generator.getModuleType()) {
380+
case storageModule.MODULE_TYPE_ROBOT:
381+
fullSender = 'self.' + sender;
382+
break;
383+
case storageModule.MODULE_TYPE_OPMODE:
384+
fullSender = 'self.robot.' + sender;
385+
break;
386+
}
387+
}
388+
if (fullSender) {
389+
generator.addRegisterEventHandlerStatement(
390+
fullSender + '.register_event_handler("' + eventName + '", self.' + funcName + ')\n');
391+
}
392+
}
393+
290394
// Functions used for creating blocks for the toolbox.
291395

292396
export function addRobotEventHandlerBlocks(
@@ -298,10 +402,8 @@ export function addRobotEventHandlerBlocks(
298402
}
299403

300404
function createRobotEventHandlerBlock(
301-
event: storageModuleContent.Event): toolboxItems.Block {
405+
event: storageModuleContent.Event): toolboxItems.Block {
302406
const extraState: EventHandlerExtraState = {
303-
// TODO(lizlooney): ask Alan what pathOfSender is for.
304-
pathOfSender: '',
305407
senderType: SenderType.ROBOT,
306408
params: [],
307409
otherBlockId: event.blockId,
@@ -319,6 +421,37 @@ function createRobotEventHandlerBlock(
319421
return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null);
320422
}
321423

424+
export function addMechanismEventHandlerBlocks(
425+
mechanismInRobot: storageModuleContent.MechanismInRobot,
426+
events: storageModuleContent.Event[],
427+
contents: toolboxItems.ContentsType[]) {
428+
events.forEach(event => {
429+
contents.push(createMechanismEventHandlerBlock(mechanismInRobot, event));
430+
});
431+
}
432+
433+
function createMechanismEventHandlerBlock(
434+
mechanismInRobot: storageModuleContent.MechanismInRobot,
435+
event: storageModuleContent.Event): toolboxItems.Block {
436+
const extraState: EventHandlerExtraState = {
437+
senderType: SenderType.MECHANISM,
438+
params: [],
439+
otherBlockId: event.blockId,
440+
mechanismBlockId: mechanismInRobot.blockId,
441+
};
442+
event.args.forEach(arg => {
443+
extraState.params.push({
444+
name: arg.name,
445+
type: arg.type,
446+
});
447+
});
448+
const fields: {[key: string]: any} = {};
449+
fields[FIELD_SENDER] = mechanismInRobot.name;
450+
fields[FIELD_EVENT_NAME] = event.name;
451+
const inputs: {[key: string]: any} = {};
452+
return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null);
453+
}
454+
322455
// Misc
323456

324457
export function getHasAnyEnabledEventHandlers(workspace: Blockly.Workspace): boolean {
@@ -327,10 +460,35 @@ export function getHasAnyEnabledEventHandlers(workspace: Blockly.Workspace): boo
327460
}).length > 0;
328461
}
329462

330-
export function getEventHandlerNames(workspace: Blockly.Workspace, names: string[]): void {
331-
// Here we collect the event names of the event handlers in the given
332-
// workspace, regardless of whether the event handler is enabled.
463+
export function getRobotEventHandlerBlocks(
464+
workspace: Blockly.Workspace,
465+
blocks: EventHandlerBlock[]): void {
333466
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
334-
names.push(block.getFieldValue(FIELD_EVENT_NAME));
467+
const eventHandlerBlock = block as EventHandlerBlock;
468+
if (eventHandlerBlock.mrcSenderType == SenderType.ROBOT) {
469+
blocks.push(eventHandlerBlock);
470+
}
471+
});
472+
}
473+
474+
export function getMechanismEventHandlerBlocks(
475+
workspace: Blockly.Workspace,
476+
mechanismBlockId: string,
477+
blocks: EventHandlerBlock[]): void {
478+
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
479+
const eventHandlerBlock = block as EventHandlerBlock;
480+
if (eventHandlerBlock.mrcSenderType == SenderType.MECHANISM) {
481+
if (eventHandlerBlock.mrcMechanismBlockId === mechanismBlockId) {
482+
blocks.push(eventHandlerBlock);
483+
}
484+
}
485+
});
486+
}
487+
488+
export function renameMechanismName(workspace: Blockly.Workspace, mechanismBlockId: string, newName: string): void {
489+
const eventHandlerBlocks: EventHandlerBlock[] = [];
490+
getMechanismEventHandlerBlocks(workspace, mechanismBlockId, eventHandlerBlocks);
491+
eventHandlerBlocks.forEach(block => {
492+
(block as EventHandlerBlock).renameMechanismName(mechanismBlockId, newName);
335493
});
336494
}

src/blocks/mrc_mechanism.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import * as storageModuleContent from '../storage/module_content';
3333
import * as storageNames from '../storage/names';
3434
import * as value from './utils/value';
3535
import { renameMethodCallers } from './mrc_call_python_function'
36+
import { renameMechanismName as renameMechanismNameInEventHandlers } from './mrc_event_handler'
3637

3738
export const BLOCK_NAME = 'mrc_mechanism';
3839
export const OUTPUT_NAME = 'mrc_mechansim';
@@ -154,6 +155,8 @@ const MECHANISM = {
154155
if (oldName && oldName !== name && oldName !== legalName) {
155156
// Rename any callers.
156157
renameMethodCallers(this.workspace, this.id, legalName);
158+
// Rename any event handlers
159+
renameMechanismNameInEventHandlers(this.workspace, this.id, legalName);
157160
}
158161
return legalName;
159162
},

0 commit comments

Comments
 (0)