|
|
@ -1,5 +1,6 @@ |
|
|
|
# distutils: language = c++ |
|
|
|
# distutils: language = c++ |
|
|
|
# cython: language_level = 3 |
|
|
|
# cython: language_level = 3 |
|
|
|
|
|
|
|
import builtins |
|
|
|
import datetime |
|
|
|
import datetime |
|
|
|
import json |
|
|
|
import json |
|
|
|
from libcpp cimport bool |
|
|
|
from libcpp cimport bool |
|
|
@ -7,6 +8,8 @@ from libcpp.string cimport string |
|
|
|
from libcpp.vector cimport vector |
|
|
|
from libcpp.vector cimport vector |
|
|
|
from libcpp.optional cimport optional |
|
|
|
from libcpp.optional cimport optional |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from openpilot.common.swaglog import cloudlog |
|
|
|
|
|
|
|
|
|
|
|
cdef extern from "common/params.h": |
|
|
|
cdef extern from "common/params.h": |
|
|
|
cpdef enum ParamKeyFlag: |
|
|
|
cpdef enum ParamKeyFlag: |
|
|
|
PERSISTENT |
|
|
|
PERSISTENT |
|
|
@ -42,6 +45,25 @@ cdef extern from "common/params.h": |
|
|
|
void clearAll(ParamKeyFlag) |
|
|
|
void clearAll(ParamKeyFlag) |
|
|
|
vector[string] allKeys() |
|
|
|
vector[string] allKeys() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PYTHON_2_CPP = { |
|
|
|
|
|
|
|
(str, STRING): lambda v: v, |
|
|
|
|
|
|
|
(builtins.bool, BOOL): lambda v: "1" if v else "0", |
|
|
|
|
|
|
|
(int, INT): str, |
|
|
|
|
|
|
|
(float, FLOAT): str, |
|
|
|
|
|
|
|
(datetime.datetime, TIME): lambda v: v.isoformat(), |
|
|
|
|
|
|
|
(dict, JSON): json.dumps, |
|
|
|
|
|
|
|
(list, JSON): json.dumps, |
|
|
|
|
|
|
|
(bytes, BYTES): lambda v: v, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
CPP_2_PYTHON = { |
|
|
|
|
|
|
|
STRING: lambda v: v.decode("utf-8"), |
|
|
|
|
|
|
|
BOOL: lambda v: v == b"1", |
|
|
|
|
|
|
|
INT: int, |
|
|
|
|
|
|
|
FLOAT: float, |
|
|
|
|
|
|
|
TIME: lambda v: datetime.datetime.fromisoformat(v.decode("utf-8")), |
|
|
|
|
|
|
|
JSON: json.loads, |
|
|
|
|
|
|
|
BYTES: lambda v: v, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
def ensure_bytes(v): |
|
|
|
def ensure_bytes(v): |
|
|
|
return v.encode() if isinstance(v, str) else v |
|
|
|
return v.encode() if isinstance(v, str) else v |
|
|
@ -74,45 +96,38 @@ cdef class Params: |
|
|
|
raise UnknownKeyName(key) |
|
|
|
raise UnknownKeyName(key) |
|
|
|
return key |
|
|
|
return key |
|
|
|
|
|
|
|
|
|
|
|
def cast(self, t, value, default): |
|
|
|
def python2cpp(self, proposed_type, expected_type, value, key): |
|
|
|
|
|
|
|
cast = PYTHON_2_CPP.get((proposed_type, expected_type)) |
|
|
|
|
|
|
|
if cast: |
|
|
|
|
|
|
|
return cast(value) |
|
|
|
|
|
|
|
raise TypeError(f"Type mismatch while writing param {key}: {proposed_type=} {expected_type=} {value=}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def cpp2python(self, t, value, default, key): |
|
|
|
if value is None: |
|
|
|
if value is None: |
|
|
|
return None |
|
|
|
return None |
|
|
|
try: |
|
|
|
try: |
|
|
|
if t == STRING: |
|
|
|
return CPP_2_PYTHON[t](value) |
|
|
|
return value.decode("utf-8") |
|
|
|
except (KeyError, TypeError, ValueError): |
|
|
|
elif t == BOOL: |
|
|
|
cloudlog.warning(f"Failed to cast param {key} with {value=} from type {t=}") |
|
|
|
return value == b"1" |
|
|
|
return self.cpp2python(t, default, None, key) |
|
|
|
elif t == INT: |
|
|
|
|
|
|
|
return int(value) |
|
|
|
|
|
|
|
elif t == FLOAT: |
|
|
|
|
|
|
|
return float(value) |
|
|
|
|
|
|
|
elif t == TIME: |
|
|
|
|
|
|
|
return datetime.datetime.fromisoformat(value.decode("utf-8")) |
|
|
|
|
|
|
|
elif t == JSON: |
|
|
|
|
|
|
|
return json.loads(value) |
|
|
|
|
|
|
|
elif t == BYTES: |
|
|
|
|
|
|
|
return value |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
raise TypeError() |
|
|
|
|
|
|
|
except (TypeError, ValueError): |
|
|
|
|
|
|
|
return self.cast(t, default, None) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get(self, key, bool block=False, bool return_default=False): |
|
|
|
def get(self, key, bool block=False, bool return_default=False): |
|
|
|
cdef string k = self.check_key(key) |
|
|
|
cdef string k = self.check_key(key) |
|
|
|
cdef ParamKeyType t = self.p.getKeyType(k) |
|
|
|
cdef ParamKeyType t = self.p.getKeyType(k) |
|
|
|
|
|
|
|
cdef optional[string] default = self.p.getKeyDefaultValue(k) |
|
|
|
cdef string val |
|
|
|
cdef string val |
|
|
|
with nogil: |
|
|
|
with nogil: |
|
|
|
val = self.p.get(k, block) |
|
|
|
val = self.p.get(k, block) |
|
|
|
|
|
|
|
|
|
|
|
default_val = self.get_default_value(k) if return_default else None |
|
|
|
default_val = (default.value() if default.has_value() else None) if return_default else None |
|
|
|
if val == b"": |
|
|
|
if val == b"": |
|
|
|
if block: |
|
|
|
if block: |
|
|
|
# If we got no value while running in blocked mode |
|
|
|
# If we got no value while running in blocked mode |
|
|
|
# it means we got an interrupt while waiting |
|
|
|
# it means we got an interrupt while waiting |
|
|
|
raise KeyboardInterrupt |
|
|
|
raise KeyboardInterrupt |
|
|
|
else: |
|
|
|
else: |
|
|
|
return self.cast(t, default_val, None) |
|
|
|
return self.cpp2python(t, default_val, None, key) |
|
|
|
return self.cast(t, val, default_val) |
|
|
|
return self.cpp2python(t, val, default_val, key) |
|
|
|
|
|
|
|
|
|
|
|
def get_bool(self, key, bool block=False): |
|
|
|
def get_bool(self, key, bool block=False): |
|
|
|
cdef string k = self.check_key(key) |
|
|
|
cdef string k = self.check_key(key) |
|
|
@ -121,6 +136,11 @@ cdef class Params: |
|
|
|
r = self.p.getBool(k, block) |
|
|
|
r = self.p.getBool(k, block) |
|
|
|
return r |
|
|
|
return r |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _put_cast(self, key, dat): |
|
|
|
|
|
|
|
cdef string k = self.check_key(key) |
|
|
|
|
|
|
|
cdef ParamKeyType t = self.p.getKeyType(k) |
|
|
|
|
|
|
|
return ensure_bytes(self.python2cpp(type(dat), t, dat, key)) |
|
|
|
|
|
|
|
|
|
|
|
def put(self, key, dat): |
|
|
|
def put(self, key, dat): |
|
|
|
""" |
|
|
|
""" |
|
|
|
Warning: This function blocks until the param is written to disk! |
|
|
|
Warning: This function blocks until the param is written to disk! |
|
|
@ -129,7 +149,7 @@ cdef class Params: |
|
|
|
in general try to avoid writing params as much as possible. |
|
|
|
in general try to avoid writing params as much as possible. |
|
|
|
""" |
|
|
|
""" |
|
|
|
cdef string k = self.check_key(key) |
|
|
|
cdef string k = self.check_key(key) |
|
|
|
cdef string dat_bytes = ensure_bytes(dat) |
|
|
|
cdef string dat_bytes = self._put_cast(key, dat) |
|
|
|
with nogil: |
|
|
|
with nogil: |
|
|
|
self.p.put(k, dat_bytes) |
|
|
|
self.p.put(k, dat_bytes) |
|
|
|
|
|
|
|
|
|
|
@ -140,7 +160,7 @@ cdef class Params: |
|
|
|
|
|
|
|
|
|
|
|
def put_nonblocking(self, key, dat): |
|
|
|
def put_nonblocking(self, key, dat): |
|
|
|
cdef string k = self.check_key(key) |
|
|
|
cdef string k = self.check_key(key) |
|
|
|
cdef string dat_bytes = ensure_bytes(dat) |
|
|
|
cdef string dat_bytes = self._put_cast(key, dat) |
|
|
|
with nogil: |
|
|
|
with nogil: |
|
|
|
self.p.putNonBlocking(k, dat_bytes) |
|
|
|
self.p.putNonBlocking(k, dat_bytes) |
|
|
|
|
|
|
|
|
|
|
@ -165,5 +185,7 @@ cdef class Params: |
|
|
|
return self.p.allKeys() |
|
|
|
return self.p.allKeys() |
|
|
|
|
|
|
|
|
|
|
|
def get_default_value(self, key): |
|
|
|
def get_default_value(self, key): |
|
|
|
cdef optional[string] default = self.p.getKeyDefaultValue(self.check_key(key)) |
|
|
|
cdef string k = self.check_key(key) |
|
|
|
return default.value() if default.has_value() else None |
|
|
|
cdef ParamKeyType t = self.p.getKeyType(k) |
|
|
|
|
|
|
|
cdef optional[string] default = self.p.getKeyDefaultValue(k) |
|
|
|
|
|
|
|
return self.cpp2python(t, default.value(), None, key) if default.has_value() else None |
|
|
|