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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

# production
/build
/dist

# python stuff
__pycache__/
Expand Down
270 changes: 195 additions & 75 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.getComponentsFromMechanism(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
149 changes: 126 additions & 23 deletions src/blocks/mrc_mechanism_component_holder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,20 @@ export const BLOCK_NAME = 'mrc_mechanism_component_holder';

const INPUT_MECHANISMS = 'MECHANISMS';
const INPUT_COMPONENTS = 'COMPONENTS';
const INPUT_PRIVATE_COMPONENTS = 'PRIVATE_COMPONENTS';
const INPUT_EVENTS = 'EVENTS';

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

type MechanismComponentHolderExtraState = {
hideMechanisms?: boolean;
hidePrivateComponents?: boolean;
}

export type MechanismComponentHolderBlock = Blockly.Block & MechanismComponentHolderMixin;
interface MechanismComponentHolderMixin extends MechanismComponentHolderMixinType {
mrcHideMechanisms: boolean;
mrcHidePrivateComponents: boolean;
}
type MechanismComponentHolderMixinType = typeof MECHANISM_COMPONENT_HOLDER;

Expand All @@ -72,15 +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);
this.appendStatementInput(INPUT_EVENTS).setCheck(EVENT_OUTPUT).appendField(Blockly.Msg.EVENTS);


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 @@ -95,30 +93,49 @@ const MECHANISM_COMPONENT_HOLDER = {
if (this.mrcHideMechanisms == true) {
extraState.hideMechanisms = this.mrcHideMechanisms;
}
if (this.mrcHidePrivateComponents == true) {
extraState.hidePrivateComponents = this.mrcHidePrivateComponents;
}
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 block to reflect the newly loaded extra state.
*/
* Update the block to reflect the newly loaded extra state.
*/
updateBlock_: function (this: MechanismComponentHolderBlock): void {
if (this.mrcHideMechanisms) {
if (this.getInput(INPUT_MECHANISMS)) {
this.removeInput(INPUT_MECHANISMS)
}
// Handle mechanisms input visibility
if (!this.mrcHideMechanisms) {
this.appendStatementInput(INPUT_MECHANISMS)
.setCheck(MECHANISM_OUTPUT)
.appendField(Blockly.Msg.MECHANISMS);
}
else {
if (this.getInput(INPUT_MECHANISMS) == null) {
this.appendStatementInput(INPUT_MECHANISMS).setCheck(MECHANISM_OUTPUT).appendField('Mechanisms');
this.moveInputBefore(INPUT_MECHANISMS, INPUT_COMPONENTS)
}

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) {
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);
}

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 @@ -179,6 +196,28 @@ const MECHANISM_COMPONENT_HOLDER = {

return components;
},
getPrivateComponents: function (this: MechanismComponentHolderBlock): storageModuleContent.Component[] {
const components: storageModuleContent.Component[] = []

// Get component blocks from the PRIVATE_COMPONENTS input
const privateComponentsInput = this.getInput(INPUT_PRIVATE_COMPONENTS);
if (privateComponentsInput && privateComponentsInput.connection) {
// Walk through all connected component blocks.
let componentBlock = privateComponentsInput.connection.targetBlock();
while (componentBlock) {
if (componentBlock.type === MRC_COMPONENT_NAME) {
const component = (componentBlock as ComponentBlock).getComponent();
if (component) {
components.push(component);
}
}
// Move to the next block in the chain
componentBlock = componentBlock.getNextBlock();
}
}

return components;
},
getEvents: function (this: MechanismComponentHolderBlock): storageModuleContent.Event[] {
const events: storageModuleContent.Event[] = []

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

const components = generator.statementToCode(block, INPUT_COMPONENTS);
const privateComponents = generator.statementToCode(block, INPUT_PRIVATE_COMPONENTS);

if (components) {
code += components;
const allComponents = components + privateComponents;
if (allComponents) {
code += allComponents;
generator.addClassMethodDefinition('define_hardware', code);
}
}
Expand All @@ -266,12 +307,13 @@ 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.
*/
export function hasAnyComponents(workspace: Blockly.Workspace): boolean {
for (const block of workspace.getBlocksByType(BLOCK_NAME)) {
// Check regular components
const componentsInput = block.getInput(INPUT_COMPONENTS);
if (componentsInput && componentsInput.connection) {
// Walk through all connected component blocks.
Expand All @@ -284,6 +326,20 @@ export function hasAnyComponents(workspace: Blockly.Workspace): boolean {
componentBlock = componentBlock.getNextBlock();
}
}

// Check private components
const privateComponentsInput = block.getInput(INPUT_PRIVATE_COMPONENTS);
if (privateComponentsInput && privateComponentsInput.connection) {
// Walk through all connected private component blocks.
let componentBlock = privateComponentsInput.connection.targetBlock();
while (componentBlock) {
if (componentBlock.type === MRC_COMPONENT_NAME && componentBlock.isEnabled()) {
return true;
}
// Move to the next block in the chain
componentBlock = componentBlock.getNextBlock();
}
}
}
return false;
}
Expand All @@ -305,6 +361,20 @@ export function getComponentPorts(workspace: Blockly.Workspace, ports: {[key: st
componentBlock = componentBlock.getNextBlock();
}
}

// Also include private components for port collection
const privateComponentsInput = block.getInput(INPUT_PRIVATE_COMPONENTS);
if (privateComponentsInput && privateComponentsInput.connection) {
// Walk through all connected private component blocks.
let componentBlock = privateComponentsInput.connection.targetBlock();
while (componentBlock) {
if (componentBlock.type === MRC_COMPONENT_NAME && componentBlock.isEnabled()) {
(componentBlock as ComponentBlock).getComponentPorts(ports);
}
// Move to the next block in the chain
componentBlock = componentBlock.getNextBlock();
}
}
});
}

Expand All @@ -330,6 +400,25 @@ export function getComponents(
});
}

export function getPrivateComponents(
workspace: Blockly.Workspace,
components: storageModuleContent.Component[]): void {
// Get the holder block and ask it for the private components.
workspace.getBlocksByType(BLOCK_NAME).forEach(block => {
const privateComponentsFromHolder: storageModuleContent.Component[] =
(block as MechanismComponentHolderBlock).getPrivateComponents();
components.push(...privateComponentsFromHolder);
});
}

export function getAllComponents(
workspace: Blockly.Workspace,
components: storageModuleContent.Component[]): void {
// Get both regular and private components for when creating a mechanism
getComponents(workspace, components);
getPrivateComponents(workspace, components);
}

export function getEvents(
workspace: Blockly.Workspace,
events: storageModuleContent.Event[]): void {
Expand All @@ -340,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;
});
}
3 changes: 3 additions & 0 deletions src/blocks/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg {
MECHANISMS: t('MECHANISMS'),
OPMODES: t('OPMODES'),
COMPONENTS: t('BLOCKLY.COMPONENTS'),
COMPONENTS_TOOLTIP: t('BLOCKLY.TOOLTIP.COMPONENTS'),
PRIVATE_COMPONENTS: t('BLOCKLY.PRIVATE_COMPONENTS'),
PRIVATE_COMPONENTS_TOOLTIP: t('BLOCKLY.TOOLTIP.PRIVATE_COMPONENTS'),
EVENTS: t('BLOCKLY.EVENTS'),
EVALUATE_BUT_IGNORE_RESULT: t('BLOCKLY.EVALUATE_BUT_IGNORE_RESULT'),
EVALUATE_BUT_IGNORE_RESULT_TOOLTIP:
Expand Down
45 changes: 41 additions & 4 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,13 +274,30 @@ 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 ||
this.currentModule?.moduleType === storageModule.ModuleType.MECHANISM) {
mechanismComponentHolder.getAllComponents(this.blocklyWorkspace, components);
}
return components;
}

public getMethodsForWithinFromWorkspace(): storageModuleContent.Method[] {
const methods: storageModuleContent.Method[] = [];
classMethodDef.getMethodsForWithin(this.blocklyWorkspace, methods);
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 @@ -415,6 +433,25 @@ export class Editor {
throw new Error('getComponentsFromMechanism: mechanism not found: ' + mechanism.className);
}

/**
* Returns ALL components (including private components) defined in the given mechanism.
* This is used when creating mechanism blocks that need all components for port parameters.
*/
public getAllComponentsFromMechanism(mechanism: storageModule.Mechanism): storageModuleContent.Component[] {
if (this.currentModule?.modulePath === mechanism.modulePath) {
return this.getAllComponentsFromWorkspace();
}
if (mechanism.className in this.mechanismClassNameToModuleContent) {
const moduleContent = this.mechanismClassNameToModuleContent[mechanism.className];
const allComponents: storageModuleContent.Component[] = [
...moduleContent.getComponents(),
...moduleContent.getPrivateComponents(),
]
return allComponents;
}
throw new Error('getAllComponentsFromMechanism: mechanism not found: ' + mechanism.className);
}

/**
* Returns the events defined in the given mechanism.
*/
Expand Down
5 changes: 4 additions & 1 deletion src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"PARAMETER": "parameter",
"PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK": "Parameters can only go in their method's block",
"COMPONENTS": "Components",
"PRIVATE_COMPONENTS": "Private Components",
"EVENTS": "Events",
"EVALUATE_BUT_IGNORE_RESULT": "evaluate but ignore result",
"NONE": "None",
Expand All @@ -61,7 +62,9 @@
"OPMODE_TYPE": "What sort of OpMode this is",
"OPMODE_ENABLED": "Whether the OpMode is shown on Driver Station",
"OPMODE_NAME": "The name shown on the Driver Station. If blank will use the class name.",
"OPMODE_GROUP": "An optional group to group OpModes on Driver Station"
"OPMODE_GROUP": "An optional group to group OpModes on Driver Station",
"COMPONENTS": "These components are visible in this mechanism, the robot, and all opmodes.",
"PRIVATE_COMPONENTS": "These components will not be visible in the robot or opmodes. They are only accessible within this mechanism."
},
"CATEGORY":{
"LISTS": "Lists",
Expand Down
Loading