# SPDX-FileCopyrightText: 2022-present Inria
# SPDX-FileCopyrightText: 2022-present Alexandre Abadie <alexandre.abadie@inria.fr>
# SPDX-FileCopyrightText: 2023-present Filip Maksimovic <filip.maksimovic@inria.fr>
# SPDX-FileCopyrightText: 2024-present Diego Badillo <diego.badillo@sansano.usm.cl>
#
# SPDX-License-Identifier: BSD-3-Clause
"""Module for the Dotbot protocol API."""
import dataclasses
from dataclasses import dataclass
from enum import IntEnum
from dotbot_utils.protocol import Payload, PayloadFieldMetadata, register_parser
[docs]
class PayloadType(IntEnum):
"""Types of DotBot payload types."""
CMD_MOVE_RAW = 0x00
CMD_RGB_LED = 0x01
ADVERTISEMENT = 0x04
GPS_POSITION = 0x05
DOTBOT_ADVERTISEMENT = 0x06
CONTROL_MODE = 0x07
LH2_WAYPOINTS = 0x08
GPS_WAYPOINTS = 0x09
SAILBOT_DATA = 0x0A
CMD_XGO_ACTION = 0x0B
LH2_PROCESSED_DATA = 0x0C
LH2_CALIBRATION_HOMOGRAPHY = 0x0E
RAW_DATA = 0x10
DOTBOT_SIMULATOR_DATA = 0xFA
[docs]
class ApplicationType(IntEnum):
"""Types of DotBot applications."""
DotBot = 0 # pylint: disable=invalid-name
SailBot = 1 # pylint: disable=invalid-name
Freebot = 2 # pylint: disable=invalid-name
XGO = 3
LH2_mini_mote = 4
[docs]
class ControlModeType(IntEnum):
"""Types of DotBot control modes."""
MANUAL = 0
AUTO = 1
[docs]
@dataclass
class PayloadAdvertisement(Payload):
"""Dataclass that holds an advertisement (emtpy)."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="application", disp="app"),
]
)
application: ApplicationType = ApplicationType.DotBot
[docs]
@dataclass
class PayloadDotBotAdvertisement(Payload):
"""Dataclass that holds a dotbot advertisement packet."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="calibrated", disp="cal."),
PayloadFieldMetadata(name="direction", disp="dir.", length=2, signed=True),
PayloadFieldMetadata(name="pos_x", disp="x", length=4),
PayloadFieldMetadata(name="pos_y", disp="y", length=4),
PayloadFieldMetadata(name="pos_z", disp="z", length=4),
PayloadFieldMetadata(name="battery", disp="bat.", length=2),
]
)
calibrated: bool = False
direction: int = 0xFFFF
pos_x: int = 0xFFFFFFFF
pos_y: int = 0xFFFFFFFF
pos_z: int = 0xFFFFFFFF
battery: int = 0
[docs]
@dataclass
class PayloadCommandMoveRaw(Payload):
"""Dataclass that holds move raw command data fields."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="lelf_x", disp="lx", signed=True),
PayloadFieldMetadata(name="lelf_y", disp="ly", signed=True),
PayloadFieldMetadata(name="right_y", disp="rx", signed=True),
PayloadFieldMetadata(name="right_y", disp="ry", signed=True),
]
)
left_x: int = 0
left_y: int = 0
right_x: int = 0
right_y: int = 0
[docs]
@dataclass
class PayloadCommandRgbLed(Payload):
"""Dataclass that holds a complete rgb led command fields."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="red"),
PayloadFieldMetadata(name="green"),
PayloadFieldMetadata(name="blue"),
]
)
red: int = 0
green: int = 0
blue: int = 0
[docs]
@dataclass
class PayloadCommandXgoAction(Payload):
"""Dataclass that holds an XGO action."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="action"),
]
)
action: int = 0
[docs]
@dataclass
class PayloadLh2ProcessedLocation(Payload):
"""Dataclass that holds LH2 processed location data."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="polynomial_index", disp="poly"),
PayloadFieldMetadata(name="lfsr_index", length=4),
PayloadFieldMetadata(name="timestamp_us", length=4),
]
)
polynomial_index: int = 0x00
lfsr_index: int = 0x00000000
timestamp_us: int = 0x00000000
[docs]
@dataclass
class PayloadLH2Location(Payload):
"""Dataclass that holds LH2 computed location data."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="pos_x", disp="x", length=4),
PayloadFieldMetadata(name="pos_y", disp="y", length=4),
PayloadFieldMetadata(name="pos_z", disp="z", length=4),
]
)
pos_x: int = 0
pos_y: int = 0
pos_z: int = 0
[docs]
@dataclass
class PayloadLh2CalibrationHomography(Payload):
"""Dataclass that holds computed LH2 homography for a basestation indicated by index."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="index", disp="idx"),
PayloadFieldMetadata(
name="homography_matrix", disp="mat.", type_=bytes, length=36
),
]
)
index: int = 0
homography_matrix: bytes = dataclasses.field(default_factory=lambda: bytearray)
[docs]
@dataclass
class PayloadGPSPosition(Payload):
"""Dataclass that holds GPS positions."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="latitude", disp="lat.", length=4, signed=True),
PayloadFieldMetadata(name="longitude", disp="long.", length=4, signed=True),
]
)
latitude: int = 0
longitude: int = 0
[docs]
@dataclass
class PayloadSailBotData(Payload):
"""Dataclass that holds SailBot data from SailBot application."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="direction", disp="dir.", length=2, signed=False),
PayloadFieldMetadata(name="latitude", disp="lat.", length=4, signed=True),
PayloadFieldMetadata(name="longitude", disp="long.", length=4, signed=True),
PayloadFieldMetadata(
name="wind_angle", disp="wind", length=2, signed=False
),
PayloadFieldMetadata(name="rudder_angle", disp="rud.", signed=True),
PayloadFieldMetadata(name="sail_angle", disp="sail.", signed=True),
]
)
direction: int = 0xFFFF
latitude: int = 0
longitude: int = 0
wind_angle: int = 0xFFFF
rudder_angle: int = 0
sail_angle: int = 0
[docs]
@dataclass
class PayloadDotBotSimulatorData(Payload):
"""Dataclass that holds direction and GPS data and heading from SailBot application."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="theta", length=2),
PayloadFieldMetadata(name="pos_x", length=4),
PayloadFieldMetadata(name="pos_y", length=4),
]
)
theta: int = 0xFFFF
pos_x: int = 0
pos_y: int = 0
[docs]
@dataclass
class PayloadControlMode(Payload):
"""Dataclass that holds a control mode message."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [PayloadFieldMetadata(name="mode", disp="mode")]
)
mode: ControlModeType = ControlModeType.MANUAL
[docs]
@dataclass
class PayloadLH2Waypoints(Payload):
"""Dataclass that holds a list of LH2 waypoints."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="threshold", disp="thr."),
PayloadFieldMetadata(name="count", disp="len."),
PayloadFieldMetadata(name="waypoints", type_=list, length=0),
]
)
threshold: int = 0
count: int = 0
waypoints: list[PayloadLH2Location] = dataclasses.field(default_factory=lambda: [])
[docs]
@dataclass
class PayloadGPSWaypoints(Payload):
"""Dataclass that holds a list of GPS waypoints."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="threshold", disp="thr."),
PayloadFieldMetadata(name="count", disp="len."),
PayloadFieldMetadata(name="waypoints", type_=list, length=0),
]
)
threshold: int = 0
count: int = 0
waypoints: list[PayloadGPSPosition] = dataclasses.field(default_factory=lambda: [])
[docs]
@dataclass
class PayloadRawData(Payload):
"""Dataclass that holds raw bytes data."""
metadata: list[PayloadFieldMetadata] = dataclasses.field(
default_factory=lambda: [
PayloadFieldMetadata(name="count", disp="len."),
PayloadFieldMetadata(name="data", type_=bytes, length=0),
]
)
count: int = 0
data: bytes = dataclasses.field(default_factory=lambda: bytearray)
register_parser(PayloadType.ADVERTISEMENT, PayloadAdvertisement)
register_parser(PayloadType.CMD_MOVE_RAW, PayloadCommandMoveRaw)
register_parser(PayloadType.CMD_RGB_LED, PayloadCommandRgbLed)
register_parser(PayloadType.CMD_XGO_ACTION, PayloadCommandXgoAction)
register_parser(PayloadType.LH2_PROCESSED_DATA, PayloadLh2ProcessedLocation)
register_parser(PayloadType.DOTBOT_ADVERTISEMENT, PayloadDotBotAdvertisement)
register_parser(PayloadType.GPS_POSITION, PayloadGPSPosition)
register_parser(PayloadType.SAILBOT_DATA, PayloadSailBotData)
register_parser(PayloadType.DOTBOT_SIMULATOR_DATA, PayloadDotBotSimulatorData)
register_parser(PayloadType.CONTROL_MODE, PayloadControlMode)
register_parser(PayloadType.LH2_WAYPOINTS, PayloadLH2Waypoints)
register_parser(PayloadType.GPS_WAYPOINTS, PayloadGPSWaypoints)
register_parser(PayloadType.RAW_DATA, PayloadRawData)
register_parser(PayloadType.LH2_CALIBRATION_HOMOGRAPHY, PayloadLh2CalibrationHomography)