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
311 changes: 187 additions & 124 deletions src/blocks/mrc_call_python_function.ts

Large diffs are not rendered by default.

77 changes: 47 additions & 30 deletions src/blocks/mrc_class_method_def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const BLOCK_NAME = 'mrc_class_method_def';

const FIELD_METHOD_NAME = 'NAME';

export type Parameter = {
type Parameter = {
name: string,
type?: string,
};
Expand All @@ -51,12 +51,12 @@ interface ClassMethodDefMixin extends ClassMethodDefMixinType {
mrcReturnType: string,
mrcParameters: Parameter[],
mrcPythonMethodName: string,
mrcMethod: commonStorage.Method | null,
mrcFuncName: string | null,
}
type ClassMethodDefMixinType = typeof CLASS_METHOD_DEF;

/** Extra state for serialising call_python_* blocks. */
export type ClassMethodDefExtraState = {
type ClassMethodDefExtraState = {
/**
* Can change name and parameters and return type
*/
Expand Down Expand Up @@ -137,18 +137,16 @@ const CLASS_METHOD_DEF = {
this.mrcCanBeCalledWithinClass = extraState.canBeCalledWithinClass;
this.mrcCanBeCalledOutsideClass = extraState.canBeCalledOutsideClass;
this.mrcPythonMethodName = extraState.pythonMethodName ? extraState.pythonMethodName : '';
this.mrcFuncName = null; // Set during python code generation.
this.mrcReturnType = extraState.returnType;
this.mrcParameters = [];
this.mrcMethod = null;

extraState.params.forEach((param) => {
this.mrcParameters.push({
'name': param.name,
'type': param.type,
});
});
this.updateBlock_();
mutateMethodCallers(this.workspace, this.getFieldValue(FIELD_METHOD_NAME), this.saveExtraState());
},
/**
* Update the block to reflect the newly loaded extra state.
Expand Down Expand Up @@ -194,7 +192,12 @@ const CLASS_METHOD_DEF = {
paramBlock.nextConnection && paramBlock.nextConnection.targetBlock();
}
this.mrcUpdateParams();
mutateMethodCallers(this.workspace, this.getFieldValue(FIELD_METHOD_NAME), this.saveExtraState());
if (this.mrcCanBeCalledWithinClass) {
const methodForWithin = this.getMethodForWithin();
if (methodForWithin) {
mutateMethodCallers(this.workspace, this.id, methodForWithin);
}
}
},
decompose: function (this: ClassMethodDefBlock, workspace: Blockly.Workspace) {
// This is a special sub-block that only gets created in the mutator UI.
Expand Down Expand Up @@ -259,15 +262,43 @@ const CLASS_METHOD_DEF = {
const oldName = nameField.getValue();
if (oldName && oldName !== name && oldName !== legalName) {
// Rename any callers.
renameMethodCallers(this.workspace, oldName, legalName);
renameMethodCallers(this.workspace, this.id, legalName);
}
return legalName;
},
getMethod: function (this: ClassMethodDefBlock): commonStorage.Method | null {
const method: commonStorage.Method = {
blockId: this.id,
visibleName: this.getFieldValue(FIELD_METHOD_NAME),
pythonName: this.mrcFuncName ? this.mrcFuncName : '',
returnType: this.mrcReturnType,
args: [{
name: 'self',
type: '',
}],
};
if (!method.pythonName) {
method.pythonName = method.visibleName;
}
this.mrcParameters.forEach(param => {
method.args.push({
name: param.name,
type: param.type ? param.type : '',
});
});
return method;
},
getMethodForWithin: function (this: ClassMethodDefBlock): commonStorage.Method | null {
return this.mrcCanBeCalledWithinClass ? this.mrcMethod : null;
if (this.mrcCanBeCalledWithinClass) {
return this.getMethod();
}
return null;
},
getMethodForOutside: function (this: ClassMethodDefBlock): commonStorage.Method | null {
return this.mrcCanBeCalledOutsideClass ? this.mrcMethod : null;
if (this.mrcCanBeCalledOutsideClass) {
return this.getMethod();
}
return null;
},
canChangeSignature: function (this: ClassMethodDefBlock): boolean {
return this.mrcCanChangeSignature;
Expand Down Expand Up @@ -342,6 +373,7 @@ export const pythonFromBlock = function (
) {
const blocklyName = block.mrcPythonMethodName ? block.mrcPythonMethodName : block.getFieldValue(FIELD_METHOD_NAME);

// Call generator.getProcedureName so our function name is not a reserved word such as "while".
const funcName = generator.getProcedureName(blocklyName);

let xfix1 = '';
Expand Down Expand Up @@ -380,7 +412,8 @@ export const pythonFromBlock = function (
generator.defineClassVariables() + branch;
}
else if (generator.inBaseClassMethod(blocklyName)){
// Special case for update, to also call the update method of the base class
// Special case for methods inherited from the based class: generate the
// call to the method in the base class.
branch = generator.INDENT + 'super().' + blocklyName + '()\n' + branch;
}
if (returnValue) {
Expand Down Expand Up @@ -416,25 +449,9 @@ export const pythonFromBlock = function (
code = generator.scrub_(block, code);
generator.addClassMethodDefinition(funcName, code);

if (block.mrcCanBeCalledWithinClass || block.mrcCanBeCalledOutsideClass) {
// Update the mrcMethod.
block.mrcMethod = {
blockId: block.id,
visibleName: block.getFieldValue(FIELD_METHOD_NAME),
pythonName: funcName,
returnType: block.mrcReturnType,
args: [{
name: 'self',
type: '',
}],
};
block.mrcParameters.forEach(param => {
block.mrcMethod!.args.push({
name: param.name,
type: param.type ? param.type : '',
});
});
}
// Save the name of the function we just generated so we can use it to create the commonStorage.Method.
// in the getMethod function.
block.mrcFuncName = funcName;

return '';
}
Expand Down
27 changes: 18 additions & 9 deletions src/blocks/mrc_component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import { MRC_STYLE_COMPONENTS } from '../themes/styles'
import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
import { getAllowedTypesForSetCheck, getClassData, getModuleData, getSubclassNames } from './utils/python';
import * as ToolboxItems from '../toolbox/items';
import * as CommonStorage from '../storage/common_storage';
import * as toolboxItems from '../toolbox/items';
import * as commonStorage from '../storage/common_storage';
import { createPortShadow } from './mrc_port';
import { createNumberShadowValue } from './utils/value';
import { ClassData, FunctionData } from './utils/python_json_types';
Expand All @@ -52,7 +52,7 @@ type ComponentExtraState = {
params?: ConstructorArg[],
}

type ComponentBlock = Blockly.Block & ComponentMixin;
export type ComponentBlock = Blockly.Block & ComponentMixin;
interface ComponentMixin extends ComponentMixinType {
mrcArgs: ConstructorArg[],
hideParams: boolean,
Expand Down Expand Up @@ -136,7 +136,16 @@ const COMPONENT = {
}
}
}
}
},
getComponent: function (this: ComponentBlock): commonStorage.Component | null {
const componentName = this.getFieldValue(FIELD_NAME);
const componentType = this.getFieldValue(FIELD_TYPE);
return {
blockId: this.id,
name: componentName,
className: componentType,
};
},
}

export const setup = function () {
Expand Down Expand Up @@ -177,8 +186,8 @@ export const pythonFromBlock = function (
return code;
}

export function getAllPossibleComponents(hideParams: boolean): ToolboxItems.ContentsType[] {
const contents: ToolboxItems.ContentsType[] = [];
export function getAllPossibleComponents(hideParams: boolean): toolboxItems.ContentsType[] {
const contents: toolboxItems.ContentsType[] = [];
// Iterate through all the components subclasses and add definition blocks.
const componentTypes = getSubclassNames('component.Component');

Expand All @@ -190,7 +199,7 @@ export function getAllPossibleComponents(hideParams: boolean): ToolboxItems.Cont

const componentName = (
'my_' +
CommonStorage.pascalCaseToSnakeCase(
commonStorage.pascalCaseToSnakeCase(
componentType.substring(componentType.lastIndexOf('.') + 1)));

classData.staticMethods.forEach(staticFunctionData => {
Expand All @@ -204,7 +213,7 @@ export function getAllPossibleComponents(hideParams: boolean): ToolboxItems.Cont
}

function createComponentBlock(
componentName: string, classData: ClassData, staticFunctionData: FunctionData, hideParams: boolean): ToolboxItems.Block {
componentName: string, classData: ClassData, staticFunctionData: FunctionData, hideParams: boolean): toolboxItems.Block {
const extraState: ComponentExtraState = {
importModule: classData.moduleName,
staticFunctionName: staticFunctionData.functionName,
Expand Down Expand Up @@ -232,7 +241,7 @@ function createComponentBlock(
}
}
}
return new ToolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null);
return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null);
}

function getPortTypeForArgument(argName: string): string | null {
Expand Down
42 changes: 34 additions & 8 deletions src/blocks/mrc_event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,22 @@
* @author [email protected] (Alan Smith)
*/
import * as Blockly from 'blockly';
import { Order } from 'blockly/python';

import { MRC_STYLE_EVENTS } from '../themes/styles'
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
import { MUTATOR_BLOCK_NAME, PARAM_CONTAINER_BLOCK_NAME, MethodMutatorArgBlock } from './mrc_param_container'
import * as ChangeFramework from './utils/change_framework';
import { BLOCK_NAME as MRC_MECHANISM_COMPONENT_HOLDER } from './mrc_mechanism_component_holder';
import * as toolboxItems from '../toolbox/items';
import * as commonStorage from '../storage/common_storage';
import { renameMethodCallers, mutateMethodCallers } from './mrc_call_python_function'

export const BLOCK_NAME = 'mrc_event';
export const OUTPUT_NAME = 'mrc_event';

const FIELD_EVENT_NAME = 'NAME';

export type Parameter = {
type Parameter = {
name: string,
type?: string,
};
Expand Down Expand Up @@ -112,7 +113,7 @@ const EVENT = {
const nameField = new Blockly.FieldTextInput(name);
input.insertFieldAt(0, nameField, FIELD_EVENT_NAME);
this.setMutator(new Blockly.icons.MutatorIcon([MUTATOR_BLOCK_NAME], this));
// nameField.setValidator(this.mrcNameFieldValidator.bind(this, nameField));
nameField.setValidator(this.mrcNameFieldValidator.bind(this, nameField));

this.mrcUpdateParams();
},
Expand All @@ -135,7 +136,7 @@ const EVENT = {
paramBlock.nextConnection && paramBlock.nextConnection.targetBlock();
}
this.mrcUpdateParams();
//mutateMethodCallers(this.workspace, this.getFieldValue(FIELD_EVENT_NAME), this.saveExtraState());
mutateMethodCallers(this.workspace, this.id, this.getEvent());
},
decompose: function (this: EventBlock, workspace: Blockly.Workspace) {
// This is a special sub-block that only gets created in the mutator UI.
Expand Down Expand Up @@ -180,6 +181,18 @@ const EVENT = {
input.removeField(fieldName);
});
},
mrcNameFieldValidator(this: EventBlock, nameField: Blockly.FieldTextInput, name: string): string {
// Strip leading and trailing whitespace.
name = name.trim();

const legalName = name;
const oldName = nameField.getValue();
if (oldName && oldName !== name && oldName !== legalName) {
// Rename any callers.
renameMethodCallers(this.workspace, this.id, legalName);
}
return legalName;
},
onBlockChanged(block: Blockly.BlockSvg, blockEvent: Blockly.Events.BlockBase): void {
const blockBlock = block as Blockly.Block;

Expand All @@ -193,9 +206,23 @@ const EVENT = {
}
// If we end up here it shouldn't be allowed
block.unplug(true);
blockBlock.setWarningText('Events can only go in the events block');
blockBlock.setWarningText('Events can only go in the events section of the robot or mechanism');
}
},
getEvent: function (this: EventBlock): commonStorage.Event {
const event: commonStorage.Event = {
blockId: this.id,
name: this.getFieldValue(FIELD_EVENT_NAME),
args: [],
};
this.mrcParameters.forEach(param => {
event.args.push({
name: param.name,
type: param.type ? param.type : '',
});
});
return event;
},
}

export const setup = function () {
Expand All @@ -204,9 +231,8 @@ export const setup = function () {

export const pythonFromBlock = function (
block: EventBlock,
generator: ExtendedPythonGenerator,
) {
//TODO (Alan): What should this do here??
generator: ExtendedPythonGenerator) {
// TODO (Alan): What should this do here??
return '';
}

Expand Down
37 changes: 27 additions & 10 deletions src/blocks/mrc_mechanism_component_holder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ import * as commonStorage from '../storage/common_storage';
import { OUTPUT_NAME as MECHANISM_OUTPUT } from './mrc_mechanism';
import { BLOCK_NAME as MRC_MECHANISM_NAME } from './mrc_mechanism';
import { BLOCK_NAME as MRC_COMPONENT_NAME } from './mrc_component';
import * as Component from './mrc_component';
import { OUTPUT_NAME as COMPONENT_OUTPUT } from './mrc_component';
import { ComponentBlock } from './mrc_component';
import { BLOCK_NAME as MRC_EVENT_NAME } from './mrc_event';
import { OUTPUT_NAME as EVENT_OUTPUT } from './mrc_event';
import { EventBlock } from './mrc_event';

export const BLOCK_NAME = 'mrc_mechanism_component_holder';

Expand Down Expand Up @@ -139,15 +140,9 @@ const MECHANISM_COMPONENT_HOLDER = {
let componentBlock = componentsInput.connection.targetBlock();
while (componentBlock) {
if (componentBlock.type === MRC_COMPONENT_NAME) {
const componentName = componentBlock.getFieldValue(Component.FIELD_NAME);
const componentType = componentBlock.getFieldValue(Component.FIELD_TYPE);

if (componentName && componentType) {
components.push({
blockId: componentBlock.id,
name: componentName,
className: componentType,
});
const component = (componentBlock as ComponentBlock).getComponent();
if (component) {
components.push(component);
}
}
// Move to the next block in the chain
Expand All @@ -157,6 +152,28 @@ const MECHANISM_COMPONENT_HOLDER = {

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

// Get event blocks from the EVENTS input
const eventsInput = this.getInput('EVENTS');
if (eventsInput && eventsInput.connection) {
// Walk through all connected event blocks.
let eventBlock = eventsInput.connection.targetBlock();
while (eventBlock) {
if (eventBlock.type === MRC_EVENT_NAME) {
const event = (eventBlock as EventBlock).getEvent();
if (event) {
events.push(event);
}
}
// Move to the next block in the chain
eventBlock = eventBlock.getNextBlock();
}
}

return events;
},
}

let toolboxUpdateTimeout: NodeJS.Timeout | null = null;
Expand Down
Loading