diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d9bb1065b..feacb2ab1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,10 +7,7 @@ env: RUN: docker run -e PYTHONWARNINGS=error --shm-size 1G --name cereal cereal /bin/sh -c RUN_NAMED: docker run -e PYTHONWARNINGS=error --shm-size 1G --rm cereal /bin/sh -c CI_RUN: docker run -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID --rm cerealci /bin/bash -c - BUILD: | - docker pull $(grep -ioP '(?<=^from)\s+\S+' Dockerfile) || true - docker pull $DOCKER_REGISTRY/cereal:latest || true - docker build --cache-from $DOCKER_REGISTRY/cereal:latest -t cereal -f Dockerfile . + BUILD: docker buildx build --pull --load --cache-to type=inline --cache-from $DOCKER_REGISTRY/cereal:latest -t cereal -f Dockerfile . PYTHONWARNINGS: error jobs: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 68691b298..cebce111b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,11 +7,11 @@ repos: - id: check-executables-have-shebangs - id: check-shebang-scripts-are-executable - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.7.1 + rev: v1.9.0 hooks: - id: mypy - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.7 + rev: v0.3.3 hooks: - id: ruff - repo: local diff --git a/SConscript b/SConscript index 1a7d9f51c..d79942445 100644 --- a/SConscript +++ b/SConscript @@ -22,7 +22,7 @@ if GetOption('extras'): # TODO: remove non shared cereal and messaging cereal_objects = env.SharedObject([f'gen/cpp/{s}.c++' for s in schema_files]) -env.Library('cereal', cereal_objects) +cereal = env.Library('cereal', cereal_objects) env.SharedLibrary('cereal_shared', cereal_objects) # Build messaging @@ -39,14 +39,13 @@ messaging_objects = env.SharedObject([ 'messaging/socketmaster.cc', ]) -messaging_lib = env.Library('messaging', messaging_objects) +messaging = env.Library('messaging', messaging_objects) Depends('messaging/impl_zmq.cc', services_h) -env.Program('messaging/bridge', ['messaging/bridge.cc'], LIBS=[messaging_lib, 'zmq', common]) +env.Program('messaging/bridge', ['messaging/bridge.cc'], LIBS=[messaging, 'zmq', common]) Depends('messaging/bridge.cc', services_h) -envCython.Program('messaging/messaging_pyx.so', 'messaging/messaging_pyx.pyx', LIBS=envCython["LIBS"]+[messaging_lib, "zmq", common]) - +messaging_python = envCython.Program('messaging/messaging_pyx.so', 'messaging/messaging_pyx.pyx', LIBS=envCython["LIBS"]+[messaging, "zmq", common]) # Build Vision IPC vipc_sources = [ @@ -62,11 +61,11 @@ else: vipc_sources += ['visionipc/visionbuf_cl.cc'] vipc_objects = env.SharedObject(vipc_sources) -vipc = env.Library('visionipc', vipc_objects) +visionipc = env.Library('visionipc', vipc_objects) vipc_frameworks = [] -vipc_libs = envCython["LIBS"] + [vipc, messaging_lib, common, "zmq"] +vipc_libs = envCython["LIBS"] + [visionipc, messaging, common, "zmq"] if arch == "Darwin": vipc_frameworks.append('OpenCL') else: @@ -75,7 +74,10 @@ envCython.Program('visionipc/visionipc_pyx.so', 'visionipc/visionipc_pyx.pyx', LIBS=vipc_libs, FRAMEWORKS=vipc_frameworks) if GetOption('extras'): - env.Program('messaging/test_runner', ['messaging/test_runner.cc', 'messaging/msgq_tests.cc'], LIBS=[messaging_lib, common]) + env.Program('messaging/test_runner', ['messaging/test_runner.cc', 'messaging/msgq_tests.cc'], LIBS=[messaging, common]) env.Program('visionipc/test_runner', ['visionipc/test_runner.cc', 'visionipc/visionipc_tests.cc'], LIBS=['pthread'] + vipc_libs, FRAMEWORKS=vipc_frameworks) + + +Export('cereal', 'messaging', 'messaging_python', 'visionipc') \ No newline at end of file diff --git a/__init__.py b/__init__.py index 440f7c570..3844358f2 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,3 @@ -# pylint: skip-file import os import capnp from typing import TYPE_CHECKING diff --git a/car.capnp b/car.capnp index e6476fb8e..201d8ef3a 100644 --- a/car.capnp +++ b/car.capnp @@ -51,7 +51,6 @@ struct CarEvent @0x9b1657f34caf3ad3 { parkBrake @29; manualRestart @30; lowSpeedLockout @31; - plannerError @32; joystickDebug @34; steerTempUnavailableSilent @35; resumeRequired @36; @@ -142,6 +141,7 @@ struct CarEvent @0x9b1657f34caf3ad3 { startupFuzzyFingerprintDEPRECATED @97; noTargetDEPRECATED @25; brakeUnavailableDEPRECATED @2; + plannerErrorDEPRECATED @32; } } @@ -323,14 +323,12 @@ struct CarControl { # Actuator commands as computed by controlsd actuators @6 :Actuators; + # moved to CarOutput + actuatorsOutputDEPRECATED @10 :Actuators; + leftBlinker @15: Bool; rightBlinker @16: Bool; - # Any car specific rate limits or quirks applied by - # the CarController are reflected in actuatorsOutput - # and matches what is sent to the car - actuatorsOutput @10 :Actuators; - orientationNED @13 :List(Float32); angularVelocity @14 :List(Float32); @@ -380,6 +378,7 @@ struct CarControl { leftLaneVisible @7: Bool; rightLaneDepart @8: Bool; leftLaneDepart @9: Bool; + leadDistanceBars @10: Int8; # 1-3: 1 is closest, 3 is farthest. some ports may utilize 2-4 bars instead enum VisualAlert { # these are the choices from the Honda @@ -418,6 +417,13 @@ struct CarControl { pitchDEPRECATED @9 :Float32; } +struct CarOutput { + # Any car specific rate limits or quirks applied by + # the CarController are reflected in actuatorsOutput + # and matches what is sent to the car + actuatorsOutput @0 :CarControl.Actuators; +} + # ****** car param ****** struct CarParams { @@ -596,12 +602,15 @@ struct CarParams { body @27; hyundaiCanfd @28; volkswagenMqbEvo @29; + chryslerCusw @30; + psa @31; } enum SteerControlType { torque @0; angle @1; - curvature @2; + + curvatureDEPRECATED @2; } enum TransmissionType { diff --git a/log.capnp b/log.capnp index d5f147706..0135cab72 100644 --- a/log.capnp +++ b/log.capnp @@ -33,6 +33,7 @@ struct InitData { deviceType @3 :DeviceType; version @4 :Text; gitCommit @10 :Text; + gitCommitDate @21 :Text; gitBranch @11 :Text; gitRemote @13 :Text; @@ -56,6 +57,7 @@ struct InitData { tici @4; pc @5; tizi @6; + mici @7; } struct PandaInfo { @@ -130,8 +132,9 @@ struct InitData { struct FrameData { frameId @0 :UInt32; - encodeId @1 :UInt32; # DEPRECATED frameIdSensor @25 :UInt32; + requestId @28 :UInt32; + encodeId @1 :UInt32; frameType @7 :FrameType; @@ -248,7 +251,7 @@ struct SensorEventData { # android struct GpsLocation struct GpsLocationData { - # Contains GpsLocationFlags bits. + # Contains module-specific flags. flags @0 :UInt16; # Represents latitude in degrees. @@ -266,8 +269,8 @@ struct GpsLocationData { # Represents heading in degrees. bearingDeg @5 :Float32; - # Represents expected accuracy in meters. (presumably 1 sigma?) - accuracy @6 :Float32; + # Represents expected horizontal accuracy in meters. + horizontalAccuracy @6 :Float32; unixTimestampMillis @7 :Int64; @@ -285,6 +288,8 @@ struct GpsLocationData { # Represents velocity accuracy in m/s. (presumably 1 sigma?) speedAccuracy @12 :Float32; + hasFix @13 :Bool; + enum SensorSource { android @0; iOS @1; @@ -295,9 +300,33 @@ struct GpsLocationData { ublox @6; trimble @7; qcomdiag @8; + unicore @9; } } +enum Desire { + none @0; + turnLeft @1; + turnRight @2; + laneChangeLeft @3; + laneChangeRight @4; + keepLeft @5; + keepRight @6; +} + +enum LaneChangeState { + off @0; + preLaneChange @1; + laneChangeStarting @2; + laneChangeFinishing @3; +} + +enum LaneChangeDirection { + none @0; + left @1; + right @2; +} + struct CanData { address @0 :UInt32; busTime @1 :UInt16; @@ -306,6 +335,8 @@ struct CanData { } struct DeviceState @0xa4d8b5af2aa492eb { + deviceType @45 :InitData.DeviceType; + networkType @22 :NetworkType; networkInfo @31 :NetworkInfo; networkStrength @24 :NetworkStrength; @@ -332,7 +363,6 @@ struct DeviceState @0xa4d8b5af2aa492eb { cpuTempC @26 :List(Float32); gpuTempC @27 :List(Float32); memoryTempC @28 :Float32; - ambientTempC @30 :Float32; nvmeTempC @35 :List(Float32); modemTempC @36 :List(Float32); pmicTempC @39 :List(Float32); @@ -405,13 +435,13 @@ struct DeviceState @0xa4d8b5af2aa492eb { chargingErrorDEPRECATED @17 :Bool; chargingDisabledDEPRECATED @18 :Bool; usbOnlineDEPRECATED @12 :Bool; + ambientTempCDEPRECATED @30 :Float32; } struct PandaState @0xa7649e2575e4591e { ignitionLine @2 :Bool; rxBufferOverflow @7 :UInt32; txBufferOverflow @8 :UInt32; - gmlanSendErrs @9 :UInt32; pandaType @10 :PandaType; ignitionCan @13 :Bool; faultStatus @15 :FaultStatus; @@ -459,7 +489,7 @@ struct PandaState @0xa7649e2575e4591e { interruptRateCan2 @3; interruptRateCan3 @4; interruptRateTach @5; - interruptRateGmlan @6; + interruptRateGmlanDEPRECATED @6; interruptRateInterrupts @7; interruptRateSpiDma @8; interruptRateSpiCs @9; @@ -494,6 +524,7 @@ struct PandaState @0xa7649e2575e4591e { redPanda @7; redPandaV2 @8; tres @9; + cuatro @10; } enum HarnessStatus { @@ -544,6 +575,7 @@ struct PandaState @0xa7649e2575e4591e { gasInterceptorDetectedDEPRECATED @4 :Bool; startedSignalDetectedDEPRECATED @5 :Bool; hasGpsDEPRECATED @6 :Bool; + gmlanSendErrsDEPRECATED @9 :UInt32; fanSpeedRpmDEPRECATED @11 :UInt16; usbPowerModeDEPRECATED @12 :PeripheralState.UsbPowerModeDEPRECATED; safetyParamDEPRECATED @20 :Int16; @@ -655,6 +687,7 @@ struct ControlsState @0x97ff69c53601abf1 { active @36 :Bool; experimentalMode @64 :Bool; + personality @66 :LongitudinalPersonality; longControlState @30 :Car.CarControl.Actuators.LongControlState; vPid @2 :Float32; @@ -667,7 +700,6 @@ struct ControlsState @0x97ff69c53601abf1 { aTarget @35 :Float32; curvature @37 :Float32; # path curvature from vehicle model desiredCurvature @61 :Float32; # lag adjusted curvatures used by lateral controllers - desiredCurvatureRate @62 :Float32; forceDecel @51 :Bool; # UI alerts @@ -689,8 +721,8 @@ struct ControlsState @0x97ff69c53601abf1 { angleState @58 :LateralAngleState; debugState @59 :LateralDebugState; torqueState @60 :LateralTorqueState; - curvatureState @65 :LateralCurvatureState; + curvatureStateDEPRECATED @65 :LateralCurvatureState; lqrStateDEPRECATED @55 :LateralLQRState; } @@ -825,6 +857,7 @@ struct ControlsState @0x97ff69c53601abf1 { steerOverrideDEPRECATED @20 :Bool; steeringAngleDesiredDegDEPRECATED @29 :Float32; canMonoTimesDEPRECATED @21 :List(UInt64); + desiredCurvatureRateDEPRECATED @62 :Float32; } # All SI units and in device frame @@ -876,7 +909,8 @@ struct ModelDataV2 { locationMonoTime @24 :UInt64; # e2e lateral planner - lateralPlannerSolution @25: LateralPlannerSolution; + lateralPlannerSolutionDEPRECATED @25: LateralPlannerSolution; + action @26: Action; struct LeadDataV2 { prob @0 :Float32; # probability that car is your lead at time t @@ -914,6 +948,9 @@ struct ModelDataV2 { desireState @5 :List(Float32); disengagePredictions @6 :DisengagePredictions; hardBrakePredicted @7 :Bool; + laneChangeState @8 :LaneChangeState; + laneChangeDirection @9 :LaneChangeDirection; + # deprecated brakeDisengageProbDEPRECATED @2 :Float32; @@ -955,6 +992,9 @@ struct ModelDataV2 { yawRateStd @7 :List(Float32); } + struct Action { + desiredCurvature @0 :Float32; + } } struct EncodeIndex { @@ -1014,7 +1054,6 @@ struct LongitudinalPlan @0xe00b5b3eba12876c { jerks @34 :List(Float32); solverExecutionTime @35 :Float32; - personality @36 :LongitudinalPersonality; enum LongitudinalPlanSource { cruise @0; @@ -1052,6 +1091,7 @@ struct LongitudinalPlan @0xe00b5b3eba12876c { eventsDEPRECATED @13 :List(Car.CarEvent); gpsTrajectoryDEPRECATED @12 :GpsTrajectory; gpsPlannerActiveDEPRECATED @19 :Bool; + personalityDEPRECATED @36 :LongitudinalPersonality; struct GpsTrajectory { x @0 :List(Float32); @@ -1845,11 +1885,12 @@ struct QcomGnss @0xde94674b07ae51c1 { } struct Clocks { - bootTimeNanos @0 :UInt64; - monotonicNanos @1 :UInt64; - monotonicRawNanos @2 :UInt64; - wallTimeNanos @3 :UInt64; - modemUptimeMillis @4 :UInt64; + wallTimeNanos @3 :UInt64; # unix epoch time + + bootTimeNanosDEPRECATED @0 :UInt64; + monotonicNanosDEPRECATED @1 :UInt64; + monotonicRawNanosDEPRECATD @2 :UInt64; + modemUptimeMillisDEPRECATED @4 :UInt64; } struct LiveMpcData { @@ -2168,6 +2209,8 @@ struct EncodeData { data @1 :Data; header @2 :Data; unixTimestampNanos @3 :UInt64; + width @4 :UInt32; + height @5 :UInt32; } struct UserFlag { @@ -2214,8 +2257,8 @@ struct Event { liveCalibration @19 :LiveCalibrationData; carState @22 :Car.CarState; carControl @23 :Car.CarControl; + carOutput @127 :Car.CarOutput; longitudinalPlan @24 :LongitudinalPlan; - lateralPlan @64 :LateralPlan; uiPlan @106 :UiPlan; ubloxGnss @34 :UbloxGnss; ubloxRaw @39 :Data; @@ -2336,5 +2379,6 @@ struct Event { pandaStateDEPRECATED @12 :PandaState; driverStateDEPRECATED @59 :DriverStateDEPRECATED; sensorEventsDEPRECATED @11 :List(SensorEventData); + lateralPlanDEPRECATED @64 :LateralPlan; } } diff --git a/messaging/__init__.py b/messaging/__init__.py index abefb7df3..cfc64c65a 100644 --- a/messaging/__init__.py +++ b/messaging/__init__.py @@ -22,7 +22,6 @@ assert wait_for_one_event NO_TRAVERSAL_LIMIT = 2**64-1 -AVG_FREQ_HISTORY = 100 context = Context() @@ -117,14 +116,14 @@ def recv_sock(sock: SubSocket, wait: bool = False) -> Optional[capnp.lib.capnp._ while 1: if wait and dat is None: - rcv = sock.receive() + recv = sock.receive() else: - rcv = sock.receive(non_blocking=True) + recv = sock.receive(non_blocking=True) - if rcv is None: # Timeout hit + if recv is None: # Timeout hit break - dat = rcv + dat = recv if dat is not None: dat = log_from_bytes(dat) @@ -155,55 +154,76 @@ def recv_one_retry(sock: SubSocket) -> capnp.lib.capnp._DynamicStructReader: class SubMaster: - def __init__(self, services: List[str], poll: Optional[List[str]] = None, + def __init__(self, services: List[str], poll: Optional[str] = None, ignore_alive: Optional[List[str]] = None, ignore_avg_freq: Optional[List[str]] = None, - ignore_valid: Optional[List[str]] = None, addr: str = "127.0.0.1"): + ignore_valid: Optional[List[str]] = None, addr: str = "127.0.0.1", frequency: Optional[float] = None): self.frame = -1 + self.seen = {s: False for s in services} self.updated = {s: False for s in services} - self.rcv_time = {s: 0. for s in services} - self.rcv_frame = {s: 0 for s in services} + self.recv_time = {s: 0. for s in services} + self.recv_frame = {s: 0 for s in services} self.alive = {s: False for s in services} self.freq_ok = {s: False for s in services} - self.recv_dts: Dict[str, Deque[float]] = {s: deque(maxlen=AVG_FREQ_HISTORY) for s in services} + self.recv_dts: Dict[str, Deque[float]] = {} self.sock = {} - self.freq = {} self.data = {} self.valid = {} self.logMonoTime = {} + self.max_freq = {} + self.min_freq = {} + self.poller = Poller() - self.non_polled_services = [s for s in services if poll is not None and - len(poll) and s not in poll] + polled_services = set([poll, ] if poll is not None else services) + self.non_polled_services = set(services) - polled_services self.ignore_average_freq = [] if ignore_avg_freq is None else ignore_avg_freq self.ignore_alive = [] if ignore_alive is None else ignore_alive self.ignore_valid = [] if ignore_valid is None else ignore_valid - self.simulation = bool(int(os.getenv("SIMULATION", "0"))) + if bool(int(os.getenv("SIMULATION", "0"))): + self.ignore_alive = services + self.ignore_average_freq = services + + # if freq and poll aren't specified, assume the max to be conservative + assert frequency is None or poll is None, "Do not specify 'frequency' - frequency of the polled service will be used." + self.update_freq = frequency or max([SERVICE_LIST[s].frequency for s in polled_services]) for s in services: - if addr is not None: - p = self.poller if s not in self.non_polled_services else None - self.sock[s] = sub_sock(s, poller=p, addr=addr, conflate=True) - self.freq[s] = SERVICE_LIST[s].frequency + p = self.poller if s not in self.non_polled_services else None + self.sock[s] = sub_sock(s, poller=p, addr=addr, conflate=True) try: data = new_message(s) - except capnp.lib.capnp.KjException: # pylint: disable=c-extension-no-member + except capnp.lib.capnp.KjException: data = new_message(s, 0) # lists self.data[s] = getattr(data.as_reader(), s) self.logMonoTime[s] = 0 - # TODO: this should default to False - self.valid[s] = True + self.valid[s] = True # FIXME: this should default to False + + freq = max(min([SERVICE_LIST[s].frequency, self.update_freq]), 1.) + if s == poll: + max_freq = freq + min_freq = freq + else: + max_freq = min(freq, self.update_freq) + if SERVICE_LIST[s].frequency >= 2*self.update_freq: + min_freq = self.update_freq + elif self.update_freq >= 2*SERVICE_LIST[s].frequency: + min_freq = freq + else: + min_freq = min(freq, freq / 2.) + self.max_freq[s] = max_freq*1.2 + self.min_freq[s] = min_freq*0.8 + self.recv_dts[s] = deque(maxlen=int(10*freq)) def __getitem__(self, s: str) -> capnp.lib.capnp._DynamicStructReader: return self.data[s] - def _check_avg_freq(self, s): - return self.rcv_time[s] > 1e-5 and self.freq[s] > 1e-5 and (s not in self.non_polled_services) \ - and (s not in self.ignore_average_freq) + def _check_avg_freq(self, s: str) -> bool: + return SERVICE_LIST[s].frequency > 0.99 and (s not in self.ignore_average_freq) and (s not in self.ignore_alive) - def update(self, timeout: int = 1000) -> None: + def update(self, timeout: int = 100) -> None: msgs = [] for sock in self.poller.poll(timeout): msgs.append(recv_one_or_none(sock)) @@ -221,64 +241,57 @@ def update_msgs(self, cur_time: float, msgs: List[capnp.lib.capnp._DynamicStruct continue s = msg.which() + self.seen[s] = True self.updated[s] = True - if self._check_avg_freq(s): - self.recv_dts[s].append(cur_time - self.rcv_time[s]) - - self.rcv_time[s] = cur_time - self.rcv_frame[s] = self.frame + if self.recv_time[s] > 1e-5: + self.recv_dts[s].append(cur_time - self.recv_time[s]) + self.recv_time[s] = cur_time + self.recv_frame[s] = self.frame self.data[s] = getattr(msg, s) self.logMonoTime[s] = msg.logMonoTime self.valid[s] = msg.valid - if self.simulation: + for s in self.data: + if SERVICE_LIST[s].frequency > 1e-5: + # alive if delay is within 10x the expected frequency + self.alive[s] = (cur_time - self.recv_time[s]) < (10. / SERVICE_LIST[s].frequency) + + # check average frequency; slow to fall, quick to recover + dts = self.recv_dts[s] + assert dts.maxlen is not None + recent_dts = list(dts)[-int(dts.maxlen / 10):] + try: + avg_freq = 1 / (sum(dts) / len(dts)) + avg_freq_recent = 1 / (sum(recent_dts) / len(recent_dts)) + except ZeroDivisionError: + avg_freq = 0 + avg_freq_recent = 0 + + avg_freq_ok = self.min_freq[s] <= avg_freq <= self.max_freq[s] + recent_freq_ok = self.min_freq[s] <= avg_freq_recent <= self.max_freq[s] + self.freq_ok[s] = avg_freq_ok or recent_freq_ok + else: self.freq_ok[s] = True self.alive[s] = True - if not self.simulation: - for s in self.data: - # arbitrary small number to avoid float comparison. If freq is 0, we can skip the check - if self.freq[s] > 1e-5: - # alive if delay is within 10x the expected frequency - self.alive[s] = (cur_time - self.rcv_time[s]) < (10. / self.freq[s]) - - # TODO: check if update frequency is high enough to not drop messages - # freq_ok if average frequency is higher than 90% of expected frequency - if self._check_avg_freq(s): - if len(self.recv_dts[s]) > 0: - avg_dt = sum(self.recv_dts[s]) / len(self.recv_dts[s]) - expected_dt = 1 / (self.freq[s] * 0.90) - self.freq_ok[s] = (avg_dt < expected_dt) - else: - self.freq_ok[s] = False - else: - self.freq_ok[s] = True - else: - self.freq_ok[s] = True - self.alive[s] = True - - def all_alive(self, service_list=None) -> bool: - if service_list is None: # check all - service_list = self.alive.keys() + def all_alive(self, service_list: Optional[List[str]] = None) -> bool: + if service_list is None: + service_list = list(self.sock.keys()) return all(self.alive[s] for s in service_list if s not in self.ignore_alive) - def all_freq_ok(self, service_list=None) -> bool: - if service_list is None: # check all - service_list = self.alive.keys() - return all(self.freq_ok[s] for s in service_list if s not in self.ignore_alive) + def all_freq_ok(self, service_list: Optional[List[str]] = None) -> bool: + if service_list is None: + service_list = list(self.sock.keys()) + return all(self.freq_ok[s] for s in service_list if self._check_avg_freq(s)) - def all_valid(self, service_list=None) -> bool: - if service_list is None: # check all - service_list = self.valid.keys() + def all_valid(self, service_list: Optional[List[str]] = None) -> bool: + if service_list is None: + service_list = list(self.sock.keys()) return all(self.valid[s] for s in service_list if s not in self.ignore_valid) - def all_checks(self, service_list=None) -> bool: - if service_list is None: # check all - service_list = self.alive.keys() - return self.all_alive(service_list=service_list) \ - and self.all_freq_ok(service_list=service_list) \ - and self.all_valid(service_list=service_list) + def all_checks(self, service_list: Optional[List[str]] = None) -> bool: + return self.all_alive(service_list) and self.all_freq_ok(service_list) and self.all_valid(service_list) class PubMaster: diff --git a/messaging/demo.py b/messaging/demo.py index f6aca5d17..e4850e32b 100644 --- a/messaging/demo.py +++ b/messaging/demo.py @@ -1,6 +1,6 @@ import time -from messaging_pyx import Context, Poller, SubSocket, PubSocket # pylint: disable=no-name-in-module, import-error +from messaging_pyx import Context, Poller, SubSocket, PubSocket MSGS = 1e5 diff --git a/messaging/stress.py b/messaging/stress.py index 388477c7b..1a27e520e 100644 --- a/messaging/stress.py +++ b/messaging/stress.py @@ -1,4 +1,4 @@ -from messaging_pyx import Context, SubSocket, PubSocket # pylint: disable=no-name-in-module, import-error +from messaging_pyx import Context, SubSocket, PubSocket if __name__ == "__main__": c = Context() diff --git a/messaging/tests/test_pub_sub_master.py b/messaging/tests/test_pub_sub_master.py index db794025d..81a1cf2d5 100755 --- a/messaging/tests/test_pub_sub_master.py +++ b/messaging/tests/test_pub_sub_master.py @@ -19,8 +19,8 @@ def setUp(self): def test_init(self): sm = messaging.SubMaster(events) - for p in [sm.updated, sm.rcv_time, sm.rcv_frame, sm.alive, - sm.sock, sm.freq, sm.data, sm.logMonoTime, sm.valid]: + for p in [sm.updated, sm.recv_time, sm.recv_frame, sm.alive, + sm.sock, sm.data, sm.logMonoTime, sm.valid]: self.assertEqual(len(cast(Sized, p)), len(events)) def test_init_state(self): @@ -29,12 +29,12 @@ def test_init_state(self): self.assertEqual(sm.frame, -1) self.assertFalse(any(sm.updated.values())) self.assertFalse(any(sm.alive.values())) - self.assertTrue(all(t == 0. for t in sm.rcv_time.values())) - self.assertTrue(all(f == 0 for f in sm.rcv_frame.values())) + self.assertTrue(all(t == 0. for t in sm.recv_time.values())) + self.assertTrue(all(f == 0 for f in sm.recv_frame.values())) self.assertTrue(all(t == 0 for t in sm.logMonoTime.values())) - for p in [sm.updated, sm.rcv_time, sm.rcv_frame, sm.alive, - sm.sock, sm.freq, sm.data, sm.logMonoTime, sm.valid]: + for p in [sm.updated, sm.recv_time, sm.recv_frame, sm.alive, + sm.sock, sm.data, sm.logMonoTime, sm.valid]: self.assertEqual(len(cast(Sized, p)), len(socks)) def test_getitem(self): @@ -74,6 +74,28 @@ def test_update_timeout(self): self.assertLess(t, 5) self.assertFalse(any(sm.updated.values())) + def test_avg_frequency_checks(self): + for poll in (True, False): + sm = messaging.SubMaster(["modelV2", "carParams", "carState", "cameraOdometry", "liveCalibration"], + poll=("modelV2" if poll else None), + frequency=(20. if not poll else None)) + + checks = { + "carState": (20, 20), + "modelV2": (20, 20 if poll else 10), + "cameraOdometry": (20, 10), + "liveCalibration": (4, 4), + "carParams": (None, None), + } + + for service, (max_freq, min_freq) in checks.items(): + if max_freq is not None: + assert sm._check_avg_freq(service) + assert sm.max_freq[service] == max_freq*1.2 + assert sm.min_freq[service] == min_freq*0.8 + else: + assert not sm._check_avg_freq(service) + def test_alive(self): pass diff --git a/pyproject.toml b/pyproject.toml index 45fbd4efa..73928614e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,11 @@ # https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml [tool.ruff] -select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF100", "A"] -ignore = ["W292", "E741", "E402", "C408", "ISC003"] +lint.select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF100", "A"] +lint.ignore = ["W292", "E741", "E402", "C408", "ISC003"] +lint.flake8-implicit-str-concat.allow-multiline=false + line-length = 160 target-version="py311" -flake8-implicit-str-concat.allow-multiline=false [mypy.tool] # third-party packages diff --git a/services.py b/services.py index 6436e3181..77ced2b3b 100755 --- a/services.py +++ b/services.py @@ -46,6 +46,7 @@ def __init__(self, port: int, should_log: bool, frequency: float, decimation: Op "androidLog": (True, 0.), "carState": (True, 100., 10), "carControl": (True, 100., 10), + "carOutput": (True, 100., 10), "longitudinalPlan": (True, 20., 5), "procLog": (True, 0.5, 15), "gpsLocationExternal": (True, 10., 10), @@ -53,12 +54,11 @@ def __init__(self, port: int, should_log: bool, frequency: float, decimation: Op "ubloxGnss": (True, 10.), "qcomGnss": (True, 2.), "gnssMeasurements": (True, 10., 10), - "clocks": (True, 1., 1), + "clocks": (True, 0.1, 1), "ubloxRaw": (True, 20.), "liveLocationKalman": (True, 20., 5), "liveParameters": (True, 20., 5), "cameraOdometry": (True, 20., 5), - "lateralPlan": (True, 20., 5), "thumbnail": (True, 0.2, 1), "onroadEvents": (True, 1., 1), "carParams": (True, 0.02, 1),