#pragma once #include #include #include #include "track_core/model.hpp" #include "track_core/pid_program.hpp" namespace track_core { struct TrackPidBandSnapshot { std::uint8_t band_id{}; std::uint8_t heart_rate{}; std::uint32_t heart_rate_sample_seq{}; bool has_heart_rate{}; bool hr_is_fresh{}; std::uint16_t step_count{}; bool has_step_count{}; bool band_is_active{}; }; class PidHrRuntime { public: using time_point = clock::time_point; PidHrRuntime(); explicit PidHrRuntime(TrackPidConfig config); void set_pid_config(TrackPidConfig config); void update_target_hr_bpm(std::uint8_t target_hr_bpm); [[nodiscard]] expected apply_pid_runtime_command( const TrackPidRuntimeCommand &command, const TrackConfig *track_config); [[nodiscard]] const TrackPidConfig &pid_config() const; void start(time_point now); void stop(); void tick(const TrackConfig *track_config, const TrackPidBandSnapshot &band, time_point now); [[nodiscard]] TrackReport state_report(time_point now) const; [[nodiscard]] TrackInfo info() const; [[nodiscard]] TrackPidStatus pid_status(const TrackPidBandSnapshot &band, time_point now) const; private: static constexpr std::uint8_t magic_pid_track_id = 0; [[nodiscard]] float effective_speed_m_s(float base_speed_m_s, const TrackConfig *track_config) const; TrackPidConfig config_{TrackPidConfig::default_config()}; bool running_{false}; Color color_{Color::white()}; time_point start_timestamp_{}; time_point last_tick_timestamp_{}; std::optional last_consumed_hr_sample_seq_; time_point last_consumed_hr_sample_time_{}; float mileage_m_{0.0F}; float base_speed_m_s_{0.0F}; float effective_speed_m_s_{0.0F}; std::unique_ptr program_state_; }; } // namespace track_core