Skip to content

Commit a64a6aa

Browse files
alan412lizlooney
andauthored
Pr private components in mechanism (#221)
* Add dist to .gitignore * Add private components to mechanism * Preface components inside mechanism * Add tooltips * Fix false warning for calling methods on components in mechanisms * removed unused import * Updated mechanism and robot module start json. * Added comment specifying that it needs to call getAllComponentsFromMechanism so it can create the port parameters. * Updated mrc_mechanism_component_holder.ts Since the mrc_mechanism_component_holder block can't change once it has been placed on the workspace, the code can be simplified. * Updated mrc_call_python_function.ts to handle mechanism component methods. The mechanismId (and mrcMechanismId) is set to the id of the mechanism so if the mechanism is renamed (in the robot), we update it. Added mrcComponentNames and mrcMapComponentNameToId. These are filled in during mrcOnLoad and are used to populate the component name dropdown field. * Set current version to 0.0.2. Added privateComponents to module content. We can use this to get the private components from a mechanism without having to use a headless blockly workspace to process the blocks. Added code to upgrade projects to version 0.0.2, which involves updating the mrc_mechanism_component_holder block in the robot and adding privateComponents in module content. * Added code to editor to get the private components and pass them to storageModuleContent.makeModuleContentText. Made some methods private where possible. Modified getAllComponentsFromMechanism so it gets the private components from a mechanism's module content without having to use a headless blockly workspace to process the mechanism's blocks. * Added comment in getRobotMechanismsCategory specifying that it needs to call getAllComponentsFromMechanism so it can create the port parameters. Simplified getComponentsCategory to call getAllComponentsFromWorkspace for both robot and mechanism modules and added comments explaining why. --------- Co-authored-by: Liz Looney <[email protected]>
1 parent 93d41f0 commit a64a6aa

15 files changed

+507
-123
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
# production
1212
/build
13+
/dist
1314

1415
# python stuff
1516
__pycache__/

src/blocks/mrc_call_python_function.ts

Lines changed: 195 additions & 75 deletions
Large diffs are not rendered by default.

src/blocks/mrc_mechanism.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,9 @@ const MECHANISM = {
205205
}
206206

207207
if (foundMechanism) {
208-
const components: storageModuleContent.Component[] = [];
209-
components.push(...editor.getComponentsFromMechanism(foundMechanism));
208+
// Here we need all the components (regular and private) from the mechanism because we need
209+
// to create port parameters for all the components.
210+
const components = editor.getAllComponentsFromMechanism(foundMechanism);
210211

211212
// If the mechanism class name has changed, update this blcok.
212213
if (this.getFieldValue(FIELD_TYPE) !== foundMechanism.className) {

src/blocks/mrc_mechanism_component_holder.ts

Lines changed: 126 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,20 @@ export const BLOCK_NAME = 'mrc_mechanism_component_holder';
4141

4242
const INPUT_MECHANISMS = 'MECHANISMS';
4343
const INPUT_COMPONENTS = 'COMPONENTS';
44+
const INPUT_PRIVATE_COMPONENTS = 'PRIVATE_COMPONENTS';
4445
const INPUT_EVENTS = 'EVENTS';
4546

4647
export const TOOLBOX_UPDATE_EVENT = 'toolbox-update-requested';
4748

4849
type MechanismComponentHolderExtraState = {
4950
hideMechanisms?: boolean;
51+
hidePrivateComponents?: boolean;
5052
}
5153

5254
export type MechanismComponentHolderBlock = Blockly.Block & MechanismComponentHolderMixin;
5355
interface MechanismComponentHolderMixin extends MechanismComponentHolderMixinType {
5456
mrcHideMechanisms: boolean;
57+
mrcHidePrivateComponents: boolean;
5558
}
5659
type MechanismComponentHolderMixinType = typeof MECHANISM_COMPONENT_HOLDER;
5760

@@ -72,15 +75,10 @@ function setName(block: Blockly.BlockSvg){
7275

7376
const MECHANISM_COMPONENT_HOLDER = {
7477
/**
75-
* Block initialization.
76-
*/
78+
* Block initialization.
79+
*/
7780
init: function (this: MechanismComponentHolderBlock): void {
7881
this.setInputsInline(false);
79-
this.appendStatementInput(INPUT_MECHANISMS).setCheck(MECHANISM_OUTPUT).appendField(Blockly.Msg.MECHANISMS);
80-
this.appendStatementInput(INPUT_COMPONENTS).setCheck(COMPONENT_OUTPUT).appendField(Blockly.Msg.COMPONENTS);
81-
this.appendStatementInput(INPUT_EVENTS).setCheck(EVENT_OUTPUT).appendField(Blockly.Msg.EVENTS);
82-
83-
8482
this.setOutput(false);
8583
this.setStyle(MRC_STYLE_MECHANISMS);
8684
ChangeFramework.registerCallback(MRC_COMPONENT_NAME, [Blockly.Events.BLOCK_MOVE, Blockly.Events.BLOCK_CHANGE], this.onBlockChanged);
@@ -95,30 +93,49 @@ const MECHANISM_COMPONENT_HOLDER = {
9593
if (this.mrcHideMechanisms == true) {
9694
extraState.hideMechanisms = this.mrcHideMechanisms;
9795
}
96+
if (this.mrcHidePrivateComponents == true) {
97+
extraState.hidePrivateComponents = this.mrcHidePrivateComponents;
98+
}
9899
return extraState;
99100
},
100101
/**
101-
* Applies the given state to this block.
102-
*/
102+
* Applies the given state to this block.
103+
*/
103104
loadExtraState: function (this: MechanismComponentHolderBlock, extraState: MechanismComponentHolderExtraState): void {
104105
this.mrcHideMechanisms = (extraState.hideMechanisms == undefined) ? false : extraState.hideMechanisms;
106+
this.mrcHidePrivateComponents = (extraState.hidePrivateComponents == undefined) ? false : extraState.hidePrivateComponents;
105107
this.updateBlock_();
106108
},
107109
/**
108-
* Update the block to reflect the newly loaded extra state.
109-
*/
110+
* Update the block to reflect the newly loaded extra state.
111+
*/
110112
updateBlock_: function (this: MechanismComponentHolderBlock): void {
111-
if (this.mrcHideMechanisms) {
112-
if (this.getInput(INPUT_MECHANISMS)) {
113-
this.removeInput(INPUT_MECHANISMS)
114-
}
113+
// Handle mechanisms input visibility
114+
if (!this.mrcHideMechanisms) {
115+
this.appendStatementInput(INPUT_MECHANISMS)
116+
.setCheck(MECHANISM_OUTPUT)
117+
.appendField(Blockly.Msg.MECHANISMS);
115118
}
116-
else {
117-
if (this.getInput(INPUT_MECHANISMS) == null) {
118-
this.appendStatementInput(INPUT_MECHANISMS).setCheck(MECHANISM_OUTPUT).appendField('Mechanisms');
119-
this.moveInputBefore(INPUT_MECHANISMS, INPUT_COMPONENTS)
120-
}
119+
120+
const componentsField = new Blockly.FieldLabel(Blockly.Msg.COMPONENTS);
121+
this.appendStatementInput(INPUT_COMPONENTS)
122+
.setCheck(COMPONENT_OUTPUT)
123+
.appendField(componentsField);
124+
125+
// Handle private components input visibility
126+
if (!this.mrcHidePrivateComponents) {
127+
const privateComponentsField = new Blockly.FieldLabel(Blockly.Msg.PRIVATE_COMPONENTS);
128+
this.appendStatementInput(INPUT_PRIVATE_COMPONENTS)
129+
.setCheck(COMPONENT_OUTPUT)
130+
.appendField(privateComponentsField);
131+
// Set tooltips on both componentsField and privateComponentsField.
132+
componentsField.setTooltip(Blockly.Msg.COMPONENTS_TOOLTIP);
133+
privateComponentsField.setTooltip(Blockly.Msg.PRIVATE_COMPONENTS_TOOLTIP);
121134
}
135+
136+
this.appendStatementInput(INPUT_EVENTS)
137+
.setCheck(EVENT_OUTPUT)
138+
.appendField(Blockly.Msg.EVENTS);
122139
},
123140
onBlockChanged: function (block: Blockly.BlockSvg, blockEvent: Blockly.Events.BlockBase) {
124141
if (blockEvent.type == Blockly.Events.BLOCK_MOVE) {
@@ -179,6 +196,28 @@ const MECHANISM_COMPONENT_HOLDER = {
179196

180197
return components;
181198
},
199+
getPrivateComponents: function (this: MechanismComponentHolderBlock): storageModuleContent.Component[] {
200+
const components: storageModuleContent.Component[] = []
201+
202+
// Get component blocks from the PRIVATE_COMPONENTS input
203+
const privateComponentsInput = this.getInput(INPUT_PRIVATE_COMPONENTS);
204+
if (privateComponentsInput && privateComponentsInput.connection) {
205+
// Walk through all connected component blocks.
206+
let componentBlock = privateComponentsInput.connection.targetBlock();
207+
while (componentBlock) {
208+
if (componentBlock.type === MRC_COMPONENT_NAME) {
209+
const component = (componentBlock as ComponentBlock).getComponent();
210+
if (component) {
211+
components.push(component);
212+
}
213+
}
214+
// Move to the next block in the chain
215+
componentBlock = componentBlock.getNextBlock();
216+
}
217+
}
218+
219+
return components;
220+
},
182221
getEvents: function (this: MechanismComponentHolderBlock): storageModuleContent.Event[] {
183222
const events: storageModuleContent.Event[] = []
184223

@@ -243,9 +282,11 @@ function pythonFromBlockInMechanism(block: MechanismComponentHolderBlock, genera
243282
code += '):\n';
244283

245284
const components = generator.statementToCode(block, INPUT_COMPONENTS);
285+
const privateComponents = generator.statementToCode(block, INPUT_PRIVATE_COMPONENTS);
246286

247-
if (components) {
248-
code += components;
287+
const allComponents = components + privateComponents;
288+
if (allComponents) {
289+
code += allComponents;
249290
generator.addClassMethodDefinition('define_hardware', code);
250291
}
251292
}
@@ -266,12 +307,13 @@ export const pythonFromBlock = function (
266307

267308
// Misc
268309

269-
/**n
310+
/**
270311
* Returns true if the given workspace has a mrc_mechanism_component_holder
271312
* block that contains at least one component.
272313
*/
273314
export function hasAnyComponents(workspace: Blockly.Workspace): boolean {
274315
for (const block of workspace.getBlocksByType(BLOCK_NAME)) {
316+
// Check regular components
275317
const componentsInput = block.getInput(INPUT_COMPONENTS);
276318
if (componentsInput && componentsInput.connection) {
277319
// Walk through all connected component blocks.
@@ -284,6 +326,20 @@ export function hasAnyComponents(workspace: Blockly.Workspace): boolean {
284326
componentBlock = componentBlock.getNextBlock();
285327
}
286328
}
329+
330+
// Check private components
331+
const privateComponentsInput = block.getInput(INPUT_PRIVATE_COMPONENTS);
332+
if (privateComponentsInput && privateComponentsInput.connection) {
333+
// Walk through all connected private component blocks.
334+
let componentBlock = privateComponentsInput.connection.targetBlock();
335+
while (componentBlock) {
336+
if (componentBlock.type === MRC_COMPONENT_NAME && componentBlock.isEnabled()) {
337+
return true;
338+
}
339+
// Move to the next block in the chain
340+
componentBlock = componentBlock.getNextBlock();
341+
}
342+
}
287343
}
288344
return false;
289345
}
@@ -305,6 +361,20 @@ export function getComponentPorts(workspace: Blockly.Workspace, ports: {[key: st
305361
componentBlock = componentBlock.getNextBlock();
306362
}
307363
}
364+
365+
// Also include private components for port collection
366+
const privateComponentsInput = block.getInput(INPUT_PRIVATE_COMPONENTS);
367+
if (privateComponentsInput && privateComponentsInput.connection) {
368+
// Walk through all connected private component blocks.
369+
let componentBlock = privateComponentsInput.connection.targetBlock();
370+
while (componentBlock) {
371+
if (componentBlock.type === MRC_COMPONENT_NAME && componentBlock.isEnabled()) {
372+
(componentBlock as ComponentBlock).getComponentPorts(ports);
373+
}
374+
// Move to the next block in the chain
375+
componentBlock = componentBlock.getNextBlock();
376+
}
377+
}
308378
});
309379
}
310380

@@ -330,6 +400,25 @@ export function getComponents(
330400
});
331401
}
332402

403+
export function getPrivateComponents(
404+
workspace: Blockly.Workspace,
405+
components: storageModuleContent.Component[]): void {
406+
// Get the holder block and ask it for the private components.
407+
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
408+
const privateComponentsFromHolder: storageModuleContent.Component[] =
409+
(block as MechanismComponentHolderBlock).getPrivateComponents();
410+
components.push(...privateComponentsFromHolder);
411+
});
412+
}
413+
414+
export function getAllComponents(
415+
workspace: Blockly.Workspace,
416+
components: storageModuleContent.Component[]): void {
417+
// Get both regular and private components for when creating a mechanism
418+
getComponents(workspace, components);
419+
getPrivateComponents(workspace, components);
420+
}
421+
333422
export function getEvents(
334423
workspace: Blockly.Workspace,
335424
events: storageModuleContent.Event[]): void {
@@ -340,3 +429,17 @@ export function getEvents(
340429
events.push(...eventsFromHolder);
341430
});
342431
}
432+
433+
/**
434+
* Hide private components.
435+
* This function should only be called when upgrading old projects.
436+
*/
437+
export function hidePrivateComponents(workspace: Blockly.Workspace) {
438+
// Make sure the workspace is headless.
439+
if (workspace.rendered) {
440+
throw new Error('hidePrivateComponents should never be called with a rendered workspace.');
441+
}
442+
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
443+
(block as MechanismComponentHolderBlock).mrcHidePrivateComponents = true;
444+
});
445+
}

src/blocks/tokens.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg {
3838
MECHANISMS: t('MECHANISMS'),
3939
OPMODES: t('OPMODES'),
4040
COMPONENTS: t('BLOCKLY.COMPONENTS'),
41+
COMPONENTS_TOOLTIP: t('BLOCKLY.TOOLTIP.COMPONENTS'),
42+
PRIVATE_COMPONENTS: t('BLOCKLY.PRIVATE_COMPONENTS'),
43+
PRIVATE_COMPONENTS_TOOLTIP: t('BLOCKLY.TOOLTIP.PRIVATE_COMPONENTS'),
4144
EVENTS: t('BLOCKLY.EVENTS'),
4245
EVALUATE_BUT_IGNORE_RESULT: t('BLOCKLY.EVALUATE_BUT_IGNORE_RESULT'),
4346
EVALUATE_BUT_IGNORE_RESULT_TOOLTIP:

src/editor/editor.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,25 +246,26 @@ export class Editor {
246246
const blocks = Blockly.serialization.workspaces.save(this.blocklyWorkspace);
247247
const mechanisms: storageModuleContent.MechanismInRobot[] = this.getMechanismsFromWorkspace();
248248
const components: storageModuleContent.Component[] = this.getComponentsFromWorkspace();
249+
const privateComponents: storageModuleContent.Component[] = this.getPrivateComponentsFromWorkspace();
249250
const events: storageModuleContent.Event[] = this.getEventsFromWorkspace();
250251
const methods: storageModuleContent.Method[] = (
251252
this.currentModule?.moduleType === storageModule.ModuleType.ROBOT ||
252253
this.currentModule?.moduleType === storageModule.ModuleType.MECHANISM)
253254
? this.getMethodsForOutsideFromWorkspace()
254255
: [];
255256
return storageModuleContent.makeModuleContentText(
256-
this.currentModule, blocks, mechanisms, components, events, methods);
257+
this.currentModule, blocks, mechanisms, components, privateComponents, events, methods);
257258
}
258259

259-
public getMechanismsFromWorkspace(): storageModuleContent.MechanismInRobot[] {
260+
private getMechanismsFromWorkspace(): storageModuleContent.MechanismInRobot[] {
260261
const mechanisms: storageModuleContent.MechanismInRobot[] = [];
261262
if (this.currentModule?.moduleType === storageModule.ModuleType.ROBOT) {
262263
mechanismComponentHolder.getMechanisms(this.blocklyWorkspace, mechanisms);
263264
}
264265
return mechanisms;
265266
}
266267

267-
public getComponentsFromWorkspace(): storageModuleContent.Component[] {
268+
private getComponentsFromWorkspace(): storageModuleContent.Component[] {
268269
const components: storageModuleContent.Component[] = [];
269270
if (this.currentModule?.moduleType === storageModule.ModuleType.ROBOT ||
270271
this.currentModule?.moduleType === storageModule.ModuleType.MECHANISM) {
@@ -273,13 +274,30 @@ export class Editor {
273274
return components;
274275
}
275276

277+
private getPrivateComponentsFromWorkspace(): storageModuleContent.Component[] {
278+
const components: storageModuleContent.Component[] = [];
279+
if (this.currentModule?.moduleType === storageModule.ModuleType.MECHANISM) {
280+
mechanismComponentHolder.getPrivateComponents(this.blocklyWorkspace, components);
281+
}
282+
return components;
283+
}
284+
285+
public getAllComponentsFromWorkspace(): storageModuleContent.Component[] {
286+
const components: storageModuleContent.Component[] = [];
287+
if (this.currentModule?.moduleType === storageModule.ModuleType.ROBOT ||
288+
this.currentModule?.moduleType === storageModule.ModuleType.MECHANISM) {
289+
mechanismComponentHolder.getAllComponents(this.blocklyWorkspace, components);
290+
}
291+
return components;
292+
}
293+
276294
public getMethodsForWithinFromWorkspace(): storageModuleContent.Method[] {
277295
const methods: storageModuleContent.Method[] = [];
278296
classMethodDef.getMethodsForWithin(this.blocklyWorkspace, methods);
279297
return methods;
280298
}
281299

282-
public getMethodsForOutsideFromWorkspace(): storageModuleContent.Method[] {
300+
private getMethodsForOutsideFromWorkspace(): storageModuleContent.Method[] {
283301
const methods: storageModuleContent.Method[] = [];
284302
classMethodDef.getMethodsForOutside(this.blocklyWorkspace, methods);
285303
return methods;
@@ -415,6 +433,25 @@ export class Editor {
415433
throw new Error('getComponentsFromMechanism: mechanism not found: ' + mechanism.className);
416434
}
417435

436+
/**
437+
* Returns ALL components (including private components) defined in the given mechanism.
438+
* This is used when creating mechanism blocks that need all components for port parameters.
439+
*/
440+
public getAllComponentsFromMechanism(mechanism: storageModule.Mechanism): storageModuleContent.Component[] {
441+
if (this.currentModule?.modulePath === mechanism.modulePath) {
442+
return this.getAllComponentsFromWorkspace();
443+
}
444+
if (mechanism.className in this.mechanismClassNameToModuleContent) {
445+
const moduleContent = this.mechanismClassNameToModuleContent[mechanism.className];
446+
const allComponents: storageModuleContent.Component[] = [
447+
...moduleContent.getComponents(),
448+
...moduleContent.getPrivateComponents(),
449+
]
450+
return allComponents;
451+
}
452+
throw new Error('getAllComponentsFromMechanism: mechanism not found: ' + mechanism.className);
453+
}
454+
418455
/**
419456
* Returns the events defined in the given mechanism.
420457
*/

src/i18n/locales/en/translation.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"PARAMETER": "parameter",
4747
"PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK": "Parameters can only go in their method's block",
4848
"COMPONENTS": "Components",
49+
"PRIVATE_COMPONENTS": "Private Components",
4950
"EVENTS": "Events",
5051
"EVALUATE_BUT_IGNORE_RESULT": "evaluate but ignore result",
5152
"NONE": "None",
@@ -61,7 +62,9 @@
6162
"OPMODE_TYPE": "What sort of OpMode this is",
6263
"OPMODE_ENABLED": "Whether the OpMode is shown on Driver Station",
6364
"OPMODE_NAME": "The name shown on the Driver Station. If blank will use the class name.",
64-
"OPMODE_GROUP": "An optional group to group OpModes on Driver Station"
65+
"OPMODE_GROUP": "An optional group to group OpModes on Driver Station",
66+
"COMPONENTS": "These components are visible in this mechanism, the robot, and all opmodes.",
67+
"PRIVATE_COMPONENTS": "These components will not be visible in the robot or opmodes. They are only accessible within this mechanism."
6568
},
6669
"CATEGORY":{
6770
"LISTS": "Lists",

0 commit comments

Comments
 (0)