颜色修改红绿,修复帧错误卡顿bug
This commit is contained in:
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user