IQ.Pilot Release Commit @ 83fbdd3
This commit is contained in:
@@ -270,10 +270,14 @@ class Car:
|
|||||||
return tuple(attr for attr in ("gasfactor", "windfactor") if hasattr(self.CI.CC, attr))
|
return tuple(attr for attr in ("gasfactor", "windfactor") if hasattr(self.CI.CC, attr))
|
||||||
|
|
||||||
def _stored_learned_factors(self) -> dict:
|
def _stored_learned_factors(self) -> dict:
|
||||||
try:
|
# JSON-typed params come back already parsed from params_pyx; only the pure-python fallback
|
||||||
stored = json.loads(self.params.get("IQLongLearnedFactors") or b"{}")
|
# returns raw bytes
|
||||||
except ValueError:
|
stored = self.params.get("IQLongLearnedFactors")
|
||||||
stored = {}
|
if isinstance(stored, bytes | str):
|
||||||
|
try:
|
||||||
|
stored = json.loads(stored)
|
||||||
|
except ValueError:
|
||||||
|
stored = None
|
||||||
return stored if isinstance(stored, dict) else {}
|
return stored if isinstance(stored, dict) else {}
|
||||||
|
|
||||||
def _seed_learned_factors(self):
|
def _seed_learned_factors(self):
|
||||||
@@ -292,14 +296,19 @@ class Car:
|
|||||||
return
|
return
|
||||||
stored = self._stored_learned_factors()
|
stored = self._stored_learned_factors()
|
||||||
stored[str(self.CP.carFingerprint)] = {attr: float(getattr(self.CI.CC, attr)) for attr in attrs}
|
stored[str(self.CP.carFingerprint)] = {attr: float(getattr(self.CI.CC, attr)) for attr in attrs}
|
||||||
self.params.put_nonblocking("IQLongLearnedFactors", json.dumps(stored))
|
# JSON-typed params take the dict itself; params_pyx serializes and rejects pre-dumped strings
|
||||||
|
self.params.put_nonblocking("IQLongLearnedFactors", stored)
|
||||||
|
|
||||||
def state_publish(self, CS: car.CarState, CS_IQ: custom.IQCarState, RD: structs.RadarDataT | None):
|
def state_publish(self, CS: car.CarState, CS_IQ: custom.IQCarState, RD: structs.RadarDataT | None):
|
||||||
"""carState and carParams publish loop"""
|
"""carState and carParams publish loop"""
|
||||||
|
|
||||||
# persist live-learned longitudinal factors so they survive across drives
|
# persist live-learned longitudinal factors so they survive across drives; card authors the
|
||||||
|
# car's CAN stream, so a persistence failure must never take it down mid-drive
|
||||||
if self.sm.frame > 0 and self.sm.frame % int(60. / DT_CTRL) == 0:
|
if self.sm.frame > 0 and self.sm.frame % int(60. / DT_CTRL) == 0:
|
||||||
self._save_learned_factors()
|
try:
|
||||||
|
self._save_learned_factors()
|
||||||
|
except Exception:
|
||||||
|
cloudlog.exception("failed to persist learned longitudinal factors")
|
||||||
|
|
||||||
# carParams - logged every 50 seconds (> 1 per segment)
|
# carParams - logged every 50 seconds (> 1 per segment)
|
||||||
if self.sm.frame % int(50. / DT_CTRL) == 0:
|
if self.sm.frame % int(50. / DT_CTRL) == 0:
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class TestLearnedFactorPersistence:
|
|||||||
|
|
||||||
other.CI.CC.gasfactor = 0.5
|
other.CI.CC.gasfactor = 0.5
|
||||||
other._save_learned_factors()
|
other._save_learned_factors()
|
||||||
stored = json.loads(other.params.values["IQLongLearnedFactors"])
|
stored = other.params.values["IQLongLearnedFactors"]
|
||||||
assert stored["HONDA_CRV_6G"]["gasfactor"] == 2.0
|
assert stored["HONDA_CRV_6G"]["gasfactor"] == 2.0
|
||||||
assert stored["HONDA_CIVIC_BOSCH"]["gasfactor"] == 0.5
|
assert stored["HONDA_CIVIC_BOSCH"]["gasfactor"] == 0.5
|
||||||
|
|
||||||
@@ -80,3 +80,40 @@ class TestLearnedFactorPersistence:
|
|||||||
car._seed_learned_factors()
|
car._seed_learned_factors()
|
||||||
car._save_learned_factors()
|
car._save_learned_factors()
|
||||||
assert "IQLongLearnedFactors" not in car.params.values
|
assert "IQLongLearnedFactors" not in car.params.values
|
||||||
|
|
||||||
|
|
||||||
|
class TestLearnedFactorRealParams:
|
||||||
|
"""Round-trips through the actual params store so the pyx JSON type marshaling is exercised:
|
||||||
|
JSON params take dicts on put and come back parsed on get."""
|
||||||
|
|
||||||
|
def make_real_car(self, tmp_path, controller):
|
||||||
|
from iqpilot.common.params import Params
|
||||||
|
car = object.__new__(Car)
|
||||||
|
car.params = Params(str(tmp_path))
|
||||||
|
car.CI = SimpleNamespace(CC=controller)
|
||||||
|
car.CP = SimpleNamespace(carFingerprint="HONDA_CIVIC_2022")
|
||||||
|
return car
|
||||||
|
|
||||||
|
def test_save_then_seed_through_real_params(self, tmp_path):
|
||||||
|
import time
|
||||||
|
car = self.make_real_car(tmp_path, HondaLikeController())
|
||||||
|
car.CI.CC.gasfactor = 1.31
|
||||||
|
car.CI.CC.windfactor = 0.88
|
||||||
|
car._save_learned_factors()
|
||||||
|
time.sleep(0.3)
|
||||||
|
|
||||||
|
fresh = self.make_real_car(tmp_path, HondaLikeController())
|
||||||
|
fresh._seed_learned_factors()
|
||||||
|
assert fresh.CI.CC.gasfactor == 1.31
|
||||||
|
assert fresh.CI.CC.windfactor == 0.88
|
||||||
|
|
||||||
|
def test_repeated_saves_through_real_params(self, tmp_path):
|
||||||
|
import time
|
||||||
|
car = self.make_real_car(tmp_path, HondaLikeController())
|
||||||
|
for value in (1.1, 1.2, 1.3):
|
||||||
|
car.CI.CC.gasfactor = value
|
||||||
|
car._save_learned_factors()
|
||||||
|
time.sleep(0.3)
|
||||||
|
fresh = self.make_real_car(tmp_path, HondaLikeController())
|
||||||
|
fresh._seed_learned_factors()
|
||||||
|
assert fresh.CI.CC.gasfactor == 1.3
|
||||||
|
|||||||
Reference in New Issue
Block a user