Skip to content

Commit b214820

Browse files
committed
Generate python code for error handling, but only for deployed python code.
1 parent 0df4c54 commit b214820

File tree

10 files changed

+169
-27
lines changed

10 files changed

+169
-27
lines changed

server_python_scripts/blocks_base_classes/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from .decorators import Teleop, Auto, Test, Name, Group
44
from .mechanism import Mechanism
5+
from .block_execution import BlockExecution
56

67
__all__ = [
78
'Teleop',
@@ -10,4 +11,5 @@
1011
'Name',
1112
'Group',
1213
'Mechanism',
14+
'BlockExecution',
1315
]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This class provides functions used when executing blocks.
2+
3+
import json
4+
5+
class BlockExecution:
6+
current_class_name: str = ''
7+
current_block_id: str = ''
8+
current_block_type: str = ''
9+
current_block_label: str = ''
10+
current_block_execution_finished: bool = False
11+
12+
@classmethod
13+
def startBlockExecution(cls, json_text: str) -> bool:
14+
o = json.loads(json_text)
15+
cls.current_class_name = o['className']
16+
cls.current_block_id = o['blockId']
17+
cls.current_block_type = o['blockType']
18+
cls.current_block_label = o['blockLabel']
19+
cls.current_block_execution_finished = False
20+
return True
21+
22+
@classmethod
23+
def endBlockExecution(cls, value):
24+
cls.current_block_execution_finished = True
25+
return value
26+
27+
@classmethod
28+
def handleFatalError(cls, e) -> None:
29+
if cls.current_block_label:
30+
subordinating_conjunction = (
31+
'after' if cls.current_block_execution_finished else 'while')
32+
print(f'\nFatal error occurred {subordinating_conjunction} '
33+
f'executing the block labeled "{cls.current_block_label}" '
34+
f'in {cls.current_class_name}.\n')

server_python_scripts/run_opmode.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
from pathlib import Path
1818

1919
from wpilib_placeholders import PeriodicOpMode
20-
2120
from robot import Robot
21+
from blocks_base_classes import BlockExecution
2222

2323

2424
def find_opmode_class(module):
@@ -151,8 +151,9 @@ def run_opmode(opmode_file, duration=None, loop_frequency=50):
151151
print(f" Average frequency: {actual_frequency:.1f} Hz")
152152

153153
except Exception as e:
154-
print(f"Error running opmode: {e}")
155-
traceback.print_exc()
154+
print(f"\nError running opmode: {e}")
155+
#traceback.print_exc()
156+
BlockExecution.handleFatalError(e)
156157
sys.exit(1)
157158

158159

src/blocks/mrc_call_python_function.ts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,54 @@ const CALL_PYTHON_FUNCTION = {
923923
this.mrcMechanismId = oldIdToNewId[this.mrcMechanismId];
924924
}
925925
},
926+
mrcGetFullLabel: function(this: CallPythonFunctionBlock): string {
927+
switch (this.mrcFunctionKind) {
928+
case FunctionKind.BUILT_IN:
929+
return Blockly.Msg.CALL + ' ' +
930+
this.getFieldValue(FIELD_FUNCTION_NAME);
931+
case FunctionKind.MODULE:
932+
return Blockly.Msg.CALL + ' ' +
933+
this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME) + '.' +
934+
this.getFieldValue(FIELD_FUNCTION_NAME);
935+
case FunctionKind.STATIC:
936+
return Blockly.Msg.CALL + ' ' +
937+
this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME) + '.' +
938+
this.getFieldValue(FIELD_FUNCTION_NAME);
939+
case FunctionKind.CONSTRUCTOR:
940+
return Blockly.Msg.CREATE + ' ' +
941+
this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME);
942+
case FunctionKind.INSTANCE:
943+
return Blockly.Msg.CALL + ' ' +
944+
this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME) + '.' +
945+
this.getFieldValue(FIELD_FUNCTION_NAME);
946+
case FunctionKind.INSTANCE_WITHIN:
947+
return Blockly.Msg.CALL + ' ' +
948+
this.getFieldValue(FIELD_FUNCTION_NAME);
949+
case FunctionKind.EVENT:
950+
return Blockly.Msg.FIRE + ' ' +
951+
this.getFieldValue(FIELD_EVENT_NAME);
952+
case FunctionKind.INSTANCE_COMPONENT:
953+
if (this.mrcMechanismId) {
954+
return Blockly.Msg.CALL + ' ' +
955+
this.getFieldValue(FIELD_MECHANISM_NAME) + '.' +
956+
this.getFieldValue(FIELD_COMPONENT_NAME) + '.' +
957+
this.getFieldValue(FIELD_FUNCTION_NAME);
958+
} else {
959+
return Blockly.Msg.CALL + ' ' +
960+
this.getFieldValue(FIELD_COMPONENT_NAME) + '.' +
961+
this.getFieldValue(FIELD_FUNCTION_NAME);
962+
}
963+
case FunctionKind.INSTANCE_ROBOT:
964+
return Blockly.Msg.CALL + ' ' +
965+
Blockly.Msg.ROBOT_LOWER_CASE + '.' +
966+
this.getFieldValue(FIELD_FUNCTION_NAME);
967+
case FunctionKind.INSTANCE_MECHANISM:
968+
return Blockly.Msg.CALL + ' ' +
969+
this.getFieldValue(FIELD_MECHANISM_NAME) + '.' +
970+
this.getFieldValue(FIELD_FUNCTION_NAME);
971+
}
972+
return '';
973+
},
926974
upgrade_008_to_009: function(this: CallPythonFunctionBlock) {
927975
if (this.mrcFunctionKind === FunctionKind.INSTANCE_COMPONENT) {
928976
if (this.mrcComponentClassName === 'expansion_hub_motor.ExpansionHubMotor') {
@@ -1062,11 +1110,13 @@ export function pythonFromBlock(
10621110
code += delimiterBeforeArgs + codeForArgs;
10631111
}
10641112
code += ')';
1113+
let result: string | [string, number] = '';
10651114
if (block.outputConnection) {
1066-
return [code, Order.FUNCTION_CALL];
1115+
result = [code, Order.FUNCTION_CALL];
10671116
} else {
1068-
return code + '\n';
1117+
result = code + '\n';
10691118
}
1119+
return generator.addErrorHandlingCode(block, block.mrcGetFullLabel(), result);
10701120
};
10711121

10721122
function generateCodeForArguments(

src/blocks/mrc_component.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ const COMPONENT = {
300300
this.mrcComponentId = oldIdToNewId[this.mrcComponentId];
301301
}
302302
},
303+
mrcGetFullLabel: function(this: ComponentBlock): string {
304+
return this.getFieldValue(FIELD_NAME) + ' ' + Blockly.Msg.OF_TYPE + ' ' + this.getFieldValue(FIELD_TYPE);
305+
},
303306
upgrade_005_to_006: function(this: ComponentBlock) {
304307
for (let i = 0; i < this.mrcArgs.length; i++) {
305308
if (this.mrcArgs[i].type === 'Port') {
@@ -354,7 +357,7 @@ export const pythonFromBlock = function (
354357
}
355358
code += ')\n';
356359

357-
return code;
360+
return generator.addErrorHandlingCode(block, block.mrcGetFullLabel(), code);
358361
}
359362

360363
export function getAllPossibleComponents(

src/blocks/mrc_mechanism.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,9 @@ const MECHANISM = {
451451
this.mrcMechanismId = oldIdToNewId[this.mrcMechanismId];
452452
}
453453
},
454+
mrcGetFullLabel: function(this: MechanismBlock): string {
455+
return this.getFieldValue(FIELD_NAME) + ' ' + Blockly.Msg.OF_TYPE + ' ' + this.getFieldValue(FIELD_TYPE);
456+
},
454457
};
455458

456459
export const setup = function () {
@@ -499,7 +502,7 @@ export const pythonFromBlock = function (
499502
code += ')\n';
500503
code += 'self.mechanisms.append(self.' + mechanismName + ')\n';
501504

502-
return code;
505+
return generator.addErrorHandlingCode(block, block.mrcGetFullLabel(), code);
503506
}
504507

505508
export function createMechanismBlock(

src/blocks/mrc_set_python_variable.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -248,49 +248,54 @@ const SET_PYTHON_VARIABLE = {
248248
.setAlign(Blockly.inputs.Align.RIGHT)
249249
.appendField(this.mrcSelfLabel);
250250
}
251-
}
251+
},
252+
mrcGetFullLabel: function(this: SetPythonVariableBlock): string {
253+
return Blockly.Msg.SET + ' ' + this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME) + '.' +
254+
this.getFieldValue(FIELD_VARIABLE_NAME) + ' ' + Blockly.Msg.TO;
255+
},
252256
};
253257

254258
export const setup = function() {
255259
Blockly.Blocks[BLOCK_NAME] = SET_PYTHON_VARIABLE;
256260
};
257261

258262
export const pythonFromBlock = function(
259-
block: Blockly.Block,
263+
block: SetPythonVariableBlock,
260264
generator: ExtendedPythonGenerator,
261265
) {
262-
const setPythonVariableBlock = block as SetPythonVariableBlock;
263-
const varName = setPythonVariableBlock.mrcActualVariableName
264-
? setPythonVariableBlock.mrcActualVariableName
266+
const varName = block.mrcActualVariableName
267+
? block.mrcActualVariableName
265268
: block.getFieldValue(FIELD_VARIABLE_NAME);
266-
switch (setPythonVariableBlock.mrcVarKind) {
269+
let code: string;
270+
switch (block.mrcVarKind) {
267271
case VariableKind.MODULE: {
268272
const moduleName = block.getFieldValue(FIELD_MODULE_OR_CLASS_NAME);
269273
const value = generator.valueToCode(block, 'VALUE', Order.NONE);
270-
if (setPythonVariableBlock.mrcImportModule) {
271-
generator.importModule(setPythonVariableBlock.mrcImportModule);
274+
if (block.mrcImportModule) {
275+
generator.importModule(block.mrcImportModule);
272276
}
273-
const code = moduleName + '.' + varName + ' = ' + value + '\n';
274-
return code;
277+
code = moduleName + '.' + varName + ' = ' + value + '\n';
278+
break;
275279
}
276280
case VariableKind.CLASS: {
277281
const className = block.getFieldValue(FIELD_MODULE_OR_CLASS_NAME);
278282
const value = generator.valueToCode(block, 'VALUE', Order.NONE);
279-
if (setPythonVariableBlock.mrcImportModule) {
280-
generator.importModule(setPythonVariableBlock.mrcImportModule);
283+
if (block.mrcImportModule) {
284+
generator.importModule(block.mrcImportModule);
281285
}
282-
const code = className + '.' + varName + ' = ' + value + '\n';
283-
return code;
286+
code = className + '.' + varName + ' = ' + value + '\n';
287+
break;
284288
}
285289
case VariableKind.INSTANCE: {
286290
const selfValue = generator.valueToCode(block, 'SELF', Order.MEMBER);
287291
const value = generator.valueToCode(block, 'VALUE', Order.NONE);
288-
const code = selfValue + '.' + varName + ' = ' + value + '\n';
289-
return code;
292+
code = selfValue + '.' + varName + ' = ' + value + '\n';
293+
break;
290294
}
291295
default:
292296
throw new Error(Blockly.Msg['VAR_KIND_MUST_BE_MODULE_CLASS_OR_INSTANCE']);
293297
}
298+
return generator.addErrorHandlingCode(block, block.mrcGetFullLabel(), code);
294299
};
295300

296301
// Functions used for creating blocks for the toolbox.

src/blocks/utils/python.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import * as SetPythonVariable from "../mrc_set_python_variable";
3838
// Utilities related to blocks for python modules and classes, including those from RobotPy.
3939

4040
// The module for classes used by blocks that don't exist in wpilib.
41-
const MODULE_NAME_BLOCKS_BASE_CLASSES = 'blocks_base_classes';
41+
export const MODULE_NAME_BLOCKS_BASE_CLASSES = 'blocks_base_classes';
4242

4343
// TODO(lizlooney): Update these constants if necessary when we update wpilib.
4444

src/editor/extended_python_generator.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@
2020
*/
2121

2222
import * as Blockly from 'blockly/core';
23-
import { PythonGenerator } from 'blockly/python';
23+
import { Order, PythonGenerator } from 'blockly/python';
2424
import { createGeneratorContext, GeneratorContext } from './generator_context';
2525
import * as mechanismContainerHolder from '../blocks/mrc_mechanism_component_holder';
2626
import * as eventHandler from '../blocks/mrc_event_handler';
2727
import { STEPS_METHOD_NAME } from '../blocks/mrc_steps';
2828

2929
import {
30+
MODULE_NAME_BLOCKS_BASE_CLASSES,
3031
TELEOP_DECORATOR_CLASS,
3132
AUTO_DECORATOR_CLASS,
3233
TEST_DECORATOR_CLASS,
@@ -39,6 +40,13 @@ import {
3940
} from '../blocks/utils/python';
4041
import * as storageModule from '../storage/module';
4142

43+
type BlockExecutionDetails = {
44+
className: string,
45+
blockId: string,
46+
blockType: string,
47+
blockLabel: string,
48+
};
49+
4250
export class OpModeDetails {
4351
constructor(private name: string, private group: string, private enabled: boolean, private type: string) {}
4452
generateDecorators(generator: ExtendedPythonGenerator, className: string): string {
@@ -107,6 +115,8 @@ export class ExtendedPythonGenerator extends PythonGenerator {
107115
// Opmode details
108116
private opModeDetails: OpModeDetails | null = null;
109117

118+
private generateErrorHandling: boolean = false;
119+
110120
constructor() {
111121
super('Python');
112122
this.context = createGeneratorContext();
@@ -129,6 +139,34 @@ export class ExtendedPythonGenerator extends PythonGenerator {
129139
this.definitions_['variables'] = defvars.join('\n');
130140
}
131141

142+
addErrorHandlingCode(block: Blockly.Block, blockLabel: string, originalCode: string | [string, number]): string | [string, number] {
143+
if (!this.generateErrorHandling) {
144+
return originalCode;
145+
}
146+
147+
const blockExecutionDetails: BlockExecutionDetails = {
148+
className: this.context.getClassName(),
149+
blockId: block.id,
150+
blockType: block.type,
151+
blockLabel: blockLabel,
152+
};
153+
const startBlockExecution = 'BlockExecution.startBlockExecution(' +
154+
"'" + JSON.stringify(blockExecutionDetails) + "')";
155+
156+
// originalCode might be an array, for value blocks, or a string, for statement blocks.
157+
if (Array.isArray(originalCode)) {
158+
// Handle value blocks, where originalCode[0] is the generated code.
159+
const code = '(BlockExecution.endBlockExecution(' + originalCode[0] + ') ' +
160+
'if ' + startBlockExecution + ' else None)';
161+
return [code, Order.CONDITIONAL];
162+
} else {
163+
// Handle statement blocks, where originalCode is the generated code.
164+
return startBlockExecution + '\n' +
165+
originalCode +
166+
'BlockExecution.endBlockExecution(None)\n';
167+
}
168+
}
169+
132170
/*
133171
* This is called from the python generator for the mrc_class_method_def for the
134172
* init method
@@ -165,10 +203,14 @@ export class ExtendedPythonGenerator extends PythonGenerator {
165203
return "self." + varName;
166204
}
167205

168-
mrcWorkspaceToCode(workspace: Blockly.Workspace, module: storageModule.Module): string {
206+
mrcWorkspaceToCode(workspace: Blockly.Workspace, module: storageModule.Module, opt_generateErrorHandling?: boolean): string {
169207
this.workspace = workspace;
170208

171209
this.context.setModule(module);
210+
if (opt_generateErrorHandling) {
211+
this.generateErrorHandling = true;
212+
this.fromModuleImportName(MODULE_NAME_BLOCKS_BASE_CLASSES, 'BlockExecution');
213+
}
172214
this.init(workspace);
173215

174216
if (this.getModuleType() === storageModule.ModuleType.MECHANISM) {
@@ -190,6 +232,7 @@ export class ExtendedPythonGenerator extends PythonGenerator {
190232
this.importedNames = Object.create(null);
191233
this.fromModuleImportNames = Object.create(null);
192234
this.opModeDetails = null;
235+
this.generateErrorHandling = false;
193236

194237
return code;
195238
}

src/storage/create_python_files.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ async function generatePythonForModule(module: Module, storage: Storage): Promis
6969
Blockly.serialization.workspaces.load(blocks, workspace);
7070

7171
// Generate Python code.
72-
const pythonCode = extendedPythonGenerator.mrcWorkspaceToCode(workspace, module);
72+
const generateErrorHandling = true;
73+
const pythonCode = extendedPythonGenerator.mrcWorkspaceToCode(workspace, module, generateErrorHandling);
7374

7475
// Clean up the workspace
7576
workspaces.destroyHeadlessWorkspace(workspace);

0 commit comments

Comments
 (0)