diff --git a/common/op_params.py b/common/op_params.py index 1846f8fd0..be6dc1ec5 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -4,6 +4,7 @@ import json import time import string import random +from selfdrive.swaglog import cloudlog from common.travis_checker import travis @@ -30,6 +31,7 @@ class KeyInfo: live = False has_default = False has_description = False + hidden = False class opParams: @@ -49,7 +51,8 @@ class opParams: self.default_params = {'dynamic_follow': {'default': 'relaxed', 'allowed_types': [str], 'description': "Can be: ('traffic', 'relaxed', 'roadtrip'): Left to right increases in following distance.\n" "All profiles support dynamic follow so you'll get your preferred distance while\n" - "retaining the smoothness and safety of dynamic follow!", 'live': True},} + "retaining the smoothness and safety of dynamic follow!", 'live': True}, + 'op_edit_live_mode': {'default': False, 'description': 'This parameter controls which mode opEdit starts in. It should be hidden from the user with the hide key', 'hide': True}} self.params = {} self.params_file = "/data/op_params.json" @@ -126,39 +129,49 @@ class opParams: def get(self, key=None, default=None, force_update=False): # can specify a default value if key doesn't exist self.update_params(key, force_update) if key is None: - return self.params + return self.get_all() if key in self.params: - key_info = self.get_key_info(key) + key_info = self.key_info(key) if key_info.has_allowed_types: value = self.params[key] allowed_types = self.default_params[key]['allowed_types'] - valid_type = type(value) in allowed_types - if not valid_type: - if key_info.has_default: # if value in op_params.json is not correct type, use default - value = self.default_params[key]['default'] - else: # else use a standard value based on type (last resort to keep openpilot running) - value = self.value_from_types(allowed_types) + if type(value) not in allowed_types: + cloudlog.warning('op_params: User\'s value is not valid!') + if key_info.has_default: # invalid value type, try to use default value + default_value = self.default_params[key]['default'] + if type(default_value) in allowed_types: # actually check if the default is valid + # return default value because user's value of key is not in the allowed_types to avoid crashing openpilot + return default_value + else: # else use a standard value based on type (last resort to keep openpilot running if user's value is of invalid type) + return self.value_from_types(allowed_types) + else: + return value # all good, returning user's value else: - value = self.params[key] - else: - value = default + return self.params[key] # no defined allowed types, returning user's value - return value + return default # not in params - def get_key_info(self, key): + def get_all(self): # returns all non-hidden params + return {k: v for k, v in self.params.items() if not self.key_info(k).hidden} + + def key_info(self, key): key_info = KeyInfo() + if key is None: + return key_info if key in self.default_params: if 'allowed_types' in self.default_params[key]: allowed_types = self.default_params[key]['allowed_types'] if isinstance(allowed_types, list) and len(allowed_types) > 0: key_info.has_allowed_types = True - if 'live' in self.default_params[key] and self.default_params[key]['live'] is True: - key_info.live = True + if 'live' in self.default_params[key]: + key_info.live = self.default_params[key]['live'] if 'default' in self.default_params[key]: key_info.has_default = True if 'description' in self.default_params[key]: key_info.has_description = True + if 'hide' in self.default_params[key]: + key_info.hidden = self.default_params[key]['hide'] return key_info def value_from_types(self, allowed_types): @@ -173,8 +186,8 @@ class opParams: return None # unknown type def update_params(self, key, force_update): - if force_update or self.get_key_info(key).live: # if is a live param, we want to get updates while openpilot is running - if not travis and time.time() - self.last_read_time >= self.read_frequency: # make sure we aren't reading file too often + if force_update or self.key_info(key).live: # if is a live param, we want to get updates while openpilot is running + if not travis and (time.time() - self.last_read_time >= self.read_frequency or force_update): # make sure we aren't reading file too often self.params, read_status = read_params(self.params_file, self.format_default_params()) if not read_status: time.sleep(1/100.) @@ -184,4 +197,4 @@ class opParams: def delete(self, key): if key in self.params: del self.params[key] - write_params(self.params, self.params_file) + write_params(self.params, self.params_file) \ No newline at end of file diff --git a/op_edit.py b/op_edit.py index 5faaae994..617165e94 100644 --- a/op_edit.py +++ b/op_edit.py @@ -9,46 +9,33 @@ class opEdit: # use by running `python /data/openpilot/op_edit.py` self.op_params = opParams() self.params = None self.sleep_time = 1.0 - self.live_tuning = False - self.welcome() - - def welcome(self): - print('Welcome to the opParams command line editor!') - while True: - print('Would you like to enter opEdit\'s live-tuning mode?') - choice = input('[Y/n]: ').lower().strip() - if choice in ['y', 'ye', 'yes']: - self.live_tuning = True - self.run_loop() - break - elif choice in ['n', 'no', '']: - self.run_loop() - break - elif choice in ['e', 'exit']: - break + self.live_tuning = self.op_params.get('op_edit_live_mode', False) + self.run_loop() def run_loop(self): - if not self.live_tuning: - print('Here are your parameters:\n') - else: - print('Here are your live parameters:\n') + print('Welcome to the opParams command line editor!') while True: + if not self.live_tuning: + print('Here are your parameters:\n') + else: + print('Here are your live parameters:\n') self.params = self.op_params.get(force_update=True) if self.live_tuning: # only display live tunable params - self.params = {k: v for k, v in self.params.items() if self.op_params.get_key_info(k).live} + self.params = {k: v for k, v in self.params.items() if self.op_params.key_info(k).live} values_list = [self.params[i] if len(str(self.params[i])) < 20 else '{} ... {}'.format(str(self.params[i])[:30], str(self.params[i])[-15:]) for i in self.params] - live = ['(live!)' if self.op_params.get_key_info(i).live else '' for i in self.params] + live = ['(live!)' if self.op_params.key_info(i).live else '' for i in self.params] to_print = ['{}. {}: {} {}'.format(idx + 1, i, values_list[idx], live[idx]) for idx, i in enumerate(self.params)] - to_print.append('\n{}. Add new parameter!'.format(len(self.params) + 1)) - to_print.append('{}. Delete parameter!'.format(len(self.params) + 2)) + to_print.append('---\n{}. Add new parameter'.format(len(to_print) + 1)) + to_print.append('{}. Delete parameter'.format(len(to_print) + 1)) + to_print.append('{}. Toggle live tuning'.format(len(to_print) + 1)) print('\n'.join(to_print)) print('\nChoose a parameter to explore (by integer index): ') choice = input('>> ').strip() - parsed, choice = self.parse_choice(choice) + parsed, choice = self.parse_choice(choice, len(to_print)) if parsed == 'continue': continue elif parsed == 'add': @@ -57,10 +44,13 @@ class opEdit: # use by running `python /data/openpilot/op_edit.py` self.change_parameter(choice) elif parsed == 'delete': self.delete_parameter() + elif parsed == 'live': + self.live_tuning = not self.live_tuning + self.op_params.put('op_edit_live_mode', self.live_tuning) # for next opEdit startup elif parsed == 'error': return - def parse_choice(self, choice): + def parse_choice(self, choice, opt_len): if choice.isdigit(): choice = int(choice) choice -= 1 @@ -70,22 +60,23 @@ class opEdit: # use by running `python /data/openpilot/op_edit.py` else: self.message('Not an integer!') return 'retry', choice - if choice not in range(0, len(self.params) + 2): # three for add/delete parameter + if choice not in range(opt_len): # number of options to choose from self.message('Not in range!') return 'continue', choice - if choice == len(self.params): # add new parameter + if choice == opt_len - 3: # add new parameter return 'add', choice - - if choice == len(self.params) + 1: # delete parameter + elif choice == opt_len - 2: # delete parameter return 'delete', choice + elif choice == opt_len - 1: # live tuning mode + return 'live', choice return 'change', choice def change_parameter(self, choice): while True: chosen_key = list(self.params)[choice] - key_info = self.op_params.get_key_info(chosen_key) + key_info = self.op_params.key_info(chosen_key) old_value = self.params[chosen_key] print('Chosen parameter: {}'.format(chosen_key))