diff --git a/pitop/core/mixins/__init__.py b/pitop/core/mixins/__init__.py index 76c47c239..1cbab99fd 100644 --- a/pitop/core/mixins/__init__.py +++ b/pitop/core/mixins/__init__.py @@ -1,4 +1,5 @@ from .componentable import Componentable +from .digital_component_checks import DigitalComponentChecks from .recreatable import Recreatable from .stateful import Stateful from .supports_battery import SupportsBattery diff --git a/pitop/core/mixins/digital_component_checks.py b/pitop/core/mixins/digital_component_checks.py new file mode 100644 index 000000000..dc63ded2b --- /dev/null +++ b/pitop/core/mixins/digital_component_checks.py @@ -0,0 +1,20 @@ +import re + + +class DigitalComponentChecks: + """Performs basic checks on validity of user-specified port.""" + + def __init__(self, port_name): + self.port_name = port_name + + # For the sake of a helpful error message, first check if the port is actually a valid port of any kind + if not re.search("^D[0-7]$|^A[0-3]$", self.port_name): + raise ValueError( + f"{self.port_name} is not a valid port name. An example of a valid port name is D0" + ) + + # Then, in this case, verify the port is digital not analog + if re.search("^A[0-3]$", self.port_name): + raise ValueError( + f"Can't use analog port {self.port_name} for digital component. Try using a digital port, such as D0" + ) diff --git a/pitop/core/mixins/recreatable.py b/pitop/core/mixins/recreatable.py index 2bba777f2..6b5b26bc1 100644 --- a/pitop/core/mixins/recreatable.py +++ b/pitop/core/mixins/recreatable.py @@ -4,7 +4,7 @@ class Recreatable: """Represents an object that keeps track of a set of parameters that will - allow to be recreate it in the future. + allow it to be recreated in the future. The values for each key provided in the :param:`config_dict` parameter can be a constant value or a reference to a function, diff --git a/pitop/pma/adc_base.py b/pitop/pma/adc_base.py index a10a8ddeb..7b9fa4d8f 100644 --- a/pitop/pma/adc_base.py +++ b/pitop/pma/adc_base.py @@ -1,3 +1,4 @@ +import re import time from pitop.core.mixins import Recreatable, Stateful @@ -22,6 +23,18 @@ def __init__(self, port_name, pin_number=1, name="adcbase"): self._pma_port = port_name self.name = name + # For the sake of a helpful error message, first check if the port is actually a valid port of any kind + if not re.search("^D[0-7]$|^A[0-3]$", self._pma_port): + raise ValueError( + f"{self._pma_port} is not a valid port name. An example of a valid port name is A0" + ) + + # Then, in this case, verify the port is analog not digital + if re.search("^D[0-7]$", self._pma_port): + raise ValueError( + f"Can't use digital port {self._pma_port} for analog component. Try using an analog port, such as A0" + ) + self.is_current = False self.channel = get_pin_for_port(self._pma_port, pin_number) self.__adc_device = PlateInterface().get_device_mcu() diff --git a/pitop/pma/button.py b/pitop/pma/button.py index b897b511a..677b69893 100644 --- a/pitop/pma/button.py +++ b/pitop/pma/button.py @@ -1,10 +1,10 @@ from gpiozero import Button as gpiozero_Button -from pitop.core.mixins import Recreatable, Stateful +from pitop.core.mixins import DigitalComponentChecks, Recreatable, Stateful from pitop.pma.common import get_pin_for_port -class Button(Stateful, Recreatable, gpiozero_Button): +class Button(Stateful, Recreatable, DigitalComponentChecks, gpiozero_Button): """Encapsulates the behaviour of a push-button. A push-button is a simple switch mechanism for controlling some aspect of a circuit. @@ -18,6 +18,7 @@ def __init__(self, port_name, name="button"): Stateful.__init__(self) Recreatable.__init__(self, {"port_name": port_name, "name": self.name}) + DigitalComponentChecks.__init__(self, self._pma_port) gpiozero_Button.__init__(self, get_pin_for_port(self._pma_port)) @property diff --git a/pitop/pma/buzzer.py b/pitop/pma/buzzer.py index 090a98521..4c6e47715 100644 --- a/pitop/pma/buzzer.py +++ b/pitop/pma/buzzer.py @@ -1,10 +1,10 @@ from gpiozero import Buzzer as gpiozero_Buzzer -from pitop.core.mixins import Recreatable, Stateful +from pitop.core.mixins import DigitalComponentChecks, Recreatable, Stateful from pitop.pma.common import get_pin_for_port -class Buzzer(Stateful, Recreatable, gpiozero_Buzzer): +class Buzzer(Stateful, Recreatable, DigitalComponentChecks, gpiozero_Buzzer): """Encapsulates the behaviour of a simple buzzer that can be turned on and off. @@ -17,6 +17,7 @@ def __init__(self, port_name, name="buzzer"): Stateful.__init__(self) Recreatable.__init__(self, {"port_name": port_name, "name": self.name}) + DigitalComponentChecks.__init__(self, self._pma_port) gpiozero_Buzzer.__init__(self, get_pin_for_port(self._pma_port)) @property diff --git a/pitop/pma/encoder_motor.py b/pitop/pma/encoder_motor.py index 01d7e2f49..fb49db947 100644 --- a/pitop/pma/encoder_motor.py +++ b/pitop/pma/encoder_motor.py @@ -24,7 +24,7 @@ class EncoderMotor(Stateful, Recreatable): The conversions between angle, rotations and RPM used by the motor to meters and meters/second are performed considering the :data:`wheel_diameter` parameter. This parameter defaults to the diameter of the wheel included with MMK. - If a wheel of different dimmensions is attached to the motor, you'll need to measure it's diameter, in order for these + If a wheel of different dimensions is attached to the motor, you'll need to measure it's diameter, in order for these methods to work properly. :type port_name: str diff --git a/pitop/pma/led.py b/pitop/pma/led.py index 0eed07008..f2ff71102 100644 --- a/pitop/pma/led.py +++ b/pitop/pma/led.py @@ -1,10 +1,10 @@ from gpiozero import LED as gpiozero_LED -from pitop.core.mixins import Recreatable, Stateful +from pitop.core.mixins import DigitalComponentChecks, Recreatable, Stateful from pitop.pma.common import get_pin_for_port -class LED(Stateful, Recreatable, gpiozero_LED): +class LED(Stateful, Recreatable, DigitalComponentChecks, gpiozero_LED): """Encapsulates the behaviour of an LED. An LED (Light Emitting Diode) is a simple light source that can be controlled directly. @@ -18,6 +18,7 @@ def __init__(self, port_name, name="led"): Stateful.__init__(self) Recreatable.__init__(self, {"port_name": port_name, "name": self.name}) + DigitalComponentChecks.__init__(self, self._pma_port) gpiozero_LED.__init__(self, get_pin_for_port(self._pma_port)) @property diff --git a/pitop/pma/ultrasonic_sensor.py b/pitop/pma/ultrasonic_sensor.py index a2833aa14..8d6b6155e 100644 --- a/pitop/pma/ultrasonic_sensor.py +++ b/pitop/pma/ultrasonic_sensor.py @@ -1,10 +1,9 @@ +import re + from pitop.core.mixins import Recreatable, Stateful -from pitop.pma.common.utils import Port from .ultrasonic_sensor_base import UltrasonicSensorMCU, UltrasonicSensorRPI -valid_analog_ports = ["A1", "A3"] - class UltrasonicSensor(Stateful, Recreatable): def __init__( @@ -17,11 +16,24 @@ def __init__( name="ultrasonic", ): - assert port_name in Port self._pma_port = port_name self.name = name - if port_name in valid_analog_ports: + # For the sake of a helpful error message, first check if the port is actually a valid port of any kind + if not re.search("^D[0-7]$|^A[0-3]$", self._pma_port): + raise ValueError( + f"{self._pma_port} is not a valid port name. An example of a valid port name is D0" + ) + + # Then, verify it's a valid port for the Ultrasonic sensor specifically + if not re.search("^D[0-7]$|^A1$|^A3$", self._pma_port): + raise ValueError( + f"Can't use port {self._pma_port} for ultrasonic sensor. Try A1, A3 or a digital port " + f"such as D0" + ) + + # If port name is a valid analog port + if re.search("^A[0-3]$", self._pma_port): self.__ultrasonic_device = UltrasonicSensorMCU( port_name=port_name, queue_len=queue_len,