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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
# production
/build

# python stuff
__pycache__
*.egg-info

# misc
.DS_Store
.env.local
Expand Down
8 changes: 8 additions & 0 deletions server_python_scripts/blocks_base_classes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Base classes for SystemCore blocks interface."""

from .opmode import OpMode, Teleop, Auto, Test, Name, Group
from .mechanism import Mechanism
from .robot_base import RobotBase

__all__ = ['OpMode', 'Teleop', 'Auto', 'Test', 'Name', 'Group'
'Mechanism', 'RobotBase']
14 changes: 14 additions & 0 deletions server_python_scripts/blocks_base_classes/mechanism.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This is the class all mechanisms derive from

class Mechanism:
def __init__(self):
self.hardware = []
def start(self):
for hardware in self.hardware:
hardware.start()
def update(self):
for hardware in self.hardware:
hardware.update()
def stop(self):
for hardware in self.hardware:
hardware.stop()
26 changes: 26 additions & 0 deletions server_python_scripts/blocks_base_classes/opmode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# This is the base class that all OpModes derive from
class OpMode:
def __init__(self, robot):
self.robot = robot
def start(self):
self.robot.start()
def loop(self):
self.robot.update()
def stop(self):
self.robot.stop()

# For now this does nothing but it lets the decorator work
def Teleop(OpMode):
return OpMode

def Auto(OpMode):
return OpMode

def Test(OpMode):
return OpMode

def Name(OpMode, str):
return OpMode

def Group(OpMode, str):
return OpMode
21 changes: 21 additions & 0 deletions server_python_scripts/blocks_base_classes/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "blocks_base_classes"
version = "0.1.0"
description = "Base classes for SystemCore blocks interface"
requires-python = ">=3.8"
license = {text = "Apache-2.0"}
authors = [
{name = "Alan Smith", email = "[email protected]"}
]
dependencies = []

[tool.setuptools.packages.find]
where = ["."]
include = ["blocks_base_classes*"]

[tool.setuptools.package-dir]
"blocks_base_classes" = "."
14 changes: 14 additions & 0 deletions server_python_scripts/blocks_base_classes/robot_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This is the class all robots derive from

class RobotBase:
def __init__(self):
self.hardware = []
def start(self):
for hardware in self.hardware:
hardware.start()
def update(self):
for hardware in self.hardware:
hardware.update()
def stop(self):
for hardware in self.hardware:
hardware.stop()
9 changes: 0 additions & 9 deletions server_python_scripts/opmode.py

This file was deleted.

4 changes: 1 addition & 3 deletions server_python_scripts/run_opmode.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
import argparse
from pathlib import Path

# Add the current directory to Python path to import local modules
sys.path.insert(0, str(Path(__file__).parent))
from blocks_base_classes import OpMode

from opmode import OpMode
from robot import Robot


Expand Down
4 changes: 2 additions & 2 deletions src/blocks/mrc_call_python_function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ const CALL_PYTHON_FUNCTION = {
break;
}
case FunctionKind.INSTANCE_COMPONENT: {
const componentNameChoices = [];
const componentNameChoices : string[] = [];
this.mrcComponents.forEach(component => componentNameChoices.push(component.name));
if (!componentNameChoices.includes(this.mrcComponentName)) {
componentNameChoices.push(this.mrcComponentName);
Expand Down Expand Up @@ -723,7 +723,7 @@ const CALL_PYTHON_FUNCTION = {
}
}
if (indexOfComponentName != -1) {
const componentNameChoices = [];
const componentNameChoices : string[] = [];
this.mrcComponents.forEach(component => componentNameChoices.push(component.name));
titleInput.removeField(FIELD_COMPONENT_NAME);
titleInput.insertFieldAt(indexOfComponentName,
Expand Down
8 changes: 4 additions & 4 deletions src/blocks/mrc_class_method_def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ const CLASS_METHOD_DEF = {
mrcRenameParameter: function (this: ClassMethodDefBlock, oldName: string, newName: string) {
let nextBlock = this.getInputTargetBlock('STACK');

if(nextBlock){
if (nextBlock){
findConnectedBlocksOfType(nextBlock, MRC_GET_PARAMETER_BLOCK_NAME).forEach((block) => {
if (block.getFieldValue('PARAMETER_NAME') === oldName) {
block.setFieldValue(newName, 'PARAMETER_NAME');
Expand Down Expand Up @@ -362,12 +362,12 @@ export const pythonFromBlock = function (
}
if (block.mrcPythonMethodName == '__init__') {
let class_specific = generator.getClassSpecificForInit();
branch = generator.INDENT + 'super.__init__(' + class_specific + ')\n' +
branch = generator.INDENT + 'super().__init__(' + class_specific + ')\n' +
generator.defineClassVariables() + branch;
}
else if (funcName == 'update'){
else if (generator.inBaseClassMethod(blocklyName)){
// Special case for update, to also call the update method of the base class
branch = generator.INDENT + 'super.update()\n' + branch;
branch = generator.INDENT + 'super().' + blocklyName + '()\n' + branch;
}
if (returnValue) {
returnValue = generator.INDENT + 'return ' + returnValue + '\n';
Expand Down
2 changes: 1 addition & 1 deletion src/blocks/mrc_get_parameter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const GET_PARAMETER_BLOCK = {

if (blockEvent.type === Blockly.Events.BLOCK_MOVE) {
let parent = blockBlock.getRootBlock();
if( parent.type === MRC_CLASS_METHOD_DEF) {
if ( parent.type === MRC_CLASS_METHOD_DEF) {
// It is a class method definition, so we see if this variable is in it.
const classMethodDefBlock = parent as ClassMethodDefBlock;
for (const parameter of classMethodDefBlock.mrcParameters) {
Expand Down
12 changes: 5 additions & 7 deletions src/blocks/mrc_mechanism_component_holder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export const setup = function () {
}

function pythonFromBlockInRobot(block: MechanismComponentHolderBlock, generator: ExtendedPythonGenerator) {
let code = 'def define_hardware(self):\n' + generator.INDENT + 'self.hardware = []\n';
let code = 'def define_hardware(self):\n';

let mechanisms = '';
let components = '';
Expand All @@ -189,23 +189,21 @@ function pythonFromBlockInRobot(block: MechanismComponentHolderBlock, generator:
const body = mechanisms + components;
if (body != '') {
code += body;
generator.addClassMethodDefinition('define_hardware', code);
}

generator.addClassMethodDefinition('define_hardware', code);
}

function pythonFromBlockInMechanism(block: MechanismComponentHolderBlock, generator: ExtendedPythonGenerator) {
let components = '';

components = generator.statementToCode(block, 'COMPONENTS');

let code = 'def define_hardware(self' + generator.getListOfPorts(false) + '):\n' +
generator.INDENT + 'self.hardware = []\n';
let code = 'def define_hardware(self' + generator.getListOfPorts(false) + '):\n';

if (components != '') {
code += components;
}
generator.addClassMethodDefinition('define_hardware', code);
generator.addClassMethodDefinition('define_hardware', code);
}
}

export const pythonFromBlock = function (
Expand Down
46 changes: 46 additions & 0 deletions src/blocks/mrc_print.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* @license
* Copyright 2025 Porpoiseful LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @fileoverview Create a component that does a simple print statement
* @author [email protected] (Alan Smith)
*/
import * as Blockly from 'blockly';
import { PythonGenerator } from 'blockly/python';
import { Order } from 'blockly/python';

export const BLOCK_NAME = 'mrc_print';

export const setup = function() {
Blockly.Blocks[BLOCK_NAME] = {
init: function() {
this.appendValueInput('TEXT')
.setCheck('String')
.appendField(Blockly.Msg['MRC_PRINT']);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setStyle('text_blocks');
},
};
};

export const pythonFromBlock = function(
block: Blockly.Block,
generator: PythonGenerator,
) {
return 'print(' + generator.valueToCode(block, 'TEXT', Order.NONE) + ')\n';
};
4 changes: 3 additions & 1 deletion src/blocks/setup_custom_blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as Event from './mrc_event';
import * as GetParameter from './mrc_get_parameter';
import * as ParameterMutator from './mrc_param_container'
import * as EventHandler from './mrc_event_handler';
import * as Print from './mrc_print';

const customBlocks = [
CallPythonFunction,
Expand All @@ -37,13 +38,14 @@ const customBlocks = [
GetParameter,
ParameterMutator,
EventHandler,
Print
];

export const setup = function(forBlock: any) {
customBlocks.forEach(block => {
block.setup();
const maybeBlock = block as { pythonFromBlock?: any; BLOCK_NAME?: string };
if(maybeBlock.pythonFromBlock && maybeBlock.BLOCK_NAME) {
if (maybeBlock.pythonFromBlock && maybeBlock.BLOCK_NAME) {
forBlock[maybeBlock.BLOCK_NAME] = maybeBlock.pythonFromBlock;
}
});
Expand Down
45 changes: 38 additions & 7 deletions src/blocks/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
/**
* @license
* Copyright 2025 Porpoiseful LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @fileoverview Exposes translatable strings for use as Blockly.Msg tokens.
* @author [email protected] (Alan Smith)
*/

import * as Blockly from 'blockly/core';
import { M } from 'vitest/dist/chunks/reporters.d.BFLkQcL6.js';

export const customTokens = (t: (key: string) => string): typeof Blockly.Msg => {
/**
* Creates custom translation tokens for Blockly messages.
* @param t Translation function that takes a key and returns translated string.
* @return Object containing translated Blockly message tokens.
*/
export function customTokens(t: (key: string) => string): typeof Blockly.Msg {
return {
ADD_COMMENT: t('BLOCKLY.ADD_COMMENT'),
REMOVE_COMMENT: t('BLOCKLY.REMOVE_COMMENT'),
Expand All @@ -10,12 +36,15 @@ export const customTokens = (t: (key: string) => string): typeof Blockly.Msg =>
WITH: t('BLOCKLY.WITH'),
WHEN: t('BLOCKLY.WHEN'),
PARAMETER: t('BLOCKLY.PARAMETER'),
PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK: t('BLOCKLY.PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK'),
MECHANISMS: t('BLOCKLY.MECHANISMS'),
PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK:
t('BLOCKLY.PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK'),
MECHANISMS: t('MECHANISMS'),
OPMODES: t('OPMODES'),
COMPONENTS: t('BLOCKLY.COMPONENTS'),
EVENTS: t('BLOCKLY.EVENTS'),
EVALUATE_BUT_IGNORE_RESULT: t('BLOCKLY.EVALUATE_BUT_IGNORE_RESULT'),
EVALUATE_BUT_IGNORE_RESULT_TOOLTIP: t('BLOCKLY.EVALUATE_BUT_IGNORE_RESULT_TOOLTIP'),
EVALUATE_BUT_IGNORE_RESULT_TOOLTIP:
t('BLOCKLY.EVALUATE_BUT_IGNORE_RESULT_TOOLTIP'),
AUTO: t('BLOCKLY.AUTO'),
TELEOP: t('BLOCKLY.TELEOP'),
TEST: t('BLOCKLY.TEST'),
Expand All @@ -42,5 +71,7 @@ export const customTokens = (t: (key: string) => string): typeof Blockly.Msg =>
MRC_CATEGORY_EVENTS: t('BLOCKLY.CATEGORY.EVENTS'),
MRC_CATEGORY_ADD_MECHANISM: t('BLOCKLY.CATEGORY.ADD_MECHANISM'),
MRC_CATEGORY_ADD_COMPONENT: t('BLOCKLY.CATEGORY.ADD_COMPONENT'),
};
};
MRC_CATEGORY_TEST: t('BLOCKLY.CATEGORY.TEST'),
MRC_PRINT: t('BLOCKLY.PRINT'),
}
};
8 changes: 4 additions & 4 deletions src/blocks/utils/change_framework.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function registerCallback(blockType : string, blockEvents : string[], fun
export function getParentOfType(block : Blockly.Block | null, type : string) : Blockly.Block | null{
let parentBlock = block?.getParent();
while(parentBlock){
if(parentBlock.type == type){
if (parentBlock.type == type){
return parentBlock;
}
parentBlock = parentBlock.getParent();
Expand All @@ -50,14 +50,14 @@ function changeListener(e: Blockly.Events.Abstract){
let eventBlockBase = (e as Blockly.Events.BlockBase);
let workspace = Blockly.Workspace.getById(eventBlockBase.workspaceId!)
let block = (workspace?.getBlockById(eventBlockBase.blockId!)! as Blockly.BlockSvg)
if(!block){
if (!block){
return;
}
let callbackInfo = registeredCallbacks.get(block.type);
if(!callbackInfo){
if (!callbackInfo){
return;
}
if(callbackInfo[0].includes(e.type)){
if (callbackInfo[0].includes(e.type)){
callbackInfo[1](block, eventBlockBase);
}
}
Expand Down
Loading