From 93286505e119bca071ed5281e815ec2612de2480 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 22 Jun 2024 14:31:51 +0200 Subject: [PATCH 1/4] Improve Sunnylink registration and exception handling This change enhances the Sunnylink registration process by including better error checks and exception handling for cases when the public key is in use or known to be not unique. The commit also adjusts the setting of the ping time right after a successful registration. Additionally, cleanup occurred in the UI to streamline some code relating to the display of Sunnylink status. The handling of the last Sunnylink ping time has been removed from the system manager. --- common/api/sunnylink.py | 32 ++++++++++++++++++++++++-------- selfdrive/ui/qt/sidebar.cc | 29 ++++++++++++++--------------- system/manager/sunnylink.py | 4 ---- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/common/api/sunnylink.py b/common/api/sunnylink.py index e2761cc29f..76cc6f21d7 100644 --- a/common/api/sunnylink.py +++ b/common/api/sunnylink.py @@ -82,6 +82,8 @@ class SunnylinkApi(BaseApi): privkey_path = Path(Paths.persist_root()+"/comma/id_rsa") pubkey_path = Path(Paths.persist_root()+"/comma/id_rsa.pub") + start_time = time.monotonic() + successful_registration = False if not pubkey_path.is_file(): sunnylink_dongle_id = UNREGISTERED_SUNNYLINK_DONGLE_ID self._status_update("Public key not found, setting dongle ID to unregistered.") @@ -90,7 +92,6 @@ class SunnylinkApi(BaseApi): public_key = f1.read() private_key = f2.read() - start_time = time.monotonic() backoff = 1 while True: register_token = jwt.encode({'register': True, 'exp': datetime.utcnow() + timedelta(hours=1)}, private_key, algorithm='RS256') @@ -102,17 +103,29 @@ class SunnylinkApi(BaseApi): resp = self.api_get("v2/pilotauth/", method='POST', timeout=15, imei=imei1, imei2=imei2, serial=serial, comma_dongle_id=comma_dongle_id, public_key=public_key, register_token=register_token) + if resp.status_code in (409, 412): + timeout = time.monotonic() - start_time # Don't retry if the public key is already in use + key_in_use = "Public key is already in use, is your key unique? Contact your vendor for a new key." + unsafe_key = "Public key is known to not be unique and it's unsafe. Contact your vendor for a new key." + error_message = key_in_use if resp.status_code == 409 else unsafe_key + raise Exception(error_message) + if resp.status_code != 200: - raise Exception(f"Failed to register with sunnylink. Status code: {resp.status_code}") - else: - dongleauth = json.loads(resp.text) - sunnylink_dongle_id = dongleauth["device_id"] - if sunnylink_dongle_id: - self._status_update("Device registered successfully.") - break + raise Exception(f"Failed to register with sunnylink. Status code: {resp.status_code}\nData\n:{resp.text}") + + dongleauth = json.loads(resp.text) + sunnylink_dongle_id = dongleauth["device_id"] + if sunnylink_dongle_id: + self._status_update("Device registered successfully.") + successful_registration = True + break except Exception as e: if verbose: self._status_update(f"Waiting {backoff}s before retry, Exception occurred during registration: [{str(e)}]") + + with open('/data/community/crashes/error.txt', 'a') as f: + f.write(f"{datetime.now()}: {str(e)}\n") + backoff = min(backoff * 2, 60) time.sleep(backoff) @@ -123,5 +136,8 @@ class SunnylinkApi(BaseApi): self.params.put("SunnylinkDongleId", sunnylink_dongle_id or UNREGISTERED_SUNNYLINK_DONGLE_ID) + # Set the last ping time to the current time since we were just talking to the API + last_ping = int(time.monotonic() * 1e9) if successful_registration else start_time + Params().put("LastSunnylinkPingTime", str(last_ping)) self.spinner = None return sunnylink_dongle_id diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index 9463843ab2..0e6409bbb5 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -158,23 +158,22 @@ void Sidebar::updateState(const UIState &s) { auto sl_dongle_id = getSunnylinkDongleId(); auto last_sunnylink_ping_str = params.get("LastSunnylinkPingTime"); auto last_sunnylink_ping = std::stoull(last_sunnylink_ping_str.empty() ? "0" : last_sunnylink_ping_str); - auto current_nanos = nanos_since_boot(); - auto elapsed_sunnylink_ping = current_nanos - last_sunnylink_ping; + auto elapsed_sunnylink_ping = nanos_since_boot() - last_sunnylink_ping; auto sunnylink_enabled = params.getBool("SunnylinkEnabled"); - if (!sunnylink_enabled) { - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), tr("DISABLED")}, disabled_color}; - } else if(!sl_dongle_id.has_value()) { - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), tr("REGIST...")}, progress_color}; - } else if (last_sunnylink_ping == 0) { - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), tr("OFFLINE")}, warning_color}; - } else { - if (elapsed_sunnylink_ping < 80000000000ULL) { - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), tr("ONLINE")}, good_color}; - } - else { - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), tr("ERROR")}, danger_color}; - } + + QString status = tr("DISABLED"); + QColor color = disabled_color; + + if (sunnylink_enabled && last_sunnylink_ping == 0) { + // If sunnylink is enabled, but we don't have a dongle id, and we haven't received a ping yet, we are registering + status = sl_dongle_id.has_value() ? tr("OFFLINE") : tr("REGISTERING"); + color = sl_dongle_id.has_value() ? warning_color : progress_color; + } else if (sunnylink_enabled) { + // If sunnylink is enabled, we are considered online if we have received a ping in the last 80 seconds, else error. + status = elapsed_sunnylink_ping < 80000000000ULL ? tr("ONLINE") : tr("ERROR"); + color = elapsed_sunnylink_ping < 80000000000ULL ? good_color : danger_color; } + sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), status}, color }; setProperty("sunnylinkStatus", QVariant::fromValue(sunnylinkStatus)); } diff --git a/system/manager/sunnylink.py b/system/manager/sunnylink.py index 1a70532e82..84161fb817 100755 --- a/system/manager/sunnylink.py +++ b/system/manager/sunnylink.py @@ -50,10 +50,6 @@ def register_sunnylink(): sunnylink_id = SunnylinkApi(None).register_device(None, **extra_args) print(f"SunnyLinkId: {sunnylink_id}") - # Set the last ping time to the current time since we just registered - last_ping = int(time.monotonic() * 1e9) - Params().put("LastSunnylinkPingTime", str(last_ping)) - def main(): """The main method is expected to be called by the manager when the device boots up.""" From 696497c6e4f145abfd57cb0be2e979fd23805cfb Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 22 Jun 2024 14:45:04 +0200 Subject: [PATCH 2/4] Update sunnylink error logging and status display Updated the error logging in sunnylink.py to include the module name for clarity. In addition, adjusted the status display in sidebar.cc to shorten the "REGISTERING" status text for a cleaner user interface. --- common/api/sunnylink.py | 2 +- selfdrive/ui/qt/sidebar.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/api/sunnylink.py b/common/api/sunnylink.py index 76cc6f21d7..d7a83c1530 100644 --- a/common/api/sunnylink.py +++ b/common/api/sunnylink.py @@ -124,7 +124,7 @@ class SunnylinkApi(BaseApi): self._status_update(f"Waiting {backoff}s before retry, Exception occurred during registration: [{str(e)}]") with open('/data/community/crashes/error.txt', 'a') as f: - f.write(f"{datetime.now()}: {str(e)}\n") + f.write(f"[{datetime.now()}] sunnylink: {str(e)}\n") backoff = min(backoff * 2, 60) time.sleep(backoff) diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index 0e6409bbb5..8d45e62ce4 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -166,7 +166,7 @@ void Sidebar::updateState(const UIState &s) { if (sunnylink_enabled && last_sunnylink_ping == 0) { // If sunnylink is enabled, but we don't have a dongle id, and we haven't received a ping yet, we are registering - status = sl_dongle_id.has_value() ? tr("OFFLINE") : tr("REGISTERING"); + status = sl_dongle_id.has_value() ? tr("OFFLINE") : tr("REGIST..."); color = sl_dongle_id.has_value() ? warning_color : progress_color; } else if (sunnylink_enabled) { // If sunnylink is enabled, we are considered online if we have received a ping in the last 80 seconds, else error. From 5c351a994f0e1e482b3da97ebf3653f77a0da851 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 22 Jun 2024 14:47:32 +0200 Subject: [PATCH 3/4] Disable Sunnylink if registration is unsuccessful This code update sets Sunnylink to be disabled in the event that the registration does not go through successfully. This is to ensure that resources aren't wasted trying to access unregistered Sunnylink hardware. --- common/api/sunnylink.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/api/sunnylink.py b/common/api/sunnylink.py index d7a83c1530..335965995f 100644 --- a/common/api/sunnylink.py +++ b/common/api/sunnylink.py @@ -139,5 +139,10 @@ class SunnylinkApi(BaseApi): # Set the last ping time to the current time since we were just talking to the API last_ping = int(time.monotonic() * 1e9) if successful_registration else start_time Params().put("LastSunnylinkPingTime", str(last_ping)) + + # Disable sunnylink if registration was not successful + if not successful_registration: + Params().put_bool("SunnylinkEnabled", False) + self.spinner = None return sunnylink_dongle_id From fcc61ac46e4490d818582bdd1083c811c5b6a655 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 22 Jun 2024 20:00:46 +0200 Subject: [PATCH 4/4] Reset Sunnylink ping time during registration In the Sunnylink API, a line was added to reset the last ping time to zero whenever we are trying to register. The purpose of this change is to ensure that registration attempts start with a clean slate, potentially improving the reliability of the registration process. --- common/api/sunnylink.py | 1 + 1 file changed, 1 insertion(+) diff --git a/common/api/sunnylink.py b/common/api/sunnylink.py index 335965995f..4318a27c9a 100644 --- a/common/api/sunnylink.py +++ b/common/api/sunnylink.py @@ -88,6 +88,7 @@ class SunnylinkApi(BaseApi): sunnylink_dongle_id = UNREGISTERED_SUNNYLINK_DONGLE_ID self._status_update("Public key not found, setting dongle ID to unregistered.") else: + Params().put("LastSunnylinkPingTime", "0") # Reset the last ping time to 0 if we are trying to register with pubkey_path.open() as f1, privkey_path.open() as f2: public_key = f1.read() private_key = f2.read()