Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
73 changes: 73 additions & 0 deletions src/blocks/mrc_call_python_function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,25 @@ const CALL_PYTHON_FUNCTION = {
let foundComponent = false;
const componentsInScope: storageModuleContent.Component[] = [];
componentsInScope.push(...this.getComponentsFromRobot());

// If we're in a robot context, also include components from mechanisms
if (editor.getCurrentModuleType() === storageModule.ModuleType.ROBOT) {
editor.getMechanismsFromRobot().forEach(mechanismInRobot => {
const mechanism = editor.getMechanism(mechanismInRobot);
if (mechanism) {
const mechanismComponents = editor.getComponentsFromMechanism(mechanism);
mechanismComponents.forEach(component => {
// Create a copy of the component with the mechanism-prefixed name
const prefixedComponent = {
...component,
name: mechanismInRobot.name + '.' + component.name
};
componentsInScope.push(prefixedComponent);
});
}
});
}

if (editor.getCurrentModuleType() === storageModule.ModuleType.MECHANISM) {
componentsInScope.push(...editor.getComponentsFromWorkspace());
}
Expand Down Expand Up @@ -1142,6 +1161,34 @@ export function getInstanceComponentBlocks(
return contents;
}

export function getInstanceMechanismComponentBlocks(
component: storageModuleContent.Component, mechanismInRobot: storageModuleContent.MechanismInRobot): toolboxItems.ContentsType[] {
const contents: toolboxItems.ContentsType[] = [];

const classData = getClassData(component.className);
if (!classData) {
throw new Error('Could not find classData for ' + component.className);
}
const functions = classData.instanceMethods;

const componentClassData = getClassData('component.Component');
if (!componentClassData) {
throw new Error('Could not find classData for component.Component');
}
const componentFunctions = componentClassData.instanceMethods;

for (const functionData of functions) {
// Skip the functions that are also defined in componentFunctions.
if (findSuperFunctionData(functionData, componentFunctions)) {
continue;
}
const block = createInstanceMechanismComponentBlock(component, functionData, mechanismInRobot);
contents.push(block);
}

return contents;
}

function createInstanceComponentBlock(
component: storageModuleContent.Component, functionData: FunctionData): toolboxItems.Block {
const extraState: CallPythonFunctionExtraState = {
Expand All @@ -1166,6 +1213,32 @@ function createInstanceComponentBlock(
return createBlock(extraState, fields, inputs);
}

function createInstanceMechanismComponentBlock(
component: storageModuleContent.Component,
functionData: FunctionData,
mechanismInRobot: storageModuleContent.MechanismInRobot): toolboxItems.Block {
const extraState: CallPythonFunctionExtraState = {
functionKind: FunctionKind.INSTANCE_COMPONENT,
returnType: functionData.returnType,
args: [],
tooltip: functionData.tooltip,
importModule: '',
componentClassName: component.className,
componentName: mechanismInRobot.name + '.' + component.name, // Prefix with mechanism name
componentId: component.componentId,
};
const fields: {[key: string]: any} = {};
fields[FIELD_COMPONENT_NAME] = mechanismInRobot.name + '.' + component.name; // Prefix with mechanism name
fields[FIELD_FUNCTION_NAME] = functionData.functionName;
const inputs: {[key: string]: any} = {};
// For INSTANCE_COMPONENT functions, the 0 argument is 'self', but
// self is represented by the FIELD_COMPONENT_NAME field.
// We don't include the arg for the self argument because we don't need a socket for it.
const argsWithoutSelf = functionData.args.slice(1);
processArgs(argsWithoutSelf, extraState, inputs);
return createBlock(extraState, fields, inputs);
}

export function addInstanceRobotBlocks(
methods: storageModuleContent.Method[],
contents: toolboxItems.ContentsType[]) {
Expand Down
2 changes: 1 addition & 1 deletion src/blocks/mrc_mechanism.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ const MECHANISM = {

if (foundMechanism) {
const components: storageModuleContent.Component[] = [];
components.push(...editor.getComponentsFromMechanism(foundMechanism));
components.push(...editor.getAllComponentsFromMechanism(foundMechanism));

// If the mechanism class name has changed, update this blcok.
if (this.getFieldValue(FIELD_TYPE) !== foundMechanism.className) {
Expand Down
132 changes: 128 additions & 4 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 @@ -78,8 +81,16 @@ const MECHANISM_COMPONENT_HOLDER = {
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);
Expand All @@ -95,30 +106,71 @@ 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.
*/
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.
*/
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('Mechanisms');
this.appendStatementInput(INPUT_MECHANISMS).setCheck(MECHANISM_OUTPUT).appendField(Blockly.Msg.MECHANISMS);
this.moveInputBefore(INPUT_MECHANISMS, INPUT_COMPONENTS)
}
}

// 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)
}
}

// Update components tooltip based on private components visibility
this.updateComponentsTooltip_();
},
onBlockChanged: function (block: Blockly.BlockSvg, blockEvent: Blockly.Events.BlockBase) {
if (blockEvent.type == Blockly.Events.BLOCK_MOVE) {
Expand Down Expand Up @@ -179,6 +231,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 +317,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 body = components + privateComponents;
if (body) {
code += body;
generator.addClassMethodDefinition('define_hardware', code);
}
}
Expand All @@ -272,6 +348,7 @@ export const pythonFromBlock = function (
*/
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 +361,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 +396,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 +435,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 Down
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
Loading