diff --git a/apks b/apks index ac26fd4a34..d94f866391 160000 --- a/apks +++ b/apks @@ -1 +1 @@ -Subproject commit ac26fd4a343b6a202dc3ddaac6e00feabcf3a506 +Subproject commit d94f8663913f12142754b2501b174c8bcd5996ce diff --git a/common/params.py b/common/params.py index e2ef52fb74..24e31bff6a 100755 --- a/common/params.py +++ b/common/params.py @@ -78,6 +78,7 @@ keys = { "IsTakingSnapshot": [TxType.CLEAR_ON_MANAGER_START], "IsUpdateAvailable": [TxType.CLEAR_ON_MANAGER_START], "IsUploadRawEnabled": [TxType.PERSISTENT], + "LastAthenaPingTime": [TxType.PERSISTENT], "LastUpdateTime": [TxType.PERSISTENT], "LimitSetSpeed": [TxType.PERSISTENT], "LimitSetSpeedNeural": [TxType.PERSISTENT], diff --git a/selfdrive/athena/athenad.py b/selfdrive/athena/athenad.py index 90043bfed8..a8b0377c1a 100755 --- a/selfdrive/athena/athenad.py +++ b/selfdrive/athena/athenad.py @@ -22,6 +22,7 @@ from common import android from common.basedir import PERSIST from common.api import Api from common.params import Params +from common.realtime import sec_since_boot from cereal.services import service_list from selfdrive.swaglog import cloudlog @@ -272,8 +273,13 @@ def ws_proxy_send(ws, local_sock, signal_sock, end_event): def ws_recv(ws, end_event): while not end_event.is_set(): try: - data = ws.recv() - payload_queue.put_nowait(data) + opcode, data = ws.recv_data(control_frame=True) + if opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY): + if opcode == ABNF.OPCODE_TEXT: + data = data.decode("utf-8") + payload_queue.put_nowait(data) + elif opcode == ABNF.OPCODE_PING: + Params().put("LastAthenaPingTime", str(int(sec_since_boot()*1e9))) except WebSocketTimeoutException: pass except Exception: @@ -316,6 +322,7 @@ def main(): except Exception: cloudlog.exception("athenad.main.exception") conn_retries += 1 + params.delete("LastAthenaPingTime") time.sleep(backoff(conn_retries)) diff --git a/selfdrive/test/test_openpilot.py b/selfdrive/test/test_openpilot.py index 3e9a317582..624524de77 100644 --- a/selfdrive/test/test_openpilot.py +++ b/selfdrive/test/test_openpilot.py @@ -4,6 +4,7 @@ os.environ['FAKEUPLOAD'] = "1" from common.apk import update_apks, start_offroad, pm_apply_packages, android_packages from common.params import Params +from common.realtime import sec_since_boot from common.testing import phone_only from selfdrive.manager import manager_init, manager_prepare from selfdrive.manager import start_managed_process, kill_managed_process, get_running @@ -14,6 +15,7 @@ import requests import signal import subprocess import time +from datetime import datetime, timedelta DID_INIT = False @@ -108,6 +110,7 @@ def test_uploader(): @phone_only def test_athena(): print("ATHENA") + start = sec_since_boot() start_daemon_process("manage_athenad") params = Params() manage_athenad_pid = params.get("AthenadPid") @@ -155,7 +158,7 @@ def test_athena(): else: print(f'athena_post failed {e}. retrying...') - def expect_athena_registers(): + def expect_athena_registers(test_t0): resp = athena_post({ "method": "echo", "params": ["hello"], @@ -164,6 +167,10 @@ def test_athena(): }, max_retries=12, wait=5) assert resp.get('result') == "hello", f'Athena failed to register ({resp})' + last_pingtime = params.get("LastAthenaPingTime", encoding='utf8') + assert last_pingtime, last_pingtime + assert ((int(last_pingtime)/1e9) - test_t0) < (sec_since_boot() - test_t0) + try: athenad_pid = expect_athena_starts() # kill athenad and ensure it is restarted (check_output will throw if it is not) @@ -174,7 +181,7 @@ def test_athena(): print('WARNING: COMMA_JWT env not set, will not test requests to athena.comma.ai') return - expect_athena_registers() + expect_athena_registers(start) print("ATHENA: getSimInfo") resp = athena_post({ diff --git a/selfdrive/ui/paint.cc b/selfdrive/ui/paint.cc index 379115431c..955f5d9582 100644 --- a/selfdrive/ui/paint.cc +++ b/selfdrive/ui/paint.cc @@ -771,8 +771,8 @@ void ui_draw_vision_alert(UIState *s, int va_size, int va_color, const UIScene *scene = &s->scene; int ui_viz_rx = scene->ui_viz_rx; int ui_viz_rw = scene->ui_viz_rw; - bool hasSidebar = !s->scene.uilayout_sidebarcollapsed; - bool mapEnabled = s->scene.uilayout_mapenabled; + const bool hasSidebar = !scene->uilayout_sidebarcollapsed; + const bool mapEnabled = scene->uilayout_mapenabled; bool longAlert1 = strlen(va_text1) > 15; const uint8_t *color = alert_colors[va_color]; diff --git a/selfdrive/ui/sidebar.cc b/selfdrive/ui/sidebar.cc index 454a322098..74d5cbe534 100644 --- a/selfdrive/ui/sidebar.cc +++ b/selfdrive/ui/sidebar.cc @@ -3,8 +3,8 @@ #include #include "ui.hpp" -static void ui_draw_sidebar_background(UIState *s, bool hasSidebar) { - int sbr_x = hasSidebar ? 0 : -(sbr_w) + bdr_s * 2; +static void ui_draw_sidebar_background(UIState *s) { + int sbr_x = !s->scene.uilayout_sidebarcollapsed ? 0 : -(sbr_w) + bdr_s * 2; nvgBeginPath(s->vg); nvgRect(s->vg, sbr_x, 0, sbr_w, vwp_h); @@ -12,9 +12,9 @@ static void ui_draw_sidebar_background(UIState *s, bool hasSidebar) { nvgFill(s->vg); } -static void ui_draw_sidebar_settings_button(UIState *s, bool hasSidebar) { +static void ui_draw_sidebar_settings_button(UIState *s) { bool settingsActive = s->active_app == cereal_UiLayoutState_App_settings; - const int settings_btn_xr = hasSidebar ? settings_btn_x : -(sbr_w); + const int settings_btn_xr = !s->scene.uilayout_sidebarcollapsed ? settings_btn_x : -(sbr_w); nvgBeginPath(s->vg); NVGpaint imgPaint = nvgImagePattern(s->vg, settings_btn_xr, settings_btn_y, @@ -24,9 +24,9 @@ static void ui_draw_sidebar_settings_button(UIState *s, bool hasSidebar) { nvgFill(s->vg); } -static void ui_draw_sidebar_home_button(UIState *s, bool hasSidebar) { +static void ui_draw_sidebar_home_button(UIState *s) { bool homeActive = s->active_app == cereal_UiLayoutState_App_home; - const int home_btn_xr = hasSidebar ? home_btn_x : -(sbr_w); + const int home_btn_xr = !s->scene.uilayout_sidebarcollapsed ? home_btn_x : -(sbr_w); nvgBeginPath(s->vg); NVGpaint imgPaint = nvgImagePattern(s->vg, home_btn_xr, home_btn_y, @@ -36,10 +36,10 @@ static void ui_draw_sidebar_home_button(UIState *s, bool hasSidebar) { nvgFill(s->vg); } -static void ui_draw_sidebar_network_strength(UIState *s, bool hasSidebar) { +static void ui_draw_sidebar_network_strength(UIState *s) { const int network_img_h = 27; const int network_img_w = 176; - const int network_img_x = hasSidebar ? 58 : -(sbr_w); + const int network_img_x = !s->scene.uilayout_sidebarcollapsed ? 58 : -(sbr_w); const int network_img_y = 196; const int network_img = s->scene.networkType == cereal_ThermalData_NetworkType_none ? s->img_network[0] : s->img_network[s->scene.networkStrength + 1]; @@ -52,10 +52,10 @@ static void ui_draw_sidebar_network_strength(UIState *s, bool hasSidebar) { nvgFill(s->vg); } -static void ui_draw_sidebar_battery_icon(UIState *s, bool hasSidebar) { +static void ui_draw_sidebar_battery_icon(UIState *s) { const int battery_img_h = 36; const int battery_img_w = 76; - const int battery_img_x = hasSidebar ? 160 : -(sbr_w); + const int battery_img_x = !s->scene.uilayout_sidebarcollapsed ? 160 : -(sbr_w); const int battery_img_y = 255; int battery_img = strcmp(s->scene.batteryStatus, "Charging") == 0 ? @@ -75,8 +75,8 @@ static void ui_draw_sidebar_battery_icon(UIState *s, bool hasSidebar) { nvgFill(s->vg); } -static void ui_draw_sidebar_network_type(UIState *s, bool hasSidebar) { - const int network_x = hasSidebar ? 50 : -(sbr_w); +static void ui_draw_sidebar_network_type(UIState *s) { + const int network_x = !s->scene.uilayout_sidebarcollapsed ? 50 : -(sbr_w); const int network_y = 273; const int network_w = 100; const int network_h = 100; @@ -94,8 +94,8 @@ static void ui_draw_sidebar_network_type(UIState *s, bool hasSidebar) { nvgTextBox(s->vg, network_x, network_y, network_w, network_type_str, NULL); } -static void ui_draw_sidebar_metric(UIState *s, const char* label_str, const char* value_str, const int severity, const int y_offset, const char* message_str, bool hasSidebar) { - const int metric_x = hasSidebar ? 30 : -(sbr_w); +static void ui_draw_sidebar_metric(UIState *s, const char* label_str, const char* value_str, const int severity, const int y_offset, const char* message_str) { + const int metric_x = !s->scene.uilayout_sidebarcollapsed ? 30 : -(sbr_w); const int metric_y = 338 + y_offset; const int metric_w = 240; const int metric_h = message_str ? strchr(message_str, '\n') ? 124 : 100 : 148; @@ -142,7 +142,7 @@ static void ui_draw_sidebar_metric(UIState *s, const char* label_str, const char } } -static void ui_draw_sidebar_temp_metric(UIState *s, bool hasSidebar) { +static void ui_draw_sidebar_temp_metric(UIState *s) { int temp_severity; char temp_label_str[32]; char temp_value_str[32]; @@ -164,10 +164,10 @@ static void ui_draw_sidebar_temp_metric(UIState *s, bool hasSidebar) { snprintf(temp_label_str, sizeof(temp_label_str), "%s", "TEMP"); strcat(temp_value_str, temp_value_unit); - ui_draw_sidebar_metric(s, temp_label_str, temp_value_str, temp_severity, temp_y_offset, NULL, hasSidebar); + ui_draw_sidebar_metric(s, temp_label_str, temp_value_str, temp_severity, temp_y_offset, NULL); } -static void ui_draw_sidebar_panda_metric(UIState *s, bool hasSidebar) { +static void ui_draw_sidebar_panda_metric(UIState *s) { int panda_severity; char panda_message_str[32]; const int panda_y_offset = 32 + 148; @@ -194,17 +194,27 @@ static void ui_draw_sidebar_panda_metric(UIState *s, bool hasSidebar) { } } - ui_draw_sidebar_metric(s, NULL, NULL, panda_severity, panda_y_offset, panda_message_str, hasSidebar); + ui_draw_sidebar_metric(s, NULL, NULL, panda_severity, panda_y_offset, panda_message_str); +} + +static void ui_draw_sidebar_connectivity(UIState *s) { + if (s->scene.athenaStatus == NET_DISCONNECTED) { + ui_draw_sidebar_metric(s, NULL, NULL, 1, 180+158, "ATHENA\nOFFLINE"); + } else if (s->scene.athenaStatus == NET_CONNECTED) { + ui_draw_sidebar_metric(s, NULL, NULL, 0, 180+158, "ATHENA\nONLINE"); + } else { + ui_draw_sidebar_metric(s, NULL, NULL, 2, 180+158, "ATHENA\nERROR"); + } } void ui_draw_sidebar(UIState *s) { - bool hasSidebar = !s->scene.uilayout_sidebarcollapsed; - ui_draw_sidebar_background(s, hasSidebar); - ui_draw_sidebar_settings_button(s, hasSidebar); - ui_draw_sidebar_home_button(s, hasSidebar); - ui_draw_sidebar_network_strength(s, hasSidebar); - ui_draw_sidebar_battery_icon(s, hasSidebar); - ui_draw_sidebar_network_type(s, hasSidebar); - ui_draw_sidebar_temp_metric(s, hasSidebar); - ui_draw_sidebar_panda_metric(s, hasSidebar); + ui_draw_sidebar_background(s); + ui_draw_sidebar_settings_button(s); + ui_draw_sidebar_home_button(s); + ui_draw_sidebar_network_strength(s); + ui_draw_sidebar_battery_icon(s); + ui_draw_sidebar_network_type(s); + ui_draw_sidebar_temp_metric(s); + ui_draw_sidebar_panda_metric(s); + ui_draw_sidebar_connectivity(s); } diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 905656b2be..188b93da4d 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -132,6 +132,16 @@ static void read_param_float(float* param, const char* param_name) { } } +static int read_param_uint64(uint64_t* dest, const char* param_name) { + char *s; + const int result = read_db_value(NULL, param_name, &s, NULL); + if (result == 0) { + *dest = strtoull(s, NULL, 0); + free(s); + } + return result; +} + static void read_param_bool_timeout(bool* param, const char* param_name, int* timeout) { if (*timeout > 0){ (*timeout)--; @@ -150,6 +160,16 @@ static void read_param_float_timeout(float* param, const char* param_name, int* } } +static int read_param_uint64_timeout(uint64_t* dest, const char* param_name, int* timeout) { + if (*timeout > 0){ + (*timeout)--; + return 0; + } else { + return read_param_uint64(dest, param_name); + *timeout = 2 * UI_FREQ; // 0.5Hz + } +} + static void ui_init(UIState *s) { memset(s, 0, sizeof(UIState)); @@ -1026,7 +1046,14 @@ int main(int argc, char* argv[]) { read_param_bool_timeout(&s->longitudinal_control, "LongitudinalControl", &s->longitudinal_control_timeout); read_param_bool_timeout(&s->limit_set_speed, "LimitSetSpeed", &s->limit_set_speed_timeout); read_param_float_timeout(&s->speed_lim_off, "SpeedLimitOffset", &s->limit_set_speed_timeout); - + int param_read = read_param_uint64_timeout(&s->last_athena_ping, "LastAthenaPingTime", &s->last_athena_ping_timeout); + if (param_read != 0) { + s->scene.athenaStatus = NET_DISCONNECTED; + } else if (nanos_since_boot() - s->last_athena_ping < 70e9) { + s->scene.athenaStatus = NET_CONNECTED; + } else { + s->scene.athenaStatus = NET_ERROR; + } pthread_mutex_unlock(&s->lock); // the bg thread needs to be scheduled, so the main thread needs time without the lock diff --git a/selfdrive/ui/ui.hpp b/selfdrive/ui/ui.hpp index 137d07bbb8..ea3111175b 100644 --- a/selfdrive/ui/ui.hpp +++ b/selfdrive/ui/ui.hpp @@ -33,6 +33,10 @@ #define STATUS_WARNING 3 #define STATUS_ALERT 4 +#define NET_CONNECTED 0 +#define NET_DISCONNECTED 1 +#define NET_ERROR 2 + #define ALERTSIZE_NONE 0 #define ALERTSIZE_SMALL 1 #define ALERTSIZE_MID 2 @@ -157,6 +161,7 @@ typedef struct UIScene { int paTemp; int hwType; int satelliteCount; + uint8_t athenaStatus; } UIScene; typedef struct { @@ -257,9 +262,11 @@ typedef struct UIState { int longitudinal_control_timeout; int limit_set_speed_timeout; int hardware_timeout; + int last_athena_ping_timeout; bool controls_seen; + uint64_t last_athena_ping; int status; bool is_metric; bool longitudinal_control;