sbhs_robomaster.data

  1from enum import Enum, IntFlag, auto
  2from dataclasses import dataclass
  3from typing import TypeVar
  4
  5
  6class Frequency(Enum):
  7    """"""
  8    Off = "0"
  9    Hz1 = "1"
 10    Hz5 = "5"
 11    Hz10 = "10"
 12    Hz20 = "20"
 13    Hz30 = "30"
 14    Hz50 = "50"
 15
 16
 17class Mode(Enum):
 18    """"""
 19    ChassisLead = "chassis_lead"
 20    GimbalLead = "gimbal_lead"
 21    Free = "free"
 22
 23
 24class GripperStatus(Enum):
 25    """"""
 26    Closed = "0"
 27    PartiallyOpen = "1"
 28    Open = "2"
 29
 30
 31class LineType(Enum):
 32    """"""
 33    NoLine = "0"
 34    Straight = "1"
 35    Fork = "2"
 36    Intersection = "3"
 37
 38
 39class LineColour(Enum):
 40    """"""
 41    Red = "red"
 42    Blue = "blue"
 43    Green = "green"
 44
 45
 46class LedPosition(IntFlag):
 47    """
 48    Use the flag combining operator (`|`) to combine multiple leds.
 49
 50    ```py
 51    front_and_left_leds = LedPosition.Front | LedPosition.Left
 52    ```
 53
 54    `LedPosition.All` is equivalent to `LedPosition.Front | LedPosition.Back | LedPosition.Left | LedPosition.Right`.
 55    It's just a lot faster to type.
 56    """
 57    Front = auto()
 58    Back = auto()
 59    Left = auto()
 60    Right = auto()
 61    All = Front | Back | Left | Right
 62
 63
 64class LedEffect(Enum):
 65    """"""
 66    Off = "off"
 67    Solid = "solid"
 68    Pulse = "pulse"
 69    Blink = "blink"
 70    Scrolling = "scrolling"
 71
 72
 73@dataclass
 74class Response:
 75    """
 76    Wrapper around the data returned by the robot.
 77
 78    Provides convenience functions for getting the data in the correct type.
 79    """
 80    data: list[str]
 81
 82    def get_str(self, index: int) -> str:
 83        return self.data[index]
 84    
 85    def get_int(self, index: int) -> int:
 86        return int(self.data[index])
 87
 88    def get_float(self, index: int) -> float:
 89        return float(self.data[index])
 90    
 91    def get_bool(self, index: int) -> bool:
 92        if self.data[index] == "on":
 93            return True
 94        elif self.data[index] == "off":
 95            return False
 96        elif self.data[index] == "1":
 97            return True
 98        elif self.data[index] == "0":
 99            return False
100        else:
101            raise Exception("Invalid bool value")
102    
103    GetEnumT = TypeVar("GetEnumT", bound=Enum)
104    def get_enum(self, index: int, enum: type[GetEnumT]) -> GetEnumT:
105        """
106        Example usage:
107        ```py
108        class MyEnum(Enum):
109            A = "a"
110            B = "b"
111            C = "c"
112
113        response = Response(["a", "b", "c"])
114
115        response.get_enum(0, MyEnum) # MyEnum.A
116        response.get_enum(1, MyEnum) # MyEnum.B
117        response.get_enum(2, MyEnum) # MyEnum.C
118        ```
119
120        **NOTE:** This only works for enums that have strings as their underlying values.
121        """
122        return enum(self.data[index])
123
124
125@dataclass
126class WheelSpeed:
127    """All speeds are in rpm."""
128
129    front_right: float
130    front_left: float
131    back_right: float
132    back_left: float
133
134
135@dataclass
136class ChassisSpeed:
137    z: float
138    """
139    Movement forwards/backwards (relative to the rotation of the robot) in m/s.
140    """
141    
142    x: float
143    """
144    Movement right/left (relative to the rotation of the robot) in m/s.
145    """
146
147    clockwise: float
148    """
149    Rotational speed around the vertical axis in the clockwise direction in degrees/s.
150    """
151
152    wheels: WheelSpeed
153
154    @staticmethod
155    def parse(data: Response) -> "ChassisSpeed":
156        return ChassisSpeed(
157            z         = data.get_float(0),
158            x         = data.get_float(1),
159            clockwise = data.get_float(2),
160            wheels    = WheelSpeed(
161                front_right = data.get_float(3),
162                front_left  = data.get_float(4),
163                back_right  = data.get_float(5),
164                back_left   = data.get_float(6)
165            )
166        )
167
168
169@dataclass
170class ChassisRotation:
171    """
172    This actually parses the position data from the robot,
173    but removes the x and z coordinates due to them being
174    too inaccurate and being a major footgun.
175    """
176
177    clockwise: float
178    """
179    The rotation in degrees clockwise around the vertical axis,
180    relative to the robot's starting orientation.
181    """
182
183    @staticmethod
184    def parse(data: Response) -> "ChassisRotation":
185        return ChassisRotation(
186            clockwise = data.get_float(2)
187        )
188
189
190@dataclass
191class ChassisAttitude:
192    """All values are in degrees."""
193
194    pitch: float
195    roll: float
196    yaw: float
197
198    @staticmethod
199    def parse(data: Response) -> "ChassisAttitude":
200        return ChassisAttitude(
201            pitch = data.get_float(0),
202            roll  = data.get_float(1),
203            yaw   = data.get_float(2)
204        )
205
206
207@dataclass
208class ChassisStatus:
209    static: bool
210    up_hill: bool
211    down_hill: bool
212    on_slope: bool
213    pick_up: bool
214    slip: bool
215    impact_x: bool
216    impact_y: bool
217    impact_z: bool
218    roll_over: bool
219    hill_static: bool
220
221    @staticmethod
222    def parse(data: Response) -> "ChassisStatus":
223        return ChassisStatus(
224            static      = data.get_bool(0),
225            up_hill     = data.get_bool(1),
226            down_hill   = data.get_bool(2),
227            on_slope    = data.get_bool(3),
228            pick_up     = data.get_bool(4),
229            slip        = data.get_bool(5),
230            impact_x    = data.get_bool(6),
231            impact_y    = data.get_bool(7),
232            impact_z    = data.get_bool(8),
233            roll_over   = data.get_bool(9),
234            hill_static = data.get_bool(10)
235        )
236
237
238@dataclass
239class Point:
240    x: float
241    y: float
242    tangent: float
243    curvature: float
244
245
246@dataclass
247class Line:
248    type: LineType
249    points: list[Point]
250
251    @staticmethod
252    def parse(data: Response) -> "Line":
253        point_count = (len(data.data) - 1) // 4
254
255        match data.get_int(0):
256            case 0:
257                line_type = LineType.NoLine
258            case 1:
259                line_type = LineType.Straight
260            case 2:
261                line_type = LineType.Fork
262            case 3:
263                line_type = LineType.Intersection
264            case _:
265                raise Exception("Invalid line type")
266
267        points: list[Point] = []
268
269        for i in range(point_count):
270            points.append(Point(
271                x         = data.get_float(i * 4 + 1),
272                y         = data.get_float(i * 4 + 2),
273                tangent   = data.get_float(i * 4 + 3),
274                curvature = data.get_float(i * 4 + 4)
275            ))
276
277        return Line(line_type, points)
278
279
280@dataclass
281class Colour:
282    """All values are from 0 to 255, inclusive."""
283    
284    r: float
285    g: float
286    b: float
class Frequency(enum.Enum):
 7class Frequency(Enum):
 8    """"""
 9    Off = "0"
10    Hz1 = "1"
11    Hz5 = "5"
12    Hz10 = "10"
13    Hz20 = "20"
14    Hz30 = "30"
15    Hz50 = "50"
Off = <Frequency.Off: '0'>
Hz1 = <Frequency.Hz1: '1'>
Hz5 = <Frequency.Hz5: '5'>
Hz10 = <Frequency.Hz10: '10'>
Hz20 = <Frequency.Hz20: '20'>
Hz30 = <Frequency.Hz30: '30'>
Hz50 = <Frequency.Hz50: '50'>
Inherited Members
enum.Enum
name
value
class Mode(enum.Enum):
18class Mode(Enum):
19    """"""
20    ChassisLead = "chassis_lead"
21    GimbalLead = "gimbal_lead"
22    Free = "free"
ChassisLead = <Mode.ChassisLead: 'chassis_lead'>
GimbalLead = <Mode.GimbalLead: 'gimbal_lead'>
Free = <Mode.Free: 'free'>
Inherited Members
enum.Enum
name
value
class GripperStatus(enum.Enum):
25class GripperStatus(Enum):
26    """"""
27    Closed = "0"
28    PartiallyOpen = "1"
29    Open = "2"
Closed = <GripperStatus.Closed: '0'>
PartiallyOpen = <GripperStatus.PartiallyOpen: '1'>
Open = <GripperStatus.Open: '2'>
Inherited Members
enum.Enum
name
value
class LineType(enum.Enum):
32class LineType(Enum):
33    """"""
34    NoLine = "0"
35    Straight = "1"
36    Fork = "2"
37    Intersection = "3"
NoLine = <LineType.NoLine: '0'>
Straight = <LineType.Straight: '1'>
Fork = <LineType.Fork: '2'>
Intersection = <LineType.Intersection: '3'>
Inherited Members
enum.Enum
name
value
class LineColour(enum.Enum):
40class LineColour(Enum):
41    """"""
42    Red = "red"
43    Blue = "blue"
44    Green = "green"
Red = <LineColour.Red: 'red'>
Blue = <LineColour.Blue: 'blue'>
Green = <LineColour.Green: 'green'>
Inherited Members
enum.Enum
name
value
class LedPosition(enum.IntFlag):
47class LedPosition(IntFlag):
48    """
49    Use the flag combining operator (`|`) to combine multiple leds.
50
51    ```py
52    front_and_left_leds = LedPosition.Front | LedPosition.Left
53    ```
54
55    `LedPosition.All` is equivalent to `LedPosition.Front | LedPosition.Back | LedPosition.Left | LedPosition.Right`.
56    It's just a lot faster to type.
57    """
58    Front = auto()
59    Back = auto()
60    Left = auto()
61    Right = auto()
62    All = Front | Back | Left | Right

Use the flag combining operator (|) to combine multiple leds.

front_and_left_leds = LedPosition.Front | LedPosition.Left

LedPosition.All is equivalent to LedPosition.Front | LedPosition.Back | LedPosition.Left | LedPosition.Right. It's just a lot faster to type.

Front = <LedPosition.Front: 1>
Back = <LedPosition.Back: 2>
Left = <LedPosition.Left: 4>
Right = <LedPosition.Right: 8>
All = <LedPosition.All: 15>
Inherited Members
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
enum.Enum
name
value
class LedEffect(enum.Enum):
65class LedEffect(Enum):
66    """"""
67    Off = "off"
68    Solid = "solid"
69    Pulse = "pulse"
70    Blink = "blink"
71    Scrolling = "scrolling"
Off = <LedEffect.Off: 'off'>
Solid = <LedEffect.Solid: 'solid'>
Pulse = <LedEffect.Pulse: 'pulse'>
Scrolling = <LedEffect.Scrolling: 'scrolling'>
Inherited Members
enum.Enum
name
value
@dataclass
class Response:
 74@dataclass
 75class Response:
 76    """
 77    Wrapper around the data returned by the robot.
 78
 79    Provides convenience functions for getting the data in the correct type.
 80    """
 81    data: list[str]
 82
 83    def get_str(self, index: int) -> str:
 84        return self.data[index]
 85    
 86    def get_int(self, index: int) -> int:
 87        return int(self.data[index])
 88
 89    def get_float(self, index: int) -> float:
 90        return float(self.data[index])
 91    
 92    def get_bool(self, index: int) -> bool:
 93        if self.data[index] == "on":
 94            return True
 95        elif self.data[index] == "off":
 96            return False
 97        elif self.data[index] == "1":
 98            return True
 99        elif self.data[index] == "0":
100            return False
101        else:
102            raise Exception("Invalid bool value")
103    
104    GetEnumT = TypeVar("GetEnumT", bound=Enum)
105    def get_enum(self, index: int, enum: type[GetEnumT]) -> GetEnumT:
106        """
107        Example usage:
108        ```py
109        class MyEnum(Enum):
110            A = "a"
111            B = "b"
112            C = "c"
113
114        response = Response(["a", "b", "c"])
115
116        response.get_enum(0, MyEnum) # MyEnum.A
117        response.get_enum(1, MyEnum) # MyEnum.B
118        response.get_enum(2, MyEnum) # MyEnum.C
119        ```
120
121        **NOTE:** This only works for enums that have strings as their underlying values.
122        """
123        return enum(self.data[index])

Wrapper around the data returned by the robot.

Provides convenience functions for getting the data in the correct type.

Response(data: list[str])
data: list[str]
def get_str(self, index: int) -> str:
83    def get_str(self, index: int) -> str:
84        return self.data[index]
def get_int(self, index: int) -> int:
86    def get_int(self, index: int) -> int:
87        return int(self.data[index])
def get_float(self, index: int) -> float:
89    def get_float(self, index: int) -> float:
90        return float(self.data[index])
def get_bool(self, index: int) -> bool:
 92    def get_bool(self, index: int) -> bool:
 93        if self.data[index] == "on":
 94            return True
 95        elif self.data[index] == "off":
 96            return False
 97        elif self.data[index] == "1":
 98            return True
 99        elif self.data[index] == "0":
100            return False
101        else:
102            raise Exception("Invalid bool value")
def get_enum(self, index: int, enum: type[~GetEnumT]) -> ~GetEnumT:
105    def get_enum(self, index: int, enum: type[GetEnumT]) -> GetEnumT:
106        """
107        Example usage:
108        ```py
109        class MyEnum(Enum):
110            A = "a"
111            B = "b"
112            C = "c"
113
114        response = Response(["a", "b", "c"])
115
116        response.get_enum(0, MyEnum) # MyEnum.A
117        response.get_enum(1, MyEnum) # MyEnum.B
118        response.get_enum(2, MyEnum) # MyEnum.C
119        ```
120
121        **NOTE:** This only works for enums that have strings as their underlying values.
122        """
123        return enum(self.data[index])

Example usage:

class MyEnum(Enum):
    A = "a"
    B = "b"
    C = "c"

response = Response(["a", "b", "c"])

response.get_enum(0, MyEnum) # MyEnum.A
response.get_enum(1, MyEnum) # MyEnum.B
response.get_enum(2, MyEnum) # MyEnum.C

NOTE: This only works for enums that have strings as their underlying values.

@dataclass
class WheelSpeed:
126@dataclass
127class WheelSpeed:
128    """All speeds are in rpm."""
129
130    front_right: float
131    front_left: float
132    back_right: float
133    back_left: float

All speeds are in rpm.

WheelSpeed( front_right: float, front_left: float, back_right: float, back_left: float)
front_right: float
front_left: float
back_right: float
back_left: float
@dataclass
class ChassisSpeed:
136@dataclass
137class ChassisSpeed:
138    z: float
139    """
140    Movement forwards/backwards (relative to the rotation of the robot) in m/s.
141    """
142    
143    x: float
144    """
145    Movement right/left (relative to the rotation of the robot) in m/s.
146    """
147
148    clockwise: float
149    """
150    Rotational speed around the vertical axis in the clockwise direction in degrees/s.
151    """
152
153    wheels: WheelSpeed
154
155    @staticmethod
156    def parse(data: Response) -> "ChassisSpeed":
157        return ChassisSpeed(
158            z         = data.get_float(0),
159            x         = data.get_float(1),
160            clockwise = data.get_float(2),
161            wheels    = WheelSpeed(
162                front_right = data.get_float(3),
163                front_left  = data.get_float(4),
164                back_right  = data.get_float(5),
165                back_left   = data.get_float(6)
166            )
167        )
ChassisSpeed( z: float, x: float, clockwise: float, wheels: WheelSpeed)
z: float

Movement forwards/backwards (relative to the rotation of the robot) in m/s.

x: float

Movement right/left (relative to the rotation of the robot) in m/s.

clockwise: float

Rotational speed around the vertical axis in the clockwise direction in degrees/s.

wheels: WheelSpeed
@staticmethod
def parse(data: Response) -> ChassisSpeed:
155    @staticmethod
156    def parse(data: Response) -> "ChassisSpeed":
157        return ChassisSpeed(
158            z         = data.get_float(0),
159            x         = data.get_float(1),
160            clockwise = data.get_float(2),
161            wheels    = WheelSpeed(
162                front_right = data.get_float(3),
163                front_left  = data.get_float(4),
164                back_right  = data.get_float(5),
165                back_left   = data.get_float(6)
166            )
167        )
@dataclass
class ChassisRotation:
170@dataclass
171class ChassisRotation:
172    """
173    This actually parses the position data from the robot,
174    but removes the x and z coordinates due to them being
175    too inaccurate and being a major footgun.
176    """
177
178    clockwise: float
179    """
180    The rotation in degrees clockwise around the vertical axis,
181    relative to the robot's starting orientation.
182    """
183
184    @staticmethod
185    def parse(data: Response) -> "ChassisRotation":
186        return ChassisRotation(
187            clockwise = data.get_float(2)
188        )

This actually parses the position data from the robot, but removes the x and z coordinates due to them being too inaccurate and being a major footgun.

ChassisRotation(clockwise: float)
clockwise: float

The rotation in degrees clockwise around the vertical axis, relative to the robot's starting orientation.

@staticmethod
def parse( data: Response) -> ChassisRotation:
184    @staticmethod
185    def parse(data: Response) -> "ChassisRotation":
186        return ChassisRotation(
187            clockwise = data.get_float(2)
188        )
@dataclass
class ChassisAttitude:
191@dataclass
192class ChassisAttitude:
193    """All values are in degrees."""
194
195    pitch: float
196    roll: float
197    yaw: float
198
199    @staticmethod
200    def parse(data: Response) -> "ChassisAttitude":
201        return ChassisAttitude(
202            pitch = data.get_float(0),
203            roll  = data.get_float(1),
204            yaw   = data.get_float(2)
205        )

All values are in degrees.

ChassisAttitude(pitch: float, roll: float, yaw: float)
pitch: float
roll: float
yaw: float
@staticmethod
def parse( data: Response) -> ChassisAttitude:
199    @staticmethod
200    def parse(data: Response) -> "ChassisAttitude":
201        return ChassisAttitude(
202            pitch = data.get_float(0),
203            roll  = data.get_float(1),
204            yaw   = data.get_float(2)
205        )
@dataclass
class ChassisStatus:
208@dataclass
209class ChassisStatus:
210    static: bool
211    up_hill: bool
212    down_hill: bool
213    on_slope: bool
214    pick_up: bool
215    slip: bool
216    impact_x: bool
217    impact_y: bool
218    impact_z: bool
219    roll_over: bool
220    hill_static: bool
221
222    @staticmethod
223    def parse(data: Response) -> "ChassisStatus":
224        return ChassisStatus(
225            static      = data.get_bool(0),
226            up_hill     = data.get_bool(1),
227            down_hill   = data.get_bool(2),
228            on_slope    = data.get_bool(3),
229            pick_up     = data.get_bool(4),
230            slip        = data.get_bool(5),
231            impact_x    = data.get_bool(6),
232            impact_y    = data.get_bool(7),
233            impact_z    = data.get_bool(8),
234            roll_over   = data.get_bool(9),
235            hill_static = data.get_bool(10)
236        )
ChassisStatus( static: bool, up_hill: bool, down_hill: bool, on_slope: bool, pick_up: bool, slip: bool, impact_x: bool, impact_y: bool, impact_z: bool, roll_over: bool, hill_static: bool)
static: bool
up_hill: bool
down_hill: bool
on_slope: bool
pick_up: bool
slip: bool
impact_x: bool
impact_y: bool
impact_z: bool
roll_over: bool
hill_static: bool
@staticmethod
def parse( data: Response) -> ChassisStatus:
222    @staticmethod
223    def parse(data: Response) -> "ChassisStatus":
224        return ChassisStatus(
225            static      = data.get_bool(0),
226            up_hill     = data.get_bool(1),
227            down_hill   = data.get_bool(2),
228            on_slope    = data.get_bool(3),
229            pick_up     = data.get_bool(4),
230            slip        = data.get_bool(5),
231            impact_x    = data.get_bool(6),
232            impact_y    = data.get_bool(7),
233            impact_z    = data.get_bool(8),
234            roll_over   = data.get_bool(9),
235            hill_static = data.get_bool(10)
236        )
@dataclass
class Point:
239@dataclass
240class Point:
241    x: float
242    y: float
243    tangent: float
244    curvature: float
Point(x: float, y: float, tangent: float, curvature: float)
x: float
y: float
tangent: float
curvature: float
@dataclass
class Line:
247@dataclass
248class Line:
249    type: LineType
250    points: list[Point]
251
252    @staticmethod
253    def parse(data: Response) -> "Line":
254        point_count = (len(data.data) - 1) // 4
255
256        match data.get_int(0):
257            case 0:
258                line_type = LineType.NoLine
259            case 1:
260                line_type = LineType.Straight
261            case 2:
262                line_type = LineType.Fork
263            case 3:
264                line_type = LineType.Intersection
265            case _:
266                raise Exception("Invalid line type")
267
268        points: list[Point] = []
269
270        for i in range(point_count):
271            points.append(Point(
272                x         = data.get_float(i * 4 + 1),
273                y         = data.get_float(i * 4 + 2),
274                tangent   = data.get_float(i * 4 + 3),
275                curvature = data.get_float(i * 4 + 4)
276            ))
277
278        return Line(line_type, points)
Line( type: LineType, points: list[Point])
type: LineType
points: list[Point]
@staticmethod
def parse(data: Response) -> Line:
252    @staticmethod
253    def parse(data: Response) -> "Line":
254        point_count = (len(data.data) - 1) // 4
255
256        match data.get_int(0):
257            case 0:
258                line_type = LineType.NoLine
259            case 1:
260                line_type = LineType.Straight
261            case 2:
262                line_type = LineType.Fork
263            case 3:
264                line_type = LineType.Intersection
265            case _:
266                raise Exception("Invalid line type")
267
268        points: list[Point] = []
269
270        for i in range(point_count):
271            points.append(Point(
272                x         = data.get_float(i * 4 + 1),
273                y         = data.get_float(i * 4 + 2),
274                tangent   = data.get_float(i * 4 + 3),
275                curvature = data.get_float(i * 4 + 4)
276            ))
277
278        return Line(line_type, points)
@dataclass
class Colour:
281@dataclass
282class Colour:
283    """All values are from 0 to 255, inclusive."""
284    
285    r: float
286    g: float
287    b: float

All values are from 0 to 255, inclusive.

Colour(r: float, g: float, b: float)
r: float
g: float
b: float