sr-robot3 2024.0.0rc2__py3-none-any.whl → 2025.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sr/robot3/_version.py +14 -2
- sr/robot3/arduino.py +91 -25
- sr/robot3/astoria.py +7 -2
- sr/robot3/camera.py +92 -24
- sr/robot3/kch.py +256 -30
- sr/robot3/logging.py +1 -1
- sr/robot3/marker.py +8 -0
- sr/robot3/motor_board.py +42 -10
- sr/robot3/mqtt.py +23 -8
- sr/robot3/power_board.py +54 -13
- sr/robot3/raw_serial.py +71 -10
- sr/robot3/robot.py +100 -28
- sr/robot3/serial_wrapper.py +25 -13
- sr/robot3/servo_board.py +44 -11
- sr/robot3/simulator/__init__.py +0 -0
- sr/robot3/simulator/camera.py +75 -0
- sr/robot3/simulator/time_server.py +94 -0
- sr/robot3/timeout.py +30 -4
- sr/robot3/utils.py +41 -1
- {sr_robot3-2024.0.0rc2.dist-info → sr_robot3-2025.0.0.dist-info}/METADATA +17 -15
- sr_robot3-2025.0.0.dist-info/RECORD +29 -0
- {sr_robot3-2024.0.0rc2.dist-info → sr_robot3-2025.0.0.dist-info}/WHEEL +1 -1
- sr_robot3-2024.0.0rc2.dist-info/RECORD +0 -26
- {sr_robot3-2024.0.0rc2.dist-info → sr_robot3-2025.0.0.dist-info}/LICENSE +0 -0
- {sr_robot3-2024.0.0rc2.dist-info → sr_robot3-2025.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
import struct
|
2
|
+
|
3
|
+
import cv2
|
4
|
+
import numpy as np
|
5
|
+
from april_vision import FrameSource
|
6
|
+
from numpy.typing import NDArray
|
7
|
+
from serial import serial_for_url
|
8
|
+
|
9
|
+
from ..utils import BoardInfo
|
10
|
+
|
11
|
+
HEADER_SIZE = 5 # 1 byte for the type, 4 bytes for the length
|
12
|
+
IMAGE_TAG_ID = 0
|
13
|
+
|
14
|
+
|
15
|
+
class WebotsRemoteCameraSource(FrameSource):
|
16
|
+
# Webots cameras include an alpha channel, this informs april_vision of how to handle it
|
17
|
+
COLOURSPACE = cv2.COLOR_BGRA2GRAY
|
18
|
+
|
19
|
+
def __init__(self, camera_info: BoardInfo) -> None:
|
20
|
+
self.calibration = (0.0, 0.0, 0.0, 0.0)
|
21
|
+
# Use pyserial to give a nicer interface for connecting to the camera socket
|
22
|
+
self._serial = serial_for_url(camera_info.url, baudrate=115200, timeout=None)
|
23
|
+
|
24
|
+
# Check the camera is connected
|
25
|
+
response = self._make_request("*IDN?")
|
26
|
+
if not response.split(b":")[1].lower().startswith(b"cam"):
|
27
|
+
raise RuntimeError(f"Camera not connected to a camera, returned: {response!r}")
|
28
|
+
|
29
|
+
# Get the calibration data for this camera
|
30
|
+
response = self._make_request("CAM:CALIBRATION?")
|
31
|
+
|
32
|
+
# The calibration data is returned as a string of floats separated by colons
|
33
|
+
new_calibration = tuple(map(float, response.split(b":")))
|
34
|
+
assert len(new_calibration) == 4, f"Invalid calibration data: {new_calibration}"
|
35
|
+
self.calibration = new_calibration
|
36
|
+
|
37
|
+
# Get the image size for this camera
|
38
|
+
response = self._make_request("CAM:RESOLUTION?")
|
39
|
+
self.image_size = tuple(map(int, response.split(b":")))
|
40
|
+
assert len(self.image_size) == 2, f"Invalid image dimensions: {self.image_size}"
|
41
|
+
|
42
|
+
def read(self, fresh: bool = True) -> NDArray:
|
43
|
+
"""
|
44
|
+
The method for getting a new frame.
|
45
|
+
|
46
|
+
:param fresh: Whether to flush the device's buffer before capturing
|
47
|
+
the frame, unused.
|
48
|
+
"""
|
49
|
+
self._serial.write(b"CAM:FRAME!\n")
|
50
|
+
# The image is encoded as a TLV (Type, Length, Value) packet
|
51
|
+
# so we need to read the header to get the type and length of the image
|
52
|
+
header = self._serial.read(HEADER_SIZE)
|
53
|
+
assert len(header) == HEADER_SIZE, f"Invalid header length: {len(header)}"
|
54
|
+
img_tag, img_len = struct.unpack('>BI', header)
|
55
|
+
assert img_tag == IMAGE_TAG_ID, f"Invalid image tag: {img_tag}"
|
56
|
+
|
57
|
+
# Get the image data now we know the length
|
58
|
+
img_data = self._serial.read(img_len)
|
59
|
+
assert len(img_data) == img_len, f"Invalid image data length: {len(img_data)}"
|
60
|
+
|
61
|
+
rgb_frame_raw: NDArray[np.uint8] = np.frombuffer(img_data, np.uint8)
|
62
|
+
|
63
|
+
# Height is first, then width, then channels
|
64
|
+
return rgb_frame_raw.reshape((self.image_size[1], self.image_size[0], 4))
|
65
|
+
|
66
|
+
def close(self) -> None:
|
67
|
+
"""Close the underlying socket on exit."""
|
68
|
+
self._serial.close()
|
69
|
+
|
70
|
+
def _make_request(self, command: str) -> bytes:
|
71
|
+
self._serial.write(command.encode() + b"\n")
|
72
|
+
response = self._serial.readline()
|
73
|
+
if not response.endswith(b"\n") or response.startswith(b"NACK:"):
|
74
|
+
raise RuntimeError(f"Failed to communicate with camera, returned: {response!r}")
|
75
|
+
return response
|
@@ -0,0 +1,94 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import logging
|
4
|
+
from datetime import datetime
|
5
|
+
|
6
|
+
from ..exceptions import BoardDisconnectionError, IncorrectBoardError
|
7
|
+
from ..serial_wrapper import SerialWrapper
|
8
|
+
from ..utils import BoardIdentity, get_simulator_boards
|
9
|
+
|
10
|
+
logger = logging.getLogger(__name__)
|
11
|
+
|
12
|
+
BAUDRATE = 115200
|
13
|
+
|
14
|
+
|
15
|
+
class TimeServer:
|
16
|
+
@staticmethod
|
17
|
+
def get_board_type() -> str:
|
18
|
+
"""
|
19
|
+
Return the type of the board.
|
20
|
+
|
21
|
+
:return: The literal string 'TimeServer'.
|
22
|
+
"""
|
23
|
+
return 'TimeServer'
|
24
|
+
|
25
|
+
def __init__(
|
26
|
+
self,
|
27
|
+
serial_port: str,
|
28
|
+
initial_identity: BoardIdentity | None = None,
|
29
|
+
) -> None:
|
30
|
+
if initial_identity is None:
|
31
|
+
initial_identity = BoardIdentity()
|
32
|
+
self._serial = SerialWrapper(
|
33
|
+
serial_port,
|
34
|
+
BAUDRATE,
|
35
|
+
identity=initial_identity,
|
36
|
+
# Disable the timeout so sleep works properly
|
37
|
+
timeout=None,
|
38
|
+
)
|
39
|
+
|
40
|
+
self._identity = self.identify()
|
41
|
+
if self._identity.board_type != self.get_board_type():
|
42
|
+
raise IncorrectBoardError(self._identity.board_type, self.get_board_type())
|
43
|
+
self._serial.set_identity(self._identity)
|
44
|
+
|
45
|
+
@classmethod
|
46
|
+
def initialise(cls) -> 'TimeServer' | None:
|
47
|
+
# The filter here is the name of the emulated board in the simulator
|
48
|
+
boards = get_simulator_boards('TimeServer')
|
49
|
+
|
50
|
+
if not boards:
|
51
|
+
return None
|
52
|
+
|
53
|
+
board_info = boards[0]
|
54
|
+
|
55
|
+
# Create board identity from the info given
|
56
|
+
initial_identity = BoardIdentity(
|
57
|
+
manufacturer='sbot_simulator',
|
58
|
+
board_type=board_info.type_str,
|
59
|
+
asset_tag=board_info.serial_number,
|
60
|
+
)
|
61
|
+
|
62
|
+
try:
|
63
|
+
board = cls(board_info.url, initial_identity)
|
64
|
+
except BoardDisconnectionError:
|
65
|
+
logger.warning(
|
66
|
+
f"Simulator specified time server at port {board_info.url!r}, "
|
67
|
+
"could not be identified. Ignoring this device")
|
68
|
+
return None
|
69
|
+
except IncorrectBoardError as err:
|
70
|
+
logger.warning(
|
71
|
+
f"Board returned type {err.returned_type!r}, "
|
72
|
+
f"expected {err.expected_type!r}. Ignoring this device")
|
73
|
+
return None
|
74
|
+
|
75
|
+
return board
|
76
|
+
|
77
|
+
def identify(self) -> BoardIdentity:
|
78
|
+
"""
|
79
|
+
Get the identity of the board.
|
80
|
+
|
81
|
+
:return: The identity of the board.
|
82
|
+
"""
|
83
|
+
response = self._serial.query('*IDN?')
|
84
|
+
return BoardIdentity(*response.split(':'))
|
85
|
+
|
86
|
+
def get_time(self) -> float:
|
87
|
+
time_str = self._serial.query('TIME?')
|
88
|
+
return datetime.fromisoformat(time_str).timestamp()
|
89
|
+
|
90
|
+
def sleep(self, duration: float) -> None:
|
91
|
+
if duration < 0:
|
92
|
+
raise ValueError("sleep length must be non-negative")
|
93
|
+
|
94
|
+
self._serial.query(f'SLEEP:{int(duration * 1000)}')
|
sr/robot3/timeout.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Functions for killing the robot after a certain amount of time."""
|
2
|
+
import atexit
|
2
3
|
import logging
|
3
4
|
import os
|
4
5
|
import signal
|
@@ -9,6 +10,9 @@ from typing import Optional
|
|
9
10
|
|
10
11
|
logger = logging.getLogger(__name__)
|
11
12
|
|
13
|
+
TIMEOUT_MESSAGE = "Timeout expired: Game Over!"
|
14
|
+
EXIT_ATTEMPTS = 0
|
15
|
+
|
12
16
|
|
13
17
|
def timeout_handler(signal_type: int, stack_frame: Optional[FrameType]) -> None:
|
14
18
|
"""
|
@@ -17,13 +21,34 @@ def timeout_handler(signal_type: int, stack_frame: Optional[FrameType]) -> None:
|
|
17
21
|
This function is called when the timeout expires and will stop the robot's main process.
|
18
22
|
In order for this to work, any threads that are created must be daemons.
|
19
23
|
|
24
|
+
If the process doesn't terminate clearly (perhaps because the exception was caught),
|
25
|
+
exit less cleanly.
|
26
|
+
|
20
27
|
NOTE: This function is not called on Windows.
|
21
28
|
|
22
|
-
:param signal_type: The
|
29
|
+
:param signal_type: The signal that triggered this handler
|
23
30
|
:param stack_frame: The stack frame at the time of the signal
|
24
31
|
"""
|
25
|
-
|
26
|
-
|
32
|
+
global EXIT_ATTEMPTS
|
33
|
+
EXIT_ATTEMPTS += 1
|
34
|
+
|
35
|
+
if sys.platform == "win32":
|
36
|
+
raise AssertionError("This function should not be called on Windows")
|
37
|
+
|
38
|
+
if EXIT_ATTEMPTS == 1:
|
39
|
+
# Allow 2 seconds for the process to exit cleanly before killing it
|
40
|
+
signal.alarm(2)
|
41
|
+
logger.info(TIMEOUT_MESSAGE)
|
42
|
+
# Exit cleanly
|
43
|
+
exit(0)
|
44
|
+
else:
|
45
|
+
# The process didn't exit cleanly, so first call the cleanup handlers
|
46
|
+
# and use an unhanded alarm to force python to exit with a core dump
|
47
|
+
signal.signal(signal.SIGALRM, signal.SIG_DFL)
|
48
|
+
signal.alarm(2) # Allow 2 seconds for cleanup
|
49
|
+
|
50
|
+
atexit._run_exitfuncs()
|
51
|
+
exit(0)
|
27
52
|
|
28
53
|
|
29
54
|
def win_timeout_handler() -> None:
|
@@ -35,7 +60,7 @@ def win_timeout_handler() -> None:
|
|
35
60
|
|
36
61
|
NOTE: This function is only called on Windows.
|
37
62
|
"""
|
38
|
-
logger.info(
|
63
|
+
logger.info(TIMEOUT_MESSAGE)
|
39
64
|
os.kill(os.getpid(), signal.SIGTERM)
|
40
65
|
|
41
66
|
|
@@ -52,6 +77,7 @@ def kill_after_delay(timeout_seconds: int) -> None:
|
|
52
77
|
# Windows doesn't have SIGALRM,
|
53
78
|
# so we approximate its functionality using a delayed SIGTERM
|
54
79
|
timer = Timer(timeout_seconds, win_timeout_handler)
|
80
|
+
timer.daemon = True
|
55
81
|
timer.start()
|
56
82
|
else:
|
57
83
|
signal.signal(signal.SIGALRM, timeout_handler)
|
sr/robot3/utils.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
from __future__ import annotations
|
3
3
|
|
4
4
|
import logging
|
5
|
+
import os
|
5
6
|
import signal
|
6
7
|
import socket
|
7
8
|
from abc import ABC, abstractmethod
|
@@ -14,6 +15,8 @@ from serial.tools.list_ports_common import ListPortInfo
|
|
14
15
|
T = TypeVar('T')
|
15
16
|
logger = logging.getLogger(__name__)
|
16
17
|
|
18
|
+
IN_SIMULATOR = os.environ.get('WEBOTS_SIMULATOR', '') == '1'
|
19
|
+
|
17
20
|
|
18
21
|
class BoardIdentity(NamedTuple):
|
19
22
|
"""
|
@@ -34,6 +37,13 @@ class BoardIdentity(NamedTuple):
|
|
34
37
|
sw_version: str = ""
|
35
38
|
|
36
39
|
|
40
|
+
class BoardInfo(NamedTuple):
|
41
|
+
"""A container for the information about a board connection."""
|
42
|
+
url: str
|
43
|
+
serial_number: str
|
44
|
+
type_str: str
|
45
|
+
|
46
|
+
|
37
47
|
class Board(ABC):
|
38
48
|
"""
|
39
49
|
This is the base class for all boards.
|
@@ -123,7 +133,7 @@ def singular(container: Mapping[str, T]) -> T:
|
|
123
133
|
|
124
134
|
:param container: A mapping of connected boards of a type
|
125
135
|
:raises RuntimeError: If there is not exactly one of this type of board connected
|
126
|
-
:return:
|
136
|
+
:return: The connected board of the type
|
127
137
|
"""
|
128
138
|
length = len(container)
|
129
139
|
|
@@ -228,6 +238,36 @@ def ensure_atexit_on_term() -> None:
|
|
228
238
|
signal.signal(signal.SIGTERM, handle_signal)
|
229
239
|
|
230
240
|
|
241
|
+
def get_simulator_boards(board_filter: str = '') -> list[BoardInfo]:
|
242
|
+
"""
|
243
|
+
Get a list of all boards configured in the simulator.
|
244
|
+
|
245
|
+
This is used to support discovery of boards in the simulator environment.
|
246
|
+
|
247
|
+
:param board_filter: A filter to only return boards of a certain type
|
248
|
+
:return: A list of board connection information
|
249
|
+
"""
|
250
|
+
if 'WEBOTS_ROBOT' not in os.environ:
|
251
|
+
return []
|
252
|
+
|
253
|
+
simulator_data = os.environ['WEBOTS_ROBOT'].splitlines()
|
254
|
+
simulator_boards = []
|
255
|
+
|
256
|
+
for board_data in simulator_data:
|
257
|
+
board_data = board_data.rstrip('/')
|
258
|
+
board_fragment, serial_number = board_data.rsplit('/', 1)
|
259
|
+
board_url, board_type = board_fragment.rsplit('/', 1)
|
260
|
+
|
261
|
+
board_info = BoardInfo(url=board_url, serial_number=serial_number, type_str=board_type)
|
262
|
+
|
263
|
+
if board_filter and board_info.type_str != board_filter:
|
264
|
+
continue
|
265
|
+
|
266
|
+
simulator_boards.append(board_info)
|
267
|
+
|
268
|
+
return simulator_boards
|
269
|
+
|
270
|
+
|
231
271
|
def list_ports() -> None:
|
232
272
|
"""
|
233
273
|
Print a list of all connected USB serial ports.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sr-robot3
|
3
|
-
Version:
|
3
|
+
Version: 2025.0.0
|
4
4
|
Summary: Student Robotics API for Python 3
|
5
5
|
Author-email: Student Robotics <kit-team@studentrobotics.org>
|
6
6
|
License: MIT License
|
@@ -37,22 +37,24 @@ Classifier: Topic :: Education
|
|
37
37
|
Requires-Python: >=3.8
|
38
38
|
Description-Content-Type: text/markdown
|
39
39
|
License-File: LICENSE
|
40
|
-
Requires-Dist: pyserial
|
41
|
-
Requires-Dist: april-vision
|
42
|
-
Requires-Dist: paho-mqtt
|
43
|
-
Requires-Dist: pydantic
|
44
|
-
Requires-Dist: typing-extensions
|
40
|
+
Requires-Dist: pyserial<4,>=3
|
41
|
+
Requires-Dist: april-vision==2.2.0
|
42
|
+
Requires-Dist: paho-mqtt<3,>=2
|
43
|
+
Requires-Dist: pydantic<2,>=1.9.1
|
44
|
+
Requires-Dist: typing-extensions; python_version < "3.10"
|
45
|
+
Requires-Dist: tomli<3,>=2.0.1; python_version < "3.11"
|
45
46
|
Provides-Extra: dev
|
46
|
-
Requires-Dist: flake8
|
47
|
-
Requires-Dist: isort
|
48
|
-
Requires-Dist:
|
49
|
-
Requires-Dist:
|
50
|
-
Requires-Dist:
|
51
|
-
Requires-Dist: pytest
|
52
|
-
Requires-Dist:
|
53
|
-
Requires-Dist:
|
47
|
+
Requires-Dist: flake8; extra == "dev"
|
48
|
+
Requires-Dist: isort; extra == "dev"
|
49
|
+
Requires-Dist: build; extra == "dev"
|
50
|
+
Requires-Dist: types-pyserial; extra == "dev"
|
51
|
+
Requires-Dist: pytest; extra == "dev"
|
52
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
53
|
+
Requires-Dist: paho-mqtt<3,>=2; extra == "dev"
|
54
|
+
Requires-Dist: mypy==1.10.0; python_version < "3.9" and extra == "dev"
|
55
|
+
Requires-Dist: mypy<2,>=1.7; python_version >= "3.9" and extra == "dev"
|
54
56
|
Provides-Extra: vision
|
55
|
-
Requires-Dist: opencv-python-headless
|
57
|
+
Requires-Dist: opencv-python-headless<5,>=4; extra == "vision"
|
56
58
|
|
57
59
|
# sr-robot3
|
58
60
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
sr/robot3/__init__.py,sha256=lW7j65ruMEkRNJjZzp8929TefPMaMhqXzpsGPJqMkRs,1476
|
2
|
+
sr/robot3/_version.py,sha256=-X88o2Nur4PzeUeRdAC4dP3gTVlb9JWw08awOdH4TYg,417
|
3
|
+
sr/robot3/arduino.py,sha256=9rGsvGDb1JoHB4c7W61R16Y3eb0-UZ9TE49Qu7BJIZE,16934
|
4
|
+
sr/robot3/astoria.py,sha256=0EIXoxvs1glkGuzLDYG7HptKucc8q7sdWAQiu96NG24,8547
|
5
|
+
sr/robot3/camera.py,sha256=b2r8-ttQgLqbMv9BI6qWb5sHjeg4Zb5VrGo2uWMYGd8,8686
|
6
|
+
sr/robot3/exceptions.py,sha256=A8cpuvFaIFazWSrp9QNts9ikINWymgoGALv4JAL-SU8,1381
|
7
|
+
sr/robot3/game_specific.py,sha256=X8vLUbTRdNPZEggC6LxZzaNb9oqMly8-3PyQoD3B1ho,282
|
8
|
+
sr/robot3/kch.py,sha256=CxMbRztCNz75Uikv3KaViV6zYlXsJuBbI6qg2AsQW4Q,12500
|
9
|
+
sr/robot3/logging.py,sha256=1KO1yW2RP9qSUHr-i1NOMM68szxML5qJyDKtdt34uyg,3464
|
10
|
+
sr/robot3/marker.py,sha256=6LzT59xjEFroljcDsejfcUeU-5dlDAEWbPepp-25fOY,5403
|
11
|
+
sr/robot3/motor_board.py,sha256=6yYhz0WSLzPvAzREIYUmCLeCtc6Qwkm7hdTOPFgRZ70,11356
|
12
|
+
sr/robot3/mqtt.py,sha256=1xKQ0uwkupURWFXrvsD75TE-GlltfbrpVNqoabgmsrc,6679
|
13
|
+
sr/robot3/power_board.py,sha256=3Skbc7kmG1pr2K6n94POkMOXT4jzbOiYCz2cb60CWv8,17734
|
14
|
+
sr/robot3/py.typed,sha256=dXL7b_5DnfZ_09rOcxu7D-0rT56DRm26uGUohPJOPQc,94
|
15
|
+
sr/robot3/raw_serial.py,sha256=rdh0lyS9tJDyHbAZKyJSO8lKvdJ3-_iZzvmUSOdbo08,7368
|
16
|
+
sr/robot3/robot.py,sha256=cqUrPFver_yAjdTPBLqPjsoliSuNdiHn7RGrpXLsuMA,15888
|
17
|
+
sr/robot3/serial_wrapper.py,sha256=CDRVFbIILf4ahiToIHrVf02hz_GXpFeazDHdw3rGgX0,9327
|
18
|
+
sr/robot3/servo_board.py,sha256=1GppUoqdx9wUA551bO95dEbtCGdzS10twv8OZ3S6t8U,12505
|
19
|
+
sr/robot3/timeout.py,sha256=qBPCOwnOW9h0fLnL-peraO9RsaKWc92TcTYNgyTzo_g,2678
|
20
|
+
sr/robot3/utils.py,sha256=dA2WyB73hnw41jLkOpk6ail7mfrW3oxKU0KrpQuHv1Y,9146
|
21
|
+
sr/robot3/calibrations/__init__.py,sha256=TTL-lkU5tj3EszSEInRTftNvX5rsMyRUSHf12RlJ4QY,130
|
22
|
+
sr/robot3/simulator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
|
+
sr/robot3/simulator/camera.py,sha256=thenTnlyHzHrosUg9juUSfYCs2U4dfkS7vFoto1b3_k,3138
|
24
|
+
sr/robot3/simulator/time_server.py,sha256=MwFJM7p3vqf4balmGwIMXcCLBVJQgnq2X4gY_agalwc,2859
|
25
|
+
sr_robot3-2025.0.0.dist-info/LICENSE,sha256=d9EjkBp2jG1JOJ3zKfKRwBtPe1nhh1VByrw4iH3A9WA,1101
|
26
|
+
sr_robot3-2025.0.0.dist-info/METADATA,sha256=iQFR8uEwpNe7S497QqT56Pash4wDEbw2Ud0V7K6ZjJY,5322
|
27
|
+
sr_robot3-2025.0.0.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
|
28
|
+
sr_robot3-2025.0.0.dist-info/top_level.txt,sha256=XFzWuC7umCVdV-O6IuC9Rl0qehvEC-U34M9WJ2-2h3E,3
|
29
|
+
sr_robot3-2025.0.0.dist-info/RECORD,,
|
@@ -1,26 +0,0 @@
|
|
1
|
-
sr/robot3/__init__.py,sha256=lW7j65ruMEkRNJjZzp8929TefPMaMhqXzpsGPJqMkRs,1476
|
2
|
-
sr/robot3/_version.py,sha256=SXjMGAhD6YctTnTcwOWelhbWC-EouEhUcdYCxGBqZ-s,169
|
3
|
-
sr/robot3/arduino.py,sha256=sz-9AAg649Spp70sit3GwrwvDhsMfAM7yv94Dgg3gfM,14362
|
4
|
-
sr/robot3/astoria.py,sha256=dyrK_vMFHl9F-E9sXgDGNbuW3SLcTCQzNgu_-d7uB0Q,8435
|
5
|
-
sr/robot3/camera.py,sha256=UKvJFLGVptZLquNpGne34AaEnqw07CpH8C-gaxPdUrQ,6621
|
6
|
-
sr/robot3/exceptions.py,sha256=A8cpuvFaIFazWSrp9QNts9ikINWymgoGALv4JAL-SU8,1381
|
7
|
-
sr/robot3/game_specific.py,sha256=X8vLUbTRdNPZEggC6LxZzaNb9oqMly8-3PyQoD3B1ho,282
|
8
|
-
sr/robot3/kch.py,sha256=uPdzxdbFxCS0F7UdkUsIuh7xXyKs8Jw4QpHBeVw_k-U,5413
|
9
|
-
sr/robot3/logging.py,sha256=QHvdxWieSyQcu2pKpjG6ur3A8BWGwDia4h3lOjBKDZ4,3447
|
10
|
-
sr/robot3/marker.py,sha256=FDRWl-tNN7xeRnlQLRitjumuligJmVxa2Mr49n1YurE,4886
|
11
|
-
sr/robot3/motor_board.py,sha256=qfwbchUdfdZoSfeTwNuG24GR2cCmo_5vdFSkQtPIQxk,10109
|
12
|
-
sr/robot3/mqtt.py,sha256=6JQdPlx1LpVE1g_n2hbQ2vAOUKwWrtNcZlH-F_q-0Lc,6300
|
13
|
-
sr/robot3/power_board.py,sha256=tKBwuqDqqc_N0jdOxBstHe_eoZ1glOTv1eEuV_1287k,16213
|
14
|
-
sr/robot3/py.typed,sha256=dXL7b_5DnfZ_09rOcxu7D-0rT56DRm26uGUohPJOPQc,94
|
15
|
-
sr/robot3/raw_serial.py,sha256=nXNdaPfPL7l9dznkuaniRjb8EYnE4_wsQPi7dESxUXE,4876
|
16
|
-
sr/robot3/robot.py,sha256=iPvbvFFamZe1x0ebgUqsTHmlKUEr3MZoGHuzAD4Pqdo,13239
|
17
|
-
sr/robot3/serial_wrapper.py,sha256=iBN9GvOQdxPSz3LO3Mqxoskm7wltN7YeiORg_yIV0jQ,8832
|
18
|
-
sr/robot3/servo_board.py,sha256=EUTxCXq79wGWK2CAmc3nzviGBeJpNq1F_wz-CX2FKmY,11234
|
19
|
-
sr/robot3/timeout.py,sha256=QA-Rer05wl1wnt4hKVXwlMnMAsyaqTvcLTU7dmrr6IE,1843
|
20
|
-
sr/robot3/utils.py,sha256=tpLVRVEC_6nBdiBxecSDgLmExy6_bLhrJKkMPrN2vew,7936
|
21
|
-
sr/robot3/calibrations/__init__.py,sha256=TTL-lkU5tj3EszSEInRTftNvX5rsMyRUSHf12RlJ4QY,130
|
22
|
-
sr_robot3-2024.0.0rc2.dist-info/LICENSE,sha256=d9EjkBp2jG1JOJ3zKfKRwBtPe1nhh1VByrw4iH3A9WA,1101
|
23
|
-
sr_robot3-2024.0.0rc2.dist-info/METADATA,sha256=GhMKUBWNH2otMOa7laLSTndBVeX3J9OITfv2QWkJJxY,5180
|
24
|
-
sr_robot3-2024.0.0rc2.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
25
|
-
sr_robot3-2024.0.0rc2.dist-info/top_level.txt,sha256=XFzWuC7umCVdV-O6IuC9Rl0qehvEC-U34M9WJ2-2h3E,3
|
26
|
-
sr_robot3-2024.0.0rc2.dist-info/RECORD,,
|
File without changes
|
File without changes
|