mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-08-21 09:13:46 +08:00
SL: bugfix parameter handling in sunnylink restore and remote setting (#1234)
* refactor: improve parameter handling in sunnylink for robustness - Updated `get_param_as_byte` to return `None` for nonexistent parameters. - Enhanced param compression and encoding in `sunnylinkd`. * refactor: centralize parameter restoration with new helper function - Added `save_param_from_base64_encoded_string` to handle param decoding and saving. - Updated backup manager and sunnylinkd to use the new method. - Improved code readability and reduced duplication in parameter handling logic. * don't bother * clean
This commit is contained in:
@@ -23,7 +23,7 @@ from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutExce
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from sunnypilot.sunnylink.api import SunnylinkApi
|
||||
from sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte
|
||||
from sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte, save_param_from_base64_encoded_string
|
||||
|
||||
SUNNYLINK_ATHENA_HOST = os.getenv('SUNNYLINK_ATHENA_HOST', 'wss://ws.stg.api.sunnypilot.ai')
|
||||
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
|
||||
@@ -184,14 +184,18 @@ def getParams(params_keys: list[str], compression: bool = False) -> str | dict[s
|
||||
|
||||
try:
|
||||
param_keys_validated = [key for key in params_keys if key in getParamsAllKeys()]
|
||||
params_dict: dict[str, list[dict[str, str | bool | int ]]] = {"params": [
|
||||
{
|
||||
params_dict: dict[str, list[dict[str, str | bool | int]]] = {"params": []}
|
||||
for key in param_keys_validated:
|
||||
value = get_param_as_byte(key)
|
||||
if value is None:
|
||||
continue
|
||||
|
||||
params_dict["params"].append({
|
||||
"key": key,
|
||||
"value": base64.b64encode(gzip.compress(get_param_as_byte(key)) if compression else get_param_as_byte(key)).decode('utf-8'),
|
||||
"value": base64.b64encode(gzip.compress(value) if compression else value).decode('utf-8'),
|
||||
"type": int(params.get_type(key).value),
|
||||
"is_compressed": compression
|
||||
} for key in param_keys_validated
|
||||
]}
|
||||
})
|
||||
|
||||
response = {str(param.get('key')): str(param.get('value')) for param in params_dict.get("params", [])}
|
||||
response |= {"params": json.dumps(params_dict.get("params", []))} # Upcoming for settings v1
|
||||
@@ -204,15 +208,9 @@ def getParams(params_keys: list[str], compression: bool = False) -> str | dict[s
|
||||
|
||||
@dispatcher.add_method
|
||||
def saveParams(params_to_update: dict[str, str], compression: bool = False) -> None:
|
||||
params = Params()
|
||||
params_dict = {key: base64.b64decode(value) for key, value in params_to_update.items()}
|
||||
|
||||
if compression:
|
||||
params_dict = {key: gzip.decompress(value) for key, value in params_dict.items()}
|
||||
|
||||
for key, value in params_dict.items():
|
||||
for key, value in params_to_update.items():
|
||||
try:
|
||||
params.put(key, value)
|
||||
save_param_from_base64_encoded_string(key, value, compression)
|
||||
except Exception as e:
|
||||
cloudlog.error(f"sunnylinkd.saveParams.exception {e}")
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
from openpilot.common.git import get_branch
|
||||
from openpilot.common.params import Params, ParamKeyType, ParamKeyFlag
|
||||
from openpilot.common.params import Params, ParamKeyFlag
|
||||
from openpilot.common.realtime import Ratekeeper
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.system.version import get_version
|
||||
@@ -20,7 +20,7 @@ from openpilot.system.version import get_version
|
||||
from cereal import messaging, custom
|
||||
from sunnypilot.sunnylink.api import SunnylinkApi
|
||||
from sunnypilot.sunnylink.backups.utils import decrypt_compressed_data, encrypt_compress_data, SnakeCaseEncoder
|
||||
from sunnypilot.sunnylink.utils import get_param_as_byte
|
||||
from sunnypilot.sunnylink.utils import get_param_as_byte, save_param_from_base64_encoded_string
|
||||
|
||||
|
||||
class OperationType(Enum):
|
||||
@@ -173,8 +173,7 @@ class BackupManagerSP:
|
||||
self._update_progress(75.0, OperationType.RESTORE)
|
||||
|
||||
# Apply configuration
|
||||
all_values_encoded = self._get_metadata_value(backup_metadata, "all_values_encoded", "false")
|
||||
self._apply_config(config_data, str(all_values_encoded).lower() == "true")
|
||||
self._apply_config(config_data)
|
||||
|
||||
self.restore_status = custom.BackupManagerSP.Status.completed
|
||||
self._update_progress(100.0, OperationType.RESTORE)
|
||||
@@ -187,7 +186,7 @@ class BackupManagerSP:
|
||||
self._report_status()
|
||||
return False
|
||||
|
||||
def _apply_config(self, config_data: dict[str, str], all_values_encoded: bool = False) -> None:
|
||||
def _apply_config(self, config_data: dict[str, str]) -> None:
|
||||
"""Applies configuration data from a backup, but only for parameters marked as backupable."""
|
||||
backupable_params = [k.decode('utf-8') for k in self.params.all_keys(ParamKeyFlag.BACKUP)]
|
||||
backupable_set_lower = {p.lower() for p in backupable_params}
|
||||
@@ -199,26 +198,8 @@ class BackupManagerSP:
|
||||
if param.lower() in backupable_set_lower:
|
||||
# Find real param name (with correct casing)
|
||||
real_param = next(p for p in backupable_params if p.lower() == param.lower())
|
||||
param_type = self.params.get_type(real_param)
|
||||
try:
|
||||
value = base64.b64decode(encoded_value) if all_values_encoded else encoded_value
|
||||
|
||||
if param_type != ParamKeyType.BYTES:
|
||||
value = value.decode('utf-8') # type: ignore
|
||||
|
||||
if param_type == ParamKeyType.STRING:
|
||||
value = value
|
||||
elif param_type == ParamKeyType.BOOL:
|
||||
value = value.lower() in ('true', '1', 'yes') # type: ignore
|
||||
elif param_type == ParamKeyType.INT:
|
||||
value = int(value) # type: ignore
|
||||
elif param_type == ParamKeyType.FLOAT:
|
||||
value = float(value) # type: ignore
|
||||
elif param_type == ParamKeyType.TIME:
|
||||
value = str(value)
|
||||
elif param_type == ParamKeyType.JSON:
|
||||
value = json.loads(value)
|
||||
self.params.put(real_param, value)
|
||||
save_param_from_base64_encoded_string(real_param, encoded_value)
|
||||
restored_count += 1
|
||||
except Exception as e:
|
||||
cloudlog.error(f"Failed to restore param {param}: {str(e)}")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import base64
|
||||
import gzip
|
||||
import json
|
||||
from sunnypilot.sunnylink.api import SunnylinkApi, UNREGISTERED_SUNNYLINK_DONGLE_ID
|
||||
from openpilot.common.params import Params, ParamKeyType
|
||||
@@ -58,13 +60,45 @@ def get_api_token():
|
||||
print(f"API Token: {token}")
|
||||
|
||||
|
||||
def get_param_as_byte(param_name: str) -> bytes:
|
||||
def get_param_as_byte(param_name: str) -> bytes | None:
|
||||
"""Get a parameter as bytes. Returns None if the parameter does not exist."""
|
||||
params = Params()
|
||||
param = params.get(param_name)
|
||||
param_type = params.get_type(param_name)
|
||||
if param is None:
|
||||
return None
|
||||
|
||||
param_type = params.get_type(param_name)
|
||||
if param_type == ParamKeyType.BYTES:
|
||||
return bytes(param)
|
||||
elif param_type == ParamKeyType.JSON:
|
||||
return json.dumps(param).encode('utf-8')
|
||||
return str(param).encode('utf-8')
|
||||
|
||||
|
||||
def save_param_from_base64_encoded_string(param_name: str, base64_encoded_data: str, is_compressed=False) -> None:
|
||||
"""Save a parameter from bytes. Overwrites the parameter if it already exists."""
|
||||
params = Params()
|
||||
# Find real param name (with correct casing)
|
||||
param_type = params.get_type(param_name)
|
||||
value = base64.b64decode(base64_encoded_data)
|
||||
|
||||
if is_compressed:
|
||||
value = gzip.decompress(value)
|
||||
|
||||
# We convert to string anything that isn't bytes first. We later transform further.
|
||||
if param_type != ParamKeyType.BYTES:
|
||||
value = value.decode('utf-8') # type: ignore
|
||||
|
||||
if param_type == ParamKeyType.STRING:
|
||||
value = value
|
||||
elif param_type == ParamKeyType.BOOL:
|
||||
value = value.lower() in ('true', '1', 'yes') # type: ignore
|
||||
elif param_type == ParamKeyType.INT:
|
||||
value = int(value) # type: ignore
|
||||
elif param_type == ParamKeyType.FLOAT:
|
||||
value = float(value) # type: ignore
|
||||
elif param_type == ParamKeyType.TIME:
|
||||
value = str(value) # type: ignore
|
||||
elif param_type == ParamKeyType.JSON:
|
||||
value = json.loads(value)
|
||||
params.put(param_name, value)
|
||||
|
||||
Reference in New Issue
Block a user