Skip to content

Commit 407a909

Browse files
authored
Add sample component for SparkFun LED Stick (#99)
* Added sample implementation for SparkFun LED Stick. * Add md file to show the blocks for the SparkFun LED Stick. * Added toolbox for SparkFun LED Stick. Fixed toolbox for Rev Touch Sensor (from Alan). * Fixed typos. * Specify the type of the color arguments. * Added support for calling component methods to the mrc_call_python_function block. Handle case where a component name collides with a variable name. Added getComponentNames method to the editor class so the mrc_call_python_function blocks can populate the component name dropdown fields. Added robot parameter to OpMode init method. Modified mrc_class_method_def so it generates the call to super().__init__() for __init__ methods. Save the component names and types in the files. Added blocks to component_samples_category.ts. * Show the component name in the instance method call tooltip. * Pass robot to OpMode.__init__. * Removed wpilib from the component names shown in the tooltips. * Where component methods are called, generate self.robot.<component name> instead of self.<component name> Don't generate self.<component name> = robot.<component name> in the __init__ methods. * Put toolbox categories in alphabetical order. Show manufacturer name on toolbox categories. * Updated md file. * more edits * Changed tooltip to use the word method instead of function. * Specify widths of images. * Set image widths to 50%. * More f'ing around with image sizes. * Renamed file to index.md.
1 parent 4c1169f commit 407a909

18 files changed

+814
-30
lines changed
31.4 KB
Loading
48.1 KB
Loading
114 KB
Loading
118 KB
Loading
153 KB
Loading
90.1 KB
Loading
205 KB
Loading

external_samples/docs/index.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Component Blocks
2+
3+
## Block Details
4+
5+
### Dropdown shows components of the specific type.
6+
7+
<img src="BlockWithComponentDropdown.png" width="291">
8+
9+
### Tooltip shows that the block calls a method on a component, followed by details about what that method does.
10+
11+
<img src="BlockWithTooltip.png" width="342">
12+
13+
## Component Toolbox Categories
14+
15+
### REV ColorRangeSensor
16+
17+
<img src="REV_ColorRangeSensor.png" width="531">
18+
19+
### REV Servo
20+
21+
<img src="REV_Servo.png" width="581">
22+
23+
### REV SmartMotor
24+
25+
<img src="REV_SmartMotor.png" width="622">
26+
27+
### REV TouchSensor
28+
29+
<img src="REV_TouchSensor.png" width="492">
30+
31+
### SparkFun LEDStick
32+
33+
<img src="SparkFun_LEDStick.png" width="666">
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from component import Component, PortType, InvalidPortException
2+
from enum import Enum
3+
import wpilib
4+
5+
class SparkFunLEDStick(Component):
6+
def __init__(self, ports : list[tuple[PortType, int]]):
7+
portType, port = ports[0]
8+
if portType != PortType.I2C_PORT:
9+
raise InvalidPortException
10+
self.port = port
11+
12+
# Component methods
13+
14+
def get_manufacturer(self) -> str:
15+
return "SparkFun"
16+
17+
def get_name(self) -> str:
18+
return "SparkFun Qwiic LED Strip"
19+
20+
def get_part_number(self) -> str:
21+
return "COM-18354"
22+
23+
def get_url(self) -> str:
24+
return "https://www.sparkfun.com/sparkfun-qwiic-led-stick-apa102c.html"
25+
26+
def get_version(self) -> tuple[int, int, str]:
27+
return (1, 0, "")
28+
29+
def stop(self) -> None:
30+
self.turn_all_off()
31+
32+
def reset(self) -> None:
33+
pass
34+
35+
def get_connection_port_type(self) -> list[PortType]:
36+
return [PortType.I2C_PORT]
37+
38+
def periodic(self) -> None:
39+
pass
40+
41+
# SparkFunLEDStick methods
42+
43+
def set_color(self, position: int, color: wpilib.Color) -> None:
44+
'''Change the color of an individual LED.'''
45+
pass # TODO: implement
46+
47+
def set_color(self, color: wpilib.Color) -> None:
48+
'''Change the color of all LEDs to a single color.'''
49+
pass # TODO: implement
50+
51+
def set_colors(self, colors: list[int]) -> None:
52+
'''Change the color of all LEDs using a list.'''
53+
pass # TODO: implement
54+
55+
def set_brightness(self, position: int, brightness: int) -> None:
56+
'''Set the brightness of an individual LED.'''
57+
pass # TODO: implement
58+
59+
def set_brightness(self, brightness: int) -> None:
60+
'''Set the brightness of all LEDs.'''
61+
pass # TODO: implement
62+
63+
def turn_all_off(self) -> None:
64+
'''Turn all LEDs off.'''
65+
pass # TODO: implement

src/blocks/mrc_call_python_function.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ import * as Blockly from 'blockly';
2424
import { Order } from 'blockly/python';
2525

2626
import * as pythonUtils from './utils/generated/python';
27+
import { createFieldDropdown } from '../fields/FieldDropdown';
2728
import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
2829
import { getAllowedTypesForSetCheck, getOutputCheck } from './utils/python';
2930
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
3031
import { MRC_STYLE_FUNCTIONS } from '../themes/styles'
3132
import { ClassMethodDefExtraState } from './mrc_class_method_def'
33+
import { Editor } from '../editor/editor';
3234

3335
// A block to call a python function.
3436

@@ -40,10 +42,13 @@ enum FunctionKind {
4042
CONSTRUCTOR = 'constructor',
4143
INSTANCE = 'instance',
4244
INSTANCE_WITHIN = 'instance_within',
45+
INSTANCE_COMPONENT = 'instance_component'
4346
}
4447

4548
const RETURN_TYPE_NONE = 'None';
4649

50+
const FIELD_COMPONENT_NAME = 'COMPONENT_NAME';
51+
4752
export type FunctionArg = {
4853
name: string,
4954
type: string,
@@ -58,6 +63,8 @@ interface CallPythonFunctionMixin extends CallPythonFunctionMixinType {
5863
mrcImportModule: string,
5964
mrcActualFunctionName: string,
6065
mrcExportedFunction: boolean,
66+
mrcComponentClassName: string,
67+
mrcComponentName: string, // Do not access directly. Call getComponentName.
6168
renameMethod(this: CallPythonFunctionBlock, newName: string): void;
6269
mutateMethod(this: CallPythonFunctionBlock, defBlockExtraState: ClassMethodDefExtraState): void;
6370
}
@@ -99,6 +106,14 @@ type CallPythonFunctionExtraState = {
99106
* user's Project).
100107
*/
101108
exportedFunction: boolean,
109+
/**
110+
* The component name. Specified only if the function kind is INSTANCE_COMPONENT.
111+
*/
112+
componentName?: string,
113+
/**
114+
* The component class name. Specified only if the function kind is INSTANCE_COMPONENT.
115+
*/
116+
componentClassName?: string,
102117
};
103118

104119
const CALL_PYTHON_FUNCTION = {
@@ -138,6 +153,13 @@ const CALL_PYTHON_FUNCTION = {
138153
tooltip = 'Calls the method ' + functionName + '.';
139154
break;
140155
}
156+
case FunctionKind.INSTANCE_COMPONENT: {
157+
const className = this.mrcComponentClassName;
158+
const functionName = this.getFieldValue(pythonUtils.FIELD_FUNCTION_NAME);
159+
tooltip = 'Calls the method ' + className + '.' + functionName +
160+
' on the component named ' + this.getComponentName() + '.';
161+
break;
162+
}
141163
default:
142164
throw new Error('mrcFunctionKind has unexpected value: ' + this.mrcFunctionKind)
143165
}
@@ -174,6 +196,12 @@ const CALL_PYTHON_FUNCTION = {
174196
if (this.mrcActualFunctionName) {
175197
extraState.actualFunctionName = this.mrcActualFunctionName;
176198
}
199+
if (this.mrcComponentClassName) {
200+
extraState.componentClassName = this.mrcComponentClassName;
201+
}
202+
if (this.getField(FIELD_COMPONENT_NAME)) {
203+
extraState.componentName = this.getComponentName();
204+
}
177205
return extraState;
178206
},
179207
/**
@@ -199,6 +227,10 @@ const CALL_PYTHON_FUNCTION = {
199227
? extraState.actualFunctionName : '';
200228
this.mrcExportedFunction = extraState.exportedFunction
201229
? extraState.exportedFunction : false;
230+
this.mrcComponentClassName = extraState.componentClassName
231+
? extraState.componentClassName : '';
232+
this.mrcComponentName = extraState.componentName
233+
? extraState.componentName : '';
202234
this.updateBlock_();
203235
},
204236
/**
@@ -260,6 +292,19 @@ const CALL_PYTHON_FUNCTION = {
260292
}
261293
break;
262294
}
295+
case FunctionKind.INSTANCE_COMPONENT: {
296+
const componentNames = Editor.getComponentNames(this.workspace, this.mrcComponentClassName);
297+
const componentName = this.getComponentName();
298+
if (!componentNames.includes(componentName)) {
299+
componentNames.push(componentName);
300+
}
301+
this.appendDummyInput('TITLE')
302+
.appendField('call')
303+
.appendField(createFieldDropdown(componentNames), FIELD_COMPONENT_NAME)
304+
.appendField('.')
305+
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_FUNCTION_NAME);
306+
break;
307+
}
263308
default:
264309
throw new Error('mrcFunctionKind has unexpected value: ' + this.mrcFunctionKind)
265310
}
@@ -293,6 +338,15 @@ const CALL_PYTHON_FUNCTION = {
293338
this.removeInput('ARG' + i);
294339
}
295340
},
341+
getComponentName(this: CallPythonFunctionBlock): string {
342+
// If the COMPONENT_NAME field has been created, get the field value, which the user may have changed.
343+
// If the COMPONENT_NAME field has not been created, get the component name from this.mrcComponentName.
344+
return (this.getField(FIELD_COMPONENT_NAME))
345+
? this.getFieldValue(FIELD_COMPONENT_NAME) : this.mrcComponentName;
346+
},
347+
getComponentClassName(this: CallPythonFunctionBlock): string {
348+
return this.mrcComponentClassName;
349+
},
296350
renameMethod: function(this: CallPythonFunctionBlock, newName: string): void {
297351
this.setFieldValue(newName, pythonUtils.FIELD_FUNCTION_NAME);
298352
},
@@ -365,6 +419,14 @@ export const pythonFromBlock = function(
365419
code = 'self.' + functionName;
366420
break;
367421
}
422+
case FunctionKind.INSTANCE_COMPONENT: {
423+
const componentName = callPythonFunctionBlock.getComponentName();
424+
const functionName = callPythonFunctionBlock.mrcActualFunctionName
425+
? callPythonFunctionBlock.mrcActualFunctionName
426+
: block.getFieldValue(pythonUtils.FIELD_FUNCTION_NAME);
427+
code = 'self.robot.' + componentName + '.' + functionName;
428+
break;
429+
}
368430
default:
369431
throw new Error('mrcFunctionKind has unexpected value: ' + callPythonFunctionBlock.mrcFunctionKind)
370432
}

0 commit comments

Comments
 (0)