Skip to content
Merged
4 changes: 2 additions & 2 deletions src/blocks/mrc_call_python_function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ type CallPythonFunctionExtraState = {
* Specified only if the function kind is INSTANCE_MECHANISM.
*/
mechanismBlockId?: string,
};
}

const CALL_PYTHON_FUNCTION = {
/**
Expand Down Expand Up @@ -697,7 +697,7 @@ const CALL_PYTHON_FUNCTION = {
}
}
if (!foundMechanism) {
warnings.push('This block calls a method on a mechanism that no longer exists.');
warnings.push('This block calls a method in a mechanism that no longer exists.');
}
}
}
Expand Down
212 changes: 185 additions & 27 deletions src/blocks/mrc_event_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { createFieldFlydown } from '../fields/field_flydown';
import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
import { MRC_STYLE_EVENT_HANDLER } from '../themes/styles';
import * as toolboxItems from '../toolbox/items';
import * as storageModule from '../storage/module';
import * as storageModuleContent from '../storage/module_content';

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

interface EventHandlerMixin extends EventHandlerMixinType {
mrcPathOfSender: string;
mrcSenderType: SenderType;
mrcParameters: Parameter[];
mrcOtherBlockId: string,
mrcMechanismBlockId: string,
}

type EventHandlerMixinType = typeof EVENT_HANDLER;

/** Extra state for serialising event handler blocks. */
export interface EventHandlerExtraState {
pathOfSender: string;
senderType: SenderType;
/** The parameters of the event handler. */
params: Parameter[];
/** The id of the mrc_event block that defines the event. */
otherBlockId: string,
/**
* The id of the mrc_mechanism block that adds the mechanism to the robot.
* Specified only if the sender type is MECHANISM.
*/
mechanismBlockId?: string,
}

const EVENT_HANDLER = {
Expand All @@ -77,8 +82,8 @@ const EVENT_HANDLER = {
init(this: EventHandlerBlock): void {
this.appendDummyInput('TITLE')
.appendField(Blockly.Msg.WHEN)
.appendField(createFieldNonEditableText('sender'), FIELD_SENDER)
.appendField(createFieldNonEditableText('eventName'), FIELD_EVENT_NAME);
.appendField(createFieldNonEditableText(''), FIELD_SENDER)
.appendField(createFieldNonEditableText(''), FIELD_EVENT_NAME);
this.appendDummyInput('PARAMS')
.appendField(Blockly.Msg.WITH);
this.setOutput(false);
Expand All @@ -94,11 +99,13 @@ const EVENT_HANDLER = {
*/
saveExtraState(this: EventHandlerBlock): EventHandlerExtraState {
const extraState: EventHandlerExtraState = {
pathOfSender: this.mrcPathOfSender,
senderType: this.mrcSenderType,
params: [],
otherBlockId: this.mrcOtherBlockId,
};
if (this.mrcMechanismBlockId) {
extraState.mechanismBlockId = this.mrcMechanismBlockId;
}

this.mrcParameters.forEach((param) => {
extraState.params.push({
Expand All @@ -114,10 +121,11 @@ const EVENT_HANDLER = {
* Applies the given state to this block.
*/
loadExtraState(this: EventHandlerBlock, extraState: EventHandlerExtraState): void {
this.mrcPathOfSender = extraState.pathOfSender;
this.mrcSenderType = extraState.senderType;
this.mrcParameters = [];
this.mrcOtherBlockId = extraState.otherBlockId;
this.mrcMechanismBlockId = extraState.mechanismBlockId
? extraState.mechanismBlockId : '';

extraState.params.forEach((param) => {
this.mrcParameters.push({
Expand Down Expand Up @@ -158,29 +166,30 @@ const EVENT_HANDLER = {
input.removeField(fieldName);
});
},

/**
* mrcOnLoad is called for each EventHandlerBlock when the blocks are loaded in the blockly
* workspace.
*/
mrcOnLoad: function(this: EventHandlerBlock): void {
// mrcOnLoad is called for each EventHandlerBlock when the blocks are loaded in the blockly workspace.
const warnings: string[] = [];

// If this block is an event handler for a robot event, check that the robot event
// still exists and hasn't been changed.
// If the robot event doesn't exist, put a visible warning on this block.
// If the robot event has changed, update the block if possible or put a
// visible warning on it.
if (this.mrcSenderType === SenderType.ROBOT) {
let foundRobotEvent = false;
const editor = Editor.getEditorForBlocklyWorkspace(this.workspace);
if (editor) {
const editor = Editor.getEditorForBlocklyWorkspace(this.workspace);
if (editor) {
if (this.mrcSenderType === SenderType.ROBOT) {
// This block is an event handler for a robot event.
// Check whether the robot event still exists and whether it has been changed.
// If the robot event doesn't exist, put a visible warning on this block.
// If the robot event has changed, update the block if possible or put a
// visible warning on it.
let foundRobotEvent = false;
const robotEvents = editor.getEventsFromRobot();
for (const robotEvent of robotEvents) {
if (robotEvent.blockId === this.mrcOtherBlockId) {
foundRobotEvent = true;

// If the event name has changed, we can fix this block.
if (this.getFieldValue(FIELD_EVENT_NAME) !== robotEvent.name) {
this.setFieldValue(robotEvent.name, FIELD_EVENT_NAME);
}

this.mrcParameters = [];
robotEvent.args.forEach(arg => {
this.mrcParameters.push({
Expand All @@ -198,6 +207,65 @@ const EVENT_HANDLER = {
warnings.push('This block is an event handler for an event that no longer exists.');
}
}

if (this.mrcSenderType === SenderType.MECHANISM) {
// This block is an event handler for a mechanism event.
// Check whether the mechanism still exists, whether it has been
// changed, whether the event still exists, and whether the event has
// been changed.
// If the mechanism doesn't exist, put a visible warning on this block.
// If the mechanism has changed, update the block if possible or put a
// visible warning on it.
// If the event doesn't exist, put a visible warning on this block.
// If the event has changed, update the block if possible or put a
// visible warning on it.
let foundMechanism = false;
const mechanismsInRobot = editor.getMechanismsFromRobot();
for (const mechanismInRobot of mechanismsInRobot) {
if (mechanismInRobot.blockId === this.mrcMechanismBlockId) {
foundMechanism = true;

// If the mechanism name has changed, we can handle that.
if (this.getFieldValue(FIELD_SENDER) !== mechanismInRobot.name) {
this.setFieldValue(mechanismInRobot.name, FIELD_SENDER);
}

let foundMechanismEvent = false;
const mechanism = editor.getMechanism(mechanismInRobot);
const mechanismEvents: storageModuleContent.Event[] = mechanism
? editor.getEventsFromMechanism(mechanism) : [];
for (const mechanismEvent of mechanismEvents) {
if (mechanismEvent.blockId === this.mrcOtherBlockId) {
foundMechanismEvent = true;
if (this.getFieldValue(FIELD_EVENT_NAME) !== mechanismEvent.name) {
this.setFieldValue(mechanismEvent.name, FIELD_EVENT_NAME);
}

this.mrcParameters = [];
mechanismEvent.args.forEach(arg => {
this.mrcParameters.push({
name: arg.name,
type: arg.type,
});
});
this.mrcUpdateParams();

// Since we found the mechanism event, we can break out of the loop.
break;
}
}
if (!foundMechanismEvent) {
warnings.push('This block is an event handler for an event that no longer exists.');
}

// Since we found the mechanism, we can break out of the loop.
break;
}
}
if (!foundMechanism) {
warnings.push('This block is an event handler for an event in a mechanism that no longer exists.');
}
}
}

if (warnings.length) {
Expand All @@ -211,6 +279,16 @@ const EVENT_HANDLER = {
this.setWarningText(null, WARNING_ID_EVENT_CHANGED);
}
},
getEventBlockId: function(this: EventHandlerBlock): string {
return this.mrcOtherBlockId;
},
renameMechanismName: function(this: EventHandlerBlock, mechanismBlockId: string, newName: string): void {
// renameMechanismName is called when a mechanism block in the same module is modified.
if (this.mrcSenderType === SenderType.MECHANISM &&
mechanismBlockId === this.mrcMechanismBlockId) {
this.setFieldValue(newName, FIELD_SENDER);
}
},
};

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

generator.addClassMethodDefinition(funcName, code);
generator.addEventHandler(sender, eventName, funcName);
generateRegisterEventHandler(block, generator, sender, eventName, funcName);

return '';
}

function generateRegisterEventHandler(
block: EventHandlerBlock,
generator: ExtendedPythonGenerator,
sender: string,
eventName: string,
funcName: string) {
// Create the line of code that will register this event handler.
let fullSender = '';
if (block.mrcSenderType === SenderType.ROBOT) {
fullSender = 'self.' + sender;
} else if (block.mrcSenderType === SenderType.MECHANISM) {
switch (generator.getModuleType()) {
case storageModule.MODULE_TYPE_ROBOT:
fullSender = 'self.' + sender;
break;
case storageModule.MODULE_TYPE_OPMODE:
fullSender = 'self.robot.' + sender;
break;
}
}
if (fullSender) {
generator.addRegisterEventHandlerStatement(
fullSender + '.register_event_handler("' + eventName + '", self.' + funcName + ')\n');
}
}

// Functions used for creating blocks for the toolbox.

export function addRobotEventHandlerBlocks(
Expand All @@ -298,10 +402,8 @@ export function addRobotEventHandlerBlocks(
}

function createRobotEventHandlerBlock(
event: storageModuleContent.Event): toolboxItems.Block {
event: storageModuleContent.Event): toolboxItems.Block {
const extraState: EventHandlerExtraState = {
// TODO(lizlooney): ask Alan what pathOfSender is for.
pathOfSender: '',
senderType: SenderType.ROBOT,
params: [],
otherBlockId: event.blockId,
Expand All @@ -319,6 +421,37 @@ function createRobotEventHandlerBlock(
return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null);
}

export function addMechanismEventHandlerBlocks(
mechanismInRobot: storageModuleContent.MechanismInRobot,
events: storageModuleContent.Event[],
contents: toolboxItems.ContentsType[]) {
events.forEach(event => {
contents.push(createMechanismEventHandlerBlock(mechanismInRobot, event));
});
}

function createMechanismEventHandlerBlock(
mechanismInRobot: storageModuleContent.MechanismInRobot,
event: storageModuleContent.Event): toolboxItems.Block {
const extraState: EventHandlerExtraState = {
senderType: SenderType.MECHANISM,
params: [],
otherBlockId: event.blockId,
mechanismBlockId: mechanismInRobot.blockId,
};
event.args.forEach(arg => {
extraState.params.push({
name: arg.name,
type: arg.type,
});
});
const fields: {[key: string]: any} = {};
fields[FIELD_SENDER] = mechanismInRobot.name;
fields[FIELD_EVENT_NAME] = event.name;
const inputs: {[key: string]: any} = {};
return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null);
}

// Misc

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

export function getEventHandlerNames(workspace: Blockly.Workspace, names: string[]): void {
// Here we collect the event names of the event handlers in the given
// workspace, regardless of whether the event handler is enabled.
export function getRobotEventHandlerBlocks(
workspace: Blockly.Workspace,
blocks: EventHandlerBlock[]): void {
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
names.push(block.getFieldValue(FIELD_EVENT_NAME));
const eventHandlerBlock = block as EventHandlerBlock;
if (eventHandlerBlock.mrcSenderType == SenderType.ROBOT) {
blocks.push(eventHandlerBlock);
}
});
}

export function getMechanismEventHandlerBlocks(
workspace: Blockly.Workspace,
mechanismBlockId: string,
blocks: EventHandlerBlock[]): void {
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
const eventHandlerBlock = block as EventHandlerBlock;
if (eventHandlerBlock.mrcSenderType == SenderType.MECHANISM) {
if (eventHandlerBlock.mrcMechanismBlockId === mechanismBlockId) {
blocks.push(eventHandlerBlock);
}
}
});
}

export function renameMechanismName(workspace: Blockly.Workspace, mechanismBlockId: string, newName: string): void {
const eventHandlerBlocks: EventHandlerBlock[] = [];
getMechanismEventHandlerBlocks(workspace, mechanismBlockId, eventHandlerBlocks);
eventHandlerBlocks.forEach(block => {
(block as EventHandlerBlock).renameMechanismName(mechanismBlockId, newName);
});
}
3 changes: 3 additions & 0 deletions src/blocks/mrc_mechanism.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import * as storageModuleContent from '../storage/module_content';
import * as storageNames from '../storage/names';
import * as value from './utils/value';
import { renameMethodCallers } from './mrc_call_python_function'
import { renameMechanismName as renameMechanismNameInEventHandlers } from './mrc_event_handler'

export const BLOCK_NAME = 'mrc_mechanism';
export const OUTPUT_NAME = 'mrc_mechansim';
Expand Down Expand Up @@ -154,6 +155,8 @@ const MECHANISM = {
if (oldName && oldName !== name && oldName !== legalName) {
// Rename any callers.
renameMethodCallers(this.workspace, this.id, legalName);
// Rename any event handlers
renameMechanismNameInEventHandlers(this.workspace, this.id, legalName);
}
return legalName;
},
Expand Down
Loading