"""Hardware controller
This module contains the hardware controller class for the JetRacer.
This file can be imported and contains the following classes:
* HardwareController: Controls a specific hardware.
* HardwareResult: Contains result of hardware accesses.
* HardwareState: State of the hardware.
* JRHardwareController: Controls a NVIDIA JetRacer ROS AI Kit.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from enum import Enum
import logging
from uppaal2jetracer.jetracerros2.jetracerros2.turn_controller import TurnController
from uppaal2jetracer.jetracerros2.jetracerros2.speed_controller import SpeedController
from uppaal2jetracer.jetracerros2.jetracerros2.collision_controller import CollisionController
logger = logging.getLogger("controller")
[docs]
class HardwareController(ABC):
"""
A class to represent a hardware controller. Each method should return a :class:`HardwareResult` with a correct :class:`HardwareState`. This allows the hardware command system to savely handle hardware errors.
"""
[docs]
@abstractmethod
def shutdown(self):
"""
Shut down the hardware controller and associated hardware.
"""
[docs]
@abstractmethod
def stop(self):
"""
Stops running hardware.
"""
[docs]
class HardwareResult[T]:
"""
A class to represent a hardware result.
:ivar _hardware_state: The state of the hardware.
:vartype _hardware_state: HardwareState
:ivar _result: The result of the hardware operation.
:vartype _result: T
"""
__slots__ = ("_hardware_state", "_result")
def __init__(self, hardware_state: HardwareState, result: T):
self._hardware_state = hardware_state
self._result = result
@property
def hardware_state(self) -> HardwareState:
"""
Get the state of the hardware.
:return: The state of the hardware.
:rtype: HardwareState
"""
return self._hardware_state
@property
def result(self) -> T:
"""
Get the result of the hardware operation.
:return: The result of the hardware operation.
:rtype: T
"""
return self._result
[docs]
class HardwareState(Enum):
"""
This enum represents the state of underlying hardware.
"""
OK = 0
ERROR = 1
[docs]
class JRHardwareController(HardwareController):
"""
A class to represent a hardware controller for the JetRacer.
"""
__slots__ = ("_turn_controller", "_speed_controller", "_collision_controller")
def __init__(self):
"""
Initialize the hardware controller.
"""
self._turn_controller = TurnController()
self._speed_controller = SpeedController()
self._collision_controller = CollisionController()
logger.debug("Initialized hardware controller.")
[docs]
def shutdown(self):
"""
Shut down ROS 2 and rclpy.
"""
self._turn_controller.shutdown()
self._speed_controller.shutdown()
self._collision_controller.shutdown()
logger.debug("Shutting down hardware controller.")
[docs]
def stop(self):
"""
Stop the JetRacer.
"""
self.set_speed(0.0)
[docs]
def turn(self, theta: float) -> HardwareResult[None]:
"""
Turn the JetRacer by a given angle.
:param theta: The angle to turn the JetRacer in radians.
:type theta: float
:return: The result of the turn operation.
:rtype: HardwareResult[None]
"""
self._turn_controller.turn(theta)
logger.debug("Turned JetRacer by %f radians.", theta)
return HardwareResult(HardwareState.OK, None)
[docs]
def set_speed(self, speed: float) -> HardwareResult[None]:
"""
Set the speed of the JetRacer.
:param speed: The speed to set the JetRacer to in m/s. Range: [-1.2, 1.2]
:type speed: float
:return: The result of the set speed operation.
:rtype: HardwareResult[None]
"""
self._speed_controller.set_speed(speed)
logger.debug("Set JetRacer speed to %f m/s.", speed)
return HardwareResult(HardwareState.OK, None)
[docs]
def potential_collision(self, collision_threshold: float = None) -> HardwareResult[bool]:
"""
Check if there is a potential collision ahead.
:return: True if there is a potential collision ahead, False otherwise.
:rtype: HardwareResult[bool]
"""
result = self._collision_controller.potential_collision(collision_threshold)
logger.debug("Checked for potential collision ahead. Result: %s", result)
return HardwareResult(HardwareState.OK, result)
[docs]
def free_ahead(self, collision_threshold: float = None) -> HardwareResult[bool]:
"""
Check if there is enough free space ahead.
:return: True if there is enough free space ahead, False otherwise.
:rtype: HardwareResult[bool
"""
result = self._collision_controller.free_ahead(collision_threshold)
logger.debug("Checked for free space ahead. Result: %s", result)
return HardwareResult(HardwareState.OK, result)