颜色修改红绿,修复帧错误卡顿bug

This commit is contained in:
2025-12-16 14:25:48 +08:00
parent c86c24488c
commit a1f7f337c2
15 changed files with 2201 additions and 917 deletions

View File

@@ -11,6 +11,8 @@
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <nlohmann/json_fwd.hpp>
#include <sstream>
#include <system_error>
#include <nlohmann/json.hpp>
@@ -20,6 +22,33 @@ namespace {
using nlohmann::json;
// 旧的 JSON 导出实现保留在此,避免直接删除,便于回退。
// bool is_simple_array(const json& value) { ... }
// void dump_compact_json(...)
// json serialize_tactile_frame(const DecodedFrame& frame) { ... }
std::string payload_to_csv_row(const std::vector<std::uint8_t>& payload) {
// Combine every 2 bytes (little-endian) into one 16-bit value, output in decimal.
std::ostringstream oss;
bool first = true;
for (std::size_t idx = 0; idx + 1U < payload.size(); idx += 2U) {
const auto value =
static_cast<std::uint16_t>(payload[idx]) | static_cast<std::uint16_t>(payload[idx + 1U] << 8U);
if (!first) {
oss << ',';
}
first = false;
oss << value;
}
return oss.str();
}
} // namespace
namespace {
using nlohmann::json;
bool is_simple_array(const json& value) {
if (!value.is_array()) {
return false;
@@ -30,11 +59,11 @@ bool is_simple_array(const json& value) {
}
void dump_compact_json(std::ostream& out,
const json& value,
int indent = 0,
int indent_step = 2) {
const auto indent_str = std::string(static_cast<std::size_t>(indent), ' ');
const auto child_indent = indent + indent_step;
const json& value,
int indent = 0,
int indent_step = 2) {
const auto indent_str = std::string(static_cast<std::size_t>(indent), ' ');
const auto child_indent = indent + indent_step;
const auto child_indent_str = std::string(static_cast<std::size_t>(child_indent), ' ');
if (value.is_object()) {
@@ -48,7 +77,8 @@ void dump_compact_json(std::ostream& out,
out << child_indent_str << json(it.key()).dump() << ": ";
dump_compact_json(out, it.value(), child_indent, indent_step);
}
out << '\n' << indent_str << '}';
out << '\n'
<< indent_str << '}';
return;
}
@@ -72,7 +102,7 @@ void dump_compact_json(std::ostream& out,
out << "[\n";
bool first = true;
for (const auto& item : value) {
for (const auto& item: value) {
if (!first) {
out << ",\n";
}
@@ -80,7 +110,8 @@ void dump_compact_json(std::ostream& out,
out << child_indent_str;
dump_compact_json(out, item, child_indent, indent_step);
}
out << '\n' << indent_str << ']';
out << '\n'
<< indent_str << ']';
return;
}
@@ -89,38 +120,37 @@ void dump_compact_json(std::ostream& out,
json serialize_tactile_frame(const DecodedFrame& frame) {
json result = {
{"pts", frame.pts},
{"raw_frame", frame.frame.data},
{"pressures", frame.tactile_pressures},
{ "pts", frame.pts },
{ "raw_frame", frame.frame.data },
{ "pressures", frame.tactile_pressures },
};
const auto received = frame.received_at.time_since_epoch();
result["received_at_ns"] =
std::chrono::duration_cast<std::chrono::nanoseconds>(received).count();
std::chrono::duration_cast<std::chrono::nanoseconds>(received).count();
if (frame.tactile_matrix_size) {
result["matrix"] = {
{"long_edge", frame.tactile_matrix_size->long_edge},
{"short_edge", frame.tactile_matrix_size->short_edge},
{ "long_edge", frame.tactile_matrix_size->long_edge },
{ "short_edge", frame.tactile_matrix_size->short_edge },
};
}
if (frame.tactile) {
const auto& tactile = *frame.tactile;
result["tactile"] = {
{"device_address", tactile.device_address},
{"response_function", tactile.response_function},
{"function", static_cast<std::uint8_t>(tactile.function)},
{"start_address", tactile.start_address},
{"return_byte_count", tactile.return_byte_count},
{"status", tactile.status},
{"payload", tactile.payload},
result["tactile"] = {
{ "device_address", tactile.device_address },
{ "response_function", tactile.response_function },
{ "function", static_cast<std::uint8_t>(tactile.function) },
{ "start_address", tactile.start_address },
{ "return_byte_count", tactile.return_byte_count },
{ "status", tactile.status },
{ "payload", tactile.payload },
};
}
return result;
}
} // namespace
bool WriteQueue::push(WriteRequest&& req) {
@@ -158,21 +188,20 @@ void WriteQueue::stop() {
cond_.notify_all();
}
JsonWritter::JsonWritter()
: write_thread_([this] { run(); }) {}
JsonWritter::JsonWritter(): write_thread_([this] { run(); }) {}
JsonWritter::~JsonWritter() {
stop();
}
std::future<WriteResult> JsonWritter::enqueue(std::string path,
std::deque<std::shared_ptr<DecodedFrame>> frames) {
std::deque<std::shared_ptr<DecodedFrame>> frames) {
std::promise<WriteResult> promise;
auto future = promise.get_future();
auto future = promise.get_future();
WriteRequest request{std::move(path), std::move(frames), std::move(promise)};
WriteRequest request{ std::move(path), std::move(frames), std::move(promise) };
if (!write_queue_.push(std::move(request))) {
WriteResult result{false, "writer has been stopped", request.path};
WriteResult result{ false, "writer has been stopped", request.path };
request.promise.set_value(std::move(result));
}
@@ -185,62 +214,67 @@ void JsonWritter::run() {
try {
auto result = write_once(request.path, std::move(request.frames));
request.promise.set_value(std::move(result));
} catch (const std::exception& ex) {
request.promise.set_value(WriteResult{false, ex.what(), request.path});
} catch (...) {
request.promise.set_value(WriteResult{false, "unknown error", request.path});
}
catch (const std::exception& ex) {
request.promise.set_value(WriteResult{ false, ex.what(), request.path });
}
catch (...) {
request.promise.set_value(WriteResult{ false, "unknown error", request.path });
}
}
}
WriteResult JsonWritter::write_once(const std::string& path,
std::deque<std::shared_ptr<DecodedFrame>> frames) {
std::deque<std::shared_ptr<DecodedFrame>> frames) {
if (path.empty()) {
return {false, "export path is empty", path};
return { false, "export path is empty", path };
}
json tactile_frames = json::array();
for (const auto& frame : frames) {
if (!frame) {
continue;
}
if (frame->id != CPCodecID::Tactile || !frame->tactile) {
continue;
}
tactile_frames.push_back(serialize_tactile_frame(*frame));
}
if (tactile_frames.empty()) {
return {false, "no tactile frames available for export", path};
}
json root;
root["codec"] = "tactile";
root["frames"] = std::move(tactile_frames);
std::filesystem::path fs_path(path);
if (fs_path.has_parent_path()) {
std::error_code ec;
std::filesystem::create_directories(fs_path.parent_path(), ec);
if (ec) {
return {false, "failed to create export directory: " + ec.message(), path};
return { false, "failed to create export directory: " + ec.message(), path };
}
}
std::ofstream stream(path, std::ios::binary | std::ios::trunc);
if (!stream.is_open()) {
return {false, "failed to open export file", path};
return { false, "failed to open export file", path };
}
bool wrote_any = false;
for (const auto& frame: frames) {
if (!frame) {
continue;
}
std::vector<std::uint8_t> payload;
if (frame->id == CPCodecID::Tactile && frame->tactile) {
payload = frame->tactile->payload;
}
else if (frame->id == CPCodecID::PiezoresistiveB) {
payload = tactile::extract_piezoresistive_b_payload(frame->frame);
}
if (payload.empty()) {
continue;
}
const auto row = payload_to_csv_row(payload);
stream << row << '\n';
wrote_any = true;
}
dump_compact_json(stream, root);
stream << '\n';
stream.flush();
if (!stream.good()) {
return {false, "failed to write export file", path};
return { false, "failed to write export file", path };
}
if (!wrote_any) {
return { false, "no tactile frames available for export", path };
}
return {true, {}, path};
return { true, {}, path };
}
void JsonWritter::stop() {