Skip to content

Commit 5133e6a

Browse files
authored
Merge pull request #23 from tekktrik/doc/add-typing
Add type annotations
2 parents 645d774 + bb5b81e commit 5133e6a

File tree

1 file changed

+66
-34
lines changed

1 file changed

+66
-34
lines changed

adafruit_rplidar.py

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@
3333
import warnings
3434
from collections import namedtuple
3535

36+
try:
37+
from typing import Tuple, Dict, Any, Optional, List, Iterator, Union
38+
from busio import UART
39+
from digitalio import DigitalInOut
40+
except ImportError:
41+
pass
42+
3643
# pylint:disable=invalid-name,undefined-variable,global-variable-not-assigned
3744
# pylint:disable=too-many-arguments,raise-missing-from,too-many-instance-attributes
3845

@@ -83,7 +90,7 @@ class RPLidarException(Exception):
8390
"""Basic exception class for RPLidar"""
8491

8592

86-
def _process_scan(raw):
93+
def _process_scan(raw: bytes) -> Tuple[bool, int, float, float]:
8794
"""Processes input raw data and returns measurement data"""
8895
new_scan = bool(raw[0] & 0b1)
8996
inversed_new_scan = bool((raw[0] >> 1) & 0b1)
@@ -98,7 +105,9 @@ def _process_scan(raw):
98105
return new_scan, quality, angle, distance
99106

100107

101-
def _process_express_scan(data, new_angle, frame):
108+
def _process_express_scan(
109+
data: "ExpressPacket", new_angle: float, frame: int
110+
) -> Tuple[bool, None, float, float]:
102111
new_scan = (new_angle < data.start_angle) & (frame == 1)
103112
angle = (
104113
data.start_angle
@@ -125,18 +134,28 @@ class RPLidar:
125134
express_data = False
126135
express_old_data = None
127136

128-
def __init__(self, motor_pin, port, baudrate=115200, timeout=1, logging=False):
137+
def __init__(
138+
self,
139+
motor_pin: DigitalInOut,
140+
port: UART,
141+
baudrate: int = 115200,
142+
timeout: float = 1,
143+
logging: bool = False,
144+
) -> None:
129145
"""Initialize RPLidar object for communicating with the sensor.
130146
131147
Parameters
132148
149+
motor_pin : digitalio.DigitalInOut
150+
Pin controlling the motor
133151
port : busio.UART or str
134152
Serial port instance or name of the port to which the sensor is connected
135153
baudrate : int, optional
136154
Baudrate for serial connection (the default is 115200)
137155
timeout : float, optional
138156
Serial port connection timeout in seconds (the default is 1)
139-
logging : whether to output logging information
157+
logging : bool, optional
158+
Whether to output logging information
140159
"""
141160
self.motor_pin = motor_pin
142161
self.port = port
@@ -156,17 +175,17 @@ def __init__(self, motor_pin, port, baudrate=115200, timeout=1, logging=False):
156175
self.connect()
157176
self.start_motor()
158177

159-
def log(self, level, msg):
178+
def log(self, level: str, msg: str) -> None:
160179
"""Output the level and a message if logging is enabled."""
161180
if self.logging:
162181
sys.stdout.write("{0}: {1}\n".format(level, msg))
163182

164-
def log_bytes(self, level, msg, ba):
183+
def log_bytes(self, level: str, msg: str, ba: bytes) -> None:
165184
"""Log and output a byte array in a readable way."""
166185
bs = ["%02x" % b for b in ba]
167186
self.log(level, msg + " ".join(bs))
168187

169-
def connect(self):
188+
def connect(self) -> None:
170189
"""Connects to the serial port named by the port instance var. If it was
171190
connected to another serial port disconnects from it first."""
172191
if not self.is_CP:
@@ -185,26 +204,26 @@ def connect(self):
185204
"Failed to connect to the sensor " "due to: %s" % err
186205
)
187206

188-
def disconnect(self):
207+
def disconnect(self) -> None:
189208
"""Disconnects from the serial port"""
190209
if self._serial_port is None:
191210
return
192211
self._serial_port.close()
193212

194-
def set_pwm(self, pwm):
213+
def set_pwm(self, pwm: int) -> None:
195214
"""Set the motor PWM"""
196215
assert 0 <= pwm <= MAX_MOTOR_PWM
197216
payload = struct.pack("<H", pwm)
198217
self._send_payload_cmd(SET_PWM_BYTE, payload)
199218

200-
def _control_motor(self, val):
219+
def _control_motor(self, val: bool) -> None:
201220
"""Manipulate the motor"""
202221
if self.is_CP:
203222
self.motor_pin.value = val
204223
else:
205224
self._serial_port.dtr = not val
206225

207-
def start_motor(self):
226+
def start_motor(self) -> None:
208227
"""Starts sensor motor"""
209228
self.log("info", "Starting motor")
210229
# For A1
@@ -214,7 +233,7 @@ def start_motor(self):
214233
self.set_pwm(DEFAULT_MOTOR_PWM)
215234
self.motor_running = True
216235

217-
def stop_motor(self):
236+
def stop_motor(self) -> None:
218237
"""Stops sensor motor"""
219238
self.log("info", "Stopping motor")
220239
# For A2
@@ -224,7 +243,7 @@ def stop_motor(self):
224243
self._control_motor(False)
225244
self.motor_running = False
226245

227-
def _send_payload_cmd(self, cmd, payload):
246+
def _send_payload_cmd(self, cmd: bytes, payload: bytes) -> None:
228247
"""Sends `cmd` command with `payload` to the sensor"""
229248
size = struct.pack("B", len(payload))
230249
req = SYNC_BYTE + cmd + size + payload
@@ -235,13 +254,13 @@ def _send_payload_cmd(self, cmd, payload):
235254
self._serial_port.write(req)
236255
self.log_bytes("debug", "Command sent: ", req)
237256

238-
def _send_cmd(self, cmd):
257+
def _send_cmd(self, cmd: bytes) -> None:
239258
"""Sends `cmd` command to the sensor"""
240259
req = SYNC_BYTE + cmd
241260
self._serial_port.write(req)
242261
self.log_bytes("debug", "Command sent: ", req)
243262

244-
def _read_descriptor(self):
263+
def _read_descriptor(self) -> Tuple[int, bool, int]:
245264
"""Reads descriptor packet"""
246265
descriptor = self._serial_port.read(DESCRIPTOR_LEN)
247266
self.log_bytes("debug", "Received descriptor:", descriptor)
@@ -252,7 +271,7 @@ def _read_descriptor(self):
252271
is_single = descriptor[-2] == 0
253272
return descriptor[2], is_single, descriptor[-1]
254273

255-
def _read_response(self, dsize):
274+
def _read_response(self, dsize: int) -> bytes:
256275
"""Reads response packet with length of `dsize` bytes"""
257276
self.log("debug", "Trying to read response: %d bytes" % dsize)
258277
data = self._serial_port.read(dsize)
@@ -262,7 +281,7 @@ def _read_response(self, dsize):
262281
return data
263282

264283
@property
265-
def info(self):
284+
def info(self) -> Dict[str, Any]:
266285
"""Get device information
267286
268287
Returns
@@ -290,7 +309,7 @@ def info(self):
290309
return data
291310

292311
@property
293-
def health(self):
312+
def health(self) -> Tuple[str, int]:
294313
"""Get device health state. When the core system detects some
295314
potential risk that may cause hardware failure in the future,
296315
the returned status value will be 'Warning'. But sensor can still work
@@ -318,19 +337,21 @@ def health(self):
318337
error_code = (raw[1] << 8) + raw[2]
319338
return (status, error_code)
320339

321-
def clear_input(self):
340+
def clear_input(self) -> None:
322341
"""Clears input buffer by reading all available data"""
323342
if self.scanning:
324343
raise RPLidarException("Clearing not allowed during active scanning!")
325344
self._serial_port.flushInput()
326345
self.express_frame = 32
327346
self.express_data = False
328347

329-
def start(self, scan_type=SCAN_TYPE_NORMAL):
348+
def start(self, scan_type: int = SCAN_TYPE_NORMAL) -> None:
330349
"""Start the scanning process
350+
331351
Parameters
332-
----------
333-
scan : normal, force or express.
352+
353+
scan_type : int, optional
354+
Normal, force or express; default is normal
334355
"""
335356
if self.scanning:
336357
raise RPLidarException("Scanning already running!")
@@ -374,7 +395,7 @@ def start(self, scan_type=SCAN_TYPE_NORMAL):
374395
self.scan_type = scan_type
375396
self.scanning = True
376397

377-
def stop(self):
398+
def stop(self) -> None:
378399
"""Stops scanning process, disables laser diode and the measurement
379400
system, moves sensor to the idle state."""
380401
self.log("info", "Stopping scanning")
@@ -383,30 +404,35 @@ def stop(self):
383404
self.scanning = False
384405
self.clear_input()
385406

386-
def reset(self):
407+
def reset(self) -> None:
387408
"""Resets sensor core, reverting it to a similar state as it has
388409
just been powered up."""
389410
self.log("info", "Resetting the sensor")
390411
self._send_cmd(RESET_BYTE)
391412
time.sleep(0.002)
392413
self.clear_input()
393414

394-
def iter_measurements(self, max_buf_meas=500, scan_type=SCAN_TYPE_NORMAL):
415+
def iter_measurements(
416+
self, max_buf_meas: int = 500, scan_type: int = SCAN_TYPE_NORMAL
417+
) -> Iterator[Tuple[bool, Optional[int], float, float]]:
395418
"""Iterate over measurements. Note that consumer must be fast enough,
396419
otherwise data will be accumulated inside buffer and consumer will get
397420
data with increasing lag.
398421
399422
Parameters
400423
401-
max_buf_meas : int
424+
max_buf_meas : int, optional
402425
Maximum number of measurements to be stored inside the buffer. Once
403-
number exceeds this limit buffer will be emptied out.
426+
number exceeds this limit buffer will be emptied out. Default is
427+
500.
428+
scan_type : int, optional
429+
Normal, force or express; default is normal
404430
405431
Yields
406432
407433
new_scan : bool
408434
True if measurement belongs to a new scan
409-
quality : int
435+
quality : int | None
410436
Reflected laser pulse strength
411437
angle : float
412438
The measurement heading angle in degree unit [0, 360)
@@ -474,7 +500,9 @@ def iter_measurements(self, max_buf_meas=500, scan_type=SCAN_TYPE_NORMAL):
474500
self.express_frame,
475501
)
476502

477-
def iter_measurments(self, max_buf_meas=500):
503+
def iter_measurments(
504+
self, max_buf_meas: int = 500
505+
) -> Iterator[Tuple[bool, int, float, float]]:
478506
"""For compatibility, this method wraps `iter_measurements`"""
479507
warnings.warn(
480508
"The method `iter_measurments` has been renamed "
@@ -483,18 +511,22 @@ def iter_measurments(self, max_buf_meas=500):
483511
)
484512
self.iter_measurements(max_buf_meas=max_buf_meas)
485513

486-
def iter_scans(self, max_buf_meas=500, min_len=5):
514+
def iter_scans(
515+
self, max_buf_meas: int = 500, min_len: int = 5
516+
) -> List[Union[int, float]]:
487517
"""Iterate over scans. Note that consumer must be fast enough,
488518
otherwise data will be accumulated inside buffer and consumer will get
489519
data with increasing lag.
490520
491521
Parameters
492522
493-
max_buf_meas : int
523+
max_buf_meas : int, optional
494524
Maximum number of measurements to be stored inside the buffer. Once
495-
number exceeds this limit buffer will be emptied out.
496-
min_len : int
525+
number exceeds this limit buffer will be emptied out. Default is
526+
500.
527+
min_len : int, optional
497528
Minimum number of measurements in the scan for it to be yielded.
529+
Default is 5.
498530
499531
Yields
500532
@@ -522,7 +554,7 @@ class ExpressPacket(express_packet):
522554
sign = {0: 1, 1: -1}
523555

524556
@classmethod
525-
def from_string(cls, data):
557+
def from_string(cls, data: bytes) -> "ExpressPacket":
526558
"""Decode and Instantiate the class from a string packet"""
527559
packet = bytearray(data)
528560

0 commit comments

Comments
 (0)