feat(track-core): add portable training runtimes
Move scheme and PID training runtime behavior into the pure track_core layer and expose render sinks for injected strip application. Add ESP compatibility adapters, Python bindings/test scaffolding, and in-memory render support so app_track_bt can consume core render and runtime logic without duplicating it. Cover circular/linear rendering boundaries, all scheme runtime types, scheme render_to parity, PID sample de-duplication, speed suppression, and live tuning in track-core tests.
This commit is contained in:
@@ -46,4 +46,7 @@ private:
|
||||
[[nodiscard]]
|
||||
TrackError apply_render_plan(MemoryStrip &strip, const TrackRenderPlan &plan);
|
||||
|
||||
[[nodiscard]]
|
||||
TrackRenderSink make_memory_strip_sink(MemoryStrip &strip);
|
||||
|
||||
} // namespace track_core
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#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<unit, TrackError> 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<std::uint32_t> 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<TrackPidProgramState> program_state_;
|
||||
};
|
||||
|
||||
} // namespace track_core
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "track_core/model.hpp"
|
||||
@@ -27,10 +28,34 @@ struct TrackRenderPlan {
|
||||
std::size_t span_count{};
|
||||
};
|
||||
|
||||
struct TrackRenderSink {
|
||||
using ClearFn = TrackError (*)(void *context);
|
||||
using FillFn = TrackError (*)(
|
||||
void *context,
|
||||
std::uint16_t start_led,
|
||||
std::uint16_t led_count,
|
||||
Color color);
|
||||
using ShowFn = TrackError (*)(void *context);
|
||||
|
||||
void *context{};
|
||||
ClearFn clear{};
|
||||
FillFn fill{};
|
||||
ShowFn show{};
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
TrackRenderPlan make_track_render_plan(
|
||||
const TrackConfig &config,
|
||||
const TrackInfo &info,
|
||||
const TrackReport &report);
|
||||
|
||||
[[nodiscard]]
|
||||
TrackError clear_render_sink(TrackRenderSink sink);
|
||||
|
||||
[[nodiscard]]
|
||||
TrackError apply_render_plan(TrackRenderSink sink, const TrackRenderPlan &plan);
|
||||
|
||||
[[nodiscard]]
|
||||
TrackError show_render_sink(TrackRenderSink sink);
|
||||
|
||||
} // namespace track_core
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "track_core/model.hpp"
|
||||
#include "track_core/render.hpp"
|
||||
#include "track_core/scheme_decoder.hpp"
|
||||
|
||||
namespace track_core {
|
||||
|
||||
struct SchemeTrackState {
|
||||
bool is_running{};
|
||||
std::size_t primary_segment_index{};
|
||||
std::size_t sub_segment_index{};
|
||||
float mileage_m{};
|
||||
float loop_mileage_m{};
|
||||
float speed_m_s{};
|
||||
float elapsed_s{};
|
||||
};
|
||||
|
||||
struct SchemeTrackRuntime {
|
||||
DecodedScheme scheme;
|
||||
SchemeTrackState state;
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
DecodedScheme make_speed_mileage_scheme(
|
||||
std::uint8_t id,
|
||||
Color color,
|
||||
AccelerationProfile acceleration_profile,
|
||||
std::vector<SMSegment> segments);
|
||||
|
||||
[[nodiscard]]
|
||||
DecodedScheme make_mileage_time_scheme(
|
||||
std::uint8_t id,
|
||||
Color color,
|
||||
AccelerationProfile acceleration_profile,
|
||||
std::vector<MTSegment> segments);
|
||||
|
||||
[[nodiscard]]
|
||||
DecodedScheme make_speed_time_scheme(
|
||||
std::uint8_t id,
|
||||
Color color,
|
||||
AccelerationProfile acceleration_profile,
|
||||
std::vector<STSegment> segments);
|
||||
|
||||
[[nodiscard]]
|
||||
DecodedScheme make_repeated_speed_mileage_time_scheme(
|
||||
std::uint8_t id,
|
||||
Color color,
|
||||
AccelerationProfile acceleration_profile,
|
||||
std::vector<RepeatedSMSegment> segments);
|
||||
|
||||
[[nodiscard]]
|
||||
expected<SchemeTrackRuntime, TrackError> make_scheme_track_runtime(DecodedScheme scheme);
|
||||
|
||||
[[nodiscard]]
|
||||
SchemeTrackRuntime start_scheme_track(SchemeTrackRuntime runtime);
|
||||
|
||||
[[nodiscard]]
|
||||
SchemeTrackRuntime stop_scheme_track(SchemeTrackRuntime runtime);
|
||||
|
||||
[[nodiscard]]
|
||||
SchemeTrackRuntime tick_scheme_track(
|
||||
const TrackConfig &config,
|
||||
SchemeTrackRuntime runtime,
|
||||
float delta_s);
|
||||
|
||||
[[nodiscard]]
|
||||
TrackInfo scheme_track_info(const SchemeTrackRuntime &runtime);
|
||||
|
||||
[[nodiscard]]
|
||||
TrackReport scheme_track_report(const SchemeTrackRuntime &runtime);
|
||||
|
||||
class SchemeTrainingRuntime {
|
||||
public:
|
||||
[[nodiscard]]
|
||||
bool has_program() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
bool all_stopped() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
expected<unit, TrackError> add_scheme(DecodedScheme scheme);
|
||||
|
||||
void clear();
|
||||
void start();
|
||||
void stop();
|
||||
void tick(const TrackConfig &config, float delta_s);
|
||||
|
||||
[[nodiscard]]
|
||||
TrackStateReportCollection state_collection() const;
|
||||
|
||||
[[nodiscard]]
|
||||
TrackSchemeMgrRead scheme_status() const;
|
||||
|
||||
[[nodiscard]]
|
||||
expected<unit, TrackError> render_to(const TrackConfig &config, TrackRenderSink sink) const;
|
||||
|
||||
[[nodiscard]]
|
||||
expected<std::vector<Color>, TrackError> render_pixels(const TrackConfig &config) const;
|
||||
|
||||
private:
|
||||
std::vector<SchemeTrackRuntime> tracks_;
|
||||
};
|
||||
|
||||
} // namespace track_core
|
||||
Reference in New Issue
Block a user