diff --git a/.git-crypt/.gitattributes b/.git-crypt/.gitattributes new file mode 100644 index 0000000000..665b10e8f0 --- /dev/null +++ b/.git-crypt/.gitattributes @@ -0,0 +1,4 @@ +# Do not edit this file. To specify the files to encrypt, create your own +# .gitattributes file in the directory where your files are. +* !filter !diff +*.gpg binary diff --git a/.git-crypt/keys/default/0/106E1EB7856273777A0C8BB4EF8EA444C1E7B69C.gpg b/.git-crypt/keys/default/0/106E1EB7856273777A0C8BB4EF8EA444C1E7B69C.gpg new file mode 100644 index 0000000000..97b89be16e Binary files /dev/null and b/.git-crypt/keys/default/0/106E1EB7856273777A0C8BB4EF8EA444C1E7B69C.gpg differ diff --git a/.git-crypt/keys/default/0/91D8EDBE6BD70DFB803D59292470ADBDD426DEB5.gpg b/.git-crypt/keys/default/0/91D8EDBE6BD70DFB803D59292470ADBDD426DEB5.gpg new file mode 100644 index 0000000000..3da4a7ec9c Binary files /dev/null and b/.git-crypt/keys/default/0/91D8EDBE6BD70DFB803D59292470ADBDD426DEB5.gpg differ diff --git a/.git-crypt/keys/default/0/E08F802F45946A6A3E16AD1B1609CD53B15C2462.gpg b/.git-crypt/keys/default/0/E08F802F45946A6A3E16AD1B1609CD53B15C2462.gpg new file mode 100644 index 0000000000..787ec0578d Binary files /dev/null and b/.git-crypt/keys/default/0/E08F802F45946A6A3E16AD1B1609CD53B15C2462.gpg differ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 21958355c5..aceeff95bd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -79,6 +79,12 @@ build: - "echo VERSION: ${VERSION}" - "echo CI_COMMIT_REF_NAME: ${CI_COMMIT_REF_NAME}" - git config --global --add safe.directory ${CI_PROJECT_DIR} + - | + echo "Decoding and importing GPG key..." + gpg -v --import <(echo "$GPG_PRIVATE_KEY_BASE64" | base64 -d) + - | + echo "Unlocking git-crypt..." + git-crypt unlock script: - export PYTHONPATH="$BUILD_DIR" - "echo Building Panda..." @@ -129,6 +135,8 @@ build: --exclude='**/selfdrive/ui/**/*.h' --exclude='**/selfdrive/ui/qt/offroad/sunnypilot/' --exclude='**/.git/' + --exclude='**/SConstruct' + --exclude='**/SConscript' --delete-excluded --chown=comma:comma ${BUILD_DIR}/ ${OUTPUT_DIR}/ @@ -144,7 +152,7 @@ build: when: manual - if: $NEW_BRANCH when: always - + check no source code sent: image: alpine stage: sanity diff --git a/SConstruct b/SConstruct index da70e4e587..50e60c3b95 100644 --- a/SConstruct +++ b/SConstruct @@ -7,6 +7,8 @@ import numpy as np import SCons.Errors +from openpilot.common.basedir import BASEDIR + SCons.Warnings.warningAsException(True) # pending upstream fix - https://github.com/SCons/scons/issues/4461 @@ -16,6 +18,45 @@ TICI = os.path.isfile('/TICI') AGNOS = TICI UBUNTU_FOCAL = int(subprocess.check_output('[ -f /etc/os-release ] && . /etc/os-release && [ "$ID" = "ubuntu" ] && [ "$VERSION_ID" = "20.04" ] && echo 1 || echo 0', shell=True, encoding='utf-8').rstrip()) Export('UBUNTU_FOCAL') +_DEBUG = False + +def is_internal_developer(debug=False): + def collect_required_gpg_key_ids(keys_dir): + try: + key_ids = [f.split('.')[0] for f in os.listdir(keys_dir) if f.endswith(".gpg")] + if debug: + print(f"SP: Required GPG key IDs: {key_ids}") + return key_ids + except OSError as e: + if debug: + print(f"SP: Failed to read GPG key IDs from {keys_dir}. Error: {e}") + return [] + + def is_key_available(required_gpg_key_ids): + for key_id in required_gpg_key_ids: + try: + result = subprocess.check_output(['gpg', '--list-keys', key_id], stderr=subprocess.STDOUT) + if key_id in result.decode(): + if debug: + print(f"SP: GPG key {key_id} is available.") + return True + except subprocess.CalledProcessError as e: + if debug: + print(f"SP: Failed to list GPG key {key_id}. Error:", e.output.decode().strip()) + return False + + keys_dir = os.path.join(BASEDIR, ".git-crypt/keys/default/0") + required_gpg_key_ids = collect_required_gpg_key_ids(keys_dir) + + sunnypilot = is_key_available(required_gpg_key_ids) + + if sunnypilot: + print("SP: Confirmed sunnypilot internal developer.") + print("SP: Loading sunnypilot elements ...") + elif debug: + print("SP: None of the required GPG keys are available.") + + return sunnypilot Decider('MD5-timestamp') @@ -72,6 +113,12 @@ AddOption('--minimal', default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS) help='the minimum build to run openpilot. no tests, tools, etc.') +AddOption('--sunnypilot', + action='store_true', + dest='sunnypilot', + default=is_internal_developer(_DEBUG) or True, # check if the current user is a sunnypilot developer. TODO: Remove the 'or True' part once the GPG keys are available. + help='build sunnypilot elements and other sunnypilot-specific items that are meant for internal development') + ## Architecture name breakdown (arch) ## - larch64: linux tici aarch64 ## - aarch64: linux pc aarch64 diff --git a/selfdrive/controls/lib/sunnypilot/speed_limit_controller.py b/selfdrive/controls/lib/sunnypilot/speed_limit_controller.py index d3e966bfb1..27b3de88e9 100644 --- a/selfdrive/controls/lib/sunnypilot/speed_limit_controller.py +++ b/selfdrive/controls/lib/sunnypilot/speed_limit_controller.py @@ -295,7 +295,7 @@ class SpeedLimitController: def update(self, enabled, v_ego, a_ego, sm, v_cruise_setpoint, events=Events()): _car_state = sm['carState'] - self._op_enabled = sm['controlsState'].enabled and _car_state.cruiseState.enabled and \ + self._op_enabled = enabled and sm['controlsState'].enabled and _car_state.cruiseState.enabled and \ not (_car_state.brakePressed and (not self._brake_pressed_prev or not _car_state.standstill)) and \ not (events.contains(ET.OVERRIDE_LONGITUDINAL) and self._disengage_on_accelerator) self._v_ego = v_ego diff --git a/selfdrive/modeld/custom_model_metadata.py b/selfdrive/modeld/custom_model_metadata.py index 360f7046ad..0469bd189c 100644 --- a/selfdrive/modeld/custom_model_metadata.py +++ b/selfdrive/modeld/custom_model_metadata.py @@ -42,14 +42,14 @@ class CustomModelMetadata: self.params: Params = params self.generation: ModelGeneration = self.read_model_generation_param() - self.capabilities: int = self.get_model_capabilities() + self.capabilities: ModelCapabilities = self.get_model_capabilities() self.valid: bool = self.params.get_bool("CustomDrivingModel") and not SIMULATION and \ self.capabilities != ModelCapabilities.Default def read_model_generation_param(self) -> ModelGeneration: return int(self.params.get('DrivingModelGeneration') or ModelGeneration.default) - def get_model_capabilities(self) -> int: + def get_model_capabilities(self) -> ModelCapabilities: """Returns the model capabilities for a given generation.""" if self.generation == ModelGeneration.five: return ModelCapabilities.DesiredCurvatureV2 diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index 39a161ed89..8d2bcc0ef4 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -68,7 +68,9 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D driving_model_data.frameDropPerc = frame_drop_perc action = driving_model_data.action - action.desiredCurvature = float(net_output_data['desired_curvature'][0,0]) + model_use_lateral_planner = custom_model_valid and custom_model_capabilities & ModelCapabilities.LateralPlannerSolution + if not model_use_lateral_planner: + action.desiredCurvature = float(net_output_data['desired_curvature'][0,0]) modelV2 = extended_msg.modelV2 modelV2.frameId = vipc_frame_id @@ -100,7 +102,7 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D fill_xyz_poly(poly_path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T) # lateral planning - if custom_model_valid and custom_model_capabilities & ModelCapabilities.LateralPlannerSolution: + if model_use_lateral_planner: solution = modelV2.lateralPlannerSolutionDEPRECATED solution.x, solution.y, solution.yaw, solution.yawRate = [net_output_data['lat_planner_solution'][0,:,i].tolist() for i in range(4)] solution.xStd, solution.yStd, solution.yawStd, solution.yawRateStd = [net_output_data['lat_planner_solution_stds'][0,:,i].tolist() for i in range(4)] diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index e1233b5cbc..3d49b3df14 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,6 +1,6 @@ import os import json -Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations', 'UBUNTU_FOCAL') +Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') base_libs = [common, messaging, visionipc, transformations, 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] @@ -17,27 +17,20 @@ if arch == "Darwin": # FIXME: remove this once we're on 5.15 (24.04) qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] +sp_widgets_src = [] +sp_qt_src = [] +if GetOption('sunnypilot'): + SConscript(['sunnypilot/SConscript']) + Import('sp_widgets_src', 'sp_qt_src') + qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs) widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", - "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + sp_widgets_src -widgets_src += ["qt/offroad/sunnypilot/display_settings.cc", "qt/offroad/sunnypilot/sunnypilot_settings.cc", - "qt/offroad/sunnypilot/vehicle_settings.cc", "qt/offroad/sunnypilot/visuals_settings.cc", - "qt/offroad/sunnypilot/trips_settings.cc", "qt/offroad/sunnypilot/mads_settings.cc", - "qt/offroad/sunnypilot/lane_change_settings.cc", "qt/offroad/sunnypilot/speed_limit_control_settings.cc", - "qt/offroad/sunnypilot/monitoring_settings.cc", "qt/offroad/sunnypilot/osm_settings.cc", - "qt/offroad/sunnypilot/custom_offsets_settings.cc", "qt/widgets/sunnypilot/drive_stats.cc", - "qt/offroad/sunnypilot/software_settings_sp.cc", "qt/offroad/sunnypilot/models_fetcher.cc", - "qt/offroad/sunnypilot/speed_limit_warning_settings.cc", "qt/offroad/sunnypilot/speed_limit_policy_settings.cc", - "qt/offroad/sunnypilot/sunnylink_settings.cc"] - -widgets_src += ["qt/network/sunnylink/sunnylink_client.cc", "qt/network/sunnylink/services/base_device_service.cc", - "qt/network/sunnylink/services/role_service.cc", "qt/network/sunnylink/services/user_service.cc"] - -qt_env['CPPDEFINES'] = [] +qt_env['CPPDEFINES'] = ["SUNNYPILOT"] if GetOption('sunnypilot') else [] if maps: base_libs += ['QMapLibre'] widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc", "qt/maps/map_panel.cc", @@ -53,8 +46,7 @@ qt_src = ["main.cc", "qt/sidebar.cc", "qt/body.cc", "qt/offroad/software_settings.cc", "qt/offroad/onboarding.cc", "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", - "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", - "qt/onroad_settings.cc", "qt/onroad_settings_panel.cc"] + "qt/onroad/buttons.cc", "qt/onroad/alerts.cc"] + sp_qt_src # build translation files with open(File("translations/languages.json").abspath) as f: diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript new file mode 100644 index 0000000000..9290fcea58 --- /dev/null +++ b/selfdrive/ui/sunnypilot/SConscript @@ -0,0 +1,36 @@ +widgets_src = [ + "qt/offroad/sunnypilot/custom_offsets_settings.cc", + "qt/offroad/sunnypilot/display_settings.cc", + "qt/offroad/sunnypilot/lane_change_settings.cc", + "qt/offroad/sunnypilot/mads_settings.cc", + "qt/offroad/sunnypilot/models_fetcher.cc", + "qt/offroad/sunnypilot/monitoring_settings.cc", + "qt/offroad/sunnypilot/osm_settings.cc", + "qt/offroad/sunnypilot/software_settings_sp.cc", + "qt/offroad/sunnypilot/speed_limit_control_settings.cc", + "qt/offroad/sunnypilot/speed_limit_policy_settings.cc", + "qt/offroad/sunnypilot/speed_limit_warning_settings.cc", + "qt/offroad/sunnypilot/sunnypilot_settings.cc", + "qt/offroad/sunnypilot/sunnylink_settings.cc", + "qt/offroad/sunnypilot/trips_settings.cc", + "qt/offroad/sunnypilot/vehicle_settings.cc", + "qt/offroad/sunnypilot/visuals_settings.cc", + "qt/widgets/sunnypilot/drive_stats.cc" +] + +network_src = [ + "qt/network/sunnylink/services/base_device_service.cc", + "qt/network/sunnylink/services/role_service.cc", + "qt/network/sunnylink/services/user_service.cc", + "qt/network/sunnylink/sunnylink_client.cc" +] + +qt_src = [ + "qt/onroad_settings.cc", + "qt/onroad_settings_panel.cc" +] + +sp_widgets_src = widgets_src + network_src +sp_qt_src = qt_src + +Export('sp_widgets_src', 'sp_qt_src') diff --git a/tools/replay/util.cc b/tools/replay/util.cc index a08b3b3d5e..aaf73749d5 100644 --- a/tools/replay/util.cc +++ b/tools/replay/util.cc @@ -323,7 +323,7 @@ void precise_nano_sleep(int64_t nanoseconds, std::atomic &should_exit) { req.tv_sec = nanoseconds / 1000000000; req.tv_nsec = nanoseconds % 1000000000; while (!should_exit) { -#ifdef __APPLE_ +#ifdef __APPLE__ int ret = nanosleep(&req, &rem); if (ret == 0 || errno != EINTR) break;