Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 144 additions & 97 deletions src/blocks/mrc_call_python_function.ts

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions src/blocks/mrc_mechanism.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,9 @@ const MECHANISM = {
}

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

// If the mechanism class name has changed, update this blcok.
if (this.getFieldValue(FIELD_TYPE) !== foundMechanism.className) {
Expand Down
109 changes: 44 additions & 65 deletions src/blocks/mrc_mechanism_component_holder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,10 @@ function setName(block: Blockly.BlockSvg){

const MECHANISM_COMPONENT_HOLDER = {
/**
* Block initialization.
*/
* Block initialization.
*/
init: function (this: MechanismComponentHolderBlock): void {
this.setInputsInline(false);
this.appendStatementInput(INPUT_MECHANISMS).setCheck(MECHANISM_OUTPUT).appendField(Blockly.Msg.MECHANISMS);
this.appendStatementInput(INPUT_COMPONENTS).setCheck(COMPONENT_OUTPUT).appendField(Blockly.Msg.COMPONENTS);
const privateComponentsInput = this.appendStatementInput(INPUT_PRIVATE_COMPONENTS).setCheck(COMPONENT_OUTPUT).appendField(Blockly.Msg.PRIVATE_COMPONENTS);
// Set tooltip on the private components field
const privateComponentsField = privateComponentsInput.fieldRow[0];
if (privateComponentsField) {
privateComponentsField.setTooltip(Blockly.Msg.PRIVATE_COMPONENTS_TOOLTIP);
}
this.appendStatementInput(INPUT_EVENTS).setCheck(EVENT_OUTPUT).appendField(Blockly.Msg.EVENTS);

// Update components tooltip based on private components visibility
this.updateComponentsTooltip_();

this.setOutput(false);
this.setStyle(MRC_STYLE_MECHANISMS);
ChangeFramework.registerCallback(MRC_COMPONENT_NAME, [Blockly.Events.BLOCK_MOVE, Blockly.Events.BLOCK_CHANGE], this.onBlockChanged);
Expand All @@ -112,65 +99,43 @@ const MECHANISM_COMPONENT_HOLDER = {
return extraState;
},
/**
* Applies the given state to this block.
*/
* Applies the given state to this block.
*/
loadExtraState: function (this: MechanismComponentHolderBlock, extraState: MechanismComponentHolderExtraState): void {
this.mrcHideMechanisms = (extraState.hideMechanisms == undefined) ? false : extraState.hideMechanisms;
this.mrcHidePrivateComponents = (extraState.hidePrivateComponents == undefined) ? false : extraState.hidePrivateComponents;
this.updateBlock_();
},
/**
* Update the components tooltip based on private components visibility.
* Update the block to reflect the newly loaded extra state.
*/
updateComponentsTooltip_: function (this: MechanismComponentHolderBlock): void {
const componentsInput = this.getInput(INPUT_COMPONENTS);
if (componentsInput && componentsInput.fieldRow[0]) {
const componentsField = componentsInput.fieldRow[0];
// Only show tooltip if private components are also visible (not hidden)
if (!this.mrcHidePrivateComponents) {
componentsField.setTooltip(Blockly.Msg.COMPONENTS_TOOLTIP);
} else {
componentsField.setTooltip('');
}
}
},
/**
* Update the block to reflect the newly loaded extra state.
*/
updateBlock_: function (this: MechanismComponentHolderBlock): void {
// Handle mechanisms input visibility
if (this.mrcHideMechanisms) {
if (this.getInput(INPUT_MECHANISMS)) {
this.removeInput(INPUT_MECHANISMS)
}
}
else {
if (this.getInput(INPUT_MECHANISMS) == null) {
this.appendStatementInput(INPUT_MECHANISMS).setCheck(MECHANISM_OUTPUT).appendField(Blockly.Msg.MECHANISMS);
this.moveInputBefore(INPUT_MECHANISMS, INPUT_COMPONENTS)
}
if (!this.mrcHideMechanisms) {
this.appendStatementInput(INPUT_MECHANISMS)
.setCheck(MECHANISM_OUTPUT)
.appendField(Blockly.Msg.MECHANISMS);
}

const componentsField = new Blockly.FieldLabel(Blockly.Msg.COMPONENTS);
this.appendStatementInput(INPUT_COMPONENTS)
.setCheck(COMPONENT_OUTPUT)
.appendField(componentsField);

// Handle private components input visibility
if (this.mrcHidePrivateComponents) {
if (this.getInput(INPUT_PRIVATE_COMPONENTS)) {
this.removeInput(INPUT_PRIVATE_COMPONENTS)
}
}
else {
if (this.getInput(INPUT_PRIVATE_COMPONENTS) == null) {
const privateComponentsInput = this.appendStatementInput(INPUT_PRIVATE_COMPONENTS).setCheck(COMPONENT_OUTPUT).appendField(Blockly.Msg.PRIVATE_COMPONENTS);
// Set tooltip on the field
const privateComponentsField = privateComponentsInput.fieldRow[0];
if (privateComponentsField) {
privateComponentsField.setTooltip(Blockly.Msg.PRIVATE_COMPONENTS_TOOLTIP);
}
this.moveInputBefore(INPUT_PRIVATE_COMPONENTS, INPUT_EVENTS)
}
if (!this.mrcHidePrivateComponents) {
const privateComponentsField = new Blockly.FieldLabel(Blockly.Msg.PRIVATE_COMPONENTS);
this.appendStatementInput(INPUT_PRIVATE_COMPONENTS)
.setCheck(COMPONENT_OUTPUT)
.appendField(privateComponentsField);
// Set tooltips on both componentsField and privateComponentsField.
componentsField.setTooltip(Blockly.Msg.COMPONENTS_TOOLTIP);
privateComponentsField.setTooltip(Blockly.Msg.PRIVATE_COMPONENTS_TOOLTIP);
}

// Update components tooltip based on private components visibility
this.updateComponentsTooltip_();

this.appendStatementInput(INPUT_EVENTS)
.setCheck(EVENT_OUTPUT)
.appendField(Blockly.Msg.EVENTS);
},
onBlockChanged: function (block: Blockly.BlockSvg, blockEvent: Blockly.Events.BlockBase) {
if (blockEvent.type == Blockly.Events.BLOCK_MOVE) {
Expand Down Expand Up @@ -319,9 +284,9 @@ function pythonFromBlockInMechanism(block: MechanismComponentHolderBlock, genera
const components = generator.statementToCode(block, INPUT_COMPONENTS);
const privateComponents = generator.statementToCode(block, INPUT_PRIVATE_COMPONENTS);

const body = components + privateComponents;
if (body) {
code += body;
const allComponents = components + privateComponents;
if (allComponents) {
code += allComponents;
generator.addClassMethodDefinition('define_hardware', code);
}
}
Expand All @@ -342,7 +307,7 @@ export const pythonFromBlock = function (

// Misc

/**n
/**
* Returns true if the given workspace has a mrc_mechanism_component_holder
* block that contains at least one component.
*/
Expand Down Expand Up @@ -464,3 +429,17 @@ export function getEvents(
events.push(...eventsFromHolder);
});
}

/**
* Hide private components.
* This function should only be called when upgrading old projects.
*/
export function hidePrivateComponents(workspace: Blockly.Workspace) {
// Make sure the workspace is headless.
if (workspace.rendered) {
throw new Error('hidePrivateComponents should never be called with a rendered workspace.');
}
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
(block as MechanismComponentHolderBlock).mrcHidePrivateComponents = true;
});
}
39 changes: 18 additions & 21 deletions src/editor/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,25 +246,26 @@ export class Editor {
const blocks = Blockly.serialization.workspaces.save(this.blocklyWorkspace);
const mechanisms: storageModuleContent.MechanismInRobot[] = this.getMechanismsFromWorkspace();
const components: storageModuleContent.Component[] = this.getComponentsFromWorkspace();
const privateComponents: storageModuleContent.Component[] = this.getPrivateComponentsFromWorkspace();
const events: storageModuleContent.Event[] = this.getEventsFromWorkspace();
const methods: storageModuleContent.Method[] = (
this.currentModule?.moduleType === storageModule.ModuleType.ROBOT ||
this.currentModule?.moduleType === storageModule.ModuleType.MECHANISM)
? this.getMethodsForOutsideFromWorkspace()
: [];
return storageModuleContent.makeModuleContentText(
this.currentModule, blocks, mechanisms, components, events, methods);
this.currentModule, blocks, mechanisms, components, privateComponents, events, methods);
}

public getMechanismsFromWorkspace(): storageModuleContent.MechanismInRobot[] {
private getMechanismsFromWorkspace(): storageModuleContent.MechanismInRobot[] {
const mechanisms: storageModuleContent.MechanismInRobot[] = [];
if (this.currentModule?.moduleType === storageModule.ModuleType.ROBOT) {
mechanismComponentHolder.getMechanisms(this.blocklyWorkspace, mechanisms);
}
return mechanisms;
}

public getComponentsFromWorkspace(): storageModuleContent.Component[] {
private getComponentsFromWorkspace(): storageModuleContent.Component[] {
const components: storageModuleContent.Component[] = [];
if (this.currentModule?.moduleType === storageModule.ModuleType.ROBOT ||
this.currentModule?.moduleType === storageModule.ModuleType.MECHANISM) {
Expand All @@ -273,6 +274,14 @@ export class Editor {
return components;
}

private getPrivateComponentsFromWorkspace(): storageModuleContent.Component[] {
const components: storageModuleContent.Component[] = [];
if (this.currentModule?.moduleType === storageModule.ModuleType.MECHANISM) {
mechanismComponentHolder.getPrivateComponents(this.blocklyWorkspace, components);
}
return components;
}

public getAllComponentsFromWorkspace(): storageModuleContent.Component[] {
const components: storageModuleContent.Component[] = [];
if (this.currentModule?.moduleType === storageModule.ModuleType.ROBOT ||
Expand All @@ -288,7 +297,7 @@ export class Editor {
return methods;
}

public getMethodsForOutsideFromWorkspace(): storageModuleContent.Method[] {
private getMethodsForOutsideFromWorkspace(): storageModuleContent.Method[] {
const methods: storageModuleContent.Method[] = [];
classMethodDef.getMethodsForOutside(this.blocklyWorkspace, methods);
return methods;
Expand Down Expand Up @@ -433,24 +442,12 @@ export class Editor {
return this.getAllComponentsFromWorkspace();
}
if (mechanism.className in this.mechanismClassNameToModuleContent) {
// For saved mechanisms, we need to reconstruct all components from the blocks
// since only public components are saved in the module content
const moduleContent = this.mechanismClassNameToModuleContent[mechanism.className];
const blocks = moduleContent.getBlocks();

// Create a temporary workspace to load the mechanism's blocks
const tempWorkspace = new Blockly.Workspace();
try {
Blockly.serialization.workspaces.load(blocks, tempWorkspace);

// Extract all components (public and private) from the temporary workspace
const allComponents: storageModuleContent.Component[] = [];
mechanismComponentHolder.getAllComponents(tempWorkspace, allComponents);

return allComponents;
} finally {
tempWorkspace.dispose();
}
const allComponents: storageModuleContent.Component[] = [
...moduleContent.getComponents(),
...moduleContent.getPrivateComponents(),
]
return allComponents;
}
throw new Error('getAllComponentsFromMechanism: mechanism not found: ' + mechanism.className);
}
Expand Down
4 changes: 2 additions & 2 deletions src/modules/mechanism_start.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{
"type": "mrc_class_method_def",
"x": 10,
"y": 110,
"y": 150,
"deletable": false,
"editable": false,
"extraState": {
Expand All @@ -23,7 +23,7 @@
{
"type": "mrc_class_method_def",
"x": 10,
"y": 190,
"y": 230,
"deletable": false,
"editable": false,
"extraState": {
Expand Down
4 changes: 3 additions & 1 deletion src/modules/robot_start.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
"y": 10,
"deletable": false,
"editable": false,
"extraState": {}
"extraState": {
"hidePrivateComponents" : true
}
}
]
}
Expand Down
35 changes: 31 additions & 4 deletions src/storage/module_content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,18 @@ export type Event = {
};

function startingBlocksToModuleContentText(
module: storageModule.Module, startingBlocks: { [key: string]: any }): string {
module: storageModule.Module, startingBlocks: {[key: string]: any}): string {
const mechanisms: MechanismInRobot[] = [];
const components: Component[] = [];
const privateComponents: Component[] = [];
const events: Event[] = [];
const methods: Method[] = [];
return makeModuleContentText(
module,
startingBlocks,
mechanisms,
components,
privateComponents,
events,
methods);
}
Expand Down Expand Up @@ -125,9 +127,10 @@ export function newOpModeContent(projectName: string, opModeClassName: string):
*/
export function makeModuleContentText(
module: storageModule.Module,
blocks: { [key: string]: any },
blocks: {[key: string]: any},
mechanisms: MechanismInRobot[],
components: Component[],
privateComponents: Component[],
events: Event[],
methods: Method[]): string {
if (!module.moduleId) {
Expand All @@ -139,6 +142,7 @@ export function makeModuleContentText(
blocks,
mechanisms,
components,
privateComponents,
events,
methods);
return moduleContent.getModuleContentText();
Expand All @@ -151,6 +155,7 @@ export function parseModuleContentText(moduleContentText: string): ModuleContent
!('blocks' in parsedContent) ||
!('mechanisms' in parsedContent) ||
!('components' in parsedContent) ||
!('privateComponents' in parsedContent) ||
!('events' in parsedContent) ||
!('methods' in parsedContent)) {
throw new Error('Module content text is not valid.');
Expand All @@ -161,6 +166,7 @@ export function parseModuleContentText(moduleContentText: string): ModuleContent
parsedContent.blocks,
parsedContent.mechanisms,
parsedContent.components,
parsedContent.privateComponents,
parsedContent.events,
parsedContent.methods);
}
Expand All @@ -169,9 +175,10 @@ export class ModuleContent {
constructor(
private moduleType: storageModule.ModuleType,
private moduleId: string,
private blocks : { [key: string]: any },
private blocks : {[key: string]: any},
private mechanisms: MechanismInRobot[],
private components: Component[],
private privateComponents: Component[],
private events: Event[],
private methods: Method[]) {
}
Expand All @@ -188,10 +195,14 @@ export class ModuleContent {
return this.moduleId;
}

getBlocks(): { [key: string]: any } {
getBlocks(): {[key: string]: any} {
return this.blocks;
}

setBlocks(blocks: {[key: string]: any}): void {
this.blocks = blocks;
}

getMechanisms(): MechanismInRobot[] {
return this.mechanisms;
}
Expand All @@ -200,6 +211,10 @@ export class ModuleContent {
return this.components;
}

getPrivateComponents(): Component[] {
return this.privateComponents;
}

getEvents(): Event[] {
return this.events;
}
Expand Down Expand Up @@ -252,3 +267,15 @@ export class ModuleContent {
}
}
}

/**
* Add privateComponents field.
* This function should only called when upgrading old projects.
*/
export function addPrivateComponents(moduleContentText: string): string {
const parsedContent = JSON.parse(moduleContentText);
if (!('privateComponents' in parsedContent)) {
parsedContent.privateComponents = [];
}
return JSON.stringify(parsedContent, null, 2);
}
Loading