From 17d5446650b5e4156a2178ad41b3ab61f394a315 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Sat, 6 Feb 2021 11:07:37 +0100 Subject: [PATCH] Timezoned (#19960) * untested code * add to manager * whitespace * only save on fix * cleanup * fix typo * add to ignored processes * import tici * fix param name * cleanup * only run offroad * use itertools * wait for thermald * simpler * fall back to api call * add param to override timezone Co-authored-by: Comma Device old-commit-hash: 4c04e220a15c013c86d2d1695764fc339b82c2bc --- Pipfile | 4 +- Pipfile.lock | 4 +- common/params_pyx.pyx | 4 +- selfdrive/controls/controlsd.py | 2 +- selfdrive/locationd/locationd.py | 11 +++++ selfdrive/manager.py | 6 ++- selfdrive/timezoned.py | 78 ++++++++++++++++++++++++++++++++ 7 files changed, 102 insertions(+), 7 deletions(-) create mode 100755 selfdrive/timezoned.py diff --git a/Pipfile b/Pipfile index 803076cad..1f0bcc720 100644 --- a/Pipfile +++ b/Pipfile @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:427eb342d23300f90c25e4860f2f5446693c3af6323e7a1f30f7e1ccc56921b7 -size 2116 +oid sha256:a14bdc2df2ce7c1e565fc3fef70bb9872b3e51099bf077a819cfc19dc6a88b50 +size 2137 diff --git a/Pipfile.lock b/Pipfile.lock index 52f82f5bd..7dde6b447 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9e414b95d4fd8abab9ddd37c1a2894790f09e8ab0c36e25c4959094fdc28ecf -size 211694 +oid sha256:a9c44a8d7874e9ced435de3bc9a3724f6bf366af85ba37282b865e270fd64220 +size 217383 diff --git a/common/params_pyx.pyx b/common/params_pyx.pyx index 78dd6baa6..8bb62ffa0 100755 --- a/common/params_pyx.pyx +++ b/common/params_pyx.pyx @@ -44,8 +44,9 @@ keys = { b"IsUpdateAvailable": [TxType.CLEAR_ON_MANAGER_START], b"IsUploadRawEnabled": [TxType.PERSISTENT], b"LastAthenaPingTime": [TxType.PERSISTENT], - b"LastUpdateTime": [TxType.PERSISTENT], + b"LastGPSPosition": [TxType.PERSISTENT], b"LastUpdateException": [TxType.PERSISTENT], + b"LastUpdateTime": [TxType.PERSISTENT], b"LiveParameters": [TxType.PERSISTENT], b"OpenpilotEnabledToggle": [TxType.PERSISTENT], b"LaneChangeEnabled": [TxType.PERSISTENT], @@ -58,6 +59,7 @@ keys = { b"ShouldDoUpdate": [TxType.CLEAR_ON_MANAGER_START], b"SubscriberInfo": [TxType.PERSISTENT], b"TermsVersion": [TxType.PERSISTENT], + b"Timezone": [TxType.PERSISTENT], b"TrainingVersion": [TxType.PERSISTENT], b"UpdateAvailable": [TxType.CLEAR_ON_MANAGER_START], b"UpdateFailedCount": [TxType.CLEAR_ON_MANAGER_START], diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 51eb1aa9f..690a46922 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -30,7 +30,7 @@ STEER_ANGLE_SATURATION_THRESHOLD = 2.5 # Degrees SIMULATION = "SIMULATION" in os.environ NOSENSOR = "NOSENSOR" in os.environ -IGNORE_PROCESSES = set(["rtshield", "uploader", "deleter", "loggerd", "logmessaged", "tombstoned", "logcatd", "proclogd", "clocksd", "gpsd", "updated"]) +IGNORE_PROCESSES = set(["rtshield", "uploader", "deleter", "loggerd", "logmessaged", "tombstoned", "logcatd", "proclogd", "clocksd", "gpsd", "updated", "timezoned"]) ThermalStatus = log.ThermalData.ThermalStatus State = log.ControlsState.OpenpilotState diff --git a/selfdrive/locationd/locationd.py b/selfdrive/locationd/locationd.py index 9026fc339..6f4443d82 100755 --- a/selfdrive/locationd/locationd.py +++ b/selfdrive/locationd/locationd.py @@ -1,8 +1,10 @@ #!/usr/bin/env python3 +import json import numpy as np import sympy as sp import cereal.messaging as messaging from cereal import log +from common.params import Params import common.transformations.coordinates as coord from common.transformations.orientation import ecef_euler_from_ned, \ euler_from_quat, \ @@ -295,6 +297,7 @@ def locationd_thread(sm, pm, disabled_logs=None): if pm is None: pm = messaging.PubMaster(['liveLocationKalman']) + params = Params() localizer = Localizer(disabled_logs=disabled_logs) while True: @@ -327,6 +330,14 @@ def locationd_thread(sm, pm, disabled_logs=None): msg.liveLocationKalman.gpsOK = gps_age < 1.0 pm.send('liveLocationKalman', msg) + if sm.frame % 1200 == 0 and msg.liveLocationKalman.gpsOK: # once a minute + location = { + 'latitude': msg.liveLocationKalman.positionGeodetic.value[0], + 'longitude': msg.liveLocationKalman.positionGeodetic.value[1], + 'altitude': msg.liveLocationKalman.positionGeodetic.value[2], + } + params.put("LastGPSPosition", json.dumps(location)) + def main(sm=None, pm=None): locationd_thread(sm, pm) diff --git a/selfdrive/manager.py b/selfdrive/manager.py index 6d4b042af..1f3467f8f 100755 --- a/selfdrive/manager.py +++ b/selfdrive/manager.py @@ -19,7 +19,7 @@ from common.basedir import BASEDIR from common.spinner import Spinner from common.text_window import TextWindow import selfdrive.crash as crash -from selfdrive.hardware import HARDWARE, EON, PC +from selfdrive.hardware import HARDWARE, EON, PC, TICI from selfdrive.hardware.eon.apk import update_apks, pm_apply_packages, start_offroad from selfdrive.swaglog import cloudlog, add_logentries_handler from selfdrive.version import version, dirty @@ -218,6 +218,10 @@ if EON: 'sensord', ] +if TICI: + managed_processes["timezoned"] = "selfdrive.timezoned" + persistent_processes += ['timezoned'] + car_started_processes = [ 'controlsd', 'plannerd', diff --git a/selfdrive/timezoned.py b/selfdrive/timezoned.py new file mode 100755 index 000000000..ec4bfb09a --- /dev/null +++ b/selfdrive/timezoned.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +import time +import json +import subprocess + +import requests +from timezonefinder import TimezoneFinder + +from common.params import Params +from selfdrive.swaglog import cloudlog + + +def set_timezone(valid_timezones, timezone): + if timezone not in valid_timezones: + cloudlog.error(f"Timezone not supported {timezone}") + return + + cloudlog.info(f"Setting timezone to {timezone}") + try: + subprocess.check_call(f'sudo timedatectl set-timezone {timezone}', shell=True) + except subprocess.CalledProcessError: + cloudlog.exception(f"Error setting timezone to {timezone}") + + +def main(): + params = Params() + tf = TimezoneFinder() + + # Get allowed timezones + valid_timezones = subprocess.check_output('timedatectl list-timezones', shell=True, encoding='utf8').strip().split('\n') + + while True: + time.sleep(60) + + is_onroad = params.get("IsOffroad") != b"1" + if is_onroad: + continue + + # Set based on param + timezone = params.get("Timezone", encoding='utf8') + if timezone is not None: + cloudlog.info("Setting timezone based on param") + set_timezone(valid_timezones, timezone) + continue + + location = params.get("LastGPSPosition", encoding='utf8') + + # Find timezone based on IP geolocation if no gps location is available + if location is None: + cloudlog.info("Setting timezone based on IP lookup") + try: + r = requests.get("https://ipapi.co/timezone", timeout=10) + if r.status_code == 200: + set_timezone(valid_timezones, r.text) + else: + cloudlog.error(f"Unexpected status code from api {r.status_code}") + except requests.exceptions.RequestException: + cloudlog.exception("Error getting timezone based on IP") + continue + + # Find timezone by reverse geocoding the last known gps location + else: + cloudlog.info("Setting timezone based on GPS location") + try: + location = json.loads(location) + except Exception: + cloudlog.exception("Error parsing location") + continue + + timezone = tf.timezone_at(lng=location['longitude'], lat=location['latitude']) + if timezone is None: + cloudlog.error(f"No timezone found based on location, {location}") + continue + set_timezone(valid_timezones, timezone) + + +if __name__ == "__main__": + main()