From 23e5c270886fd84c34691c45d83b2c6bf88ba174 Mon Sep 17 00:00:00 2001 From: Henry van Weelderen Date: Sat, 7 Mar 2026 14:18:35 -0800 Subject: [PATCH 01/14] Initial creation of json writer class with HPP file --- client/include/client/json_writer.hpp | 23 ++++++++++++++++++++++ client/include/common/panorama_defines.hpp | 4 +++- client/src/DataBuffer.cpp | 5 +++++ client/src/json_reader.cpp | 6 +----- client/src/json_writer.cpp | 14 +++++++++++++ client/src/mainframe.cpp | 2 +- scripts/example.json | 7 +++++-- 7 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 client/include/client/json_writer.hpp create mode 100644 client/src/json_writer.cpp diff --git a/client/include/client/json_writer.hpp b/client/include/client/json_writer.hpp new file mode 100644 index 00000000..54fc25d7 --- /dev/null +++ b/client/include/client/json_writer.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "common/panorama_defines.hpp" +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/filewritestream.h" // For writing directly to a FILE* stream +#include +#include +#include // For fopen, fclose + +class JsonWriter { +public: + JsonWriter(); // constructor + + + // returns boolean if has been written to + bool writeToJson(int sensorID, buffer_data_t data); + +//private: + + +}; \ No newline at end of file diff --git a/client/include/common/panorama_defines.hpp b/client/include/common/panorama_defines.hpp index 8880cd4c..a13289ed 100644 --- a/client/include/common/panorama_defines.hpp +++ b/client/include/common/panorama_defines.hpp @@ -6,5 +6,7 @@ typedef struct { std::time_t timestamp; // date recorded std::string dataunit; // e.g. "kPa", "mL" std::string datatype; // e.g. "temperature", "sound" + std::string sensor; + int sensorID; -} buffer_data_t; \ No newline at end of file +} buffer_data_t; \ No newline at end of file diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index d335eb7b..32d4829e 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -113,11 +113,16 @@ void DataBuffer::parseAll(/* std::vector &out */) { std::string DataBuffer::toString(const buffer_data_t& buffer_item) { //convert one struct of buffer_ into string + //bool hasUnit = buffer_item.dataunit != "\0"; std::string temp = "{"; temp = temp + "\"datatype\": \"" + buffer_item.datatype + "\", \"data\": " + std::to_string(buffer_item.data) + ", "; + + //if (hasUnit) { temp = temp + "\"dataunit\": \"" + buffer_item.dataunit + "\", "; + //} + temp = temp + "\"timestamp\": " + std::to_string(buffer_item.timestamp); temp += "}"; diff --git a/client/src/json_reader.cpp b/client/src/json_reader.cpp index 40fb9672..9c0f3cb0 100644 --- a/client/src/json_reader.cpp +++ b/client/src/json_reader.cpp @@ -42,10 +42,6 @@ buffer_data_t JsonReader::exportToBuffer(std::string json) { rapidjson::Document doc; rapidjson::ParseResult ok = doc.Parse(json.c_str()); - // if (doc.IsArray()) { - // std::cout << "dwda"; - // } - //std::cout << json << std::endl; if (!ok) { std::cerr << "JSON parse error at offset " << ok.Offset() << ": " << rapidjson::GetParseError_En(ok.Code()) << std::endl; @@ -71,7 +67,7 @@ buffer_data_t JsonReader::exportToBuffer(std::string json) { ret.datatype = sensorTypeString.c_str(); ret.data = sensorValue; ret.dataunit = sensorUnitString.c_str(); - ret.timestamp = std::time(&ret.timestamp); + ret.timestamp = std::time(nullptr); return ret; diff --git a/client/src/json_writer.cpp b/client/src/json_writer.cpp new file mode 100644 index 00000000..a47ffccb --- /dev/null +++ b/client/src/json_writer.cpp @@ -0,0 +1,14 @@ +#include "client/json_writer.hpp" +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/filewritestream.h" // For writing directly to a FILE* stream +#include +#include +#include // For fopen, fclose + +JsonWriter::JsonWriter() {} + +bool writeToJson(int sensorID, buffer_data_t data) { + return false; // stub +} \ No newline at end of file diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index f2b31471..df236ffa 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -94,7 +94,7 @@ void MainFrame::updateMessageDisplay() { for (const auto& msg : messages) { text += wxString::FromUTF8(msg.c_str()) + "\n"; } - + if (dataBuffer_->size() > 0) { //std::cout << dataBuffer_->toStringAll(); text += "\n--- DataBuffer Contents ---\n"; diff --git a/scripts/example.json b/scripts/example.json index 306db949..477d5b9f 100644 --- a/scripts/example.json +++ b/scripts/example.json @@ -1,4 +1,7 @@ [ -{"datatype": "light", "data": 0.200000, "dataunit": "nm", "timestamp": 1770504589}, -{"datatype": "temperature", "data": 29.500000, "dataunit": "celsius", "timestamp": 1770504589} +{"datatype": "temperature", "data": 29.500000, "dataunit": "celsius", "timestamp": 1772921407}, +{"datatype": "humidity", "data": 62.900002, "dataunit": "percent", "timestamp": 1772921408}, +{"datatype": "pressure", "data": 1013.250000, "dataunit": "hPa", "timestamp": 1772921408}, +{"datatype": "light", "data": 0.200000, "dataunit": "nm", "timestamp": 1772921408}, +{"datatype": "temperature", "data": 26.500000, "dataunit": "celsius", "timestamp": 1772921409} ] \ No newline at end of file From 5e2bf78d06b432ddb37129af0b991bd2ada5dad3 Mon Sep 17 00:00:00 2001 From: Henry van Weelderen Date: Sat, 7 Mar 2026 14:52:18 -0800 Subject: [PATCH 02/14] initial implementation json writer --- client/CMakeLists.txt | 2 ++ client/include/client/json_writer.hpp | 7 +++-- client/src/json_writer.cpp | 42 +++++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 58c8f3c5..c9ae830d 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -7,6 +7,7 @@ set(CLIENT_SOURCES src/message_model.cpp src/tcp_client.cpp src/json_reader.cpp + src/json_writer.cpp src/DataBuffer.cpp src/graph_panel.cpp src/sensor_manager.cpp @@ -27,6 +28,7 @@ set(CLIENT_HEADERS include/client/message_model.hpp include/client/tcp_client.hpp include/client/json_reader.hpp + include/client/json_writer.hpp include/client/buffer_base.hpp include/client/DataBuffer.hpp include/client/sensor_manager.hpp diff --git a/client/include/client/json_writer.hpp b/client/include/client/json_writer.hpp index 54fc25d7..e6465ac2 100644 --- a/client/include/client/json_writer.hpp +++ b/client/include/client/json_writer.hpp @@ -15,9 +15,10 @@ class JsonWriter { // returns boolean if has been written to - bool writeToJson(int sensorID, buffer_data_t data); - -//private: + bool writeToJson(buffer_data_t data); + Document getDocumentFromData(buffer_data_t data); +private: + std::time_t previousTimestamp; }; \ No newline at end of file diff --git a/client/src/json_writer.cpp b/client/src/json_writer.cpp index a47ffccb..fb3417c6 100644 --- a/client/src/json_writer.cpp +++ b/client/src/json_writer.cpp @@ -6,9 +6,47 @@ #include #include #include // For fopen, fclose +using namespace rapidjson; JsonWriter::JsonWriter() {} -bool writeToJson(int sensorID, buffer_data_t data) { - return false; // stub +bool JsonWriter::writeToJson(buffer_data_t data) { + Document doc = getDocumentFromData(data); + + // open/create document with name of sensorID + FILE* fp = fopen((std::to_string(data.sensorID)).c_str(), "wb"); + if (!fp) { + // file couldnt open - return false + return false; + } + + char writeBuffer[65536]; + FileWriteStream os(fp, writeBuffer, sizeof(writeBuffer)); + + //PrettyWriter writer(os); // write with indentation + + Writer writer(os); // write without indentation but more compact + + doc.Accept(writer); + + fclose(fp); + + previousTimestamp = data.timestamp; + + return true; // stub +} + +Document JsonWriter::getDocumentFromData(buffer_data_t data) { + Document doc; + doc.SetObject(); + Document::AllocatorType& allocator = doc.GetAllocator(); + + doc.AddMember("data", data.data, allocator); + doc.AddMember("timestamp", data.timestamp, allocator); + doc.AddMember("dataunit", data.dataunit, allocator); + doc.AddMember("datatype", data.datatype, allocator); + doc.AddMember("sensor", data.sensor, allocator); + doc.AddMember("sensorID", data.sensorID, allocator); + + return doc; } \ No newline at end of file From 5d78d2c0674454be5bfdf24765c9fee85d87df96 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 7 Mar 2026 14:59:09 -0800 Subject: [PATCH 03/14] added start and stop function to JsonWriter --- client/include/client/json_writer.hpp | 18 +++++++++++++--- client/src/json_writer.cpp | 31 ++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/client/include/client/json_writer.hpp b/client/include/client/json_writer.hpp index e6465ac2..fe458c44 100644 --- a/client/include/client/json_writer.hpp +++ b/client/include/client/json_writer.hpp @@ -9,16 +9,28 @@ #include #include // For fopen, fclose +#include "client/DataBuffer.hpp" +#include +#include + + +class DataBuffer; + class JsonWriter { public: - JsonWriter(); // constructor + JsonWriter(std::shared_ptr dataBuffer, const std::string& exportPath); // constructor + void start(); + void stop(); // returns boolean if has been written to bool writeToJson(buffer_data_t data); Document getDocumentFromData(buffer_data_t data); + private: - std::time_t previousTimestamp; - + std::time_t previousTimestamp = 0; + std::atomic running_{true}; + std::shared_ptr dataBuffer_; + std::string exportPath; }; \ No newline at end of file diff --git a/client/src/json_writer.cpp b/client/src/json_writer.cpp index fb3417c6..565e1ff5 100644 --- a/client/src/json_writer.cpp +++ b/client/src/json_writer.cpp @@ -8,7 +8,34 @@ #include // For fopen, fclose using namespace rapidjson; -JsonWriter::JsonWriter() {} +JsonWriter::JsonWriter(std::shared_ptr dataBuffer, const std::string& exportPath) + : dataBuffer_(dataBuffer), exportPath(exportPath) {} + +void JsonWriter::start() { + // This function will run in a separate thread and continuously check for new data in the DataBuffer. + while (running_) { + + if(dataBuffer_->readAll().size() > 0) { + buffer_data_t latestData = dataBuffer_->readAll().back(); // Get the most recent data entry + int written = 0; + + if (latestData.timestamp > previousTimestamp) { // Check if it's new data + written = writeToJson(latestData); + if(written){ + previousTimestamp = latestData.timestamp; // Update the last written timestamp + } else { + std::cout << "Failed to write data to JSON for sensorID: " << latestData.sensorID << std::endl; + } + } + + } + + } +} + +void JsonWriter::stop() { + running_ = false; +} bool JsonWriter::writeToJson(buffer_data_t data) { Document doc = getDocumentFromData(data); @@ -30,8 +57,6 @@ bool JsonWriter::writeToJson(buffer_data_t data) { doc.Accept(writer); fclose(fp); - - previousTimestamp = data.timestamp; return true; // stub } From a9b2b95bcfe2710894510fe4ae41e057b6e7d45c Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 7 Mar 2026 15:07:28 -0800 Subject: [PATCH 04/14] Create JsonWrite and start on a separate thread --- client/src/main.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/client/src/main.cpp b/client/src/main.cpp index 8c93f493..1861aaa3 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -12,6 +12,7 @@ #include "client/json_reader.hpp" #include "client/config_manager.hpp" #include "client/data_logger.hpp" +#include "client/json_writer.hpp" #include using namespace std; @@ -149,6 +150,10 @@ class PanoramaClient : public wxApp { return true; } + // --- Create and start JSON writer on separate thread --- + jsonWriter_ = std::make_shared(dataBuffer_, runtimeDir); + jsonWriterThread_ = std::make_unique(&JsonWriter::start, jsonWriter_); + // --- Create view --- MainFrame* w = new MainFrame("Panorama Client", model_, dataBuffer_); w->Show(); @@ -172,6 +177,16 @@ class PanoramaClient : public wxApp { if (tcpClient_) { tcpClient_->stop(); } + + // Clean shutdown of JSON writer + if (jsonWriter_) { + jsonWriter_->stop(); + } + + if (jsonWriterThread_ && jsonWriterThread_->joinable()) { + jsonWriterThread_->join(); + } + return wxApp::OnExit(); } @@ -180,6 +195,8 @@ class PanoramaClient : public wxApp { std::shared_ptr dataLogger_; std::shared_ptr dataBuffer_; std::unique_ptr tcpClient_; + std::shared_ptr jsonWriter_; + std::unique_ptr jsonWriterThread_; }; wxIMPLEMENT_APP(PanoramaClient); From 4bb6de7479741ada3ab58f20b4c181a6c3886211 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 7 Mar 2026 15:17:37 -0800 Subject: [PATCH 05/14] Added 10ms delay in start function --- client/include/client/json_writer.hpp | 4 +++- client/src/json_writer.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/client/include/client/json_writer.hpp b/client/include/client/json_writer.hpp index fe458c44..556175de 100644 --- a/client/include/client/json_writer.hpp +++ b/client/include/client/json_writer.hpp @@ -12,6 +12,8 @@ #include "client/DataBuffer.hpp" #include #include +#include +#include class DataBuffer; @@ -27,7 +29,7 @@ class JsonWriter { bool writeToJson(buffer_data_t data); Document getDocumentFromData(buffer_data_t data); - + private: std::time_t previousTimestamp = 0; std::atomic running_{true}; diff --git a/client/src/json_writer.cpp b/client/src/json_writer.cpp index 565e1ff5..3e49c8f0 100644 --- a/client/src/json_writer.cpp +++ b/client/src/json_writer.cpp @@ -14,6 +14,8 @@ JsonWriter::JsonWriter(std::shared_ptr dataBuffer, const std::string void JsonWriter::start() { // This function will run in a separate thread and continuously check for new data in the DataBuffer. while (running_) { + //sleep for 10 milliseconds to avoid busy waiting + std::this_thread::sleep_for(std::chrono::milliseconds(10)); if(dataBuffer_->readAll().size() > 0) { buffer_data_t latestData = dataBuffer_->readAll().back(); // Get the most recent data entry From 663a6b44c87f228315e8082b151998b33e010f1f Mon Sep 17 00:00:00 2001 From: Henry van Weelderen Date: Sat, 7 Mar 2026 15:19:00 -0800 Subject: [PATCH 06/14] changes to tet file and small updates to file path --- client/CMakeLists.txt | 2 +- client/src/json_writer.cpp | 6 ++++-- tools/pserver/pstream_json.py | 36 +++++++++++++++++++++++------------ 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index c9ae830d..ebebef6a 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -55,7 +55,7 @@ target_include_directories(panorama-client # SQLite3 - Required for config database find_package(SQLite3 REQUIRED) -target_link_libraries(panorama-client SQLite::SQLite3) +#target_link_libraries(panorama-client SQLite::SQLite3) if(WIN32) target_link_libraries(panorama-client PRIVATE ws2_32) diff --git a/client/src/json_writer.cpp b/client/src/json_writer.cpp index 3e49c8f0..ab3ec089 100644 --- a/client/src/json_writer.cpp +++ b/client/src/json_writer.cpp @@ -42,8 +42,10 @@ void JsonWriter::stop() { bool JsonWriter::writeToJson(buffer_data_t data) { Document doc = getDocumentFromData(data); - // open/create document with name of sensorID - FILE* fp = fopen((std::to_string(data.sensorID)).c_str(), "wb"); + // open/create document in rundir with name of sensorID + std::string path = exportPath + "/" + std::to_string(data.sensorID); + + FILE* fp = fopen(path.c_str(), "wb"); if (!fp) { // file couldnt open - return false return false; diff --git a/tools/pserver/pstream_json.py b/tools/pserver/pstream_json.py index 79972295..8e3fc1e3 100644 --- a/tools/pserver/pstream_json.py +++ b/tools/pserver/pstream_json.py @@ -11,24 +11,36 @@ def __init__(self): self.counter = 0 self.sample_data = [ { - "sensor": "temperature", - "value": 25.5, - "unit": "celsius" + "data": 25.5, + "timestamp": 123032032, + "dataunit": "K", + "datatype": "temperature", + "sensor:": "TM1000", + "sensorID": 1 }, { - "sensor": "humidity", - "value": 60.2, - "unit": "percent" + "data": 60.2, + "timestamp": 123032032, + "dataunit": "watercontent", + "datatype": "humidity", + "sensor:": "HD1000", + "sensorID": 2 }, { - "sensor": "pressure", - "value": 1013.25, - "unit": "hPa" + "data": 1013.25, + "timestamp": 123032032, + "dataunit": "hPa", + "datatype": "pressure", + "sensor:": "PP1000", + "sensorID": 3 }, { - "sensor": "light", - "value": 0.2, - "unit": "nm" + "data": 0.2, + "timestamp": 123032032, + "dataunit": "nm", + "datatype": "light", + "sensor:": "NM1000", + "sensorID": 4 } ] From e69f35536bab85a39b3a3ab37b790d2b9de04419 Mon Sep 17 00:00:00 2001 From: Henry van Weelderen Date: Sat, 7 Mar 2026 15:37:03 -0800 Subject: [PATCH 07/14] json writer --- client/include/client/json_writer.hpp | 2 +- client/include/common/panorama_defines.hpp | 1 + client/src/json_writer.cpp | 29 +++++++++++++++------- tools/pserver/pstream_json.py | 12 ++++----- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/client/include/client/json_writer.hpp b/client/include/client/json_writer.hpp index 556175de..133c10f6 100644 --- a/client/include/client/json_writer.hpp +++ b/client/include/client/json_writer.hpp @@ -28,7 +28,7 @@ class JsonWriter { // returns boolean if has been written to bool writeToJson(buffer_data_t data); - Document getDocumentFromData(buffer_data_t data); + rapidjson::Document getDocumentFromData(buffer_data_t data); private: std::time_t previousTimestamp = 0; diff --git a/client/include/common/panorama_defines.hpp b/client/include/common/panorama_defines.hpp index a13289ed..330f7455 100644 --- a/client/include/common/panorama_defines.hpp +++ b/client/include/common/panorama_defines.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include typedef struct { float data; // actual value diff --git a/client/src/json_writer.cpp b/client/src/json_writer.cpp index ab3ec089..3aed92c3 100644 --- a/client/src/json_writer.cpp +++ b/client/src/json_writer.cpp @@ -40,7 +40,7 @@ void JsonWriter::stop() { } bool JsonWriter::writeToJson(buffer_data_t data) { - Document doc = getDocumentFromData(data); + rapidjson::Document doc = getDocumentFromData(data); // open/create document in rundir with name of sensorID std::string path = exportPath + "/" + std::to_string(data.sensorID); @@ -65,16 +65,27 @@ bool JsonWriter::writeToJson(buffer_data_t data) { return true; // stub } -Document JsonWriter::getDocumentFromData(buffer_data_t data) { - Document doc; +rapidjson::Document JsonWriter::getDocumentFromData(buffer_data_t data) { + rapidjson::Document doc; doc.SetObject(); - Document::AllocatorType& allocator = doc.GetAllocator(); + rapidjson::Document::AllocatorType& allocator = doc.GetAllocator(); + + doc.AddMember("data", static_cast(data.data), allocator); + + doc.AddMember("timestamp", static_cast(data.timestamp), allocator); + + rapidjson::Value unit; + unit.SetString(data.dataunit.c_str(), static_cast(data.dataunit.length()), allocator); + doc.AddMember("dataunit", unit, allocator); + + rapidjson::Value type; + type.SetString(data.datatype.c_str(), static_cast(data.datatype.length()), allocator); + doc.AddMember("datatype", type, allocator); + + rapidjson::Value sensor; + sensor.SetString(data.sensor.c_str(), static_cast(data.sensor.length()), allocator); + doc.AddMember("sensor", sensor, allocator); - doc.AddMember("data", data.data, allocator); - doc.AddMember("timestamp", data.timestamp, allocator); - doc.AddMember("dataunit", data.dataunit, allocator); - doc.AddMember("datatype", data.datatype, allocator); - doc.AddMember("sensor", data.sensor, allocator); doc.AddMember("sensorID", data.sensorID, allocator); return doc; diff --git a/tools/pserver/pstream_json.py b/tools/pserver/pstream_json.py index 8e3fc1e3..d512da16 100644 --- a/tools/pserver/pstream_json.py +++ b/tools/pserver/pstream_json.py @@ -55,12 +55,12 @@ def get_next_data(self) -> bytes: json_obj["sequence"] = self.counter # Add variation to the values - if json_obj["sensor"] == "temperature": - json_obj["value"] = 25.5 + (self.counter % 10) * 0.5 - elif json_obj["sensor"] == "humidity": - json_obj["value"] = 60.2 + (self.counter % 10) * 0.3 - elif json_obj["sensor"] == "pressure": - json_obj["value"] = 1013.25 + (self.counter % 10) * 0.1 + if json_obj["datatype"] == "temperature": + json_obj["data"] = 25.5 + (self.counter % 10) * 0.5 + elif json_obj["datatype"] == "humidity": + json_obj["data"] = 60.2 + (self.counter % 10) * 0.3 + elif json_obj["datatype"] == "pressure": + json_obj["data"] = 1013.25 + (self.counter % 10) * 0.1 self.counter += 1 From 0ad2a02ee7428dbe6017fd889b32de26d512cd75 Mon Sep 17 00:00:00 2001 From: Henry van Weelderen Date: Sat, 7 Mar 2026 15:58:49 -0800 Subject: [PATCH 08/14] working sent and read, need to fix writer --- client/src/json_reader.cpp | 55 +++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/client/src/json_reader.cpp b/client/src/json_reader.cpp index 9c0f3cb0..290eaab4 100644 --- a/client/src/json_reader.cpp +++ b/client/src/json_reader.cpp @@ -54,20 +54,49 @@ buffer_data_t JsonReader::exportToBuffer(std::string json) { return ret; } - std::string sensorTypeString ( - doc["sensor"].GetString(), - doc["sensor"].GetStringLength() - ); - std::string sensorUnitString ( - doc["unit"].GetString(), - doc["unit"].GetStringLength() - ); - double sensorValue = doc["value"].GetDouble(); + ret.sensor = ""; + ret.datatype = ""; + ret.dataunit = ""; + ret.data = 0; + ret.timestamp = NULL; - ret.datatype = sensorTypeString.c_str(); - ret.data = sensorValue; - ret.dataunit = sensorUnitString.c_str(); - ret.timestamp = std::time(nullptr); + if (doc.HasMember("sensor")) { + std::string sensorTypeString ( + doc["sensor"].GetString(), + doc["sensor"].GetStringLength() + ); + ret.sensor = sensorTypeString.c_str(); + } + if (doc.HasMember("dataunit")) { + std::string sensorUnitString ( + doc["dataunit"].GetString(), + doc["dataunit"].GetStringLength() + ); + ret.dataunit = sensorUnitString.c_str(); + } + if (doc.HasMember("data")) { + double sensorValue = doc["data"].GetDouble(); + ret.data = sensorValue; + } + if (doc.HasMember("datatype")) { + std::string sensorUnitString ( + doc["datatype"].GetString(), + doc["datatype"].GetStringLength() + ); + ret.datatype = sensorUnitString.c_str(); + } + if (doc.HasMember("timestamp")) { + std::time_t sensorUnitString ( + doc["timestamp"].GetFloat() + ); + ret.timestamp = sensorUnitString; + } + + + + + + //ret.timestamp = std::time(nullptr); return ret; From 2d1e1d12c16abf9b927438971c5f9467ddaa42a6 Mon Sep 17 00:00:00 2001 From: Bianca Date: Sat, 14 Mar 2026 13:13:33 -0700 Subject: [PATCH 09/14] Added wxMathPlot area --- client/CMakeLists.txt | 5 +- client/include/client/graph_panel.hpp | 16 +--- client/src/graph_panel.cpp | 113 ++++---------------------- client/src/mainframe.cpp | 2 +- scripts/run.sh | 3 + 5 files changed, 24 insertions(+), 115 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 2c27a0eb..449c643b 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -18,6 +18,7 @@ endif() # Panorama Includes target_include_directories(panorama-client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/external/wxMathPlot_0.2.0/mathplot ) @@ -74,7 +75,7 @@ if(APPLE) # Apply wxWidgets linker flags - don't use separate_arguments to preserve framework syntax string(STRIP "${WX_LINK_FLAGS}" WX_LINK_FLAGS) - target_link_libraries(panorama-client PRIVATE ${WX_LINK_FLAGS}) + target_link_libraries(panorama-client PRIVATE ${WX_LINK_FLAGS} mathplot) message(STATUS "Using wxWidgets prebuilt from ${WXWIDGETS_PREBUILT_PATH}") else() # linux @@ -100,7 +101,7 @@ else() # linux set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WX_CXX_FLAGS}") set(WX_LIBRARIES "${WX_LINK_FLAGS}") - target_link_libraries(panorama-client "${WX_LIBRARIES}") + target_link_libraries(panorama-client "${WX_LIBRARIES}" mathplot) message(STATUS "Using locally built wxWidgets via wx-config: ${wxWidgets_CONFIG_EXECUTABLE}") endif() diff --git a/client/include/client/graph_panel.hpp b/client/include/client/graph_panel.hpp index d8acbbeb..621fb7b0 100644 --- a/client/include/client/graph_panel.hpp +++ b/client/include/client/graph_panel.hpp @@ -1,23 +1,11 @@ - #pragma once #include -#include -#include -#include +#include class GraphPanel : public wxPanel { public: GraphPanel(wxWindow* parent); private: - // Event handlers - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - - // Drawing functions - void DrawBackground(wxDC& dc); - void DrawGrid(wxDC& dc); - void DrawAxes(wxDC& dc); - - wxDECLARE_EVENT_TABLE(); + mpWindow* m_plot; }; \ No newline at end of file diff --git a/client/src/graph_panel.cpp b/client/src/graph_panel.cpp index d4b73d65..a28febc4 100644 --- a/client/src/graph_panel.cpp +++ b/client/src/graph_panel.cpp @@ -1,109 +1,26 @@ #include "client/graph_panel.hpp" -#include -#include - -wxBEGIN_EVENT_TABLE(GraphPanel, wxPanel) - EVT_PAINT(GraphPanel::OnPaint) - EVT_SIZE(GraphPanel::OnSize) -wxEND_EVENT_TABLE() GraphPanel::GraphPanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) { - SetBackgroundColour(wxColour(255, 255, 255)); - SetBackgroundStyle(wxBG_STYLE_PAINT); // double buffering so smoother -} - -// redraw the panel -void GraphPanel::OnPaint(wxPaintEvent& event) { - wxAutoBufferedPaintDC dc(this); - dc.Clear(); - - DrawBackground(dc); - DrawGrid(dc); - DrawAxes(dc); -} - -// Function: used when the panel is resized to redraw -void GraphPanel::OnSize(wxSizeEvent& event) { - Refresh(); - Update(); - - event.Skip(); -} - -// Drawing functions -void GraphPanel::DrawBackground(wxDC& dc) { - wxSize size = GetClientSize(); - - dc.SetBrush(wxBrush(wxColour(220, 220, 220))); - - dc.SetPen(*wxTRANSPARENT_PEN); - - dc.DrawRectangle(0, 0, size.GetWidth(), size.GetHeight()); -} - -// Function: to draw grid lines -void GraphPanel::DrawGrid(wxDC& dc) { - wxSize size = GetClientSize(); - - int leftMargin = 60; - int bottomMargin = 40; - int topMargin = 20; - int graphHeight = size.GetHeight() - topMargin - bottomMargin; - int graphWidth = size.GetWidth() - leftMargin - 20; - - dc.SetPen(wxPen(wxColour(90, 90, 90), 1)); - - // horizontal grid lines - for (int i = 0; i <= 4; i++) { - int y = topMargin + (graphHeight * i / 4); - dc.DrawLine(leftMargin, y, leftMargin + graphWidth, y); - } - - // vertical grid lines - for (int i = 0; i <=6; i++) { - int x = leftMargin + (graphWidth * i / 6); - dc.DrawLine(x, topMargin, x, topMargin + graphHeight); - } -} - - -void GraphPanel::DrawAxes(wxDC& dc){ - wxSize size = GetClientSize(); + m_plot = new mpWindow(this, wxID_ANY); + m_plot-> EnableDoubleBuffer(true); + m_plot-> EnableMousePanZoom(true); - int leftMargin = 60; - int bottomMargin = 40; - int topMargin = 20; - int graphHeight = size.GetHeight() - topMargin - bottomMargin; - int graphWidth = size.GetWidth() - leftMargin - 20; + // Create X & Y axes + mpScaleX* xAxis = new mpScaleX(wxT("Time (seconds)"), mpALIGN_BORDER_BOTTOM, true); + mpScaleY* yAxis = new mpScaleY(wxT("y"), mpALIGN_BORDER_LEFT, true); - dc.SetPen(wxPen(wxColour(0, 0, 0), 2)); + xAxis->SetDrawOutsideMargins(false); + yAxis->SetDrawOutsideMargins(false); - // draw y-axis - dc.DrawLine(leftMargin, topMargin, leftMargin, topMargin + graphHeight); + m_plot->AddLayer(yAxis); + m_plot->AddLayer(xAxis); - // draw x-axis - dc.DrawLine(leftMargin, topMargin + graphHeight, leftMargin + graphWidth, topMargin + graphHeight); - - // - //x axis label - for (int i = 0; i < 6; i++) { - int x = leftMargin + (graphWidth * i / 6); - int value = i; - wxString label = wxString::Format("%d", value); - wxSize textSize = dc.GetTextExtent(label); - dc.DrawText(label, x - textSize.GetWidth() / 2, size.GetHeight() - bottomMargin + 5); - } - - //y axis label - for (int i = 0; i <= 4; i++) { - int y = (topMargin + graphHeight) - (graphHeight * i / 4); - int value = i * 25; - wxString label = wxString::Format("%d", value); - wxSize textSize = dc.GetTextExtent(label); - dc.DrawText(label, leftMargin - textSize.GetWidth() - 5, y - textSize.GetHeight() / 2); - } + m_plot->SetMargins(30, 30, 85, 60); + m_plot->Fit(); + wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(m_plot, 1, wxEXPAND); + SetSizer(sizer); } - diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index c636fe7a..bc6b1a1a 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -55,7 +55,7 @@ MainFrame::MainFrame(const wxString& title, std::shared_ptr model, consolePanel_->SetSizer(consoleSizer); // Assemble splitters - rightSplitter->SplitHorizontally(dataViewPanel, graphPanel, 180); + rightSplitter->SplitHorizontally(dataViewPanel, graphPanel, 200); rightSplitter->SetMinimumPaneSize(100); rightSplitter->SetSashGravity(0.0); // keeps data panel a 180px, graph takes extra space diff --git a/scripts/run.sh b/scripts/run.sh index 5b1235ec..7b556f58 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +unset GTK_PATH + + SCRIPT_DIR=$(dirname "$(realpath "$0")") ROOT_DIR=$(realpath "$SCRIPT_DIR/..") From 76185295681f1ab9190b049da899afc24bf52f5a Mon Sep 17 00:00:00 2001 From: Alex Zhou Date: Sat, 14 Mar 2026 15:20:18 -0700 Subject: [PATCH 10/14] Fix Timestamp not updating due to type issues, converted time type to long --- client/include/client/buffer_base.hpp | 2 +- client/include/client/mainframe.hpp | 8 +++++ client/src/json_reader.cpp | 7 ++--- client/src/json_writer.cpp | 9 +++--- client/src/mainframe.cpp | 42 +++++++++++++++++---------- tools/pserver/pstream_json.py | 2 +- tools/pserver/pstreamer.py | 2 +- 7 files changed, 45 insertions(+), 27 deletions(-) diff --git a/client/include/client/buffer_base.hpp b/client/include/client/buffer_base.hpp index 81b5a1fe..5f5a6b09 100644 --- a/client/include/client/buffer_base.hpp +++ b/client/include/client/buffer_base.hpp @@ -98,6 +98,6 @@ class BufferBase { protected: std::list buffer_; mutable std::mutex mutex_; - int MAX_BUFFER_SIZE = 5; + int MAX_BUFFER_SIZE = 500; int FLUSH_THRESHOLD = 50; //percentage }; diff --git a/client/include/client/mainframe.hpp b/client/include/client/mainframe.hpp index d1998de8..a5f29799 100644 --- a/client/include/client/mainframe.hpp +++ b/client/include/client/mainframe.hpp @@ -69,6 +69,14 @@ class MainFrame : public wxFrame { wxTextCtrl* messageDisplay_; wxPanel* consolePanel_; + wxTimer updateTimer_; + std::atomic updatePending_{false}; + size_t displayedMessageCount_ = 0; + size_t displayedBufferCount_ = 0; + + void OnUpdateTimer(wxTimerEvent& event); + + }; #endif // __MAINFRAME__ \ No newline at end of file diff --git a/client/src/json_reader.cpp b/client/src/json_reader.cpp index 290eaab4..2e84a19a 100644 --- a/client/src/json_reader.cpp +++ b/client/src/json_reader.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "common/panorama_utils.hpp" JsonReader::JsonReader() {} @@ -42,6 +43,7 @@ buffer_data_t JsonReader::exportToBuffer(std::string json) { rapidjson::Document doc; rapidjson::ParseResult ok = doc.Parse(json.c_str()); + std::cout << json.c_str() << std::endl; if (!ok) { std::cerr << "JSON parse error at offset " << ok.Offset() << ": " << rapidjson::GetParseError_En(ok.Code()) << std::endl; @@ -86,10 +88,7 @@ buffer_data_t JsonReader::exportToBuffer(std::string json) { ret.datatype = sensorUnitString.c_str(); } if (doc.HasMember("timestamp")) { - std::time_t sensorUnitString ( - doc["timestamp"].GetFloat() - ); - ret.timestamp = sensorUnitString; + ret.timestamp = (long) doc["timestamp"].GetInt(); } diff --git a/client/src/json_writer.cpp b/client/src/json_writer.cpp index 3aed92c3..0db20719 100644 --- a/client/src/json_writer.cpp +++ b/client/src/json_writer.cpp @@ -14,8 +14,8 @@ JsonWriter::JsonWriter(std::shared_ptr dataBuffer, const std::string void JsonWriter::start() { // This function will run in a separate thread and continuously check for new data in the DataBuffer. while (running_) { - //sleep for 10 milliseconds to avoid busy waiting - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + //sleep for 5 milliseconds to avoid busy waiting + std::this_thread::sleep_for(std::chrono::milliseconds(5)); if(dataBuffer_->readAll().size() > 0) { buffer_data_t latestData = dataBuffer_->readAll().back(); // Get the most recent data entry @@ -45,7 +45,7 @@ bool JsonWriter::writeToJson(buffer_data_t data) { // open/create document in rundir with name of sensorID std::string path = exportPath + "/" + std::to_string(data.sensorID); - FILE* fp = fopen(path.c_str(), "wb"); + FILE* fp = fopen(path.c_str(), "ab"); if (!fp) { // file couldnt open - return false return false; @@ -54,11 +54,10 @@ bool JsonWriter::writeToJson(buffer_data_t data) { char writeBuffer[65536]; FileWriteStream os(fp, writeBuffer, sizeof(writeBuffer)); - //PrettyWriter writer(os); // write with indentation - Writer writer(os); // write without indentation but more compact doc.Accept(writer); + fputc('\n', fp); fclose(fp); diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index 1347104a..b88d3a56 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -6,6 +6,7 @@ #include "client/sensor_manager.hpp" #include "client/sensor.hpp" #include "client/settings_dialog.hpp" +#include "common/panorama_utils.hpp" #include #include #include @@ -17,6 +18,10 @@ MainFrame::MainFrame(const wxString& title, std::shared_ptr model, CreateMenuBar(); + updateTimer_.Bind(wxEVT_TIMER, &MainFrame::OnUpdateTimer, this); + updateTimer_.Start(10); // milliseconds between GUI refreshes + + // Create splitter for layout wxSplitterWindow* mainSplitter = new wxSplitterWindow(this, wxID_ANY); wxSplitterWindow* topSplitter = new wxSplitterWindow(mainSplitter, wxID_ANY); @@ -84,9 +89,7 @@ MainFrame::MainFrame(const wxString& title, std::shared_ptr model, } void MainFrame::onModelUpdated() { - // Use CallAfter to update GUI seperate from network thread or smthing - CallAfter(&MainFrame::updateMessageDisplay); - CallAfter(&MainFrame::updateDataPanel); + updatePending_.store(true); } void MainFrame::onSensorToggled() { @@ -94,21 +97,23 @@ void MainFrame::onSensorToggled() { } void MainFrame::updateMessageDisplay() { + // Append only new messages auto messages = model_->getMessages(); - wxString text; - for (const auto& msg : messages) { - text += wxString::FromUTF8(msg.c_str()) + "\n"; + for (size_t i = displayedMessageCount_; i < messages.size(); ++i) { + messageDisplay_->AppendText(wxString::FromUTF8(messages[i].c_str()) + "\n"); } - - if (dataBuffer_->size() > 0) { - //std::cout << dataBuffer_->toStringAll(); - text += "\n--- DataBuffer Contents ---\n"; - text += wxString::FromUTF8(dataBuffer_->toStringAll()); - text += "--- End of DataBuffer ---\n"; + displayedMessageCount_ = messages.size(); + + // Append only new buffer entries + auto allBuffer = dataBuffer_->readAll(); + size_t i = 0; + for (const auto& entry : allBuffer) { + if (i >= displayedBufferCount_) { + messageDisplay_->AppendText(wxString::FromUTF8(dataBuffer_->toString(entry))); + } + ++i; } - - messageDisplay_->SetValue(text); - messageDisplay_->SetInsertionPointEnd(); + displayedBufferCount_ = allBuffer.size(); } void MainFrame::updateDataPanel() { @@ -235,3 +240,10 @@ void MainFrame::OnSettingsOpen(wxCommandEvent& event) { SettingsDialog dialog(this); dialog.ShowModal(); } + +void MainFrame::OnUpdateTimer(wxTimerEvent&) { + if (updatePending_.exchange(false)) { + updateMessageDisplay(); + updateDataPanel(); + } +} diff --git a/tools/pserver/pstream_json.py b/tools/pserver/pstream_json.py index d512da16..739fa897 100644 --- a/tools/pserver/pstream_json.py +++ b/tools/pserver/pstream_json.py @@ -51,7 +51,7 @@ def get_next_data(self) -> bytes: json_obj = self.sample_data[data_index].copy() # Add timestamp and sequence number - json_obj["timestamp"] = time.time() + json_obj["timestamp"] = int(time.time()) json_obj["sequence"] = self.counter # Add variation to the values diff --git a/tools/pserver/pstreamer.py b/tools/pserver/pstreamer.py index 0aa1af38..58fece92 100644 --- a/tools/pserver/pstreamer.py +++ b/tools/pserver/pstreamer.py @@ -122,7 +122,7 @@ def is_running(self) -> bool: streamer = PStreamer() # Stream builder class # Building JSON stream using the PStreamJSON class - streamer.build_stream(PStreamJSON()).set_interval(1.0) + streamer.build_stream(PStreamJSON()).set_interval(0.054) streamer.start() From f9d04a52cf4e207e981b72c6c80c841e3f43e16d Mon Sep 17 00:00:00 2001 From: Bianca Date: Sat, 21 Mar 2026 13:08:40 -0700 Subject: [PATCH 11/14] Added Graph --- client/include/client/graph_panel.hpp | 15 ++++++ client/include/client/mainframe.hpp | 3 +- client/src/graph_panel.cpp | 66 ++++++++++++++++++++++++++- client/src/mainframe.cpp | 21 ++++++++- 4 files changed, 101 insertions(+), 4 deletions(-) diff --git a/client/include/client/graph_panel.hpp b/client/include/client/graph_panel.hpp index 621fb7b0..e29f9bcd 100644 --- a/client/include/client/graph_panel.hpp +++ b/client/include/client/graph_panel.hpp @@ -1,11 +1,26 @@ #pragma once #include #include +#include +#include +#include +#include class GraphPanel : public wxPanel { public: GraphPanel(wxWindow* parent); + void AddDataPoint(const std::string& sensorName, double value, double timestamp); + void SetVisibleSensors(const std::set& visisble); + private: mpWindow* m_plot; + + //store data for each sensor + std::map>> sensorData_; + std::map sensorLayers_; + + std::set visibleSensors_; + + void UpdateGraph(); }; \ No newline at end of file diff --git a/client/include/client/mainframe.hpp b/client/include/client/mainframe.hpp index d1998de8..e4dee36e 100644 --- a/client/include/client/mainframe.hpp +++ b/client/include/client/mainframe.hpp @@ -14,6 +14,7 @@ #include "client/sensor_data_panel.h" #include "client/sensor_manager.hpp" #include "client/sensor.hpp" +#include "client/graph_panel.hpp" #include @@ -68,7 +69,7 @@ class MainFrame : public wxFrame { std::shared_ptr dataBuffer_; wxTextCtrl* messageDisplay_; wxPanel* consolePanel_; - + GraphPanel* graphPanel_; }; #endif // __MAINFRAME__ \ No newline at end of file diff --git a/client/src/graph_panel.cpp b/client/src/graph_panel.cpp index a28febc4..9ee7bf25 100644 --- a/client/src/graph_panel.cpp +++ b/client/src/graph_panel.cpp @@ -9,7 +9,7 @@ GraphPanel::GraphPanel(wxWindow* parent) // Create X & Y axes mpScaleX* xAxis = new mpScaleX(wxT("Time (seconds)"), mpALIGN_BORDER_BOTTOM, true); - mpScaleY* yAxis = new mpScaleY(wxT("y"), mpALIGN_BORDER_LEFT, true); + mpScaleY* yAxis = new mpScaleY(wxT("value"), mpALIGN_BORDER_LEFT, true); xAxis->SetDrawOutsideMargins(false); yAxis->SetDrawOutsideMargins(false); @@ -24,3 +24,67 @@ GraphPanel::GraphPanel(wxWindow* parent) sizer->Add(m_plot, 1, wxEXPAND); SetSizer(sizer); } + +void GraphPanel::AddDataPoint(const std::string& sensorName, double value, double timestamp){ + sensorData_[sensorName].push_back({timestamp, value}); + + // Keeps the first 100 data points + if (sensorData_[sensorName].size() > 100) { + sensorData_[sensorName].erase(sensorData_[sensorName].begin()); + } + UpdateGraph(); +} + +void GraphPanel::UpdateGraph(){ + for (auto& pair : sensorLayers_){ + m_plot->DelLayer(pair.second,true); + } + sensorLayers_.clear(); + + // colours for the graph + wxColour colours[] = { + wxColour(255, 0, 0), + wxColour(0, 255, 0), + wxColour(0, 0, 255), + wxColour(255, 102, 178), + wxColour(178, 102, 255) + }; + int colourIndex = 0; + + for(auto& sensorPair : sensorData_){ + const std::string& sensorName = sensorPair.first; + auto& data = sensorPair.second; + + if(!visibleSensors_.empty() && visibleSensors_.count(sensorName) == 0) + continue; + + if (data.empty()) continue; + + std::vector xs, ys; + for(const auto& point : data){ + xs.push_back(point.first); // timestamp + ys.push_back(point.second); // value + } + + mpFXYVector* layer = new mpFXYVector(wxString(sensorName)); + layer->SetData(xs, ys); + layer->SetContinuity(true); + + wxPen pen(colours[colourIndex % 5], 2); + layer->SetPen(pen); + + m_plot->AddLayer(layer); + sensorLayers_[sensorName] = layer; + + colourIndex++; + } + + m_plot->Fit(); + m_plot->Refresh(); + m_plot->Update(); +} + +void GraphPanel::SetVisibleSensors(const std::set& visible) { + visibleSensors_ = visible; + UpdateGraph(); +} diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index bc6b1a1a..b5f91f58 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -37,7 +37,7 @@ MainFrame::MainFrame(const wxString& title, std::shared_ptr model, // Graph panel area - GraphPanel* graphPanel = new GraphPanel(rightSplitter); + graphPanel_ = new GraphPanel(rightSplitter); // Create text control for displaying messages (Console) consolePanel_ = new wxPanel(mainSplitter); @@ -55,7 +55,7 @@ MainFrame::MainFrame(const wxString& title, std::shared_ptr model, consolePanel_->SetSizer(consoleSizer); // Assemble splitters - rightSplitter->SplitHorizontally(dataViewPanel, graphPanel, 200); + rightSplitter->SplitHorizontally(dataViewPanel, graphPanel_, 200); rightSplitter->SetMinimumPaneSize(100); rightSplitter->SetSashGravity(0.0); // keeps data panel a 180px, graph takes extra space @@ -91,6 +91,10 @@ void MainFrame::onModelUpdated() { void MainFrame::onSensorToggled() { sensorDataGrid->SetActiveSensors(sensorManager_->GetEnabledSensorNames()); + + auto enabledNames = sensorManager_->GetEnabledSensorNames(); + std::set visible(enabledNames.begin(), enabledNames.end()); + graphPanel_->SetVisibleSensors(visible); } void MainFrame::updateMessageDisplay() { @@ -144,7 +148,19 @@ void MainFrame::updateDataPanel() { sensorDataGrid->UpdateReading(latestData.datatype, (double)latestData.data, latestData.dataunit); + + auto enabledNames = sensorManager_->GetEnabledSensorNames(); + std::set visible(enabledNames.begin(), enabledNames.end()); + graphPanel_->SetVisibleSensors(visible); //std::cout << "Updated " << latestData.datatype << " with value: " << latestData.data << " " << latestData.dataunit << std::endl; + + if(graphPanel_){ + graphPanel_->AddDataPoint( + latestData.datatype, + (double)latestData.data, + (double)latestData.timestamp + ); + } } } } @@ -235,3 +251,4 @@ void MainFrame::OnSettingsOpen(wxCommandEvent& event) { SettingsDialog dialog(this); dialog.ShowModal(); } + From b4804cfb0b8ceee0e9b3881a058f4e64d4a706f6 Mon Sep 17 00:00:00 2001 From: Henry van Weelderen Date: Sat, 21 Mar 2026 13:24:30 -0700 Subject: [PATCH 12/14] fix to ptr --- client/include/client/mainframe.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/client/include/client/mainframe.hpp b/client/include/client/mainframe.hpp index 7b655a6e..44637427 100644 --- a/client/include/client/mainframe.hpp +++ b/client/include/client/mainframe.hpp @@ -69,6 +69,7 @@ class MainFrame : public wxFrame { std::shared_ptr dataBuffer_; wxTextCtrl* messageDisplay_; wxPanel* consolePanel_; + GraphPanel* graphPanel_; wxTimer updateTimer_; std::atomic updatePending_{false}; From 8f05e9d35f15de045d7f1dcf21affe1dbd5b4a6b Mon Sep 17 00:00:00 2001 From: Bianca Date: Sat, 21 Mar 2026 13:35:43 -0700 Subject: [PATCH 13/14] added header for atomic types --- client/include/client/mainframe.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/client/include/client/mainframe.hpp b/client/include/client/mainframe.hpp index 44637427..d5e74de3 100644 --- a/client/include/client/mainframe.hpp +++ b/client/include/client/mainframe.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "client/sensor_data_panel.h" #include "client/sensor_manager.hpp" #include "client/sensor.hpp" From 1a6b052f75ce4ffca4a97c6c10fde4af40d6184d Mon Sep 17 00:00:00 2001 From: Henry van Weelderen Date: Sat, 21 Mar 2026 14:17:50 -0700 Subject: [PATCH 14/14] Update fix to graph --- client/include/client/DataBuffer.hpp | 2 ++ client/src/DataBuffer.cpp | 4 ++++ client/src/mainframe.cpp | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/client/include/client/DataBuffer.hpp b/client/include/client/DataBuffer.hpp index 6f56e3ac..3af69a9b 100644 --- a/client/include/client/DataBuffer.hpp +++ b/client/include/client/DataBuffer.hpp @@ -43,6 +43,8 @@ class DataBuffer : public BufferBase { // Removes extracted portion from buffer std::string extractNextJson(); + buffer_data_t consumeFront(); + // --------------------------- // JSON PARSING diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index c9c95d2b..7156289d 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -56,6 +56,10 @@ size_t DataBuffer::size() const { return BufferBase::size(); } +buffer_data_t DataBuffer::consumeFront() { + return BufferBase::extractNextBuffer(); +} + void DataBuffer::clear() { BufferBase::clear(); } diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index 69164fd1..520bc8aa 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -138,7 +138,7 @@ void MainFrame::updateDataPanel() { if (dataBuffer_->size() > 0) { - for (buffer_data_t latestData : dataBuffer_->readAll()) { + for (buffer_data_t latestData : dataBuffer_->consume()) { // Skip entries with no data-type (first sensor reading) if (latestData.datatype.empty()) continue;