refactor(track-core): remove app-facing track adapters

Keep track-core focused on portable track state, scheme runtime, PID runtime, memory strip, and render planning.

Remove the ESP-IDF/nanopb Track adapter headers and sources from the submodule so app/protobuf ownership can live in a separate firmware component.

The standalone CMake and Python binding surface remains unchanged, including the emulator-facing APIs.
This commit is contained in:
2026-06-18 17:53:14 +08:00
parent 28327ed875
commit cd5c2f64e0
8 changed files with 7 additions and 1710 deletions
-121
View File
@@ -1,121 +0,0 @@
#include "app_track_decoder.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
namespace app::track {
namespace {
track_core::Color to_core(const Color &color) {
return {
color.inner.r,
color.inner.g,
color.inner.b,
};
}
error_t from_core(track_core::TrackError error) {
switch (error) {
case track_core::TrackError::ok:
return ESP_OK;
case track_core::TrackError::invalid_arg:
return ESP_ERR_INVALID_ARG;
case track_core::TrackError::invalid_size:
return ESP_ERR_INVALID_SIZE;
case track_core::TrackError::invalid_state:
return ESP_ERR_INVALID_STATE;
case track_core::TrackError::not_supported:
return ESP_ERR_NOT_SUPPORTED;
case track_core::TrackError::range:
return ESP_ERR_INVALID_SIZE;
}
return ESP_FAIL;
}
} // namespace
TrackSchemeDecoder TrackSchemeDecoder::from_proto(const proto_type &proto) {
TrackSchemeDecoder scheme;
scheme.id = proto.id;
if (proto.has_color) {
scheme.color = Color::from_proto(proto.color);
} else {
scheme.color = Color::white();
}
auto data = std::span<const uint8_t>(proto.data.bytes, proto.data.size);
std::ranges::copy(data, std::back_inserter(scheme.binary));
return scheme;
}
TrackSchemeDecoder::proto_type TrackSchemeDecoder::to_proto() const {
proto_type proto = track_app_TrackScheme_init_default;
proto.id = id;
proto.has_color = true;
proto.color = color.to_proto();
const auto data = std::span<uint8_t>(proto.data.bytes);
const auto count = std::min(data.size(), binary.size());
std::ranges::copy(binary.begin(), binary.begin() + static_cast<std::ptrdiff_t>(count), data.begin());
proto.data.size = count;
return proto;
}
expected<track_core::DecodedScheme, error_t> TrackSchemeDecoder::decode_core() const {
using ue = unexpected<error_t>;
if (binary.empty()) {
return ue{ESP_ERR_INVALID_ARG};
}
const auto decoded = track_core::decode_scheme(id, to_core(color), binary);
if (!decoded) {
return ue{from_core(decoded.error())};
}
return *decoded;
}
TrackSchemeMgr::Add TrackSchemeMgr::Add::from_proto(const proto_type &proto) {
Add add;
assert(proto.has_scheme);
add.scheme_decoder = TrackSchemeDecoder::from_proto(proto.scheme);
return add;
}
TrackSchemeMgr::Add::proto_type TrackSchemeMgr::Add::to_proto() const {
proto_type proto = track_app_TrackSchemeMgrAdd_init_default;
proto.has_scheme = true;
proto.scheme = scheme_decoder.to_proto();
return proto;
}
TrackSchemeMgr TrackSchemeMgr::from_proto(const proto_type &proto) {
TrackSchemeMgr mgmt;
switch (proto.which_msg) {
case track_app_TrackSchemeMgr_add_tag:
mgmt.choice = Add::from_proto(proto.msg.add);
break;
case track_app_TrackSchemeMgr_clear_tag:
mgmt.choice = Clear{};
break;
default:
break;
}
return mgmt;
}
TrackSchemeMgr::proto_type TrackSchemeMgr::to_proto() const {
proto_type proto = track_app_TrackSchemeMgr_init_default;
std::visit(app::utils::overloads{
[](Unknown) {},
[&](const Add &add) {
proto.which_msg = track_app_TrackSchemeMgr_add_tag;
proto.msg.add = add.to_proto();
},
[&](Clear) {
proto.which_msg = track_app_TrackSchemeMgr_clear_tag;
proto.msg.clear = track_app_TrackSchemeMgrClear_init_default;
}},
choice);
return proto;
}
} // namespace app::track
-177
View File
@@ -1,177 +0,0 @@
#include "app_track_drawer.hpp"
#include <cassert>
#include <cstddef>
#include "track_core/render.hpp"
namespace {
track_core::Color to_core(const app::track::Color &color) {
return {
color.inner.r,
color.inner.g,
color.inner.b,
};
}
app::track::Color from_core(const track_core::Color &color) {
return {
color.r,
color.g,
color.b,
};
}
track_core::TrackDrawKind to_core(track_app_TrackDrawKind draw_kind) {
switch (draw_kind) {
case track_app_TrackDrawKind_CIRCULAR:
return track_core::TrackDrawKind::circular;
case track_app_TrackDrawKind_LINEAR:
return track_core::TrackDrawKind::linear;
}
return track_core::TrackDrawKind::circular;
}
track_core::SchemeKind to_core(app::track::SchemeKind kind) {
switch (kind) {
case track_app_TrackSchemeKind_SPEED_INPUT_MILEAGE_SEGMENTED_TIME_FREE:
return track_core::SchemeKind::speed_input_mileage_segmented_time_free;
case track_app_TrackSchemeKind_MILEAGE_INPUT_TIME_SEGMENTED_SPEED_FREE:
return track_core::SchemeKind::mileage_input_time_segmented_speed_free;
case track_app_TrackSchemeKind_SPEED_INPUT_TIME_SEGMENTED_MILEAGE_FREE:
return track_core::SchemeKind::speed_input_time_segmented_mileage_free;
case track_app_TrackSchemeKind_REPEATED_SPEED_INPUT_MILEAGE_SEGMENTATION_INPUT_TIME_SEGMENTED:
return track_core::SchemeKind::repeated_speed_input_mileage_segmentation_input_time_segmented;
}
return track_core::SchemeKind::speed_input_time_segmented_mileage_free;
}
track_core::TrackState to_core(app::track::TrackState state) {
switch (state) {
case track_app_TrackState_STOP:
return track_core::TrackState::stop;
case track_app_TrackState_RUN:
return track_core::TrackState::run;
case track_app_TrackState_TEST_DISPLAY:
return track_core::TrackState::test_display;
}
return track_core::TrackState::stop;
}
track_core::TrackConfig to_core(const app::track::TrackConfig &config) {
return {
.draw_kind = to_core(config.draw_kind),
.line_length_m = config.line_length_m,
.active_line_length_m = config.active_line_length_m,
.head_offset_m = config.head_offset_m,
.line_leds_num = config.line_leds_num,
};
}
track_core::TrackInfo to_core(const app::track::TrackInfo &info) {
return {
.kind = to_core(info.kind),
.color = to_core(info.color),
.id = info.id,
.is_running = info.is_running,
.num_segments = info.num_segments,
};
}
track_core::TrackReport to_core(const app::track::report &report) {
return {
.id = report.id,
.state = to_core(report.state),
.mileage_m = report.mileage_m,
.speed_m_s = report.speed_m_s,
.time_elapsed_ms = report.time_elapsed_ms,
};
}
track_core::TrackError to_core(error_t error) {
if (error == ESP_OK) {
return track_core::TrackError::ok;
}
return track_core::TrackError::invalid_state;
}
app::strip::StripView *strip_from_context(void *context) {
return static_cast<app::strip::StripView *>(context);
}
track_core::TrackError strip_clear(void *context) {
auto *strip = strip_from_context(context);
if (strip == nullptr) {
return track_core::TrackError::invalid_arg;
}
return to_core((*strip)->clear());
}
track_core::TrackError strip_fill(
void *context,
std::uint16_t start_led,
std::uint16_t led_count,
track_core::Color color) {
auto *strip = strip_from_context(context);
if (strip == nullptr) {
return track_core::TrackError::invalid_arg;
}
return to_core((*strip)->fill(start_led, led_count, static_cast<std::uint32_t>(from_core(color))));
}
track_core::TrackError strip_show(void *context) {
auto *strip = strip_from_context(context);
if (strip == nullptr) {
return track_core::TrackError::invalid_arg;
}
return to_core((*strip)->show());
}
} // namespace
namespace app::track {
void TrackRenderPlan::add_fill(uint16_t start_led, uint16_t led_count, Color color) {
if (led_count == 0) {
return;
}
assert(span_count < spans.size() && "TrackRenderPlan capacity exceeded");
spans[span_count++] = TrackRenderSpan{
.start_led = start_led,
.led_count = led_count,
.color = color,
};
}
TrackRenderPlan make_track_render_plan(const TrackConfig &config, const TrackInfo &info, const report &rep) {
const auto core_plan = track_core::make_track_render_plan(
to_core(config),
to_core(info),
to_core(rep));
TrackRenderPlan plan{};
for (size_t i = 0; i < core_plan.span_count; ++i) {
const auto &span = core_plan.spans[i];
plan.add_fill(span.start_led, span.led_count, from_core(span.color));
}
return plan;
}
void apply_render_plan(app::strip::StripView strip, const TrackRenderPlan &plan) {
for (size_t i = 0; i < plan.span_count; ++i) {
const auto &span = plan.spans[i];
strip->fill(span.start_led, span.led_count, span.color);
}
}
track_core::TrackRenderSink make_track_render_sink(app::strip::StripView &strip) {
return track_core::TrackRenderSink{
.context = &strip,
.clear = strip_clear,
.fill = strip_fill,
.show = strip_show,
};
}
} // namespace app::track
-107
View File
@@ -1,107 +0,0 @@
#include "app_track_model.hpp"
#include "app_track.pb.h"
#include "esp_log.h"
namespace {
app::track::config_getter global_config_getter{nullptr};
constexpr auto TAG = "app::track";
}
namespace app::track {
const char *to_str(TrackState status) {
using enum TrackState;
switch (status) {
case track_app_TrackState_STOP:
return "STOP";
case track_app_TrackState_RUN:
return "RUN";
case track_app_TrackState_TEST_DISPLAY:
return "TEST_DISPLAY";
}
return "UNKNOWN";
}
const char *to_str(TrackControllerMode mode) {
switch (mode) {
case track_app_TrackControllerMode_SCHEME:
return "SCHEME";
case track_app_TrackControllerMode_PID_HR:
return "PID_HR";
}
return "UNKNOWN";
}
const char *to_str(TrackPidStageKind kind) {
switch (kind) {
case track_app_TrackPidStageKind_NONE:
return "NONE";
case track_app_TrackPidStageKind_CONSTANT:
return "CONSTANT";
case track_app_TrackPidStageKind_PID:
return "PID";
}
return "UNKNOWN";
}
const char *to_str(track_app_TrackDrawKind draw_kind) {
switch (draw_kind) {
case track_app_TrackDrawKind_CIRCULAR:
return "CIRCULAR";
case track_app_TrackDrawKind_LINEAR:
return "LINEAR";
}
return "UNKNOWN";
}
const char *to_str(AccelerationProfile profile) {
switch (profile) {
case track_app_TrackAccelerationProfile_SMOOTH:
return "SMOOTH";
case track_app_TrackAccelerationProfile_INSTANT:
return "INSTANT";
}
return "UNKNOWN";
}
void set_global_config_getter(config_getter getter) {
global_config_getter = std::move(getter);
}
const TrackConfig &global_config() {
if (not global_config_getter) {
static const auto DEFAULT_CONFIG = TrackConfig::Default();
ESP_LOGW(TAG, "unset global config getter");
return DEFAULT_CONFIG;
}
return global_config_getter();
}
void TrackConfig::log(const char *tag, esp_log_level_t level) const {
ESP_LOG_LEVEL(level, tag,
"TrackConfig{.draw_kind=%s, "
".line_length_m=%.2f, "
".active_line_length_m=%.2f, "
".head_offset_m=%.2f, "
".line_leds_num=%" PRIu16
"}",
to_str(draw_kind),
line_length_m,
active_line_length_m,
head_offset_m,
line_leds_num);
}
void TrackStateReportCollection::log(const char *tag, esp_log_level_t level) const {
for (size_t i = 0; i < states.size(); ++i) {
const auto &report = states[i];
ESP_LOG_LEVEL(level, tag,
"[%zu] Report{.id=%" PRIu8 ", .state=%s, .mileage_m=%.2f, .speed_m_s=%.2f, .time_elapsed_ms=%" PRIu32 "}",
i,
report.id,
to_str(report.state),
report.mileage_m,
report.speed_m_s,
report.time_elapsed_ms);
}
}
}