Skip to content

Commit 485a6a7

Browse files
authored
Added toolbox blocks to add a mechanism to the robot (#187)
* In App.tsx: Pass project to Editor.loadModuleBlocks Call Editor.abandon and Editor.makeCurrent when switching between modules. In editor.ts: Added static variable currentEditor. Updated getEditorForBlocklyWorkspace to return currentEditor if workspace isn't found. Added static method getCurrentEditor. Added methods abandon and makeCurrent, which are called from App.tsx when switching between modules. Add project parameter to loadModuleBlocks. Fetch all the mechanisms in the project. In block event handler, call block.mrcOnLoad instead of block.onLoad. Added methods getMechanisms, getComponentsFromMechanism, getEventsFromMechanism, and getMethodsFromMechanism. In extended_python_generator.ts: Renamed field hasHardware to hasAnyComponents. Renamed field ports to componentPorts. Renamed field hasEventHandler to hasAnyEventHandlers. Removed method getListOfPorts. Added method getComponentPortParameters. In hardware_category.ts: Updated getHardwareCategory function so it fills in the + Mechanism category with blocks representing each the mechanisms in the project. Changed getComponentsCategory function so it takes moduleType (string) instead hideParams (boolean). In mrc_mechanism.ts: Added method mrcOnLoad to handle changes to the mechanism definition. Added function createMechanismBlock, which is called to put a add mechanism block in the toolbox when editing a robot. In mrc_mechanism_component_holder.ts: Updated code in pythonFromBlockInMechanism that calls generator.getComponentPortParameters(). Removed function getHardwarePorts. Added functions hasAnyComponents and getComponentPorts. In mrc_component.ts: Modified getComponent to collect the ports for the component. Modified getAllPossibleComponents and createComponentBlock to take moduleType instead of hideParams. Removed function getHardwarePorts. Added function getComponentPorts. In mrc_class_method_def.ts, updated code in pythonFromBlock that calls generator.getComponentPortParameters(). In mrc_call_python_function.ts, renamed onLoad to mrcOnLoad. In mrc_event_handler.ts: Renamed mrcTypeOfSender to mrcSenderType. Renamed typeOfSender to senderType. Renamed onLoad to mrcOnLoad. In client_sid_storage.ts, removed temporary code that handled old module type 'project'. In server_python_scripts/blocks_base_classes/mechanism.py, added code for events. Updated src/modules/ json files to set editable = false in the non-deletable mrc_class_method_def blocks. This prevents the user from disabling these blocks. * Added code to create a toolbox subcategory under Hardware -> Mechanisms for each mechanism that has been added to the robot. Currently the subcategories don't have any blocks in them, but there is a TODO to add blocks for each method define in the mechanism. * In mrc_call_python_function, updated mrcOnLoad for instance component blocks to look at all components that are in scope. If the current module is a mechanism then the components of the mechanism are in scope.
1 parent c19dcd5 commit 485a6a7

17 files changed

+551
-352
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,39 @@
11
# This is the class all mechanisms derive from
22

3+
from typing import Callable
4+
35
class Mechanism:
46
def __init__(self):
57
self.hardware = []
8+
# In self.event_handlers, the keys are the event names, the values are a list of handlers.
9+
self.event_handlers = {}
10+
11+
def register_event_handler(self, event_name: str, event_handler: Callable) -> None:
12+
if event_name in self.event_handlers:
13+
self.event_handlers[event_name].append(event_handler)
14+
else:
15+
self.event_handlers[event_name] = [event_handler]
16+
17+
def unregister_event_handler(self, event_name: str, event_handler: Callable) -> None:
18+
if event_name in self.event_handlers:
19+
if event_handler in self.event_handlers[event_name]:
20+
self.event_handlers[event_name].remove(event_handler)
21+
if not self.event_handlers[event_name]:
22+
del self.event_handlers[event_name]
23+
24+
def fire_event(self, event_name: str, *args) -> None:
25+
if event_name in self.event_handlers:
26+
for event_handler in self.event_handlers[event_name]:
27+
event_handler(*args)
28+
629
def start(self) -> None:
730
for hardware in self.hardware:
831
hardware.start()
32+
933
def update(self) -> None:
1034
for hardware in self.hardware:
1135
hardware.update()
36+
1237
def stop(self) -> None:
1338
for hardware in self.hardware:
1439
hardware.stop()

src/App.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
418418
generatorContext.current.setModule(currentModule);
419419
}
420420
if (blocksEditor.current) {
421-
blocksEditor.current.loadModuleBlocks(currentModule);
421+
blocksEditor.current.loadModuleBlocks(currentModule, project);
422422
}
423423
}, [currentModule]);
424424

@@ -436,11 +436,15 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
436436
generatorContext.current.setModule(currentModule);
437437
}
438438

439+
if (blocksEditor.current) {
440+
blocksEditor.current.abandon();
441+
}
439442
blocksEditor.current = new editor.Editor(newWorkspace, generatorContext.current, storage);
443+
blocksEditor.current.makeCurrent();
440444

441445
// Set the current module in the editor after creating it
442446
if (currentModule) {
443-
blocksEditor.current.loadModuleBlocks(currentModule);
447+
blocksEditor.current.loadModuleBlocks(currentModule, project);
444448
}
445449

446450
blocksEditor.current.updateToolbox(shownPythonToolboxCategories);

src/blocks/mrc_call_python_function.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -465,8 +465,8 @@ const CALL_PYTHON_FUNCTION = {
465465
}
466466
return components;
467467
},
468-
onLoad: function(this: CallPythonFunctionBlock): void {
469-
// onLoad is called for each CallPythonFunctionBlock when the blocks are loaded in the blockly workspace.
468+
mrcOnLoad: function(this: CallPythonFunctionBlock): void {
469+
// mrcOnLoad is called for each CallPythonFunctionBlock when the blocks are loaded in the blockly workspace.
470470
const warnings: string[] = [];
471471

472472
// If this block is calling a component method, check that the component
@@ -476,8 +476,13 @@ const CALL_PYTHON_FUNCTION = {
476476
// visible warning on it.
477477
if (this.mrcFunctionKind === FunctionKind.INSTANCE_COMPONENT) {
478478
let foundComponent = false;
479-
const components = this.getComponentsFromRobot();
480-
for (const component of components) {
479+
const componentsInScope: commonStorage.Component[] = [];
480+
componentsInScope.push(...this.getComponentsFromRobot());
481+
const editor = Editor.getEditorForBlocklyWorkspace(this.workspace);
482+
if (editor && editor.getCurrentModuleType() === commonStorage.MODULE_TYPE_MECHANISM) {
483+
componentsInScope.push(...editor.getComponentsFromWorkspace());
484+
}
485+
for (const component of componentsInScope) {
481486
if (component.blockId === this.mrcOtherBlockId) {
482487
foundComponent = true;
483488

@@ -495,7 +500,7 @@ const CALL_PYTHON_FUNCTION = {
495500
}
496501
if (indexOfComponentName != -1) {
497502
const componentNameChoices : string[] = [];
498-
components.forEach(component => componentNameChoices.push(component.name));
503+
componentsInScope.forEach(component => componentNameChoices.push(component.name));
499504
titleInput.removeField(FIELD_COMPONENT_NAME);
500505
titleInput.insertFieldAt(indexOfComponentName,
501506
createFieldDropdown(componentNameChoices), FIELD_COMPONENT_NAME);

src/blocks/mrc_class_method_def.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ const CLASS_METHOD_DEF = {
103103
this.updateBlock_();
104104
},
105105
/**
106-
* Returns the state of this block as a JSON serializable object.
107-
*/
106+
* Returns the state of this block as a JSON serializable object.
107+
*/
108108
saveExtraState: function (
109109
this: ClassMethodDefBlock): ClassMethodDefExtraState {
110110
const extraState: ClassMethodDefExtraState = {
@@ -424,8 +424,11 @@ export const pythonFromBlock = function (
424424

425425
let params = block.mrcParameters;
426426
let paramString = "self";
427-
if (block.mrcPythonMethodName == '__init__') {
428-
paramString += generator.getListOfPorts(false);
427+
if (generator.getModuleType() == commonStorage.MODULE_TYPE_MECHANISM && block.mrcPythonMethodName == '__init__') {
428+
const ports: string[] = generator.getComponentPortParameters();
429+
if (ports.length) {
430+
paramString += ', ' + ports.join(', ');
431+
}
429432
}
430433

431434
if (params.length != 0) {

src/blocks/mrc_component.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,13 @@ const COMPONENT = {
140140
getComponent: function (this: ComponentBlock): commonStorage.Component | null {
141141
const componentName = this.getFieldValue(FIELD_NAME);
142142
const componentType = this.getFieldValue(FIELD_TYPE);
143+
const ports: {[port: string]: string} = {};
144+
this.getComponentPorts(ports);
143145
return {
144146
blockId: this.id,
145147
name: componentName,
146148
className: componentType,
149+
ports: ports,
147150
};
148151
},
149152
getNewPort: function (this: ComponentBlock, i: number): string {
@@ -153,14 +156,12 @@ const COMPONENT = {
153156
}
154157
return this.getFieldValue(FIELD_NAME) + extension + '_port';
155158
},
156-
getHardwarePorts: function (this: ComponentBlock, ports: {[key: string]: string}): void {
157-
// Collect the hardware ports for this component block that are needed to generate
158-
// the define_hardware method. (The key is the port, the value is the type.)
159-
if (this.hideParams) {
160-
for (let i = 0; i < this.mrcArgs.length; i++) {
161-
const newPort = this.getNewPort(i);
162-
ports[newPort] = this.mrcArgs[i].type;
163-
}
159+
getComponentPorts: function (this: ComponentBlock, ports: {[key: string]: string}): void {
160+
// Collect the ports for this component block.
161+
for (let i = 0; i < this.mrcArgs.length; i++) {
162+
const newPort = this.getNewPort(i);
163+
// The key is the port, the value is the type.
164+
ports[newPort] = this.mrcArgs[i].type;
164165
}
165166
},
166167
}
@@ -197,7 +198,7 @@ export const pythonFromBlock = function (
197198
return code;
198199
}
199200

200-
export function getAllPossibleComponents(hideParams: boolean): toolboxItems.ContentsType[] {
201+
export function getAllPossibleComponents(moduleType: string): toolboxItems.ContentsType[] {
201202
const contents: toolboxItems.ContentsType[] = [];
202203
// Iterate through all the components subclasses and add definition blocks.
203204
const componentTypes = getSubclassNames('component.Component');
@@ -215,7 +216,7 @@ export function getAllPossibleComponents(hideParams: boolean): toolboxItems.Cont
215216

216217
classData.staticMethods.forEach(staticFunctionData => {
217218
if (staticFunctionData.returnType === componentType) {
218-
contents.push(createComponentBlock(componentName, classData, staticFunctionData, hideParams));
219+
contents.push(createComponentBlock(componentName, classData, staticFunctionData, moduleType));
219220
}
220221
});
221222
});
@@ -224,12 +225,12 @@ export function getAllPossibleComponents(hideParams: boolean): toolboxItems.Cont
224225
}
225226

226227
function createComponentBlock(
227-
componentName: string, classData: ClassData, staticFunctionData: FunctionData, hideParams: boolean): toolboxItems.Block {
228+
componentName: string, classData: ClassData, staticFunctionData: FunctionData, moduleType: string): toolboxItems.Block {
228229
const extraState: ComponentExtraState = {
229230
importModule: classData.moduleName,
230231
staticFunctionName: staticFunctionData.functionName,
231232
params: [],
232-
hideParams: hideParams,
233+
hideParams: (moduleType == commonStorage.MODULE_TYPE_MECHANISM),
233234
};
234235
const fields: {[key: string]: any} = {};
235236
fields[FIELD_NAME] = componentName;
@@ -241,7 +242,7 @@ function createComponentBlock(
241242
'name': argData.name,
242243
'type': argData.type,
243244
});
244-
if (!hideParams) {
245+
if (moduleType == commonStorage.MODULE_TYPE_ROBOT) {
245246
if (argData.type === 'int') {
246247
const portType = getPortTypeForArgument(argData.name);
247248
if (portType) {

src/blocks/mrc_event_handler.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export type EventHandlerBlock = Blockly.Block & EventHandlerMixin & Blockly.Bloc
5353

5454
interface EventHandlerMixin extends EventHandlerMixinType {
5555
mrcPathOfSender: string;
56-
mrcTypeOfSender: SenderType;
56+
mrcSenderType: SenderType;
5757
mrcParameters: Parameter[];
5858
mrcOtherBlockId: string,
5959
}
@@ -63,7 +63,7 @@ type EventHandlerMixinType = typeof EVENT_HANDLER;
6363
/** Extra state for serialising event handler blocks. */
6464
export interface EventHandlerExtraState {
6565
pathOfSender: string;
66-
typeOfSender: SenderType;
66+
senderType: SenderType;
6767
/** The parameters of the event handler. */
6868
params: Parameter[];
6969
/** The id of the mrc_event block that defines the event. */
@@ -95,7 +95,7 @@ const EVENT_HANDLER = {
9595
saveExtraState(this: EventHandlerBlock): EventHandlerExtraState {
9696
const extraState: EventHandlerExtraState = {
9797
pathOfSender: this.mrcPathOfSender,
98-
typeOfSender: this.mrcTypeOfSender,
98+
senderType: this.mrcSenderType,
9999
params: [],
100100
otherBlockId: this.mrcOtherBlockId,
101101
};
@@ -115,7 +115,7 @@ const EVENT_HANDLER = {
115115
*/
116116
loadExtraState(this: EventHandlerBlock, extraState: EventHandlerExtraState): void {
117117
this.mrcPathOfSender = extraState.pathOfSender;
118-
this.mrcTypeOfSender = extraState.typeOfSender;
118+
this.mrcSenderType = extraState.senderType;
119119
this.mrcParameters = [];
120120
this.mrcOtherBlockId = extraState.otherBlockId;
121121

@@ -158,16 +158,16 @@ const EVENT_HANDLER = {
158158
input.removeField(fieldName);
159159
});
160160
},
161-
onLoad: function(this: EventHandlerBlock): void {
162-
// onLoad is called for each EventHandlerBlock when the blocks are loaded in the blockly workspace.
161+
mrcOnLoad: function(this: EventHandlerBlock): void {
162+
// mrcOnLoad is called for each EventHandlerBlock when the blocks are loaded in the blockly workspace.
163163
const warnings: string[] = [];
164164

165165
// If this block is an event handler for a robot event, check that the robot event
166166
// still exists and hasn't been changed.
167167
// If the robot event doesn't exist, put a visible warning on this block.
168168
// If the robot event has changed, update the block if possible or put a
169169
// visible warning on it.
170-
if (this.mrcTypeOfSender === SenderType.ROBOT) {
170+
if (this.mrcSenderType === SenderType.ROBOT) {
171171
let foundRobotEvent = false;
172172
const editor = Editor.getEditorForBlocklyWorkspace(this.workspace);
173173
if (editor) {
@@ -301,7 +301,7 @@ function createRobotEventHandlerBlock(
301301
const extraState: EventHandlerExtraState = {
302302
// TODO(lizlooney): ask Alan what pathOfSender is for.
303303
pathOfSender: '',
304-
typeOfSender: SenderType.ROBOT,
304+
senderType: SenderType.ROBOT,
305305
params: [],
306306
otherBlockId: event.blockId,
307307
};

0 commit comments

Comments
 (0)