      if max(cpus, default=0) > 95 and not SIMULATION:
        self.events.add(EventName.highCpuUsage)
  
      # Alert if fan isn't spinning for 5 seconds
      if self.sm['pandaState'].pandaType in [PandaType.uno, PandaType.dos]:
        if self.sm['pandaState'].fanSpeedRpm == 0 and self.sm['deviceState'].fanSpeedPercentDesired > 50:
          if (self.sm.frame - self.last_functional_fan_frame) * DT_CTRL > 5.0:
            self.events.add(EventName.fanMalfunction)
        else:
          self.last_functional_fan_frame = self.sm.frame
  
 
</s> remove     // Write to rtc once per minute when no ignition present
    if ((panda->has_rtc) && !ignition && (no_ignition_cnt % 120 == 1)) {
      // Write time to RTC if it looks reasonable
      setenv("TZ","UTC",1);
      struct tm sys_time = util::get_time();

      if (util::time_valid(sys_time)) {
        struct tm rtc_time = panda->get_rtc();
        double seconds = difftime(mktime(&rtc_time), mktime(&sys_time));

        if (std::abs(seconds) > 1.1) {
          panda->set_rtc(sys_time);
          LOGW("Updating panda RTC. dt = %.2f System: %s RTC: %s",
               seconds, get_time_str(sys_time).c_str(), get_time_str(rtc_time).c_str());
        }
      }
    }

 </s> add  </s> remove     // set safety mode to NO_OUTPUT when car is off. ELM327 is an alternative if we want to leverage athenad/connect
    if (!ignition && (pandaState.safety_model != (uint8_t)(cereal::CarParams::SafetyModel::NO_OUTPUT))) {
      panda->set_safety_model(cereal::CarParams::SafetyModel::NO_OUTPUT);
 </s> add void send_peripheral_state(PubMaster *pm, Panda *panda) {
  health_t pandaState = panda->get_state();

  // build msg
  MessageBuilder msg;
  auto evt = msg.initEvent();
  evt.setValid(panda->comms_healthy);

  auto ps = evt.initPeripheralState();
  ps.setPandaType(panda->hw_type);

  if (Hardware::TICI()) {
    double read_time = millis_since_boot();
    ps.setVoltage(std::atoi(util::read_file("/sys/class/hwmon/hwmon1/in1_input").c_str()));
    ps.setCurrent(std::atoi(util::read_file("/sys/class/hwmon/hwmon1/curr1_input").c_str()));
    read_time = millis_since_boot() - read_time;
    if (read_time > 50) {
      LOGW("reading hwmon took %lfms", read_time); </s> remove     uint16_t fan_speed_rpm = panda->get_fan_speed();

    // build msg
    MessageBuilder msg;
    auto evt = msg.initEvent();
    evt.setValid(panda->comms_healthy);

    auto ps = evt.initPandaState();
    ps.setUptime(pandaState.uptime);

    if (Hardware::TICI()) {
      double read_time = millis_since_boot();
      ps.setVoltage(std::atoi(util::read_file("/sys/class/hwmon/hwmon1/in1_input").c_str()));
      ps.setCurrent(std::atoi(util::read_file("/sys/class/hwmon/hwmon1/curr1_input").c_str()));
      read_time = millis_since_boot() - read_time;
      if (read_time > 50) {
        LOGW("reading hwmon took %lfms", read_time);
      }
    } else {
      ps.setVoltage(pandaState.voltage);
      ps.setCurrent(pandaState.current);
    }
 </s> add  </s> remove     if (spoofing_started) {
      pandaState.ignition_line = 1;
    }
 </s> add   // Make sure CAN buses are live: safety_setter_thread does not work if Panda CAN are silent and there is only one other CAN node
  if (pandaState.safety_model == (uint8_t)(cereal::CarParams::SafetyModel::SILENT)) {
    panda->set_safety_model(cereal::CarParams::SafetyModel::NO_OUTPUT);
  } </s> remove     // Make sure CAN buses are live: safety_setter_thread does not work if Panda CAN are silent and there is only one other CAN node
    if (pandaState.safety_model == (uint8_t)(cereal::CarParams::SafetyModel::SILENT)) {
      panda->set_safety_model(cereal::CarParams::SafetyModel::NO_OUTPUT);
    }
 </s> add   bool ignition = ((pandaState.ignition_line != 0) || (pandaState.ignition_can != 0));
    if self.sm['peripheralState'].pandaType in [PandaType.uno, PandaType.dos]:
      if self.sm['peripheralState'].fanSpeedRpm == 0 and self.sm['deviceState'].fanSpeedPercentDesired > 50:
      if max(cpus, default=0) > 95 and not SIMULATION:
        self.events.add(EventName.highCpuUsage)
  
      # Alert if fan isn't spinning for 5 seconds
    if self.sm['peripheralState'].pandaType in [PandaType.uno, PandaType.dos]:
      if self.sm['peripheralState'].fanSpeedRpm == 0 and self.sm['deviceState'].fanSpeedPercentDesired > 50:
    if self.sm['peripheralState'].pandaType in [PandaType.uno, PandaType.dos]:
      if self.sm['peripheralState'].fanSpeedRpm == 0 and self.sm['deviceState'].fanSpeedPercentDesired > 50:
          if (self.sm.frame - self.last_functional_fan_frame) * DT_CTRL > 5.0:
            self.events.add(EventName.fanMalfunction)
        else:
          self.last_functional_fan_frame = self.sm.frame
  
keep keep keep keep replace replace keep keep keep keep keep
 Split pandaState into peripheralState and pandaState (#22385)

* publish peripheralState from boardd

* refactor consumers

* rename thread

* peripheralState has panda type too

* add to process replay

* fix device build

* properly remove hardware unsupported alert

* latest peripheralState

* remove pandaState from thermal_monitor

* put that back

* add back harness check

* fix cloudlog

* needs pandaState

* Update selfdrive/thermald/tests/test_power_monitoring.py

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>

* dont conflate

* Update selfdrive/thermald/tests/test_power_monitoring.py

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>

* pigeon is peripheral

* cleanup

* less global

* comment

* move rtc to peripheral_control_thread

* better diff?

* whitespace

* get msg

* bump cereal

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> 
selfdrive/controls/controlsd.py