From 619e3b47e65a0fb1bab894bc9a70728fac2abd8f Mon Sep 17 00:00:00 2001 From: Willian Galvani Date: Thu, 9 Jan 2025 10:47:23 -0300 Subject: [PATCH] core: ardupilot_manager: add support to all usb boards --- .../ardupilot_manager/api/v1/routers/index.py | 2 +- .../ardupilot_manager/autopilot_manager.py | 8 +- .../firmware/FirmwareDownload.py | 39 +- .../firmware/FirmwareInstall.py | 367 ++++++++++++- .../firmware/FirmwareManagement.py | 13 +- .../firmware/test_FirmwareDownload.py | 25 +- .../firmware/test_FirmwareInstall.py | 26 +- .../board_identification.py | 37 ++ .../flight_controller_detector/boards.json | 487 ++++++++++++++++++ core/services/ardupilot_manager/typedefs.py | 3 + 10 files changed, 947 insertions(+), 60 deletions(-) create mode 100644 core/services/ardupilot_manager/flight_controller_detector/boards.json diff --git a/core/services/ardupilot_manager/api/v1/routers/index.py b/core/services/ardupilot_manager/api/v1/routers/index.py index 23bbf55a99..ed45326d25 100644 --- a/core/services/ardupilot_manager/api/v1/routers/index.py +++ b/core/services/ardupilot_manager/api/v1/routers/index.py @@ -118,7 +118,7 @@ async def get_firmware_vehicle_type() -> Any: summary="Retrieve dictionary of available firmwares versions with their respective URL.", ) async def get_available_firmwares(vehicle: Vehicle, board_name: Optional[str] = None) -> Any: - return autopilot.get_available_firmwares(vehicle, (await target_board(board_name)).platform) + return autopilot.get_available_firmwares(vehicle, (await target_board(board_name))) @index_router_v1.post("/install_firmware_from_url", summary="Install firmware for given URL.") diff --git a/core/services/ardupilot_manager/autopilot_manager.py b/core/services/ardupilot_manager/autopilot_manager.py index ca69bb5888..2b14dc77bd 100644 --- a/core/services/ardupilot_manager/autopilot_manager.py +++ b/core/services/ardupilot_manager/autopilot_manager.py @@ -207,7 +207,7 @@ async def start_linux_board(self, board: LinuxFlightController) -> None: ) firmware_path = self.firmware_manager.firmware_path(self._current_board.platform) - self.firmware_manager.validate_firmware(firmware_path, self._current_board.platform) + self.firmware_manager.validate_firmware(firmware_path, self._current_board) # ArduPilot process will connect as a client on the UDP server created by the mavlink router master_endpoint = Endpoint( @@ -319,7 +319,7 @@ async def start_sitl(self) -> None: self.current_sitl_frame = frame firmware_path = self.firmware_manager.firmware_path(self._current_board.platform) - self.firmware_manager.validate_firmware(firmware_path, self._current_board.platform) + self.firmware_manager.validate_firmware(firmware_path, self._current_board) # ArduPilot SITL binary will bind TCP port 5760 (server) and the mavlink router will connect to it as a client master_endpoint = Endpoint( @@ -624,8 +624,8 @@ async def update_endpoints(self, endpoints_to_update: Set[Endpoint]) -> None: self._save_current_endpoints() await self.mavlink_manager.restart() - def get_available_firmwares(self, vehicle: Vehicle, platform: Platform) -> List[Firmware]: - return self.firmware_manager.get_available_firmwares(vehicle, platform) + def get_available_firmwares(self, vehicle: Vehicle, board: FlightController) -> List[Firmware]: + return self.firmware_manager.get_available_firmwares(vehicle, board) def install_firmware_from_file( self, firmware_path: pathlib.Path, board: FlightController, default_parameters: Optional[Parameters] = None diff --git a/core/services/ardupilot_manager/firmware/FirmwareDownload.py b/core/services/ardupilot_manager/firmware/FirmwareDownload.py index 1c6591b1f5..68dd3e640c 100644 --- a/core/services/ardupilot_manager/firmware/FirmwareDownload.py +++ b/core/services/ardupilot_manager/firmware/FirmwareDownload.py @@ -21,7 +21,7 @@ NoCandidate, NoVersionAvailable, ) -from typedefs import FirmwareFormat, Platform, PlatformType, Vehicle +from typedefs import FirmwareFormat, FlightController, Platform, PlatformType, Vehicle # TODO: This should be not necessary # Disable SSL verification @@ -123,8 +123,8 @@ def _find_version_item(self, **args: str) -> List[Dict[str, Any]]: # Make sure that the item matches all args value for item in self._manifest["firmware"]: for key, value in args.items(): - real_key = key.replace("_", "-") - if real_key not in item or item[real_key] != value: + real_key = key.replace("_", "-").lower() + if real_key not in item or item[real_key].lower() != value.lower(): break else: found_version_item.append(item) @@ -132,12 +132,12 @@ def _find_version_item(self, **args: str) -> List[Dict[str, Any]]: return found_version_item @temporary_cache(timeout_seconds=3600) - def get_available_versions(self, vehicle: Vehicle, platform: Platform) -> List[str]: + def get_available_versions(self, vehicle: Vehicle, board: FlightController) -> List[str]: """Get available firmware versions for the specific plataform and vehicle Args: vehicle (Vehicle): Desired vehicle. - platform (Platform): Desired platform. + board (FlightController): Desired Flight Controller. Returns: List[str]: List of available versions that match the specific desired configuration. @@ -147,16 +147,18 @@ def get_available_versions(self, vehicle: Vehicle, platform: Platform) -> List[s if not self._manifest_is_valid(): raise InvalidManifest("Manifest file is invalid. Cannot use it to find available versions.") - items = self._find_version_item(vehicletype=vehicle.value, platform=platform.value) + platform = board.platform.value if board.platform != Platform.GenericSerial else board.name + platform_type = board.platform.type if board.platform != Platform.GenericSerial else PlatformType.Serial + items = self._find_version_item(vehicletype=vehicle.value, platform=platform) for item in items: - if item["format"] == FirmwareDownloader._supported_firmware_formats[platform.type]: + if item["format"] == FirmwareDownloader._supported_firmware_formats[platform_type]: available_versions.append(item["mav-firmware-version-type"]) return available_versions @temporary_cache(timeout_seconds=3600) - def get_download_url(self, vehicle: Vehicle, platform: Platform, version: str = "") -> str: + def get_download_url(self, vehicle: Vehicle, board: FlightController, version: str = "") -> str: """Find a specific firmware URL from manifest that matches the arguments. Args: @@ -168,16 +170,16 @@ def get_download_url(self, vehicle: Vehicle, platform: Platform, version: str = Returns: str: URL of valid firmware. """ - versions = self.get_available_versions(vehicle, platform) - logger.debug(f"Got following versions for {vehicle} running {platform}: {versions}") + versions = self.get_available_versions(vehicle, board) + logger.debug(f"Got following versions for {vehicle} running {board}: {versions}") if not versions: - raise NoVersionAvailable(f"Could not find available firmware versions for {platform}/{vehicle}.") + raise NoVersionAvailable(f"Could not find available firmware versions for {board}/{vehicle}.") if version and version not in versions: - raise NoVersionAvailable(f"Version {version} was not found for {platform}/{vehicle}.") + raise NoVersionAvailable(f"Version {version} was not found for {board}/{vehicle}.") - firmware_format = FirmwareDownloader._supported_firmware_formats[platform.type] + firmware_format = FirmwareDownloader._supported_firmware_formats[board.platform.type] # Autodetect the latest supported version. # For .apj firmwares (e.g. Pixhawk), we use the latest STABLE version while for the others (e.g. SITL and @@ -192,21 +194,22 @@ def get_download_url(self, vehicle: Vehicle, platform: Platform, version: str = if not newest_version or Version(newest_version) < Version(semver_version): newest_version = semver_version if not newest_version: - raise NoVersionAvailable(f"No firmware versions found for {platform}/{vehicle}.") + raise NoVersionAvailable(f"No firmware versions found for {board}/{vehicle}.") version = f"STABLE-{newest_version}" else: version = "BETA" items = self._find_version_item( vehicletype=vehicle.value, - platform=platform.value, + platform=board.platform.value if board.platform == Platform.SITL else board.name, mav_firmware_version_type=version, format=firmware_format, ) if len(items) == 0: + logger.error(f"Could not find any firmware for {vehicle=}, {board=}, {version=}, {firmware_format=}") raise NoCandidate( - f"Found no candidate for configuration: {vehicle=}, {platform=}, {version=}, {firmware_format=}" + f"Found no candidate for configuration: {vehicle=}, {board=}, {version=}, {firmware_format=}" ) if len(items) != 1: @@ -216,7 +219,7 @@ def get_download_url(self, vehicle: Vehicle, platform: Platform, version: str = logger.debug(f"Downloading following firmware: {item}") return str(item["url"]) - def download(self, vehicle: Vehicle, platform: Platform, version: str = "") -> pathlib.Path: + def download(self, vehicle: Vehicle, board: FlightController, version: str = "") -> pathlib.Path: """Download a specific firmware that matches the arguments. Args: @@ -228,5 +231,5 @@ def download(self, vehicle: Vehicle, platform: Platform, version: str = "") -> p Returns: pathlib.Path: Temporary path for the firmware file. """ - url = self.get_download_url(vehicle, platform, version) + url = self.get_download_url(vehicle, board, version) return FirmwareDownloader._download(url) diff --git a/core/services/ardupilot_manager/firmware/FirmwareInstall.py b/core/services/ardupilot_manager/firmware/FirmwareInstall.py index f51c897ce0..30830a4108 100644 --- a/core/services/ardupilot_manager/firmware/FirmwareInstall.py +++ b/core/services/ardupilot_manager/firmware/FirmwareInstall.py @@ -16,13 +16,355 @@ from typedefs import FirmwareFormat, FlightController, Platform, PlatformType -def get_board_id(platform: Platform) -> int: +def get_board_id(board: Union[FlightController, str]) -> int: + print(board) + if isinstance(board, FlightController): + platform = board.name + else: + platform = board ardupilot_board_ids = { Platform.Pixhawk1: 9, Platform.Pixhawk4: 50, Platform.Pixhawk6X: 53, Platform.Pixhawk6C: 56, Platform.CubeOrange: 140, + "PX4_FMU_V1": 5, + "PX4_FMU_V2": 9, + "PX4_FMU_V3": 9, # same as FMU_V2 + "PX4_FMU_V4": 11, + "PX4_FMU_V4_PRO": 13, + "UVIFY_CORE": 20, + "PX4_FMU_V5": 50, + "PX4_FMU_V5X": 51, + "PX4_FMU_V6": 52, + "PX4_FMU_V6X": 53, + "ARK_FMU_V6X": 57, + "MINDPX_V2": 88, + "PX4_FLOW_V1": 6, + "PX4_DISCOVERY_V1": 99, + "PX4_PIO_V1": 10, + "PX4_PIO_V2": 10, # same as PIO_V1 + "PX4_PIO_V3": 13, + "PX4_AEROCORE_V1": 98, + "TAP_V1": 64, + "CRAZYFLIE": 12, + "CRAZYFLIE21": 14, + "OMNIBUSF4SD": 42, + "AUAV_X2V1": 33, + "AEROFC_V1": 65, + "TARGET_TAP_V2": 66, + "CUBE_F4": 9, + "AV_V1": 29, + "KAKUTEF7": 123, + "SMARTAP_AIRLINK": 55, + "SMARTAP_PRO": 32, + "MODALAI_FC_V1": 41775, + "MODALAI_FC_V2": 41776, + "MODALAI_VOXL2_IO": 41777, + "HOLYBRO_PIX32_V5": 78, + "HOLYBRO_CAN_GPS": 79, + "FMUK66_V3": 28, + "AV_X_V1": 29, + "FMUK66_E": 30, + "FMURT1062-V1": 31, + "ARK_CAN_FLOW": 80, + "ARK_CAN_GPS": 81, + "ARK_CAN_RTK_GPS": 82, + "ARK_CANNODE": 83, + "FF_RTK_CAN_GPS": 85, + "PDW_MAS_MAIN-V1": 86, + "ATL_MANTIS_EDU": 97, + "THE_PEACH_K1": 212, + "THE_PEACH_R1": 213, + "CUBEYELLOW": 120, + "OMNIBUSF7V2": 121, + "KAKUTEF4": 122, + "REVOLUTION": 124, + "MATEKF405": 125, + "NUCLEOF767ZI": 126, + "MATEKF405_WING": 127, + "AIRBOTF4": 128, + "SPARKYV2": 130, + "OMNIBUSF4PRO": 131, + "ANYFCF7": 132, + "OMNIBUSNANOV6": 133, + "SPEEDYBEEF4": 134, + "F35LIGHTNING": 135, + "MRO_X2V1_777": 136, + "OMNIBUSF4V6": 137, + "HELIOSPRING": 138, + "DURANDAL": 139, + "CUBEORANGE": 140, + "MRO_CONTROL_ZERO": 141, + "MRO_CONTROL_ZERO_OEM": 142, + "MATEKF765_WING": 143, + "JDMINIF405": 144, + "KAKUTEF7_MINI": 145, + "H757I_EVAL": 146, + "F4BY": 20, # value due to previous release by vendor + "MAZZYSTARDRONE": 188, + "VRBRAIN_V51": 1151, + "VRBRAIN_V52": 1152, + "VRBRAIN_V54": 1154, + "VRCORE_V10": 1910, + "VRUBRAIN_V51": 1351, + "F103_PERIPH": 1000, + "CUAV_GPS": 1001, + "OMNIBUSF4": 1002, + "CUBEBLACKPLUS": 1003, + "F303_PERIPH": 1004, + "ZUBAXGNSS": 1005, + "NIGHTCRAWLER": 1006, + "SKYBOT": 1007, + "FRSKY_R9": 1008, + "CUAV_NORA": 1009, + "CUAV_X7_PRO": 1010, + "SUCCEXF4": 1011, + "LIGHTSPARKMINI": 1012, + "MATEKH743": 1013, + "MATEKF405_GPS": 1014, + "MRO_NEXUS": 1015, + "HITEC_MOSAIC": 1016, + "MRO_PIXRACER_PRO": 1017, + "TWD_H7": 1018, + "MAMBA405": 1019, + "H31_PIXC4": 1020, + "QioTekZealotF427": 1021, + "MRO_CTRL_ZERO_CLASSIC": 1022, + "MRO_CTRL_ZERO_H7": 1023, + "MRO_CTRL_ZERO_OEM_H7": 1024, + "BEASTH7": 1025, + "BEASTF7": 1026, + "FlywooF745": 1027, + "FreeflyRTK": 1028, + "luminousbee5": 1029, + "KAKUTEF4_MINI": 1030, + "H31_PIXC4_PI": 1031, + "H31_PIXC4_JETSON": 1032, + "CUBEORANGE_JOEY": 1033, + "SierraF9P": 1034, + "HolybroGPS": 1035, + "QioTekZealotH743": 1036, + "HEREPRO": 1037, + "MAMBABASICF4": 1038, + "ARGOSDYNE_DP1000": 1039, + "Nucleo491": 1040, + "mRoM10095": 1041, + "FlywooF745Nano": 1042, + "HERE3PLUS": 1043, + "BirdCANdy": 1044, + "SKYSTARSF405DJI": 1045, + "HITEC_AIRSPEED": 1046, + "NucleoL496": 1047, + "KakuteH7": 1048, + "ICSI_Kestrel": 1049, + "SierraL431": 1050, + "NucleoL476": 1051, + "SierraF405": 1052, + "CarbonixL496": 1053, + "MatekF405_TE": 1054, + "SierraF412": 1055, + "BEASTH7v2": 1056, + "BEASTF7v2": 1057, + "KakuteH7Mini": 1058, + "JHEMCUGSF405A": 1059, + "SPRACINGH7": 1060, + "DEVEBOXH7": 1061, + "MatekL431": 1062, + "CUBEORANGEPLUS": 1063, + "CarbonixF405": 1064, + "QioTekAdeptF407": 1065, + "QioTekAdeptF427": 1066, + "FlyingMoonF407": 1067, + "FlyingMoonF427": 1068, + "CUBERED_PRIMARY": 1069, + "CUBERED_SECONDARY": 1070, + "GreenSight_UltraBlue": 1071, + "GreenSight_microBlue": 1072, + "MAMBAH743_V4": 1073, + "REAPERF745_V2": 1074, + "SKYSTARSH7HD": 1075, + "PixSurveyA1": 1076, + "AEROFOX_AIRSPEED": 1077, + "ATOMRCF405": 1078, + "CUBENODE": 1079, + "AEROFOX_PMU": 1080, + "JHEMCUGF16F405": 1081, + "SPEEDYBEEF4V3": 1082, + "PixPilot-V6": 1083, + "JFB100": 1084, + "C_RTK2_HP": 1085, + "JUMPER_XIAKE800": 1086, + "Sierra_F1": 1087, + "HolybroCompass": 1088, + "FOXEERH743_V1": 1089, + "PixFlamingoL4R5_V1": 1090, + "Sierra-TrueNavPro": 1091, + "Sierra-TrueNav": 1092, + "Sierra-TrueNorth": 1093, + "Sierra-TrueSpeed": 1094, + "Sierra-PrecisionPoint": 1095, + "PixPilot-V3": 1096, + "PixSurveyA2": 1097, + "mRoCANPWM": 1098, + "FlywooF405S_AIO": 1099, + "mRoCANPower": 1100, + "mRoControlOne": 1101, + "rFCU": 1102, + "rGNSS": 1103, + "AEROFOX_AIRSPEED_DLVR": 1104, + "KakuteH7-Wing": 1105, + "SpeedyBeeF405WING": 1106, + "PixSurveyA-IND": 1107, + "SPRACINGH7RF": 1108, + "AEROFOX_GNSS_F9P": 1109, + "JFB110": 1110, + "SDMODELH7V1": 1111, + "FlyingMoonH743": 1112, + "YJUAV_A6": 1113, + "YJUAV_A6Nano": 1114, + "ACNS_CM4PILOT": 1115, + "ACNS_F405AIO": 1116, + "BLITZF7AIO": 1117, + "RADIX2HD": 1118, + "HEEWING_F405": 1119, + "PodmanH7": 1120, + "mRo-M10053": 1121, + "mRo-M10044": 1122, + "SIYI_N7": 1123, + "mRoCZOEM_revG": 1124, + "BETAFPV_F405": 1125, + "QioTekAdeptH743": 1126, + "YJUAV_A6SE": 1127, + "QioTekAdept_6C": 1128, + "PixFlamingoL4R5_V2": 1129, + "PixFlamingoF427_V1": 1130, + "PixFlamingoF767_V1": 1131, + "PixFlamingoH743I": 1132, + "PixFlamingoH743V": 1133, + "AR-F407SmartBat": 1134, + "SPEEDYBEEF4MINI": 1135, + "SPEEDYBEEF4V4": 1136, + "FlywooF405Pro": 1137, + "TMOTORH7": 1138, + "MICOAIR405": 1139, + "PixPilot-C3": 1140, + "YJUAV_A6SE_H743": 1141, + "FSO_POWER_STACK": 1142, + "ATOMRCF405NAVI_DLX": 1143, + "YJUAV_A6Ultra": 1144, + "TULIP_BATTMON": 1145, + "AnyleafH7": 1146, + "mRoKitCANrevC": 1147, + "BotBloxSwitch": 1148, + "MatekH7A3": 1149, + "MicoAir405v2": 1150, + "ORAQF405PRO": 1155, + "CBU_StampH743": 1156, + "FOXEERF405_V2": 1157, + "CSKY405": 1158, + "NxtPX4v2": 1159, + "PixPilot-V6PRO": 1160, + "MicoAir405Mini": 1161, + "BlitzH7Pro": 1162, + "BlitzF7Mini": 1163, + "BlitzF7": 1164, + "3DR-ASAUAV": 1165, + "MicoAir743": 1166, + "BlitzH7Wing": 1168, + "SDMODELH7V2": 1167, + "JHEMCUF405WING": 1169, + "MatekG474": 1170, + "PhenixH7_lite": 1171, + "PhenixH7_Pro": 1172, + "2RAWH743": 1173, + "X-MAV-AP-H743V2": 1174, + "BETAFPV_F4_2_3S_20A": 1175, + "MicoAir743AIOv1": 1176, + "CrazyF405": 1177, + "FlywooF405HD_AIOv2": 1180, + "FlywooH743Pro": 1181, + "ESP32_PERIPH": 1205, + "ESP32S3_PERIPH": 1206, + "CSKY-PMU": 1212, + "MUPilot": 1222, + "CBUnmanned-CM405-FC": 1301, + "KHA_ETH": 1315, + "FlysparkF4": 1361, + "CUBEORANGE_PERIPH": 1400, + "CUBEBLACK_PERIPH": 1401, + "PIXRACER_PERIPH": 1402, + "SWBOOMBOARD_PERIPH": 1403, + "VIMDRONES_FLOW": 1404, + "VIMDRONES_MOSAIC_X5": 1405, + "VIMDRONES_MOSAIC_H": 1406, + "VIMDRONES_PERIPH": 1407, + "PIXHAWK6X_PERIPH": 1408, + "CUBERED_PERIPH": 1409, + "RadiolinkPIX6": 1410, + "JHEMCU-H743HD": 1411, + "LongbowF405": 1422, + "MountainEagleH743": 1444, + "StellarF4": 1500, + "GEPRCF745BTHD": 1501, + "HGLRCF405V4": 1524, + "F4BY_F427": 1530, + "MFT-SEMA100": 2000, + "AET-H743-Basic": 2024, + "SakuraRC-H743": 2714, + "KRSHKF7_MINI": 4000, + "HAKRC_F405": 4200, + "HAKRC_F405Wing": 4201, + "AIRVOLUTE_DCS2": 5200, + "AOCODA-RC-H743DUAL": 5210, + "AOCODA-RC-F405V3": 5211, + "UAV-DEV-HAT-H7": 5220, + "UAV-DEV-NucPilot-H7": 5221, + "UAV-DEV-M10S-L4": 5222, + "UAV-DEV-F9P-G4": 5223, + "UAV-DEV-UM982-G4": 5224, + "UAV-DEV-M20D-G4": 5225, + "UAV-DEV-Sensorboard-G4": 5226, + "UAV-DEV-PWM-G4": 5227, + "UAV-DEV-AUAV-H7": 5228, + "UAV-DEV-FC-H7": 5229, + "TM-SYS-BeastFC": 5240, + "TM-SYS-Sensornode": 5241, + "TM-SYS-OpenHDFPV": 5242, + "TM-SYS-VisualNAV": 5243, + "TM-SYS-Airspeed": 5244, + "TBS_LUCID_H7": 5250, + "TBS_LUCID_PRO": 5251, + "Sierra-TrueNavPro-G4": 5301, + "Sierra-TrueNavIC": 5302, + "Sierra-TrueNorth-G4": 5303, + "Sierra-TrueSpeed-G4": 5304, + "Sierra-PrecisionPoint-G4": 5305, + "Sierra-AeroNex": 5306, + "Sierra-TrueFlow": 5307, + "Sierra-TrueNavIC-Pro": 5308, + "Sierra-F1-Pro": 5309, + "Holybro-PMU-F4": 5401, + "Holybro-UM982-G4": 5402, + "Holybro-UM960-H7": 5403, + "Holybro-PERIPH-H7": 5404, + "Holybro-DroneCAN-Airspeed": 5405, + "Holybro-KakuteF4-Wing": 5406, + "MATEKH743SE": 5501, + "ZeroOne_X6": 5600, + "ZeroOne_PMU": 5601, + "ZeroOne_GNSS": 5602, + "DroneBuild_G1": 5700, + "DroneBuild_G2": 5701, + "MFE_PDB_CAN": 6100, + "MFE_POS3_CAN": 6101, + "MFE_RTK_CAN": 6102, + "MFE_AirSpeed_CAN": 6103, + "CUAV-7-NANO": 7000, + "VUAV-V7pro": 7100, + "AEROFOX_H7": 7110, + "CubeOrange_ODID": 10140, + "Pixhawk6_ODID": 10053, } return ardupilot_board_ids.get(platform, -1) @@ -62,12 +404,15 @@ def __init__(self) -> None: pass @staticmethod - def _validate_apj(firmware_path: pathlib.Path, platform: Platform) -> None: + def _validate_apj(firmware_path: pathlib.Path, board: FlightController) -> None: + logger.debug(f"Validating APJ firmware for board {board}.") try: with open(firmware_path, "r", encoding="utf-8") as firmware_file: firmware_data = firmware_file.read() firm_board_id = int(json.loads(firmware_data).get("board_id", -1)) - expected_board_id = get_board_id(platform) + logger.debug(f"firm_board_id: {firm_board_id}") + expected_board_id = get_board_id(board) + logger.debug(f"expected_board_id: {expected_board_id}") if expected_board_id == -1: raise UnsupportedPlatform("Firmware validation is not implemented for this board yet.") if firm_board_id == -1: @@ -113,18 +458,19 @@ def _validate_elf(firmware_path: pathlib.Path, platform: Platform) -> None: raise InvalidFirmwareFile("Given firmware is not a supported version.") from error @staticmethod - def validate_firmware(firmware_path: pathlib.Path, platform: Platform) -> None: + def validate_firmware(firmware_path: pathlib.Path, board: FlightController) -> None: """Check if given firmware is valid for given platform.""" - firmware_format = FirmwareDownloader._supported_firmware_formats[platform.type] - + logger.debug(f"Validating firmware for board {board}.") + logger.debug(f"platform type: {board.platform.type}") + firmware_format = FirmwareDownloader._supported_firmware_formats[board.platform.type] + logger.debug(f"Firmware format: {firmware_format}") if firmware_format == FirmwareFormat.APJ: - FirmwareInstaller._validate_apj(firmware_path, platform) + FirmwareInstaller._validate_apj(firmware_path, board) return if firmware_format == FirmwareFormat.ELF: - FirmwareInstaller._validate_elf(firmware_path, platform) + FirmwareInstaller._validate_elf(firmware_path, board.platform) return - raise UnsupportedPlatform("Firmware validation is not implemented for this platform.") @staticmethod @@ -148,11 +494,12 @@ def install_firmware( if not new_firmware_path.is_file(): raise InvalidFirmwareFile("Given path is not a valid file.") + logger.debug(f"Installing firmware for board {board}, from {new_firmware_path}.") firmware_format = FirmwareDownloader._supported_firmware_formats[board.platform.type] if firmware_format == FirmwareFormat.ELF: self.add_run_permission(new_firmware_path) - self.validate_firmware(new_firmware_path, board.platform) + self.validate_firmware(new_firmware_path, board) if board.type == PlatformType.Serial: firmware_uploader = FirmwareUploader() diff --git a/core/services/ardupilot_manager/firmware/FirmwareManagement.py b/core/services/ardupilot_manager/firmware/FirmwareManagement.py index 1d063a1a07..97e5822204 100644 --- a/core/services/ardupilot_manager/firmware/FirmwareManagement.py +++ b/core/services/ardupilot_manager/firmware/FirmwareManagement.py @@ -78,14 +78,15 @@ def is_firmware_installed(self, board: FlightController) -> bool: raise UnsupportedPlatform("Install check is not implemented for this platform.") - def get_available_firmwares(self, vehicle: Vehicle, platform: Platform) -> List[Firmware]: + def get_available_firmwares(self, vehicle: Vehicle, board: FlightController) -> List[Firmware]: firmwares = [] - versions = self.firmware_download.get_available_versions(vehicle, platform) + versions = self.firmware_download.get_available_versions(vehicle, board) if not versions: raise NoVersionAvailable(f"Failed to find any version for vehicle {vehicle}.") for version in versions: try: - url = self.firmware_download.get_download_url(vehicle, platform, version) + logger.debug(f"Fetching URL for version {version} on vehicle {vehicle} and board {board}.") + url = self.firmware_download.get_download_url(vehicle, board, version) firmware = Firmware(name=version, url=url) firmwares.append(firmware) except Exception as error: @@ -159,7 +160,7 @@ def install_firmware_from_url( self.install_firmware_from_file(temporary_file, board, default_parameters) def install_firmware_from_params(self, vehicle: Vehicle, board: FlightController, version: str = "") -> None: - url = self.firmware_download.get_download_url(vehicle, board.platform, version) + url = self.firmware_download.get_download_url(vehicle, board, version) self.install_firmware_from_url(url, board) def restore_default_firmware(self, board: FlightController) -> None: @@ -169,5 +170,5 @@ def restore_default_firmware(self, board: FlightController) -> None: self.install_firmware_from_file(self.default_firmware_path(board.platform), board) @staticmethod - def validate_firmware(firmware_path: pathlib.Path, platform: Platform) -> None: - FirmwareInstaller.validate_firmware(firmware_path, platform) + def validate_firmware(firmware_path: pathlib.Path, board: FlightController) -> None: + FirmwareInstaller.validate_firmware(firmware_path, board) diff --git a/core/services/ardupilot_manager/firmware/test_FirmwareDownload.py b/core/services/ardupilot_manager/firmware/test_FirmwareDownload.py index 1246b7a417..28b6ab2034 100644 --- a/core/services/ardupilot_manager/firmware/test_FirmwareDownload.py +++ b/core/services/ardupilot_manager/firmware/test_FirmwareDownload.py @@ -4,7 +4,12 @@ import pytest from firmware.FirmwareDownload import FirmwareDownloader -from typedefs import Platform, Vehicle +from typedefs import FlightController, Platform, Vehicle + +Pixhawk1 = FlightController(name="Pixhawk1", manufacturer="3DR", platform=Platform.Pixhawk1) +Pixhawk4 = FlightController(name="Pixhawk4", manufacturer="Holybro", platform=Platform.Pixhawk4) +SITL = FlightController(name="SITL", manufacturer="ArduPilot Team", platform=Platform.SITL) +Navigator = FlightController(name="Navigator", manufacturer="Blue Robotics", platform=Platform.Navigator) def test_static() -> None: @@ -21,17 +26,17 @@ def test_firmware_download() -> None: assert firmware_download.download_manifest(), "Failed to download/validate manifest file." versions = firmware_download._find_version_item( - vehicletype="Sub", format="apj", mav_firmware_version_type="STABLE-4.0.1", platform=Platform.Pixhawk1 + vehicletype="Sub", format="apj", mav_firmware_version_type="STABLE-4.0.1", platform=Pixhawk1.name ) assert len(versions) == 1, "Failed to find a single firmware." versions = firmware_download._find_version_item( - vehicletype="Sub", mav_firmware_version_type="STABLE-4.0.1", platform=Platform.Pixhawk1 + vehicletype="Sub", mav_firmware_version_type="STABLE-4.0.1", platform=Pixhawk1.name ) # There are two versions, one for the firmware and one with the bootloader assert len(versions) == 2, "Failed to find multiple versions." - available_versions = firmware_download.get_available_versions(Vehicle.Sub, Platform.Pixhawk1) + available_versions = firmware_download.get_available_versions(Vehicle.Sub, Pixhawk1) assert len(available_versions) == len(set(available_versions)), "Available versions are not unique." test_available_versions = ["STABLE-4.0.1", "STABLE-4.0.0", "OFFICIAL", "DEV", "BETA"] @@ -40,21 +45,21 @@ def test_firmware_download() -> None: ), "Available versions are missing know versions." assert firmware_download.download( - Vehicle.Sub, Platform.Pixhawk1, "STABLE-4.0.1" + Vehicle.Sub, Pixhawk1, "STABLE-4.0.1" ), "Failed to download a valid firmware file." - assert firmware_download.download(Vehicle.Sub, Platform.Pixhawk1), "Failed to download latest valid firmware file." + assert firmware_download.download(Vehicle.Sub, Pixhawk1), "Failed to download latest valid firmware file." - assert firmware_download.download(Vehicle.Sub, Platform.Pixhawk4), "Failed to download latest valid firmware file." + assert firmware_download.download(Vehicle.Sub, Pixhawk4), "Failed to download latest valid firmware file." - assert firmware_download.download(Vehicle.Sub, Platform.SITL), "Failed to download SITL." + assert firmware_download.download(Vehicle.Sub, SITL), "Failed to download SITL." # skipt these tests for MacOS if platform.system() == "Darwin": pytest.skip("Skipping test for MacOS") # It'll fail if running in an arch different of ARM if "x86" in os.uname().machine: - assert firmware_download.download(Vehicle.Sub, Platform.Navigator), "Failed to download navigator binary." + assert firmware_download.download(Vehicle.Sub, Navigator), "Failed to download navigator binary." else: with pytest.raises(Exception): - firmware_download.download(Vehicle.Sub, Platform.Navigator) + firmware_download.download(Vehicle.Sub, Navigator) diff --git a/core/services/ardupilot_manager/firmware/test_FirmwareInstall.py b/core/services/ardupilot_manager/firmware/test_FirmwareInstall.py index 559e5aa30c..17a4464d69 100644 --- a/core/services/ardupilot_manager/firmware/test_FirmwareInstall.py +++ b/core/services/ardupilot_manager/firmware/test_FirmwareInstall.py @@ -8,32 +8,36 @@ from firmware.FirmwareInstall import FirmwareInstaller from typedefs import FlightController, Platform, Vehicle +Pixhawk1 = FlightController(name="Pixhawk1", manufacturer="3DR", platform=Platform.Pixhawk1) +Pixhawk4 = FlightController(name="Pixhawk4", manufacturer="Holybro", platform=Platform.Pixhawk4) +SITL = FlightController(name="SITL", manufacturer="ArduPilot Team", platform=Platform.SITL) +Navigator = FlightController(name="Navigator", manufacturer="Blue Robotics", platform=Platform.Navigator) + def test_firmware_validation() -> None: downloader = FirmwareDownloader() installer = FirmwareInstaller() # Pixhawk1 and Pixhawk4 APJ firmwares should always work - temporary_file = downloader.download(Vehicle.Sub, Platform.Pixhawk1) - installer.validate_firmware(temporary_file, Platform.Pixhawk1) + temporary_file = downloader.download(Vehicle.Sub, Pixhawk1) + installer.validate_firmware(temporary_file, Pixhawk1) - temporary_file = downloader.download(Vehicle.Sub, Platform.Pixhawk4) - installer.validate_firmware(temporary_file, Platform.Pixhawk4) + temporary_file = downloader.download(Vehicle.Sub, Pixhawk4) + installer.validate_firmware(temporary_file, Pixhawk4) # New SITL firmwares should always work, except for MacOS # there are no SITL builds for MacOS if platform.system() != "Darwin": - temporary_file = downloader.download(Vehicle.Sub, Platform.SITL, version="DEV") - installer.validate_firmware(temporary_file, Platform.SITL) + temporary_file = downloader.download(Vehicle.Sub, SITL, version="DEV") + installer.validate_firmware(temporary_file, SITL) # Raise when validating Navigator firmwares (as test platform is x86) - temporary_file = downloader.download(Vehicle.Sub, Platform.Navigator) + temporary_file = downloader.download(Vehicle.Sub, Navigator) with pytest.raises(InvalidFirmwareFile): - installer.validate_firmware(temporary_file, Platform.Navigator) + installer.validate_firmware(temporary_file, Navigator) # Install SITL firmware if platform.system() != "Darwin": # there are no SITL builds for MacOS - temporary_file = downloader.download(Vehicle.Sub, Platform.SITL, version="DEV") - board = FlightController(name="SITL", manufacturer="ArduPilot Team", platform=Platform.SITL) - installer.install_firmware(temporary_file, board, pathlib.Path(f"{temporary_file}_dest")) + temporary_file = downloader.download(Vehicle.Sub, SITL, version="DEV") + installer.install_firmware(temporary_file, SITL, pathlib.Path(f"{temporary_file}_dest")) diff --git a/core/services/ardupilot_manager/flight_controller_detector/board_identification.py b/core/services/ardupilot_manager/flight_controller_detector/board_identification.py index d536497b0e..5cbfa03a93 100644 --- a/core/services/ardupilot_manager/flight_controller_detector/board_identification.py +++ b/core/services/ardupilot_manager/flight_controller_detector/board_identification.py @@ -1,8 +1,11 @@ +import json from enum import Enum +from pathlib import Path from typing import List from pydantic import BaseModel +from flight_controller_detector.metadata_preprocessor import ManifestHandler from typedefs import Platform @@ -32,3 +35,37 @@ class SerialBoardIdentifier(BaseModel): SerialBoardIdentifier(attribute=SerialAttr.manufacturer, id_value="Hex/ProfiCNC", platform=Platform.GenericSerial), SerialBoardIdentifier(attribute=SerialAttr.manufacturer, id_value="Holybro", platform=Platform.GenericSerial), ] + + +def get_boards_cache_path() -> Path: + """Get the path to the boards cache file.""" + cache_dir = Path("/usr/blueos/userdata/.cache") + cache_dir.mkdir(parents=True, exist_ok=True) + return cache_dir / "boards.json" + + +def load_board_identifiers() -> List[SerialBoardIdentifier]: + """Load board identifiers from the manifest, using cache when possible.""" + boards_path = get_boards_cache_path() + boards_path.parent.mkdir(parents=True, exist_ok=True) + + # Process manifest and update cache if needed + handler = ManifestHandler() + handler.process_and_export(boards_path) + + with open(boards_path, encoding="utf-8") as f: + json_data = json.load(f) + + # Extract all unique board names + board_names = set() + for boards_list in json_data.values(): + board_names.update(boards_list) + + return [ + SerialBoardIdentifier(attribute=SerialAttr.product, id_value=board_name, platform=Platform.GenericSerial) + for board_name in board_names + ] + + +# Load dynamic board identifiers from manifest +identifiers.extend(load_board_identifiers()) diff --git a/core/services/ardupilot_manager/flight_controller_detector/boards.json b/core/services/ardupilot_manager/flight_controller_detector/boards.json new file mode 100644 index 0000000000..9d4f79b2ce --- /dev/null +++ b/core/services/ardupilot_manager/flight_controller_detector/boards.json @@ -0,0 +1,487 @@ +{ + "1209:5741": [ + "BeastH7", + "BeastH7v2", + "Here4FC", + "QioTekAdeptF407", + "ReaperF745", + "SpeedyBeeF405Mini", + "PixFlamingo", + "speedybeef4v4", + "AtomRCF405NAVI", + "KakuteF7", + "ACNS-CM4Pilot", + "MatekF405-CAN", + "DevEBoxH7v2", + "KakuteH7Mini-Nand", + "Pixhawk1-1M", + "OMNIBUSF7V2", + "FlyingMoonF407", + "KakuteF4", + "Pixhawk1-1M-bdshot", + "KakuteH7", + "SDMODELH7V1", + "BeastF7v2", + "MatekF765-Wing-bdshot", + "MicoAir405v2", + "Pixhawk1-bdshot", + "Pixhawk1", + "Aocoda-RC-H743Dual", + "revo-mini-i2c-bdshot", + "BeastF7", + "TMotorH743", + "MatekF405-Wing", + "SPRacingH7", + "fmuv2", + "DrotekP3Pro", + "speedybeef4", + "MambaH743v4", + "FlyingMoonH743", + "SPRacingH7RF", + "BlitzF745AIO", + "KakuteH7-bdshot", + "MatekF765-Wing", + "MatekF405-STD", + "KakuteH7v2", + "mRoX21", + "sparky2", + "revo-mini", + "PixPilot-V3", + "revo-mini-i2c", + "PixSurveyA1", + "FlyingMoonF427", + "PixFlamingo-F767", + "PixC4-Jetson", + "speedybeef4v3", + "Pixracer-bdshot", + "MatekF405-Wing-bdshot", + "FoxeerF405v2", + "OrqaF405Pro", + "Pixracer", + "SpeedyBeeF405WING", + "FoxeerH743v1", + "luminousbee5", + "KakuteF4Mini", + "RADIX2HD", + "KakuteF7-bdshot", + "fmuv3", + "PixPilot-C3", + "omnibusf4pro-bdshot", + "mini-pix", + "ACNS-F405AIO", + "SDMODELH7V2", + "mindpx-v2", + "airbotf4", + "FlywooF745", + "FlywooF405Pro", + "AnyleafH7", + "OmnibusNanoV6-bdshot", + "revo-mini-bdshot", + "PixSurveyA1-IND", + "MambaF405v2", + "omnibusf4", + "BlitzF745", + "MatekF405-TE-bdshot", + "R9Pilot", + "MambaF405-2022", + "F35Lightning", + "SkystarsH7HD-bdshot", + "Airvolute-DCS2", + "OmnibusNanoV6", + "MatekF405", + "CubeRedSecondary", + "KakuteF7Mini", + "MambaF405US-I2C", + "BlitzMiniF745", + "omnibusf4v6", + "MatekF405-TE", + "FlywooF745Nano", + "QioTekZealotF427", + "SuccexF4", + "MatekF405-bdshot", + "omnibusf4pro", + "KakuteH7Mini", + "revo-mini-sd", + "JHEMCU-GSF405A", + "FlywooF405S-AIO", + "BETAFPV-F405", + "JHEMCU-GSF405A-RX2", + "MatekH7A3", + "CSKY405", + "LongBowF405WING", + "JHEM_JHEF405", + "GEPRCF745BTHD", + "X-MAV-AP-H743v2", + "MicoAir743", + "FlywooH743Pro", + "MicoAir405Mini", + "CUAV-Pixhack-v3", + "KakuteF4-Wing", + "NxtPX4v2", + "CubeSolo", + "skyviper-v2450", + "crazyflie2", + "skyviper-journey", + "MatekF765-SE", + "MatekH743", + "FlywooF405HD-AIOv2", + "HEEWING-F405", + "HEEWING-F405v2", + "Sierra-F412", + "Sierra-F405", + "MatekL431-DShot", + "MatekL431-Proximity", + "f303-PWM", + "Hitec-Airspeed", + "f303-GPS", + "f303-MatekGPS", + "MatekL431-RC", + "BirdCANdy", + "f405-MatekAirspeed", + "Nucleo-L476", + "mRo-M10095", + "HolybroGPS", + "CarbonixL496", + "CarbonixF405", + "Pixracer-periph", + "MatekL431-Periph", + "Here4AP", + "MatekL431-Serial", + "f103-GPS", + "rGNSS", + "f103-QiotekPeriph", + "MatekL431-HWTelem", + "ARK_CANNODE", + "Nucleo-G491", + "f303-Universal", + "ARK_RTK_GPS", + "ARK_GPS", + "Sierra-TrueNavPro-G4", + "Nucleo-L496", + "AR-F407SmartBat", + "sw-spar-f407", + "mRoCANPWM-M10126", + "f103-Airspeed", + "CUAV_GPS", + "HitecMosaic", + "f303-TempSensor", + "BotBloxSwitch", + "Sierra-TrueNavPro", + "MatekL431-BattMon", + "f303-M10070", + "f103-ADSB", + "C-RTK2-HP", + "Sierra-TrueNavIC", + "MatekL431-EFI", + "CubePilot-PPPGW", + "Sierra-PrecisionPoint", + "kha_eth", + "AeroFox-GNSS_F9P", + "MatekL431-Rangefinder", + "AeroFox-Airspeed", + "MatekL431-bdshot", + "MatekL431-GPS", + "f405-MatekGPS", + "Sierra-TrueNorth", + "MatekL431-ADSB", + "FreeflyRTK", + "AeroFox-Airspeed-DLVR", + "f103-RangeFinder", + "CubePilot-CANMod", + "mRoKitCANrevC", + "Sierra-F9P", + "Sierra-L431", + "AeroFox-PMU", + "Sierra-TrueSpeed", + "ZubaxGNSS", + "f303-M10025", + "f303-HWESC", + "HolybroG4_Compass", + "HolybroG4_GPS", + "MatekL431-Airspeed", + "sw-nav-f405", + "CubeNode", + "BotBloxDroneNet", + "MatekG474-Periph", + "MatekL431-APDTelem", + "HolybroF4_PMU", + "CubeNode-ETH", + "MatekL431-MagHiRes", + "MatekG474-DShot", + "MFE_POS3_CAN", + "sw-boom-f407", + "HolybroG4_Airspeed" + ], + "1209:5740": [ + "BlitzWingH743", + "KakuteH7-Wing", + "mRoControlZeroOEMH7", + "YJUAV_A6SE_H743", + "IFLIGHT_2RAW_H7", + "YJUAV_A6SE", + "ZeroOneX6", + "CUAV-Nora", + "QioTekZealotH743-bdshot", + "MFT-SEMA100", + "MatekF765-SE", + "MatekH743", + "VUAV-V7pro", + "fmuv5", + "CUAVv5-bdshot", + "Pixhawk6X-bdshot", + "CUAVv5Nano", + "mRoControlZeroClassic", + "CUAV-7-Nano", + "MatekH743-bdshot", + "Pixhawk6X", + "Pixhawk4", + "mRoControlZeroH7-bdshot", + "CUAV-X7", + "JFB110", + "AIRLink", + "TBS-Colibri-F7", + "RadiolinkPIX6", + "mRoX21-777", + "mRoNexus", + "PH4-mini-bdshot", + "mRoControlZeroF7", + "PH4-mini", + "CUAVv5Nano-bdshot", + "mRoPixracerPro-bdshot", + "PixSurveyA2", + "QioTekZealotH743", + "mRoControlZeroH7", + "modalai_fc-v1", + "mRoPixracerPro", + "JFB100", + "Pixhawk4-bdshot", + "CUAV-X7-bdshot", + "YJUAV_A6Ultra", + "mRoCZeroOEMH7-bdshot", + "ARKV6X", + "PixPilot-V6", + "BlitzH743Pro", + "YJUAV_A6", + "CUAVv5", + "CUAV-Nora-bdshot", + "SIYI_N7", + "SkySakuraH743", + "MUPilot", + "TBS_LUCID_H7", + "CBU-H7-Stamp", + "AEROFOX-H7", + "AET-H743-Basic", + "JHEMCU-H743HD", + "PixPilot-V6PRO", + "Swan-K1", + "Pixhawk6X-PPPGW", + "MatekH743-periph" + ], + "2dae:1058": [ + "CubeOrangePlus-bdshot", + "CubeOrangePlus", + "CubeOrangePlus-SimOnHardWare" + ], + "2dae:1015": [ + "CubePurple" + ], + "26ac:0011": [ + "CubePurple", + "Pixhawk1-1M", + "Pixhawk1-1M-bdshot", + "Pixhawk1-bdshot", + "Pixhawk1", + "CubeBlack", + "fmuv2", + "mRoX21", + "fmuv3", + "PX4-v2", + "CUAV-Pixhack-v3", + "CubeSolo", + "skyviper-v2450", + "CubeGreen-solo", + "skyviper-journey", + "PX4-v3", + "PX4-quad-v2", + "PX4-octa-v2", + "PX4-tri-v2", + "PX4-hexa-v2", + "PX4-octa-quad-v2", + "PX4-heli-hil-v2", + "PX4-quad-hil-v2", + "PX4-y6-v2" + ], + "35a7:0001": [ + "thepeach-k1" + ], + "35a7:0002": [ + "thepeach-r1" + ], + "3162:004b": [ + "Durandal", + "Durandal-bdshot" + ], + "2dae:1059": [ + "CubeRedPrimary", + "CubeRedPrimary-PPPGW" + ], + "27ac:1154": [ + "VRBrain-v54" + ], + "3162:0053": [ + "Pixhawk6C-bdshot", + "Pixhawk6C" + ], + "3185:0038": [ + "Pixhawk6C-bdshot", + "Pixhawk6C" + ], + "2dae:1011": [ + "CubeBlack", + "CubeGreen-solo", + "CubeBlack+", + "CubeBlack-periph" + ], + "26ac:0013": [ + "DrotekP3Pro", + "PX4-v4" + ], + "26ac:0032": [ + "fmuv5", + "CUAVv5-bdshot", + "CUAVv5Nano", + "Pixhawk4", + "TBS-Colibri-F7", + "PH4-mini-bdshot", + "PH4-mini", + "CUAVv5Nano-bdshot", + "Pixhawk4-bdshot", + "Pix32v5", + "CUAVv5", + "Swan-K1" + ], + "3185:0035": [ + "Pixhawk6X-bdshot", + "Pixhawk6X" + ], + "2dae:1101": [ + "CubeBlack+" + ], + "27ac:1351": [ + "VRUBrain-v51" + ], + "2dae:1012": [ + "CubeYellow", + "CubeYellow-bdshot" + ], + "26ac:0012": [ + "Pixracer-bdshot", + "Pixracer", + "fmuv4", + "PX4-v4", + "PX4-quad-v4", + "PX4-octa-v4", + "PX4-tri-v4", + "PX4-hexa-v4", + "PX4-octa-quad-v4", + "PX4-y6-v4" + ], + "27ac:1152": [ + "VRBrain-v52" + ], + "3162:0051": [ + "Pixhawk5X" + ], + "27ac:0201": [ + "F4BY", + "F4BY_F427" + ], + "27ac:1151": [ + "VRBrain-v51" + ], + "26ac:0030": [ + "mindpx-v2" + ], + "2dae:1016": [ + "CubeOrange", + "CubeOrange-bdshot", + "CubeOrange-SimOnHardWare", + "CubeOrange-periph" + ], + "3162:004e": [ + "Pix32v5" + ], + "27ac:1910": [ + "VRCore-v10" + ], + "26ac:1124": [ + "3DRControlZeroG" + ], + "0483:5740": [ + "KakuteF7", + "OMNIBUSF7V2", + "KakuteF4", + "Pixhawk1", + "MatekF405-Wing", + "fmuv2", + "DrotekP3Pro", + "fmuv5", + "speedybeef4", + "CUAVv5Nano", + "fmuv4", + "MatekF405-STD", + "mRoX21", + "TBS-Colibri-F7", + "revo-mini", + "mRoX21-777", + "Pixracer", + "fmuv3", + "F4BY", + "mini-pix", + "mindpx-v2", + "airbotf4", + "F35Lightning", + "OmnibusNanoV6", + "MatekF405", + "omnibusf4v6", + "CUAVv5", + "omnibusf4pro", + "Pixhawk1-1M", + "CubeSolo", + "MatekF765-Wing", + "skyviper-journey", + "skyviper-v2450", + "sparky2", + "omnibusf4", + "KakuteF7Mini", + "mRoControlZeroF7", + "CubeBlack+", + "CubeGreen-solo" + ], + "2dae:1005": [ + "CubePurple" + ], + "3162:0047": [ + "Pixhawk4" + ], + "2dae:1002": [ + "CubeYellow" + ], + "3162:0049": [ + "PH4-mini" + ], + "2dae:1017": [ + "CubeOrange" + ], + "26ac:0010": [ + "PX4-v1", + "PX4-quad-v1", + "PX4-octa-v1", + "PX4-tri-v1", + "PX4-hexa-v1", + "PX4-octa-quad-v1", + "PX4-heli-hil-v1", + "PX4-quad-hil-v1", + "PX4-y6-v1" + ] +} \ No newline at end of file diff --git a/core/services/ardupilot_manager/typedefs.py b/core/services/ardupilot_manager/typedefs.py index 94bcf03348..631a7eaf5e 100644 --- a/core/services/ardupilot_manager/typedefs.py +++ b/core/services/ardupilot_manager/typedefs.py @@ -154,6 +154,9 @@ class FlightController(BaseModel): def type(self) -> PlatformType: return self.platform.type + def __hash__(self) -> int: + return hash(self.name + self.platform) + class AvailableBoards(BaseModel): regular: List[FlightController]