From 97249cd9bd813edd74ae9d939eb8ba01188caddb Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Feb 2026 16:11:11 -0800 Subject: [PATCH 01/26] working on passing and processing commands through terminal --- client/CMakeLists.txt | 2 + client/include/client/DataBuffer.hpp | 4 ++ client/include/client/command_processor.hpp | 24 +++++++++ client/include/common/panorama_defines.hpp | 4 ++ client/src/DataBuffer.cpp | 36 ++++++++++++++ client/src/command_processor.cpp | 54 +++++++++++++++++++++ client/src/main.cpp | 17 +++++++ tools/pserver/pserver.py | 4 +- 8 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 client/include/client/command_processor.hpp create mode 100644 client/src/command_processor.cpp diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 6b0194f3..879d9d1f 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -15,6 +15,7 @@ set(CLIENT_SOURCES src/config_manager.cpp src/data_logger.cpp src/settings_dialog.cpp + src/command_processor.cpp ) set(COMMON_HEADERS @@ -37,6 +38,7 @@ set(CLIENT_HEADERS include/client/config_manager.hpp include/client/data_logger.hpp include/client/settings_dialog.hpp + include/client/command_processor.hpp ) # Executable settings diff --git a/client/include/client/DataBuffer.hpp b/client/include/client/DataBuffer.hpp index 6f56e3ac..5e5062ca 100644 --- a/client/include/client/DataBuffer.hpp +++ b/client/include/client/DataBuffer.hpp @@ -63,6 +63,10 @@ class DataBuffer : public BufferBase { void exportBuffer(std::string exportPath); + void convertData(std::string dataType, std::string targetUnit); + + void addOffset(std::string dataType, float offsetValue); + private: // Raw buffer storing incoming data std::list buffer_; diff --git a/client/include/client/command_processor.hpp b/client/include/client/command_processor.hpp new file mode 100644 index 00000000..c8409a31 --- /dev/null +++ b/client/include/client/command_processor.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "client/DataBuffer.hpp" +#include +#include +#include +#include + +class DataBuffer; + +class CommandProcessor { +public: + CommandProcessor(std::shared_ptr dataBuffer); + void start(); //run command loop + void stop(); + +private: + std::shared_ptr dataBuffer_; + std::string command; + std::atomic running_{true}; + + void processCommand(const std::string& command); +}; + diff --git a/client/include/common/panorama_defines.hpp b/client/include/common/panorama_defines.hpp index 8880cd4c..2c18f727 100644 --- a/client/include/common/panorama_defines.hpp +++ b/client/include/common/panorama_defines.hpp @@ -6,5 +6,9 @@ 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; + std::string sensorID; + */ } buffer_data_t; \ No newline at end of file diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index d27976fa..826a6bab 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -161,4 +161,40 @@ void DataBuffer::exportBuffer(std::string exportPath) { fputs(json_content.c_str(), fp); fclose(fp); return; +} + +void DataBuffer::convertData(std::string dataType, std::string targetUnit) { + //convert all data of type dataType in buffer_ to targetUnit + //e.g. if dataType = "temperature" and targetUnit = "F", convert all temperature data in buffer_ to Fahrenheit + std::lock_guard lock(mutex_); + + for (auto& item : buffer_) { + if (item.datatype == dataType) { + if (dataType == "temperature") { + if (targetUnit == "farenheit") { + // Convert Celsius to Fahrenheit + item.data = item.data * 9.0 / 5.0 + 32; + item.dataunit = "farenheit"; + } else if (targetUnit == "celcius") { + // Convert Fahrenheit to Celsius + item.data = (item.data - 32) * 5.0 / 9.0; + item.dataunit = "celcius"; + } + } + // Add more data types and unit conversions as needed + } + } +} + +void DataBuffer::addOffset(std::string dataType, float offsetValue) { + //add offsetValue to all data of type dataType in buffer_ + //e.g. if dataType = "temperature" and offsetValue = "5", add 5 to all temperature data in buffer_ + std::lock_guard lock(mutex_); + + //TO DO: print to the screen the buffer data after adding offset for debugging + for (auto& item : buffer_) { + if (item.datatype == dataType) { + item.data += offsetValue; + } + } } \ No newline at end of file diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp new file mode 100644 index 00000000..5828adfc --- /dev/null +++ b/client/src/command_processor.cpp @@ -0,0 +1,54 @@ +#include "client/command_processor.hpp" +#include "client/DataBuffer.hpp" + + +/* +TO DO: add stop mechanism for command processor thread, currently it runs indefinitely and can only be stopped by exiting the program +TO DO: add command_processor in onExit() in main.cpp +TO DO: handle race conditions between command processor and tcp client both accessing data buffer using mutex +*/ + +CommandProcessor::CommandProcessor(std::shared_ptr dataBuffer) + : dataBuffer_(dataBuffer) { + +} + +void CommandProcessor::start() { + while(running_) { + std::cout << "Enter command (type 'exit' to quit): "; + std::getline(std::cin, command); + + if (command == "exit") { + break; + } + + processCommand(command); + } +} + +void CommandProcessor::stop() { + running_ = false; +} + +void CommandProcessor::processCommand(const std::string& command) { + /* + command: + conversion + add_offset + */ + std::string cmdType = command.substr(0, command.find(' ')); + + //get the parameters from command + size_t firstSpace = command.find(' '); + size_t secondSpace = command.find(' ', firstSpace + 1); + std::string firstParameter = command.substr(firstSpace + 1, secondSpace - firstSpace - 1); + std::string secondParameter = command.substr(secondSpace + 1); + + if(cmdType == "conversion") { + dataBuffer_->convertData(firstParameter, secondParameter); + } else if (cmdType == "add_offset") { + dataBuffer_->addOffset(firstParameter, std::stof(secondParameter)); + } else { + std::cout << "Unknown command: " << command << std::endl; + } +} \ No newline at end of file diff --git a/client/src/main.cpp b/client/src/main.cpp index 4025d35f..ba646d4d 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/command_processor.hpp" #include using namespace std; @@ -134,6 +135,11 @@ class PanoramaClient : public wxApp { // --- Create DataBuffer --- dataBuffer_ = std::make_shared(runtimeDir + "/data"); + // --- Create CommandProcessor on a separate thread--- + cmdProcessor_ = std::make_shared(dataBuffer_); + cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); + + // --- Create and start TCP client on separate thread --- tcpClient_ = std::make_unique("127.0.0.1", 3000, model_, dataBuffer_, dataLogger_); tcpClient_->start(); @@ -172,6 +178,15 @@ class PanoramaClient : public wxApp { if (tcpClient_) { tcpClient_->stop(); } + + if (cmdProcessor_) { + cmdProcessor_->stop(); + } + + if (cmdThread_ && cmdThread_->joinable()) { + cmdThread_->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 cmdProcessor_; + std::unique_ptr cmdThread_; }; wxIMPLEMENT_APP(PanoramaClient); diff --git a/tools/pserver/pserver.py b/tools/pserver/pserver.py index 3bf80d83..24335ff9 100644 --- a/tools/pserver/pserver.py +++ b/tools/pserver/pserver.py @@ -25,7 +25,7 @@ def send_stream(self, client_socket, client_address, streamer: PStreamer): while True: data = streamer.get_data(timeout=2.0) if data is None: - pwarning("PServer", "No data available from streamer") + #pwarning("PServer", "No data available from streamer") continue client_socket.sendall(data) @@ -49,7 +49,7 @@ def main(): server = PServer() streamer = PStreamer() - streamer.build_stream(PStreamJSON()).set_interval(0.4) + streamer.build_stream(PStreamJSON()).set_interval(10.0) # Set interval to 10 seconds for testing streamer.start() From 0bd849c2b7d4991a84450964ba8be2bb71787a64 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Feb 2026 16:55:04 -0800 Subject: [PATCH 02/26] added toStringAll(), clear(), and size() to the command processing; now they can be called through terminal --- client/include/client/DataBuffer.hpp | 4 ---- client/include/client/buffer_base.hpp | 2 +- client/src/DataBuffer.cpp | 32 ++++++++++----------------- client/src/command_processor.cpp | 19 +++++++++++----- client/src/main.cpp | 8 +++---- tools/pserver/pserver.py | 4 ++-- 6 files changed, 32 insertions(+), 37 deletions(-) diff --git a/client/include/client/DataBuffer.hpp b/client/include/client/DataBuffer.hpp index 5e5062ca..6f56e3ac 100644 --- a/client/include/client/DataBuffer.hpp +++ b/client/include/client/DataBuffer.hpp @@ -63,10 +63,6 @@ class DataBuffer : public BufferBase { void exportBuffer(std::string exportPath); - void convertData(std::string dataType, std::string targetUnit); - - void addOffset(std::string dataType, float offsetValue); - private: // Raw buffer storing incoming data std::list buffer_; diff --git a/client/include/client/buffer_base.hpp b/client/include/client/buffer_base.hpp index 81b5a1fe..9d1115bc 100644 --- a/client/include/client/buffer_base.hpp +++ b/client/include/client/buffer_base.hpp @@ -99,5 +99,5 @@ class BufferBase { std::list buffer_; mutable std::mutex mutex_; int MAX_BUFFER_SIZE = 5; - int FLUSH_THRESHOLD = 50; //percentage + int FLUSH_THRESHOLD = 100; //percentage }; diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index 826a6bab..0b34c631 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -163,38 +163,30 @@ void DataBuffer::exportBuffer(std::string exportPath) { return; } -void DataBuffer::convertData(std::string dataType, std::string targetUnit) { - //convert all data of type dataType in buffer_ to targetUnit - //e.g. if dataType = "temperature" and targetUnit = "F", convert all temperature data in buffer_ to Fahrenheit +/* +void DataBuffer::replaceData(std::string dataType, float targetValue) { + //replace all data of type dataType in buffer_ with targetValue std::lock_guard lock(mutex_); - for (auto& item : buffer_) { + std::cout << "Replacing data of type " << dataType << " with value " << targetValue << std::endl; + std::cout << "Buffer before replaceData: " << toStringAll() << std::endl; + + for (auto& item : readAll()) { if (item.datatype == dataType) { - if (dataType == "temperature") { - if (targetUnit == "farenheit") { - // Convert Celsius to Fahrenheit - item.data = item.data * 9.0 / 5.0 + 32; - item.dataunit = "farenheit"; - } else if (targetUnit == "celcius") { - // Convert Fahrenheit to Celsius - item.data = (item.data - 32) * 5.0 / 9.0; - item.dataunit = "celcius"; - } - } - // Add more data types and unit conversions as needed + item.data = targetValue; + std::cout << "Replaced data of type " << dataType << " with value " << targetValue << std::endl; } } } void DataBuffer::addOffset(std::string dataType, float offsetValue) { //add offsetValue to all data of type dataType in buffer_ - //e.g. if dataType = "temperature" and offsetValue = "5", add 5 to all temperature data in buffer_ std::lock_guard lock(mutex_); - //TO DO: print to the screen the buffer data after adding offset for debugging - for (auto& item : buffer_) { + for (auto& item : readAll()) { if (item.datatype == dataType) { item.data += offsetValue; } } -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp index 5828adfc..556822c9 100644 --- a/client/src/command_processor.cpp +++ b/client/src/command_processor.cpp @@ -22,6 +22,7 @@ void CommandProcessor::start() { break; } + processCommand(command); } } @@ -33,8 +34,7 @@ void CommandProcessor::stop() { void CommandProcessor::processCommand(const std::string& command) { /* command: - conversion - add_offset + */ std::string cmdType = command.substr(0, command.find(' ')); @@ -44,11 +44,18 @@ void CommandProcessor::processCommand(const std::string& command) { std::string firstParameter = command.substr(firstSpace + 1, secondSpace - firstSpace - 1); std::string secondParameter = command.substr(secondSpace + 1); - if(cmdType == "conversion") { - dataBuffer_->convertData(firstParameter, secondParameter); - } else if (cmdType == "add_offset") { - dataBuffer_->addOffset(firstParameter, std::stof(secondParameter)); + if(cmdType == "toStringAll") { + std::cout << dataBuffer_->toStringAll() << std::endl; + + } else if (cmdType == "clear") { + dataBuffer_->clear(); + + } else if (cmdType == "size"){ + std::cout << "Buffer size: " << dataBuffer_->size() << std::endl; + } else { std::cout << "Unknown command: " << command << std::endl; + } + } \ No newline at end of file diff --git a/client/src/main.cpp b/client/src/main.cpp index ba646d4d..a64a7874 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -134,10 +134,6 @@ class PanoramaClient : public wxApp { // --- Create DataBuffer --- dataBuffer_ = std::make_shared(runtimeDir + "/data"); - - // --- Create CommandProcessor on a separate thread--- - cmdProcessor_ = std::make_shared(dataBuffer_); - cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); // --- Create and start TCP client on separate thread --- @@ -155,6 +151,10 @@ class PanoramaClient : public wxApp { return true; } + // --- Create CommandProcessor on a separate thread--- + cmdProcessor_ = std::make_shared(dataBuffer_); + cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); + // --- Create view --- MainFrame* w = new MainFrame("Panorama Client", model_, dataBuffer_); w->Show(); diff --git a/tools/pserver/pserver.py b/tools/pserver/pserver.py index 24335ff9..3bf80d83 100644 --- a/tools/pserver/pserver.py +++ b/tools/pserver/pserver.py @@ -25,7 +25,7 @@ def send_stream(self, client_socket, client_address, streamer: PStreamer): while True: data = streamer.get_data(timeout=2.0) if data is None: - #pwarning("PServer", "No data available from streamer") + pwarning("PServer", "No data available from streamer") continue client_socket.sendall(data) @@ -49,7 +49,7 @@ def main(): server = PServer() streamer = PStreamer() - streamer.build_stream(PStreamJSON()).set_interval(10.0) # Set interval to 10 seconds for testing + streamer.build_stream(PStreamJSON()).set_interval(0.4) streamer.start() From 047401d5cb7603eb9e200a0a4f079c3e30873f64 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Feb 2026 17:18:44 -0800 Subject: [PATCH 03/26] added printAll method in dataBuffer to print the buffer contents to the terminal --- client/include/client/DataBuffer.hpp | 2 ++ client/src/DataBuffer.cpp | 35 +++++----------------------- client/src/command_processor.cpp | 5 ++-- 3 files changed, 11 insertions(+), 31 deletions(-) diff --git a/client/include/client/DataBuffer.hpp b/client/include/client/DataBuffer.hpp index 6f56e3ac..30acbedb 100644 --- a/client/include/client/DataBuffer.hpp +++ b/client/include/client/DataBuffer.hpp @@ -61,6 +61,8 @@ class DataBuffer : public BufferBase { std::string toStringAll(); + void printAll(); + void exportBuffer(std::string exportPath); private: diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index 0b34c631..912cc4fb 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -144,6 +144,11 @@ std::string DataBuffer::toStringAll() { return res; } +void DataBuffer::printAll() { + //print all buffer_ as string + std::cout << toStringAll() << std::endl; +} + void DataBuffer::exportBuffer(std::string exportPath) { //Export the entire buffer (make a local JSON file under client/src/) @@ -161,32 +166,4 @@ void DataBuffer::exportBuffer(std::string exportPath) { fputs(json_content.c_str(), fp); fclose(fp); return; -} - -/* -void DataBuffer::replaceData(std::string dataType, float targetValue) { - //replace all data of type dataType in buffer_ with targetValue - std::lock_guard lock(mutex_); - - std::cout << "Replacing data of type " << dataType << " with value " << targetValue << std::endl; - std::cout << "Buffer before replaceData: " << toStringAll() << std::endl; - - for (auto& item : readAll()) { - if (item.datatype == dataType) { - item.data = targetValue; - std::cout << "Replaced data of type " << dataType << " with value " << targetValue << std::endl; - } - } -} - -void DataBuffer::addOffset(std::string dataType, float offsetValue) { - //add offsetValue to all data of type dataType in buffer_ - std::lock_guard lock(mutex_); - - for (auto& item : readAll()) { - if (item.datatype == dataType) { - item.data += offsetValue; - } - } -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp index 556822c9..e0e9cac3 100644 --- a/client/src/command_processor.cpp +++ b/client/src/command_processor.cpp @@ -44,8 +44,8 @@ void CommandProcessor::processCommand(const std::string& command) { std::string firstParameter = command.substr(firstSpace + 1, secondSpace - firstSpace - 1); std::string secondParameter = command.substr(secondSpace + 1); - if(cmdType == "toStringAll") { - std::cout << dataBuffer_->toStringAll() << std::endl; + if(cmdType == "printAll") { + dataBuffer_->printAll(); } else if (cmdType == "clear") { dataBuffer_->clear(); @@ -58,4 +58,5 @@ void CommandProcessor::processCommand(const std::string& command) { } + //add more commands as needed } \ No newline at end of file From 0a2b78e30b59c53d733dfdf4328b8e5139aa553f Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 7 Mar 2026 14:18:22 -0800 Subject: [PATCH 04/26] added sensorID and sensor name in panorama defines --- client/include/common/panorama_defines.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/include/common/panorama_defines.hpp b/client/include/common/panorama_defines.hpp index 2c18f727..844a3c1a 100644 --- a/client/include/common/panorama_defines.hpp +++ b/client/include/common/panorama_defines.hpp @@ -6,9 +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; - std::string sensorID; - */ + int sensorID; } buffer_data_t; \ No newline at end of file From 7a04ff6641370a52e484d413b1e768f5c048c103 Mon Sep 17 00:00:00 2001 From: Henry van Weelderen Date: Sat, 21 Mar 2026 14:43:20 -0700 Subject: [PATCH 05/26] initial creation data filter --- client/include/client/data_filters.hpp | 15 +++++++++++++++ client/src/data_filters.cpp | 9 +++++++++ 2 files changed, 24 insertions(+) create mode 100644 client/include/client/data_filters.hpp create mode 100644 client/src/data_filters.cpp diff --git a/client/include/client/data_filters.hpp b/client/include/client/data_filters.hpp new file mode 100644 index 00000000..537c6efa --- /dev/null +++ b/client/include/client/data_filters.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +class DataFilters { +public: + DataFilters(); + + int kalmanFilter(int input); + +private: + std::list kalmanList; + int MAX_KALMAN_SIZE; +}; \ No newline at end of file diff --git a/client/src/data_filters.cpp b/client/src/data_filters.cpp new file mode 100644 index 00000000..3822be2a --- /dev/null +++ b/client/src/data_filters.cpp @@ -0,0 +1,9 @@ +#include "client/data_filters.hpp"; + + +DataFilters::DataFilters() {} + + +int DataFilters::kalmanFilter(int input) { + +} \ No newline at end of file From 5ede9a2951d4444f4a6bd1c4b324c52cb377aa02 Mon Sep 17 00:00:00 2001 From: Henry van Weelderen Date: Fri, 10 Apr 2026 19:25:18 -0700 Subject: [PATCH 06/26] to fix enter info --- client/include/client/command_processor.hpp | 7 +- client/include/client/graph_panel.hpp | 8 +- client/include/client/mainframe.hpp | 8 +- client/include/client/post_processing.hpp | 21 +++ client/src/DataBuffer.cpp | 19 +-- client/src/command_processor.cpp | 21 ++- client/src/graph_panel.cpp | 18 ++- client/src/json_reader.cpp | 6 +- client/src/main.cpp | 15 +- client/src/mainframe.cpp | 30 +++- client/src/post_processing.cpp | 36 +++++ tools/client_hasna.py | 49 ------- tools/server_hasna.py | 146 -------------------- 13 files changed, 147 insertions(+), 237 deletions(-) create mode 100644 client/include/client/post_processing.hpp create mode 100644 client/src/post_processing.cpp delete mode 100644 tools/client_hasna.py delete mode 100644 tools/server_hasna.py diff --git a/client/include/client/command_processor.hpp b/client/include/client/command_processor.hpp index c8409a31..a84eacba 100644 --- a/client/include/client/command_processor.hpp +++ b/client/include/client/command_processor.hpp @@ -1,21 +1,22 @@ #pragma once #include "client/DataBuffer.hpp" +#include "client/post_processing.hpp" + #include #include #include #include -class DataBuffer; - class CommandProcessor { public: - CommandProcessor(std::shared_ptr dataBuffer); + CommandProcessor(std::shared_ptr dataBuffer, std::shared_ptr postProcessor); void start(); //run command loop void stop(); private: std::shared_ptr dataBuffer_; + std::shared_ptr postProcessor_; std::string command; std::atomic running_{true}; diff --git a/client/include/client/graph_panel.hpp b/client/include/client/graph_panel.hpp index e49f20eb..e67ac485 100644 --- a/client/include/client/graph_panel.hpp +++ b/client/include/client/graph_panel.hpp @@ -5,10 +5,13 @@ #include #include #include +#include +#include +#include "client/post_processing.hpp" class GraphPanel : public wxPanel { public: - GraphPanel(wxWindow* parent); + GraphPanel(wxWindow* parent, std::shared_ptr postProcessor); void AddDataPoint(const std::string& sensorName, double value, double timestamp); void SetVisibleSensors(const std::set& visisble); @@ -29,5 +32,6 @@ class GraphPanel : public wxPanel { void DrawAxes(wxDC& dc); void UpdateGraph(); - wxDECLARE_EVENT_TABLE(); + std::shared_ptr postProcessor_; + std::mutex dataMutex_; }; \ No newline at end of file diff --git a/client/include/client/mainframe.hpp b/client/include/client/mainframe.hpp index 249c7b99..220b3dc6 100644 --- a/client/include/client/mainframe.hpp +++ b/client/include/client/mainframe.hpp @@ -10,12 +10,14 @@ #include #include #include +#include #include #include #include "client/sensor_data_panel.h" #include "client/sensor_manager.hpp" #include "client/sensor.hpp" #include "client/graph_panel.hpp" +#include "client/post_processing.hpp" #include class MessageModel; @@ -45,7 +47,7 @@ class MainFrame : public wxFrame { }; MainFrame(const wxString& title, std::shared_ptr model, - std::shared_ptr dataBuffer, + std::shared_ptr dataBuffer, std::shared_ptr postProcessor, TcpClient* tcpClient, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(1200, 800)); @@ -96,7 +98,9 @@ class MainFrame : public wxFrame { wxPanel* esp32Banner_ = nullptr; wxBoxSizer* mainSizer_ = nullptr; std::atomic esp32BannerPending_{false}; - bool esp32BannerVisible_ = false; + bool esp32BannerVisible_ = false; std::shared_ptr postProcessor_; + + }; #endif // __MAINFRAME__ \ No newline at end of file diff --git a/client/include/client/post_processing.hpp b/client/include/client/post_processing.hpp new file mode 100644 index 00000000..501aea85 --- /dev/null +++ b/client/include/client/post_processing.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +class PostProcessing { +public: + PostProcessing(); + float processData(float data); + void reset(); + void addOffset(float offset); + void addScaling(float scaleFactor); + void updateDataBase(); + +private: + float currentOffset = 0.0; + float currentScaleFactor = 1.0; + std::mutex mutex_; + +}; \ No newline at end of file diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index 13063b31..7705231e 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -12,19 +12,10 @@ DataBuffer::~DataBuffer() { } void DataBuffer::writeData(buffer_data_t jsonChunk) { - // Append the new chunk of raw JSON data to the buffer. This functions only job is to store raw - // inbound data in a way that doesn't lose anything later the parser will call extractNextJson() - // parseNextJson() - // std::cout << "[DataBuffer] Writing data to buffer:" << std::endl; - // std::cout << "[DataBuffer] a: '" << jsonChunk.a << "' a_data: " << jsonChunk.a_data << std::endl; - // std::cout << "[DataBuffer] b: '" << jsonChunk.b << "' b_data: " << jsonChunk.b_data << std::endl; - // std::cout << "[DataBuffer] Buffer size before write: " << size() << std::endl; - - //get the runtime directory path from DataLogger to export buffer if it exceeds threshold - - //DataLogger logger; - //std::string exportPath = logger.getLogFilePath(); + //before writing, do necessary post processing on jsonChunk + //jsonChunk.data = PostProcessing.processData(jsonChunk.data); + write(jsonChunk); if ((int)size() > FLUSH_THRESHOLD * MAX_BUFFER_SIZE / 100) { @@ -32,9 +23,7 @@ void DataBuffer::writeData(buffer_data_t jsonChunk) { exportBuffer(logFilePath_); } - // std::cout << "[DataBuffer] Buffer size after write: " << size() << std::endl; - // std::cout << buffer_.size(); - // std::cout << "[DataBuffer] : " << toStringAll() << std::endl; + } void DataBuffer::setData(buffer_data_t jsonData) { diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp index e0e9cac3..1a559f99 100644 --- a/client/src/command_processor.cpp +++ b/client/src/command_processor.cpp @@ -1,15 +1,14 @@ #include "client/command_processor.hpp" #include "client/DataBuffer.hpp" - /* TO DO: add stop mechanism for command processor thread, currently it runs indefinitely and can only be stopped by exiting the program TO DO: add command_processor in onExit() in main.cpp TO DO: handle race conditions between command processor and tcp client both accessing data buffer using mutex */ -CommandProcessor::CommandProcessor(std::shared_ptr dataBuffer) - : dataBuffer_(dataBuffer) { +CommandProcessor::CommandProcessor(std::shared_ptr dataBuffer, std::shared_ptr postProcessor) + : dataBuffer_(dataBuffer), postProcessor_(postProcessor) { } @@ -52,10 +51,22 @@ void CommandProcessor::processCommand(const std::string& command) { } else if (cmdType == "size"){ std::cout << "Buffer size: " << dataBuffer_->size() << std::endl; - + } else if (cmdType == "reset"){ + //reset all post processing parameters to default values + postProcessor_->addOffset(0.0); + postProcessor_->addScaling(1.0); + //postProcessor_->reset(); + //std::cout << "Post processing parameters reset to default values." << std::endl; + } else if (cmdType == "setOffset"){ + float offset = std::stof(firstParameter); + postProcessor_->addOffset(offset); + //std::cout << "Offset set to: " << offset << std::endl; + } else if (cmdType == "setScale"){ + float scaleFactor = std::stof(firstParameter); + postProcessor_->addScaling(scaleFactor); + //std::cout << "Scale factor set to: " << scaleFactor << std::endl; } else { std::cout << "Unknown command: " << command << std::endl; - } //add more commands as needed diff --git a/client/src/graph_panel.cpp b/client/src/graph_panel.cpp index 82153b91..4607aa1f 100644 --- a/client/src/graph_panel.cpp +++ b/client/src/graph_panel.cpp @@ -9,8 +9,8 @@ wxBEGIN_EVENT_TABLE(GraphPanel, wxPanel) EVT_SIZE(GraphPanel::OnSize) wxEND_EVENT_TABLE() -GraphPanel::GraphPanel(wxWindow* parent) - : wxPanel(parent, wxID_ANY) { +GraphPanel::GraphPanel(wxWindow* parent, std::shared_ptr postProcessor) + : wxPanel(parent, wxID_ANY), postProcessor_(postProcessor) { m_plot = new mpWindow(this, wxID_ANY); m_plot-> EnableDoubleBuffer(true); @@ -130,6 +130,8 @@ void GraphPanel::DrawAxes(wxDC& dc){ } void GraphPanel::AddDataPoint(const std::string& sensorName, double value, double timestamp){ + std::lock_guard lock(dataMutex_); + sensorData_[sensorName].push_back({timestamp, value}); // Keeps the first 100 data points @@ -140,11 +142,13 @@ void GraphPanel::AddDataPoint(const std::string& sensorName, double value, doubl } void GraphPanel::UpdateGraph(){ + std::lock_guard lock(dataMutex_); + for (auto& pair : sensorLayers_){ m_plot->DelLayer(pair.second,true); } sensorLayers_.clear(); - + // colours for the graph wxColour colours[] = { wxColour(255, 0, 0), @@ -167,7 +171,13 @@ void GraphPanel::UpdateGraph(){ std::vector xs, ys; for(const auto& point : data){ xs.push_back(point.first); // timestamp - ys.push_back(point.second); // value + + //add necessary offset and scaling to the data point before plotting + auto value = point.second; + value = postProcessor_->processData(value); + //std::cout << "Processed value for sensor " << sensorName << ": " << value << std::endl; // Debug output + //ys.push_back(point.second); // value + ys.push_back(value); // value } mpFXYVector* layer = new mpFXYVector(wxString(sensorName)); diff --git a/client/src/json_reader.cpp b/client/src/json_reader.cpp index a1e79834..61b443bb 100644 --- a/client/src/json_reader.cpp +++ b/client/src/json_reader.cpp @@ -43,7 +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; + //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; @@ -101,10 +101,6 @@ buffer_data_t JsonReader::exportToBuffer(std::string json) { ret.timestamp = (long) doc["timestamp"].GetInt(); } - - - - //ret.timestamp = std::time(nullptr); return ret; diff --git a/client/src/main.cpp b/client/src/main.cpp index 99f70371..f03b6430 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -173,12 +173,14 @@ class PanoramaClient : public wxApp { jsonWriter_ = std::make_shared(dataBuffer_, runtimeDir); jsonWriterThread_ = std::make_unique(&JsonWriter::start, jsonWriter_); + auto postProcessor = std::make_shared(); + // --- Create CommandProcessor on a separate thread--- - cmdProcessor_ = std::make_shared(dataBuffer_); + cmdProcessor_ = std::make_shared(dataBuffer_, postProcessor); cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); // --- Create view --- - MainFrame* w = new MainFrame("Panorama Client", model_, dataBuffer_, tcpClient_.get()); + MainFrame* w = new MainFrame("Panorama Client", model_, dataBuffer_, tcpClient_.get(), postProcessor); w->Show(); @@ -201,11 +203,18 @@ class PanoramaClient : public wxApp { tcpClient_->stop(); } + //clean shutdown of command processor + if (cmdProcessor_) { + cmdProcessor_->stop(); + } + if (cmdThread_ && cmdThread_->joinable()) { + cmdThread_->join(); + } + // Clean shutdown of JSON writer if (jsonWriter_) { jsonWriter_->stop(); } - if (jsonWriterThread_ && jsonWriterThread_->joinable()) { jsonWriterThread_->join(); } diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index 224c9e6d..dc59f7c4 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -16,10 +16,10 @@ #include MainFrame::MainFrame(const wxString& title, std::shared_ptr model, - std::shared_ptr dataBuffer, + std::shared_ptr dataBuffer, std::shared_ptr postProcessor, TcpClient* tcpClient, const wxPoint& pos, const wxSize& size) - : wxFrame(nullptr, wxID_ANY, title, pos, size), model_(model), dataBuffer_(dataBuffer), tcpClient_(tcpClient) { + : wxFrame(nullptr, wxID_ANY, title, pos, size), model_(model), dataBuffer_(dataBuffer), tcpClient_(tcpClient), postProcessor_(postProcessor) { CreateMenuBar(); @@ -83,7 +83,7 @@ MainFrame::MainFrame(const wxString& title, std::shared_ptr model, // Graph panel area - graphPanel_ = new GraphPanel(rightSplitter); + graphPanel_ = new GraphPanel(rightSplitter, postProcessor_); // Create text control for displaying messages (Console) consolePanel_ = new wxPanel(mainSplitter); @@ -158,6 +158,17 @@ void MainFrame::updateMessageDisplay() { for (size_t i = displayedMessageCount_; i < messages.size(); ++i) { messageDisplay_->AppendText(wxString::FromUTF8(messages[i].c_str()) + "\n"); } + + //print dataBuffer contents + /* + 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 @@ -169,6 +180,8 @@ void MainFrame::updateMessageDisplay() { } ++i; } + + displayedBufferCount_ = allBuffer.size(); } @@ -211,6 +224,16 @@ void MainFrame::updateDataPanel() { graphPanel_->SetVisibleSensors(visible); //std::cout << "Updated " << latestData.datatype << " with value: " << latestData.data << " " << latestData.dataunit << std::endl; + if (graphPanel_) { + graphPanel_->wxCallAfter( + &GraphPanel::AddDataPoint, + latestData.datatype, + (double)latestData.data, + (double)latestData.timestamp + ); + } + + /* if(graphPanel_){ graphPanel_->AddDataPoint( latestData.datatype, @@ -218,6 +241,7 @@ void MainFrame::updateDataPanel() { (double)latestData.timestamp ); } + */ } } } diff --git a/client/src/post_processing.cpp b/client/src/post_processing.cpp new file mode 100644 index 00000000..7f551e7e --- /dev/null +++ b/client/src/post_processing.cpp @@ -0,0 +1,36 @@ +#include "client/post_processing.hpp" + +PostProcessing::PostProcessing() { + +} + +float PostProcessing::processData(float data) { + //std::cout << "currentOffset: " << currentOffset << ", currentScaleFactor: " << currentScaleFactor << std::endl; // Debug output + std::lock_guard lock(mutex_); + return data*currentScaleFactor + currentOffset; +} + +void PostProcessing::addOffset(float offset) { + std::lock_guard lock(mutex_); + currentOffset = offset; //record the offset value + std::cout << "Offset set to: " << offset << std::endl; +} + +void PostProcessing::addScaling(float scaleFactor) { + std::lock_guard lock(mutex_); + currentScaleFactor = scaleFactor; //record the scale factor value + std::cout << "Scale factor set to: " << scaleFactor << std::endl; +} + +void PostProcessing::reset() { + //reset all post processing parameters to default values + addOffset(0); + addScaling(1.0); +} + +void PostProcessing::updateDataBase() { + //TODO: update the database accordingly + + +} + diff --git a/tools/client_hasna.py b/tools/client_hasna.py deleted file mode 100644 index 5289897b..00000000 --- a/tools/client_hasna.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -""" -esp32_client.py ---------------- -A simple TCP client that connects to the ESP32 emulator (server) -and prints telemetry data as it is received. - -Usage: - python esp32_client.py --host localhost --port 7000 -""" - -import socket -import argparse - -def main(): - # Set up command-line arguments - parser = argparse.ArgumentParser(description="Simple ESP32 telemetry client") - parser.add_argument("--host", default="localhost", help="Server host to connect to") - parser.add_argument("--port", type=int, default=7000, help="Server port to connect to") - args = parser.parse_args() - - # Create a TCP socket - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - print(f"Connecting to {args.host}:{args.port} ...") - s.connect((args.host, args.port)) - print("Connected. Listening for telemetry...\n") - - try: - # Keep receiving data from the server - buffer = b"" - while True: - data = s.recv(1024) # Read up to 1024 bytes - if not data: - print("Connection closed by server.") - break - buffer += data - - # Split incoming data by newline (each JSON record ends with \n) - while b"\n" in buffer: - line, buffer = buffer.split(b"\n", 1) - print("<-", line.decode().strip()) # Print decoded JSON line - - except KeyboardInterrupt: - print("\nStopped by user.") - except ConnectionResetError: - print("Server disconnected unexpectedly.") - -if __name__ == "__main__": - main() diff --git a/tools/server_hasna.py b/tools/server_hasna.py deleted file mode 100644 index aec424b4..00000000 --- a/tools/server_hasna.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 -""" -esp32_emulator_tcp.py ---------------------- - -This script emulates an ESP32 device that sends sensor-like data (telemetry) -over a TCP connection. You can run it either as a SERVER (default) or as a CLIENT. - -- In server mode: it waits for a connection, then sends telemetry data. -- In client mode: it connects to a remote server and sends telemetry data. - -Usage examples: - Server (listens for incoming connection): - python esp32_emulator_tcp.py --host 0.0.0.0 --port 7000 - - Client (connects to a server at a given IP): - python esp32_emulator_tcp.py --client --host 192.168.1.5 --port 7000 -""" - -import socket # Networking (TCP/UDP communication) -import time # For delays and timestamps -import json # To send structured data (JSON format) -import argparse # To handle command-line arguments -import random # To simulate random sensor readings - - -# ------------------------------------------------------------------- -# Function: build_telemetry -# Purpose: Generates a JSON string with fake ESP32-like sensor data -# ------------------------------------------------------------------- -def build_telemetry(counter): - # Create a dictionary representing sensor data - """ - data = { - "device": "esp32-emulator-tcp", # name/type of the device - "ts": int(time.time()), # current timestamp (Unix time) - "counter": counter, # packet counter, increments each send - "temperature_c": round(20 + random.uniform(-2, 2), 2), # random temp - "humidity_pct": round(50 + random.uniform(-5, 5), 1) # random humidity - } - """ - data = "Hello World" - # Convert dictionary to JSON and add newline so each record is on its own line - return json.dumps(data) + "\n" - - -# ------------------------------------------------------------------- -# Function: server_mode -# Purpose: Acts like a TCP server (the ESP32 emulator waits for a client) -# ------------------------------------------------------------------- -def server_mode(host, port, interval): - # Create a TCP socket using IPv4 - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - # Bind socket to address and port - s.bind((host, port)) - # Start listening for incoming connections (allow 1 queued connection) - s.listen(1) - print(f"Listening on {host}:{port} — waiting for client...") - - # Accept a connection (blocking until a client connects) - conn, addr = s.accept() - # Use "with" to ensure connection closes cleanly on exit - with conn: - print("Client connected:", addr) - counter = 0 - try: - # Continuous loop: generate and send telemetry every 'interval' seconds - while True: - # Generate fake telemetry data - line = build_telemetry(counter).encode("utf-8") - # Send it over the socket - conn.sendall(line) - # Print what we sent to the console (for debugging) - print("->", line.decode().strip()) - counter += 1 - # Wait for the next transmission - time.sleep(interval) - - # Handle connection loss - except BrokenPipeError: - print("Client disconnected.") - # Handle Ctrl+C (manual stop) - except KeyboardInterrupt: - print("Stopped by user.") - - -# ------------------------------------------------------------------- -# Function: client_mode -# Purpose: Acts like a TCP client (connects to a server and sends data) -# ------------------------------------------------------------------- -def client_mode(host, port, interval): - # Create a TCP socket - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - print(f"Connecting to {host}:{port} ...") - # Connect to the specified server - s.connect((host, port)) - print("Connected.") - counter = 0 - try: - # Same loop as server: generate and send telemetry repeatedly - while True: - line = build_telemetry(counter).encode("utf-8") - s.sendall(line) - print("->", line.decode().strip()) - counter += 1 - time.sleep(interval) - - except BrokenPipeError: - print("Server disconnected.") - except KeyboardInterrupt: - print("Stopped by user.") - - -# ------------------------------------------------------------------- -# Function: main -# Purpose: Entry point — parses command-line arguments and decides mode -# ------------------------------------------------------------------- -def main(): - # Create an argument parser for command-line options - p = argparse.ArgumentParser(description="ESP32 TCP emulator") - - # Add arguments - p.add_argument("--host", default="0.0.0.0", - help="Host IP to bind or connect to (default: 0.0.0.0 for server)") - p.add_argument("--port", type=int, default=7000, - help="Port number to use (default: 7000)") - p.add_argument("--interval", type=float, default=1.0, - help="Seconds between telemetry sends (default: 1.0)") - p.add_argument("--client", action="store_true", - help="Run in client mode instead of server mode") - - # Parse command-line arguments - args = p.parse_args() - - # Decide which mode to run based on --client flag - if args.client: - client_mode(args.host, args.port, args.interval) - else: - server_mode(args.host, args.port, args.interval) - - -# ------------------------------------------------------------------- -# Standard Python entry point -# ------------------------------------------------------------------- -if __name__ == "__main__": - main() From ee8c27c2448dc4ae9520da50e0cb70c3bbcdf0e1 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Mar 2026 15:02:55 -0700 Subject: [PATCH 07/26] Added post_processing files --- client/src/post_processing.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/post_processing.cpp b/client/src/post_processing.cpp index 7f551e7e..c67fa1ad 100644 --- a/client/src/post_processing.cpp +++ b/client/src/post_processing.cpp @@ -23,9 +23,10 @@ void PostProcessing::addScaling(float scaleFactor) { } void PostProcessing::reset() { + std::lock_guard lock(mutex_); //reset all post processing parameters to default values - addOffset(0); - addScaling(1.0); + addOffset(0); //reset offset to 0; + addScaling(1.0); //reset scale factor to 1; } void PostProcessing::updateDataBase() { From 8eac2922439f2f0a8f41bdf58352d32407ea9b40 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Mar 2026 15:44:48 -0700 Subject: [PATCH 08/26] Fixed a deadlock in reset method --- client/include/client/graph_panel.hpp | 2 -- client/src/command_processor.cpp | 11 +++++------ client/src/graph_panel.cpp | 8 +------- client/src/main.cpp | 1 + client/src/mainframe.cpp | 14 +++----------- client/src/post_processing.cpp | 5 ++--- 6 files changed, 12 insertions(+), 29 deletions(-) diff --git a/client/include/client/graph_panel.hpp b/client/include/client/graph_panel.hpp index e67ac485..e5dfd45a 100644 --- a/client/include/client/graph_panel.hpp +++ b/client/include/client/graph_panel.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include "client/post_processing.hpp" class GraphPanel : public wxPanel { @@ -33,5 +32,4 @@ class GraphPanel : public wxPanel { void UpdateGraph(); std::shared_ptr postProcessor_; - std::mutex dataMutex_; }; \ No newline at end of file diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp index 1a559f99..e48d1bbe 100644 --- a/client/src/command_processor.cpp +++ b/client/src/command_processor.cpp @@ -51,20 +51,19 @@ void CommandProcessor::processCommand(const std::string& command) { } else if (cmdType == "size"){ std::cout << "Buffer size: " << dataBuffer_->size() << std::endl; + } else if (cmdType == "reset"){ //reset all post processing parameters to default values - postProcessor_->addOffset(0.0); - postProcessor_->addScaling(1.0); - //postProcessor_->reset(); - //std::cout << "Post processing parameters reset to default values." << std::endl; + postProcessor_->reset(); + } else if (cmdType == "setOffset"){ float offset = std::stof(firstParameter); postProcessor_->addOffset(offset); - //std::cout << "Offset set to: " << offset << std::endl; + } else if (cmdType == "setScale"){ float scaleFactor = std::stof(firstParameter); postProcessor_->addScaling(scaleFactor); - //std::cout << "Scale factor set to: " << scaleFactor << std::endl; + } else { std::cout << "Unknown command: " << command << std::endl; } diff --git a/client/src/graph_panel.cpp b/client/src/graph_panel.cpp index 4607aa1f..653c21bf 100644 --- a/client/src/graph_panel.cpp +++ b/client/src/graph_panel.cpp @@ -130,8 +130,6 @@ void GraphPanel::DrawAxes(wxDC& dc){ } void GraphPanel::AddDataPoint(const std::string& sensorName, double value, double timestamp){ - std::lock_guard lock(dataMutex_); - sensorData_[sensorName].push_back({timestamp, value}); // Keeps the first 100 data points @@ -142,8 +140,6 @@ void GraphPanel::AddDataPoint(const std::string& sensorName, double value, doubl } void GraphPanel::UpdateGraph(){ - std::lock_guard lock(dataMutex_); - for (auto& pair : sensorLayers_){ m_plot->DelLayer(pair.second,true); } @@ -175,9 +171,7 @@ void GraphPanel::UpdateGraph(){ //add necessary offset and scaling to the data point before plotting auto value = point.second; value = postProcessor_->processData(value); - //std::cout << "Processed value for sensor " << sensorName << ": " << value << std::endl; // Debug output - //ys.push_back(point.second); // value - ys.push_back(value); // value + ys.push_back(value); } mpFXYVector* layer = new mpFXYVector(wxString(sensorName)); diff --git a/client/src/main.cpp b/client/src/main.cpp index f03b6430..646f917f 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -173,6 +173,7 @@ class PanoramaClient : public wxApp { jsonWriter_ = std::make_shared(dataBuffer_, runtimeDir); jsonWriterThread_ = std::make_unique(&JsonWriter::start, jsonWriter_); + // --- Create PostProcessing as a shared pointer --- auto postProcessor = std::make_shared(); // --- Create CommandProcessor on a separate thread--- diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index dc59f7c4..674645cc 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -224,16 +224,7 @@ void MainFrame::updateDataPanel() { graphPanel_->SetVisibleSensors(visible); //std::cout << "Updated " << latestData.datatype << " with value: " << latestData.data << " " << latestData.dataunit << std::endl; - if (graphPanel_) { - graphPanel_->wxCallAfter( - &GraphPanel::AddDataPoint, - latestData.datatype, - (double)latestData.data, - (double)latestData.timestamp - ); - } - - /* + if(graphPanel_){ graphPanel_->AddDataPoint( latestData.datatype, @@ -241,7 +232,8 @@ void MainFrame::updateDataPanel() { (double)latestData.timestamp ); } - */ + + } } } diff --git a/client/src/post_processing.cpp b/client/src/post_processing.cpp index c67fa1ad..7f551e7e 100644 --- a/client/src/post_processing.cpp +++ b/client/src/post_processing.cpp @@ -23,10 +23,9 @@ void PostProcessing::addScaling(float scaleFactor) { } void PostProcessing::reset() { - std::lock_guard lock(mutex_); //reset all post processing parameters to default values - addOffset(0); //reset offset to 0; - addScaling(1.0); //reset scale factor to 1; + addOffset(0); + addScaling(1.0); } void PostProcessing::updateDataBase() { From 8ef93a61188a43ebfe2f7285e755e011fa7dda84 Mon Sep 17 00:00:00 2001 From: Henry van Weelderen Date: Sat, 21 Mar 2026 14:43:20 -0700 Subject: [PATCH 09/26] initial creation data filter --- client/include/client/data_filters.hpp | 15 +++++++++++++++ client/src/data_filters.cpp | 9 +++++++++ 2 files changed, 24 insertions(+) create mode 100644 client/include/client/data_filters.hpp create mode 100644 client/src/data_filters.cpp diff --git a/client/include/client/data_filters.hpp b/client/include/client/data_filters.hpp new file mode 100644 index 00000000..537c6efa --- /dev/null +++ b/client/include/client/data_filters.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +class DataFilters { +public: + DataFilters(); + + int kalmanFilter(int input); + +private: + std::list kalmanList; + int MAX_KALMAN_SIZE; +}; \ No newline at end of file diff --git a/client/src/data_filters.cpp b/client/src/data_filters.cpp new file mode 100644 index 00000000..3822be2a --- /dev/null +++ b/client/src/data_filters.cpp @@ -0,0 +1,9 @@ +#include "client/data_filters.hpp"; + + +DataFilters::DataFilters() {} + + +int DataFilters::kalmanFilter(int input) { + +} \ No newline at end of file From 91c32522985c27f414fe2b93632d2e33a057e5d2 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Feb 2026 16:11:11 -0800 Subject: [PATCH 10/26] working on passing and processing commands through terminal --- client/include/client/DataBuffer.hpp | 4 ++ client/include/client/command_processor.hpp | 24 +++++++++ client/src/DataBuffer.cpp | 36 ++++++++++++++ client/src/command_processor.cpp | 54 +++++++++++++++++++++ client/src/main.cpp | 17 +++++++ tools/pserver/pserver.py | 4 +- 6 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 client/include/client/command_processor.hpp create mode 100644 client/src/command_processor.cpp diff --git a/client/include/client/DataBuffer.hpp b/client/include/client/DataBuffer.hpp index 3af69a9b..7cdf6178 100644 --- a/client/include/client/DataBuffer.hpp +++ b/client/include/client/DataBuffer.hpp @@ -65,6 +65,10 @@ class DataBuffer : public BufferBase { void exportBuffer(std::string exportPath); + void convertData(std::string dataType, std::string targetUnit); + + void addOffset(std::string dataType, float offsetValue); + private: // Raw buffer storing incoming data std::list buffer_; diff --git a/client/include/client/command_processor.hpp b/client/include/client/command_processor.hpp new file mode 100644 index 00000000..c8409a31 --- /dev/null +++ b/client/include/client/command_processor.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "client/DataBuffer.hpp" +#include +#include +#include +#include + +class DataBuffer; + +class CommandProcessor { +public: + CommandProcessor(std::shared_ptr dataBuffer); + void start(); //run command loop + void stop(); + +private: + std::shared_ptr dataBuffer_; + std::string command; + std::atomic running_{true}; + + void processCommand(const std::string& command); +}; + diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index 7156289d..e241a819 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -170,4 +170,40 @@ void DataBuffer::exportBuffer(std::string exportPath) { fputs(json_content.c_str(), fp); fclose(fp); return; +} + +void DataBuffer::convertData(std::string dataType, std::string targetUnit) { + //convert all data of type dataType in buffer_ to targetUnit + //e.g. if dataType = "temperature" and targetUnit = "F", convert all temperature data in buffer_ to Fahrenheit + std::lock_guard lock(mutex_); + + for (auto& item : buffer_) { + if (item.datatype == dataType) { + if (dataType == "temperature") { + if (targetUnit == "farenheit") { + // Convert Celsius to Fahrenheit + item.data = item.data * 9.0 / 5.0 + 32; + item.dataunit = "farenheit"; + } else if (targetUnit == "celcius") { + // Convert Fahrenheit to Celsius + item.data = (item.data - 32) * 5.0 / 9.0; + item.dataunit = "celcius"; + } + } + // Add more data types and unit conversions as needed + } + } +} + +void DataBuffer::addOffset(std::string dataType, float offsetValue) { + //add offsetValue to all data of type dataType in buffer_ + //e.g. if dataType = "temperature" and offsetValue = "5", add 5 to all temperature data in buffer_ + std::lock_guard lock(mutex_); + + //TO DO: print to the screen the buffer data after adding offset for debugging + for (auto& item : buffer_) { + if (item.datatype == dataType) { + item.data += offsetValue; + } + } } \ No newline at end of file diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp new file mode 100644 index 00000000..5828adfc --- /dev/null +++ b/client/src/command_processor.cpp @@ -0,0 +1,54 @@ +#include "client/command_processor.hpp" +#include "client/DataBuffer.hpp" + + +/* +TO DO: add stop mechanism for command processor thread, currently it runs indefinitely and can only be stopped by exiting the program +TO DO: add command_processor in onExit() in main.cpp +TO DO: handle race conditions between command processor and tcp client both accessing data buffer using mutex +*/ + +CommandProcessor::CommandProcessor(std::shared_ptr dataBuffer) + : dataBuffer_(dataBuffer) { + +} + +void CommandProcessor::start() { + while(running_) { + std::cout << "Enter command (type 'exit' to quit): "; + std::getline(std::cin, command); + + if (command == "exit") { + break; + } + + processCommand(command); + } +} + +void CommandProcessor::stop() { + running_ = false; +} + +void CommandProcessor::processCommand(const std::string& command) { + /* + command: + conversion + add_offset + */ + std::string cmdType = command.substr(0, command.find(' ')); + + //get the parameters from command + size_t firstSpace = command.find(' '); + size_t secondSpace = command.find(' ', firstSpace + 1); + std::string firstParameter = command.substr(firstSpace + 1, secondSpace - firstSpace - 1); + std::string secondParameter = command.substr(secondSpace + 1); + + if(cmdType == "conversion") { + dataBuffer_->convertData(firstParameter, secondParameter); + } else if (cmdType == "add_offset") { + dataBuffer_->addOffset(firstParameter, std::stof(secondParameter)); + } else { + std::cout << "Unknown command: " << command << std::endl; + } +} \ No newline at end of file diff --git a/client/src/main.cpp b/client/src/main.cpp index 7f536bd6..17dfb7c2 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -13,6 +13,7 @@ #include "client/config_manager.hpp" #include "client/data_logger.hpp" #include "client/json_writer.hpp" +#include "client/command_processor.hpp" #include using namespace std; @@ -135,6 +136,11 @@ class PanoramaClient : public wxApp { // --- Create DataBuffer --- dataBuffer_ = std::make_shared(runtimeDir + "/data"); + // --- Create CommandProcessor on a separate thread--- + cmdProcessor_ = std::make_shared(dataBuffer_); + cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); + + // --- Create and start TCP client on separate thread --- std::string tcpHost; int tcpPort; @@ -189,6 +195,15 @@ class PanoramaClient : public wxApp { tcpClient_->stop(); } + if (cmdProcessor_) { + cmdProcessor_->stop(); + } + + if (cmdThread_ && cmdThread_->joinable()) { + cmdThread_->join(); + } + + // Clean shutdown of JSON writer if (jsonWriter_) { jsonWriter_->stop(); @@ -206,6 +221,8 @@ class PanoramaClient : public wxApp { std::shared_ptr dataLogger_; std::shared_ptr dataBuffer_; std::unique_ptr tcpClient_; + std::shared_ptr cmdProcessor_; + std::unique_ptr cmdThread_; std::shared_ptr jsonWriter_; std::unique_ptr jsonWriterThread_; }; diff --git a/tools/pserver/pserver.py b/tools/pserver/pserver.py index 6eaafe77..c44ef5d2 100644 --- a/tools/pserver/pserver.py +++ b/tools/pserver/pserver.py @@ -71,7 +71,7 @@ def send_stream(self, client_socket, client_address, streamer: PStreamer): data = streamer.get_data(timeout=2.0) if data is None: - pwarning("PServer", "No data available from streamer") + #pwarning("PServer", "No data available from streamer") continue client_socket.sendall(data) @@ -95,7 +95,7 @@ def main(): server = PServer() streamer = PStreamer() - streamer.build_stream(PStreamJSON()).set_interval(0.4) + streamer.build_stream(PStreamJSON()).set_interval(10.0) # Set interval to 10 seconds for testing streamer.start() From 191c86bfe80962183b9966e9a843797a54c61adc Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Feb 2026 16:55:04 -0800 Subject: [PATCH 11/26] added toStringAll(), clear(), and size() to the command processing; now they can be called through terminal --- client/include/client/DataBuffer.hpp | 4 ---- client/include/client/buffer_base.hpp | 4 ++-- client/src/DataBuffer.cpp | 32 ++++++++++----------------- client/src/command_processor.cpp | 19 +++++++++++----- client/src/main.cpp | 8 +++---- tools/pserver/pserver.py | 4 ++-- 6 files changed, 33 insertions(+), 38 deletions(-) diff --git a/client/include/client/DataBuffer.hpp b/client/include/client/DataBuffer.hpp index 7cdf6178..3af69a9b 100644 --- a/client/include/client/DataBuffer.hpp +++ b/client/include/client/DataBuffer.hpp @@ -65,10 +65,6 @@ class DataBuffer : public BufferBase { void exportBuffer(std::string exportPath); - void convertData(std::string dataType, std::string targetUnit); - - void addOffset(std::string dataType, float offsetValue); - private: // Raw buffer storing incoming data std::list buffer_; diff --git a/client/include/client/buffer_base.hpp b/client/include/client/buffer_base.hpp index 5f5a6b09..9d1115bc 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 = 500; - int FLUSH_THRESHOLD = 50; //percentage + int MAX_BUFFER_SIZE = 5; + int FLUSH_THRESHOLD = 100; //percentage }; diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index e241a819..e69021d7 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -172,38 +172,30 @@ void DataBuffer::exportBuffer(std::string exportPath) { return; } -void DataBuffer::convertData(std::string dataType, std::string targetUnit) { - //convert all data of type dataType in buffer_ to targetUnit - //e.g. if dataType = "temperature" and targetUnit = "F", convert all temperature data in buffer_ to Fahrenheit +/* +void DataBuffer::replaceData(std::string dataType, float targetValue) { + //replace all data of type dataType in buffer_ with targetValue std::lock_guard lock(mutex_); - for (auto& item : buffer_) { + std::cout << "Replacing data of type " << dataType << " with value " << targetValue << std::endl; + std::cout << "Buffer before replaceData: " << toStringAll() << std::endl; + + for (auto& item : readAll()) { if (item.datatype == dataType) { - if (dataType == "temperature") { - if (targetUnit == "farenheit") { - // Convert Celsius to Fahrenheit - item.data = item.data * 9.0 / 5.0 + 32; - item.dataunit = "farenheit"; - } else if (targetUnit == "celcius") { - // Convert Fahrenheit to Celsius - item.data = (item.data - 32) * 5.0 / 9.0; - item.dataunit = "celcius"; - } - } - // Add more data types and unit conversions as needed + item.data = targetValue; + std::cout << "Replaced data of type " << dataType << " with value " << targetValue << std::endl; } } } void DataBuffer::addOffset(std::string dataType, float offsetValue) { //add offsetValue to all data of type dataType in buffer_ - //e.g. if dataType = "temperature" and offsetValue = "5", add 5 to all temperature data in buffer_ std::lock_guard lock(mutex_); - //TO DO: print to the screen the buffer data after adding offset for debugging - for (auto& item : buffer_) { + for (auto& item : readAll()) { if (item.datatype == dataType) { item.data += offsetValue; } } -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp index 5828adfc..556822c9 100644 --- a/client/src/command_processor.cpp +++ b/client/src/command_processor.cpp @@ -22,6 +22,7 @@ void CommandProcessor::start() { break; } + processCommand(command); } } @@ -33,8 +34,7 @@ void CommandProcessor::stop() { void CommandProcessor::processCommand(const std::string& command) { /* command: - conversion - add_offset + */ std::string cmdType = command.substr(0, command.find(' ')); @@ -44,11 +44,18 @@ void CommandProcessor::processCommand(const std::string& command) { std::string firstParameter = command.substr(firstSpace + 1, secondSpace - firstSpace - 1); std::string secondParameter = command.substr(secondSpace + 1); - if(cmdType == "conversion") { - dataBuffer_->convertData(firstParameter, secondParameter); - } else if (cmdType == "add_offset") { - dataBuffer_->addOffset(firstParameter, std::stof(secondParameter)); + if(cmdType == "toStringAll") { + std::cout << dataBuffer_->toStringAll() << std::endl; + + } else if (cmdType == "clear") { + dataBuffer_->clear(); + + } else if (cmdType == "size"){ + std::cout << "Buffer size: " << dataBuffer_->size() << std::endl; + } else { std::cout << "Unknown command: " << command << std::endl; + } + } \ No newline at end of file diff --git a/client/src/main.cpp b/client/src/main.cpp index 17dfb7c2..433f3b26 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -135,10 +135,6 @@ class PanoramaClient : public wxApp { // --- Create DataBuffer --- dataBuffer_ = std::make_shared(runtimeDir + "/data"); - - // --- Create CommandProcessor on a separate thread--- - cmdProcessor_ = std::make_shared(dataBuffer_); - cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); // --- Create and start TCP client on separate thread --- @@ -167,6 +163,10 @@ class PanoramaClient : public wxApp { return true; } + // --- Create CommandProcessor on a separate thread--- + cmdProcessor_ = std::make_shared(dataBuffer_); + cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); + // --- Create and start JSON writer on separate thread --- jsonWriter_ = std::make_shared(dataBuffer_, runtimeDir); jsonWriterThread_ = std::make_unique(&JsonWriter::start, jsonWriter_); diff --git a/tools/pserver/pserver.py b/tools/pserver/pserver.py index c44ef5d2..6eaafe77 100644 --- a/tools/pserver/pserver.py +++ b/tools/pserver/pserver.py @@ -71,7 +71,7 @@ def send_stream(self, client_socket, client_address, streamer: PStreamer): data = streamer.get_data(timeout=2.0) if data is None: - #pwarning("PServer", "No data available from streamer") + pwarning("PServer", "No data available from streamer") continue client_socket.sendall(data) @@ -95,7 +95,7 @@ def main(): server = PServer() streamer = PStreamer() - streamer.build_stream(PStreamJSON()).set_interval(10.0) # Set interval to 10 seconds for testing + streamer.build_stream(PStreamJSON()).set_interval(0.4) streamer.start() From 08b4aa62504ab5bd31120afbcf408424d98bdac6 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Feb 2026 17:18:44 -0800 Subject: [PATCH 12/26] added printAll method in dataBuffer to print the buffer contents to the terminal --- client/include/client/DataBuffer.hpp | 2 ++ client/src/DataBuffer.cpp | 35 +++++----------------------- client/src/command_processor.cpp | 5 ++-- 3 files changed, 11 insertions(+), 31 deletions(-) diff --git a/client/include/client/DataBuffer.hpp b/client/include/client/DataBuffer.hpp index 3af69a9b..8e70eb3f 100644 --- a/client/include/client/DataBuffer.hpp +++ b/client/include/client/DataBuffer.hpp @@ -63,6 +63,8 @@ class DataBuffer : public BufferBase { std::string toStringAll(); + void printAll(); + void exportBuffer(std::string exportPath); private: diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index e69021d7..13063b31 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -153,6 +153,11 @@ std::string DataBuffer::toStringAll() { return res; } +void DataBuffer::printAll() { + //print all buffer_ as string + std::cout << toStringAll() << std::endl; +} + void DataBuffer::exportBuffer(std::string exportPath) { //Export the entire buffer (make a local JSON file under client/src/) @@ -170,32 +175,4 @@ void DataBuffer::exportBuffer(std::string exportPath) { fputs(json_content.c_str(), fp); fclose(fp); return; -} - -/* -void DataBuffer::replaceData(std::string dataType, float targetValue) { - //replace all data of type dataType in buffer_ with targetValue - std::lock_guard lock(mutex_); - - std::cout << "Replacing data of type " << dataType << " with value " << targetValue << std::endl; - std::cout << "Buffer before replaceData: " << toStringAll() << std::endl; - - for (auto& item : readAll()) { - if (item.datatype == dataType) { - item.data = targetValue; - std::cout << "Replaced data of type " << dataType << " with value " << targetValue << std::endl; - } - } -} - -void DataBuffer::addOffset(std::string dataType, float offsetValue) { - //add offsetValue to all data of type dataType in buffer_ - std::lock_guard lock(mutex_); - - for (auto& item : readAll()) { - if (item.datatype == dataType) { - item.data += offsetValue; - } - } -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp index 556822c9..e0e9cac3 100644 --- a/client/src/command_processor.cpp +++ b/client/src/command_processor.cpp @@ -44,8 +44,8 @@ void CommandProcessor::processCommand(const std::string& command) { std::string firstParameter = command.substr(firstSpace + 1, secondSpace - firstSpace - 1); std::string secondParameter = command.substr(secondSpace + 1); - if(cmdType == "toStringAll") { - std::cout << dataBuffer_->toStringAll() << std::endl; + if(cmdType == "printAll") { + dataBuffer_->printAll(); } else if (cmdType == "clear") { dataBuffer_->clear(); @@ -58,4 +58,5 @@ void CommandProcessor::processCommand(const std::string& command) { } + //add more commands as needed } \ No newline at end of file From f579dd3c99b4dc74b3eb79bc62d3413753ac7619 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Mar 2026 15:00:14 -0700 Subject: [PATCH 13/26] Implemented adding offset and scaling through terminal --- client/include/client/command_processor.hpp | 7 +- client/include/client/graph_panel.hpp | 8 +- client/include/client/mainframe.hpp | 6 +- client/src/DataBuffer.cpp | 19 +-- client/src/command_processor.cpp | 21 ++- client/src/graph_panel.cpp | 18 ++- client/src/json_reader.cpp | 6 +- client/src/main.cpp | 12 +- client/src/mainframe.cpp | 30 +++- tools/client_hasna.py | 49 ------- tools/server_hasna.py | 146 -------------------- 11 files changed, 86 insertions(+), 236 deletions(-) delete mode 100644 tools/client_hasna.py delete mode 100644 tools/server_hasna.py diff --git a/client/include/client/command_processor.hpp b/client/include/client/command_processor.hpp index c8409a31..a84eacba 100644 --- a/client/include/client/command_processor.hpp +++ b/client/include/client/command_processor.hpp @@ -1,21 +1,22 @@ #pragma once #include "client/DataBuffer.hpp" +#include "client/post_processing.hpp" + #include #include #include #include -class DataBuffer; - class CommandProcessor { public: - CommandProcessor(std::shared_ptr dataBuffer); + CommandProcessor(std::shared_ptr dataBuffer, std::shared_ptr postProcessor); void start(); //run command loop void stop(); private: std::shared_ptr dataBuffer_; + std::shared_ptr postProcessor_; std::string command; std::atomic running_{true}; diff --git a/client/include/client/graph_panel.hpp b/client/include/client/graph_panel.hpp index e49f20eb..23d6e560 100644 --- a/client/include/client/graph_panel.hpp +++ b/client/include/client/graph_panel.hpp @@ -5,10 +5,13 @@ #include #include #include +#include +#include +#include "client/post_processing.hpp" class GraphPanel : public wxPanel { public: - GraphPanel(wxWindow* parent); + GraphPanel(wxWindow* parent, std::shared_ptr postProcessor); void AddDataPoint(const std::string& sensorName, double value, double timestamp); void SetVisibleSensors(const std::set& visisble); @@ -30,4 +33,7 @@ class GraphPanel : public wxPanel { void UpdateGraph(); wxDECLARE_EVENT_TABLE(); + + std::shared_ptr postProcessor_; + std::mutex dataMutex_; }; \ No newline at end of file diff --git a/client/include/client/mainframe.hpp b/client/include/client/mainframe.hpp index 249c7b99..d38fbf28 100644 --- a/client/include/client/mainframe.hpp +++ b/client/include/client/mainframe.hpp @@ -10,12 +10,14 @@ #include #include #include +#include #include #include #include "client/sensor_data_panel.h" #include "client/sensor_manager.hpp" #include "client/sensor.hpp" #include "client/graph_panel.hpp" +#include "client/post_processing.hpp" #include class MessageModel; @@ -45,7 +47,7 @@ class MainFrame : public wxFrame { }; MainFrame(const wxString& title, std::shared_ptr model, - std::shared_ptr dataBuffer, + std::shared_ptr dataBuffer, std::shared_ptr postProcessor, TcpClient* tcpClient, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(1200, 800)); @@ -91,6 +93,8 @@ class MainFrame : public wxFrame { void OnUpdateTimer(wxTimerEvent& event); + std::shared_ptr postProcessor_; + // Auto detecting ESP32s std::unique_ptr esp32Scanner_; wxPanel* esp32Banner_ = nullptr; diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index 13063b31..7705231e 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -12,19 +12,10 @@ DataBuffer::~DataBuffer() { } void DataBuffer::writeData(buffer_data_t jsonChunk) { - // Append the new chunk of raw JSON data to the buffer. This functions only job is to store raw - // inbound data in a way that doesn't lose anything later the parser will call extractNextJson() - // parseNextJson() - // std::cout << "[DataBuffer] Writing data to buffer:" << std::endl; - // std::cout << "[DataBuffer] a: '" << jsonChunk.a << "' a_data: " << jsonChunk.a_data << std::endl; - // std::cout << "[DataBuffer] b: '" << jsonChunk.b << "' b_data: " << jsonChunk.b_data << std::endl; - // std::cout << "[DataBuffer] Buffer size before write: " << size() << std::endl; - - //get the runtime directory path from DataLogger to export buffer if it exceeds threshold - - //DataLogger logger; - //std::string exportPath = logger.getLogFilePath(); + //before writing, do necessary post processing on jsonChunk + //jsonChunk.data = PostProcessing.processData(jsonChunk.data); + write(jsonChunk); if ((int)size() > FLUSH_THRESHOLD * MAX_BUFFER_SIZE / 100) { @@ -32,9 +23,7 @@ void DataBuffer::writeData(buffer_data_t jsonChunk) { exportBuffer(logFilePath_); } - // std::cout << "[DataBuffer] Buffer size after write: " << size() << std::endl; - // std::cout << buffer_.size(); - // std::cout << "[DataBuffer] : " << toStringAll() << std::endl; + } void DataBuffer::setData(buffer_data_t jsonData) { diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp index e0e9cac3..1a559f99 100644 --- a/client/src/command_processor.cpp +++ b/client/src/command_processor.cpp @@ -1,15 +1,14 @@ #include "client/command_processor.hpp" #include "client/DataBuffer.hpp" - /* TO DO: add stop mechanism for command processor thread, currently it runs indefinitely and can only be stopped by exiting the program TO DO: add command_processor in onExit() in main.cpp TO DO: handle race conditions between command processor and tcp client both accessing data buffer using mutex */ -CommandProcessor::CommandProcessor(std::shared_ptr dataBuffer) - : dataBuffer_(dataBuffer) { +CommandProcessor::CommandProcessor(std::shared_ptr dataBuffer, std::shared_ptr postProcessor) + : dataBuffer_(dataBuffer), postProcessor_(postProcessor) { } @@ -52,10 +51,22 @@ void CommandProcessor::processCommand(const std::string& command) { } else if (cmdType == "size"){ std::cout << "Buffer size: " << dataBuffer_->size() << std::endl; - + } else if (cmdType == "reset"){ + //reset all post processing parameters to default values + postProcessor_->addOffset(0.0); + postProcessor_->addScaling(1.0); + //postProcessor_->reset(); + //std::cout << "Post processing parameters reset to default values." << std::endl; + } else if (cmdType == "setOffset"){ + float offset = std::stof(firstParameter); + postProcessor_->addOffset(offset); + //std::cout << "Offset set to: " << offset << std::endl; + } else if (cmdType == "setScale"){ + float scaleFactor = std::stof(firstParameter); + postProcessor_->addScaling(scaleFactor); + //std::cout << "Scale factor set to: " << scaleFactor << std::endl; } else { std::cout << "Unknown command: " << command << std::endl; - } //add more commands as needed diff --git a/client/src/graph_panel.cpp b/client/src/graph_panel.cpp index 82153b91..4607aa1f 100644 --- a/client/src/graph_panel.cpp +++ b/client/src/graph_panel.cpp @@ -9,8 +9,8 @@ wxBEGIN_EVENT_TABLE(GraphPanel, wxPanel) EVT_SIZE(GraphPanel::OnSize) wxEND_EVENT_TABLE() -GraphPanel::GraphPanel(wxWindow* parent) - : wxPanel(parent, wxID_ANY) { +GraphPanel::GraphPanel(wxWindow* parent, std::shared_ptr postProcessor) + : wxPanel(parent, wxID_ANY), postProcessor_(postProcessor) { m_plot = new mpWindow(this, wxID_ANY); m_plot-> EnableDoubleBuffer(true); @@ -130,6 +130,8 @@ void GraphPanel::DrawAxes(wxDC& dc){ } void GraphPanel::AddDataPoint(const std::string& sensorName, double value, double timestamp){ + std::lock_guard lock(dataMutex_); + sensorData_[sensorName].push_back({timestamp, value}); // Keeps the first 100 data points @@ -140,11 +142,13 @@ void GraphPanel::AddDataPoint(const std::string& sensorName, double value, doubl } void GraphPanel::UpdateGraph(){ + std::lock_guard lock(dataMutex_); + for (auto& pair : sensorLayers_){ m_plot->DelLayer(pair.second,true); } sensorLayers_.clear(); - + // colours for the graph wxColour colours[] = { wxColour(255, 0, 0), @@ -167,7 +171,13 @@ void GraphPanel::UpdateGraph(){ std::vector xs, ys; for(const auto& point : data){ xs.push_back(point.first); // timestamp - ys.push_back(point.second); // value + + //add necessary offset and scaling to the data point before plotting + auto value = point.second; + value = postProcessor_->processData(value); + //std::cout << "Processed value for sensor " << sensorName << ": " << value << std::endl; // Debug output + //ys.push_back(point.second); // value + ys.push_back(value); // value } mpFXYVector* layer = new mpFXYVector(wxString(sensorName)); diff --git a/client/src/json_reader.cpp b/client/src/json_reader.cpp index a1e79834..61b443bb 100644 --- a/client/src/json_reader.cpp +++ b/client/src/json_reader.cpp @@ -43,7 +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; + //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; @@ -101,10 +101,6 @@ buffer_data_t JsonReader::exportToBuffer(std::string json) { ret.timestamp = (long) doc["timestamp"].GetInt(); } - - - - //ret.timestamp = std::time(nullptr); return ret; diff --git a/client/src/main.cpp b/client/src/main.cpp index 433f3b26..992be89d 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -171,8 +171,14 @@ class PanoramaClient : public wxApp { jsonWriter_ = std::make_shared(dataBuffer_, runtimeDir); jsonWriterThread_ = std::make_unique(&JsonWriter::start, jsonWriter_); + auto postProcessor = std::make_shared(); + + // --- Create CommandProcessor on a separate thread--- + cmdProcessor_ = std::make_shared(dataBuffer_, postProcessor); + cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); + // --- Create view --- - MainFrame* w = new MainFrame("Panorama Client", model_, dataBuffer_, tcpClient_.get()); + MainFrame* w = new MainFrame("Panorama Client", model_, dataBuffer_, postProcessor, tcpClient_.get()); w->Show(); @@ -195,20 +201,18 @@ class PanoramaClient : public wxApp { tcpClient_->stop(); } + //clean shutdown of command processor if (cmdProcessor_) { cmdProcessor_->stop(); } - if (cmdThread_ && cmdThread_->joinable()) { cmdThread_->join(); } - // Clean shutdown of JSON writer if (jsonWriter_) { jsonWriter_->stop(); } - if (jsonWriterThread_ && jsonWriterThread_->joinable()) { jsonWriterThread_->join(); } diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index 224c9e6d..cddb06dd 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -16,10 +16,10 @@ #include MainFrame::MainFrame(const wxString& title, std::shared_ptr model, - std::shared_ptr dataBuffer, + std::shared_ptr dataBuffer, std::shared_ptr postProcessor, TcpClient* tcpClient, const wxPoint& pos, const wxSize& size) - : wxFrame(nullptr, wxID_ANY, title, pos, size), model_(model), dataBuffer_(dataBuffer), tcpClient_(tcpClient) { + : wxFrame(nullptr, wxID_ANY, title, pos, size), model_(model), dataBuffer_(dataBuffer), postProcessor_(postProcessor), tcpClient_(tcpClient) { CreateMenuBar(); @@ -83,7 +83,7 @@ MainFrame::MainFrame(const wxString& title, std::shared_ptr model, // Graph panel area - graphPanel_ = new GraphPanel(rightSplitter); + graphPanel_ = new GraphPanel(rightSplitter, postProcessor_); // Create text control for displaying messages (Console) consolePanel_ = new wxPanel(mainSplitter); @@ -158,6 +158,17 @@ void MainFrame::updateMessageDisplay() { for (size_t i = displayedMessageCount_; i < messages.size(); ++i) { messageDisplay_->AppendText(wxString::FromUTF8(messages[i].c_str()) + "\n"); } + + //print dataBuffer contents + /* + 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 @@ -169,6 +180,8 @@ void MainFrame::updateMessageDisplay() { } ++i; } + + displayedBufferCount_ = allBuffer.size(); } @@ -211,6 +224,16 @@ void MainFrame::updateDataPanel() { graphPanel_->SetVisibleSensors(visible); //std::cout << "Updated " << latestData.datatype << " with value: " << latestData.data << " " << latestData.dataunit << std::endl; + if (graphPanel_) { + graphPanel_->wxCallAfter( + &GraphPanel::AddDataPoint, + latestData.datatype, + (double)latestData.data, + (double)latestData.timestamp + ); + } + + /* if(graphPanel_){ graphPanel_->AddDataPoint( latestData.datatype, @@ -218,6 +241,7 @@ void MainFrame::updateDataPanel() { (double)latestData.timestamp ); } + */ } } } diff --git a/tools/client_hasna.py b/tools/client_hasna.py deleted file mode 100644 index 5289897b..00000000 --- a/tools/client_hasna.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -""" -esp32_client.py ---------------- -A simple TCP client that connects to the ESP32 emulator (server) -and prints telemetry data as it is received. - -Usage: - python esp32_client.py --host localhost --port 7000 -""" - -import socket -import argparse - -def main(): - # Set up command-line arguments - parser = argparse.ArgumentParser(description="Simple ESP32 telemetry client") - parser.add_argument("--host", default="localhost", help="Server host to connect to") - parser.add_argument("--port", type=int, default=7000, help="Server port to connect to") - args = parser.parse_args() - - # Create a TCP socket - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - print(f"Connecting to {args.host}:{args.port} ...") - s.connect((args.host, args.port)) - print("Connected. Listening for telemetry...\n") - - try: - # Keep receiving data from the server - buffer = b"" - while True: - data = s.recv(1024) # Read up to 1024 bytes - if not data: - print("Connection closed by server.") - break - buffer += data - - # Split incoming data by newline (each JSON record ends with \n) - while b"\n" in buffer: - line, buffer = buffer.split(b"\n", 1) - print("<-", line.decode().strip()) # Print decoded JSON line - - except KeyboardInterrupt: - print("\nStopped by user.") - except ConnectionResetError: - print("Server disconnected unexpectedly.") - -if __name__ == "__main__": - main() diff --git a/tools/server_hasna.py b/tools/server_hasna.py deleted file mode 100644 index aec424b4..00000000 --- a/tools/server_hasna.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 -""" -esp32_emulator_tcp.py ---------------------- - -This script emulates an ESP32 device that sends sensor-like data (telemetry) -over a TCP connection. You can run it either as a SERVER (default) or as a CLIENT. - -- In server mode: it waits for a connection, then sends telemetry data. -- In client mode: it connects to a remote server and sends telemetry data. - -Usage examples: - Server (listens for incoming connection): - python esp32_emulator_tcp.py --host 0.0.0.0 --port 7000 - - Client (connects to a server at a given IP): - python esp32_emulator_tcp.py --client --host 192.168.1.5 --port 7000 -""" - -import socket # Networking (TCP/UDP communication) -import time # For delays and timestamps -import json # To send structured data (JSON format) -import argparse # To handle command-line arguments -import random # To simulate random sensor readings - - -# ------------------------------------------------------------------- -# Function: build_telemetry -# Purpose: Generates a JSON string with fake ESP32-like sensor data -# ------------------------------------------------------------------- -def build_telemetry(counter): - # Create a dictionary representing sensor data - """ - data = { - "device": "esp32-emulator-tcp", # name/type of the device - "ts": int(time.time()), # current timestamp (Unix time) - "counter": counter, # packet counter, increments each send - "temperature_c": round(20 + random.uniform(-2, 2), 2), # random temp - "humidity_pct": round(50 + random.uniform(-5, 5), 1) # random humidity - } - """ - data = "Hello World" - # Convert dictionary to JSON and add newline so each record is on its own line - return json.dumps(data) + "\n" - - -# ------------------------------------------------------------------- -# Function: server_mode -# Purpose: Acts like a TCP server (the ESP32 emulator waits for a client) -# ------------------------------------------------------------------- -def server_mode(host, port, interval): - # Create a TCP socket using IPv4 - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - # Bind socket to address and port - s.bind((host, port)) - # Start listening for incoming connections (allow 1 queued connection) - s.listen(1) - print(f"Listening on {host}:{port} — waiting for client...") - - # Accept a connection (blocking until a client connects) - conn, addr = s.accept() - # Use "with" to ensure connection closes cleanly on exit - with conn: - print("Client connected:", addr) - counter = 0 - try: - # Continuous loop: generate and send telemetry every 'interval' seconds - while True: - # Generate fake telemetry data - line = build_telemetry(counter).encode("utf-8") - # Send it over the socket - conn.sendall(line) - # Print what we sent to the console (for debugging) - print("->", line.decode().strip()) - counter += 1 - # Wait for the next transmission - time.sleep(interval) - - # Handle connection loss - except BrokenPipeError: - print("Client disconnected.") - # Handle Ctrl+C (manual stop) - except KeyboardInterrupt: - print("Stopped by user.") - - -# ------------------------------------------------------------------- -# Function: client_mode -# Purpose: Acts like a TCP client (connects to a server and sends data) -# ------------------------------------------------------------------- -def client_mode(host, port, interval): - # Create a TCP socket - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - print(f"Connecting to {host}:{port} ...") - # Connect to the specified server - s.connect((host, port)) - print("Connected.") - counter = 0 - try: - # Same loop as server: generate and send telemetry repeatedly - while True: - line = build_telemetry(counter).encode("utf-8") - s.sendall(line) - print("->", line.decode().strip()) - counter += 1 - time.sleep(interval) - - except BrokenPipeError: - print("Server disconnected.") - except KeyboardInterrupt: - print("Stopped by user.") - - -# ------------------------------------------------------------------- -# Function: main -# Purpose: Entry point — parses command-line arguments and decides mode -# ------------------------------------------------------------------- -def main(): - # Create an argument parser for command-line options - p = argparse.ArgumentParser(description="ESP32 TCP emulator") - - # Add arguments - p.add_argument("--host", default="0.0.0.0", - help="Host IP to bind or connect to (default: 0.0.0.0 for server)") - p.add_argument("--port", type=int, default=7000, - help="Port number to use (default: 7000)") - p.add_argument("--interval", type=float, default=1.0, - help="Seconds between telemetry sends (default: 1.0)") - p.add_argument("--client", action="store_true", - help="Run in client mode instead of server mode") - - # Parse command-line arguments - args = p.parse_args() - - # Decide which mode to run based on --client flag - if args.client: - client_mode(args.host, args.port, args.interval) - else: - server_mode(args.host, args.port, args.interval) - - -# ------------------------------------------------------------------- -# Standard Python entry point -# ------------------------------------------------------------------- -if __name__ == "__main__": - main() From c8168484e11dcd4d900d199033409e5b2ac3d0dc Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Mar 2026 15:02:55 -0700 Subject: [PATCH 14/26] Added post_processing files --- client/include/client/post_processing.hpp | 21 +++++++++++++ client/src/post_processing.cpp | 37 +++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 client/include/client/post_processing.hpp create mode 100644 client/src/post_processing.cpp diff --git a/client/include/client/post_processing.hpp b/client/include/client/post_processing.hpp new file mode 100644 index 00000000..501aea85 --- /dev/null +++ b/client/include/client/post_processing.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +class PostProcessing { +public: + PostProcessing(); + float processData(float data); + void reset(); + void addOffset(float offset); + void addScaling(float scaleFactor); + void updateDataBase(); + +private: + float currentOffset = 0.0; + float currentScaleFactor = 1.0; + std::mutex mutex_; + +}; \ No newline at end of file diff --git a/client/src/post_processing.cpp b/client/src/post_processing.cpp new file mode 100644 index 00000000..c67fa1ad --- /dev/null +++ b/client/src/post_processing.cpp @@ -0,0 +1,37 @@ +#include "client/post_processing.hpp" + +PostProcessing::PostProcessing() { + +} + +float PostProcessing::processData(float data) { + //std::cout << "currentOffset: " << currentOffset << ", currentScaleFactor: " << currentScaleFactor << std::endl; // Debug output + std::lock_guard lock(mutex_); + return data*currentScaleFactor + currentOffset; +} + +void PostProcessing::addOffset(float offset) { + std::lock_guard lock(mutex_); + currentOffset = offset; //record the offset value + std::cout << "Offset set to: " << offset << std::endl; +} + +void PostProcessing::addScaling(float scaleFactor) { + std::lock_guard lock(mutex_); + currentScaleFactor = scaleFactor; //record the scale factor value + std::cout << "Scale factor set to: " << scaleFactor << std::endl; +} + +void PostProcessing::reset() { + std::lock_guard lock(mutex_); + //reset all post processing parameters to default values + addOffset(0); //reset offset to 0; + addScaling(1.0); //reset scale factor to 1; +} + +void PostProcessing::updateDataBase() { + //TODO: update the database accordingly + + +} + From 0c97d4dd2713843617f1afbc18687e478d345b0e Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Mar 2026 15:44:48 -0700 Subject: [PATCH 15/26] Fixed a deadlock in reset method --- client/include/client/graph_panel.hpp | 2 -- client/src/command_processor.cpp | 11 +++++------ client/src/graph_panel.cpp | 8 +------- client/src/main.cpp | 1 + client/src/mainframe.cpp | 14 +++----------- client/src/post_processing.cpp | 5 ++--- 6 files changed, 12 insertions(+), 29 deletions(-) diff --git a/client/include/client/graph_panel.hpp b/client/include/client/graph_panel.hpp index 23d6e560..3c655b95 100644 --- a/client/include/client/graph_panel.hpp +++ b/client/include/client/graph_panel.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include "client/post_processing.hpp" class GraphPanel : public wxPanel { @@ -35,5 +34,4 @@ class GraphPanel : public wxPanel { wxDECLARE_EVENT_TABLE(); std::shared_ptr postProcessor_; - std::mutex dataMutex_; }; \ No newline at end of file diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp index 1a559f99..e48d1bbe 100644 --- a/client/src/command_processor.cpp +++ b/client/src/command_processor.cpp @@ -51,20 +51,19 @@ void CommandProcessor::processCommand(const std::string& command) { } else if (cmdType == "size"){ std::cout << "Buffer size: " << dataBuffer_->size() << std::endl; + } else if (cmdType == "reset"){ //reset all post processing parameters to default values - postProcessor_->addOffset(0.0); - postProcessor_->addScaling(1.0); - //postProcessor_->reset(); - //std::cout << "Post processing parameters reset to default values." << std::endl; + postProcessor_->reset(); + } else if (cmdType == "setOffset"){ float offset = std::stof(firstParameter); postProcessor_->addOffset(offset); - //std::cout << "Offset set to: " << offset << std::endl; + } else if (cmdType == "setScale"){ float scaleFactor = std::stof(firstParameter); postProcessor_->addScaling(scaleFactor); - //std::cout << "Scale factor set to: " << scaleFactor << std::endl; + } else { std::cout << "Unknown command: " << command << std::endl; } diff --git a/client/src/graph_panel.cpp b/client/src/graph_panel.cpp index 4607aa1f..653c21bf 100644 --- a/client/src/graph_panel.cpp +++ b/client/src/graph_panel.cpp @@ -130,8 +130,6 @@ void GraphPanel::DrawAxes(wxDC& dc){ } void GraphPanel::AddDataPoint(const std::string& sensorName, double value, double timestamp){ - std::lock_guard lock(dataMutex_); - sensorData_[sensorName].push_back({timestamp, value}); // Keeps the first 100 data points @@ -142,8 +140,6 @@ void GraphPanel::AddDataPoint(const std::string& sensorName, double value, doubl } void GraphPanel::UpdateGraph(){ - std::lock_guard lock(dataMutex_); - for (auto& pair : sensorLayers_){ m_plot->DelLayer(pair.second,true); } @@ -175,9 +171,7 @@ void GraphPanel::UpdateGraph(){ //add necessary offset and scaling to the data point before plotting auto value = point.second; value = postProcessor_->processData(value); - //std::cout << "Processed value for sensor " << sensorName << ": " << value << std::endl; // Debug output - //ys.push_back(point.second); // value - ys.push_back(value); // value + ys.push_back(value); } mpFXYVector* layer = new mpFXYVector(wxString(sensorName)); diff --git a/client/src/main.cpp b/client/src/main.cpp index 992be89d..db879216 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -171,6 +171,7 @@ class PanoramaClient : public wxApp { jsonWriter_ = std::make_shared(dataBuffer_, runtimeDir); jsonWriterThread_ = std::make_unique(&JsonWriter::start, jsonWriter_); + // --- Create PostProcessing as a shared pointer --- auto postProcessor = std::make_shared(); // --- Create CommandProcessor on a separate thread--- diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index cddb06dd..63f29ad4 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -224,16 +224,7 @@ void MainFrame::updateDataPanel() { graphPanel_->SetVisibleSensors(visible); //std::cout << "Updated " << latestData.datatype << " with value: " << latestData.data << " " << latestData.dataunit << std::endl; - if (graphPanel_) { - graphPanel_->wxCallAfter( - &GraphPanel::AddDataPoint, - latestData.datatype, - (double)latestData.data, - (double)latestData.timestamp - ); - } - - /* + if(graphPanel_){ graphPanel_->AddDataPoint( latestData.datatype, @@ -241,7 +232,8 @@ void MainFrame::updateDataPanel() { (double)latestData.timestamp ); } - */ + + } } } diff --git a/client/src/post_processing.cpp b/client/src/post_processing.cpp index c67fa1ad..7f551e7e 100644 --- a/client/src/post_processing.cpp +++ b/client/src/post_processing.cpp @@ -23,10 +23,9 @@ void PostProcessing::addScaling(float scaleFactor) { } void PostProcessing::reset() { - std::lock_guard lock(mutex_); //reset all post processing parameters to default values - addOffset(0); //reset offset to 0; - addScaling(1.0); //reset scale factor to 1; + addOffset(0); + addScaling(1.0); } void PostProcessing::updateDataBase() { From 327f2b324479fb71bd7cffb630f2d0780c96bb12 Mon Sep 17 00:00:00 2001 From: Alex Zhou Date: Sat, 14 Mar 2026 15:42:48 -0700 Subject: [PATCH 16/26] Removed firmware cache files and updated gitignore --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 02595cce..902a4c81 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,8 @@ __pycache__/ firmware/panorama/.cache/ # Runtime directory -rundir/ \ No newline at end of file +rundir/ + + +# Firmware related: +firmware/panorama/.cache/ \ No newline at end of file From 4e4c0c8dbbd0a071d711fbe458e73c10aea172f9 Mon Sep 17 00:00:00 2001 From: Alex Zhou Date: Sat, 21 Mar 2026 13:49:27 -0700 Subject: [PATCH 17/26] Add ESP32 AP TCP connection --- client/include/client/tcp_client.hpp | 1 - client/src/main.cpp | 5 +---- client/src/mainframe.cpp | 17 ++++++----------- client/src/tcp_client.cpp | 10 ---------- firmware/panorama/src/test.cpp | 14 ++++++++------ 5 files changed, 15 insertions(+), 32 deletions(-) diff --git a/client/include/client/tcp_client.hpp b/client/include/client/tcp_client.hpp index e6b6a744..981d76c4 100644 --- a/client/include/client/tcp_client.hpp +++ b/client/include/client/tcp_client.hpp @@ -18,7 +18,6 @@ class TcpClient { void start(); void stop(); void reconnectWith(const std::string& host, int port); - void sendCommand(const std::string& cmd); private: void run(); diff --git a/client/src/main.cpp b/client/src/main.cpp index db879216..c910e6f1 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -142,10 +142,7 @@ class PanoramaClient : public wxApp { int tcpPort; bool autoReconnect; int reconnectDelay; - if (parser.isNoEspMode()) { - tcpHost = "127.0.0.1"; - tcpPort = 3000; - } else if (!config.getTcpSettings(tcpHost, tcpPort, autoReconnect, reconnectDelay)) { + if (!config.getTcpSettings(tcpHost, tcpPort, autoReconnect, reconnectDelay)) { tcpHost = "127.0.0.1"; tcpPort = 3000; } diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index 63f29ad4..fece63d9 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -7,10 +7,8 @@ #include "client/sensor.hpp" #include "client/settings_dialog.hpp" #include "common/panorama_utils.hpp" -#include "common/panorama_colours.hpp" #include "client/tcp_client.hpp" #include "client/config_manager.hpp" -#include "client/esp32_scanner.hpp" #include #include #include @@ -18,8 +16,10 @@ MainFrame::MainFrame(const wxString& title, std::shared_ptr model, std::shared_ptr dataBuffer, std::shared_ptr postProcessor, TcpClient* tcpClient, + TcpClient* tcpClient, const wxPoint& pos, const wxSize& size) : wxFrame(nullptr, wxID_ANY, title, pos, size), model_(model), dataBuffer_(dataBuffer), postProcessor_(postProcessor), tcpClient_(tcpClient) { + : wxFrame(nullptr, wxID_ANY, title, pos, size), model_(model), dataBuffer_(dataBuffer), postProcessor_(postProcessor), tcpClient_(tcpClient) { CreateMenuBar(); @@ -334,15 +334,10 @@ void MainFrame::OnSettingsOpen(wxCommandEvent& event) { } } -void MainFrame::OnStartStream(wxCommandEvent& event) { - if (tcpClient_) { - tcpClient_->sendCommand("START"); - } -} - -void MainFrame::OnStopStream(wxCommandEvent& event) { - if (tcpClient_) { - tcpClient_->sendCommand("STOP"); +void MainFrame::OnUpdateTimer(wxTimerEvent&) { + if (updatePending_.exchange(false)) { + updateMessageDisplay(); + updateDataPanel(); } } diff --git a/client/src/tcp_client.cpp b/client/src/tcp_client.cpp index 039c39a6..ed854db9 100644 --- a/client/src/tcp_client.cpp +++ b/client/src/tcp_client.cpp @@ -101,16 +101,6 @@ void TcpClient::run() { } } -void TcpClient::sendCommand(const std::string& cmd) { - if (socket_ != INVALID_SOCKET) { - std::string msg = cmd + "\n"; - send(socket_, msg.c_str(), msg.size(), 0); - model_->addMessage("Sent command: " + cmd); - } else { - model_->addMessage("Error: Not connected, cannot send command"); - } -} - void TcpClient::reconnectWith(const std::string& host, int port) { host_ = host; port_ = port; diff --git a/firmware/panorama/src/test.cpp b/firmware/panorama/src/test.cpp index e2d9e8b5..e260837f 100644 --- a/firmware/panorama/src/test.cpp +++ b/firmware/panorama/src/test.cpp @@ -9,6 +9,9 @@ const uint16_t PORT = 9000; // Onboard LED pin const int LED_PIN = 2; +// Onboard LED pin +const int LED_PIN = 2; + // HC-SR04 pins (adjust to match your wiring) const int TRIG_PIN = 5; const int ECHO_PIN = 18; @@ -128,13 +131,12 @@ void loop() { String json = "{" - "\"sensor\":\"" + String(s.sensor) + "\"," - "\"dataunit\":\"" + String(s.dataunit) + "\"," - "\"data\":" + String(value, 2) + "," - "\"datatype\":\"" + String(s.datatype) + "\"," - "\"sensorID\":" + String(s.sensorID) + "," + "\"sensor\":\"" + String(SENSOR_NAME) + "\"," + "\"unit\":\"cm\"," + "\"value\":" + String(distanceCm, 2) + "," + "\"sensor_id\":" + String(SENSOR_ID) + "," "\"seq\":" + String(seq++) + "," - "\"timestamp\":" + String(timestamp) + + "\"timestamp_ms\":" + String(timestamp) + "}\n"; client.print(json); From 50eafc5a8b4b547f2dad3ccb748325dde7b66979 Mon Sep 17 00:00:00 2001 From: Alex Zhou Date: Sat, 21 Mar 2026 15:19:18 -0700 Subject: [PATCH 18/26] Added ESP32 scanning ability --- client/src/mainframe.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index fece63d9..55166c5c 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -7,8 +7,10 @@ #include "client/sensor.hpp" #include "client/settings_dialog.hpp" #include "common/panorama_utils.hpp" +#include "common/panorama_colours.hpp" #include "client/tcp_client.hpp" #include "client/config_manager.hpp" +#include "client/esp32_scanner.hpp" #include #include #include From 1ee6c862d9e59e4bf40ab411959e800c45a09e58 Mon Sep 17 00:00:00 2001 From: Alex Zhou Date: Sat, 21 Mar 2026 15:42:29 -0700 Subject: [PATCH 19/26] Fixed merge conflict issues --- client/include/client/graph_panel.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/client/include/client/graph_panel.hpp b/client/include/client/graph_panel.hpp index 3c655b95..35b4e141 100644 --- a/client/include/client/graph_panel.hpp +++ b/client/include/client/graph_panel.hpp @@ -34,4 +34,5 @@ class GraphPanel : public wxPanel { wxDECLARE_EVENT_TABLE(); std::shared_ptr postProcessor_; + wxDECLARE_EVENT_TABLE(); }; \ No newline at end of file From c378e66db2914dfc31884c9b9e82776eaa86293e Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Feb 2026 16:11:11 -0800 Subject: [PATCH 20/26] working on passing and processing commands through terminal --- client/include/client/DataBuffer.hpp | 4 ++++ client/src/DataBuffer.cpp | 36 ++++++++++++++++++++++++++++ client/src/main.cpp | 16 +++++++++++++ tools/pserver/pserver.py | 4 ++-- 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/client/include/client/DataBuffer.hpp b/client/include/client/DataBuffer.hpp index 8e70eb3f..26200100 100644 --- a/client/include/client/DataBuffer.hpp +++ b/client/include/client/DataBuffer.hpp @@ -67,6 +67,10 @@ class DataBuffer : public BufferBase { void exportBuffer(std::string exportPath); + void convertData(std::string dataType, std::string targetUnit); + + void addOffset(std::string dataType, float offsetValue); + private: // Raw buffer storing incoming data std::list buffer_; diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index 7705231e..e9cb9f63 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -164,4 +164,40 @@ void DataBuffer::exportBuffer(std::string exportPath) { fputs(json_content.c_str(), fp); fclose(fp); return; +} + +void DataBuffer::convertData(std::string dataType, std::string targetUnit) { + //convert all data of type dataType in buffer_ to targetUnit + //e.g. if dataType = "temperature" and targetUnit = "F", convert all temperature data in buffer_ to Fahrenheit + std::lock_guard lock(mutex_); + + for (auto& item : buffer_) { + if (item.datatype == dataType) { + if (dataType == "temperature") { + if (targetUnit == "farenheit") { + // Convert Celsius to Fahrenheit + item.data = item.data * 9.0 / 5.0 + 32; + item.dataunit = "farenheit"; + } else if (targetUnit == "celcius") { + // Convert Fahrenheit to Celsius + item.data = (item.data - 32) * 5.0 / 9.0; + item.dataunit = "celcius"; + } + } + // Add more data types and unit conversions as needed + } + } +} + +void DataBuffer::addOffset(std::string dataType, float offsetValue) { + //add offsetValue to all data of type dataType in buffer_ + //e.g. if dataType = "temperature" and offsetValue = "5", add 5 to all temperature data in buffer_ + std::lock_guard lock(mutex_); + + //TO DO: print to the screen the buffer data after adding offset for debugging + for (auto& item : buffer_) { + if (item.datatype == dataType) { + item.data += offsetValue; + } + } } \ No newline at end of file diff --git a/client/src/main.cpp b/client/src/main.cpp index c910e6f1..bc103ac5 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -137,6 +137,11 @@ class PanoramaClient : public wxApp { dataBuffer_ = std::make_shared(runtimeDir + "/data"); + // --- Create CommandProcessor on a separate thread--- + cmdProcessor_ = std::make_shared(dataBuffer_); + cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); + + // --- Create and start TCP client on separate thread --- std::string tcpHost; int tcpPort; @@ -215,6 +220,15 @@ class PanoramaClient : public wxApp { jsonWriterThread_->join(); } + + if (cmdProcessor_) { + cmdProcessor_->stop(); + } + + if (cmdThread_ && cmdThread_->joinable()) { + cmdThread_->join(); + } + return wxApp::OnExit(); } @@ -227,6 +241,8 @@ class PanoramaClient : public wxApp { std::unique_ptr cmdThread_; std::shared_ptr jsonWriter_; std::unique_ptr jsonWriterThread_; + std::shared_ptr cmdProcessor_; + std::unique_ptr cmdThread_; }; wxIMPLEMENT_APP(PanoramaClient); diff --git a/tools/pserver/pserver.py b/tools/pserver/pserver.py index 6eaafe77..c44ef5d2 100644 --- a/tools/pserver/pserver.py +++ b/tools/pserver/pserver.py @@ -71,7 +71,7 @@ def send_stream(self, client_socket, client_address, streamer: PStreamer): data = streamer.get_data(timeout=2.0) if data is None: - pwarning("PServer", "No data available from streamer") + #pwarning("PServer", "No data available from streamer") continue client_socket.sendall(data) @@ -95,7 +95,7 @@ def main(): server = PServer() streamer = PStreamer() - streamer.build_stream(PStreamJSON()).set_interval(0.4) + streamer.build_stream(PStreamJSON()).set_interval(10.0) # Set interval to 10 seconds for testing streamer.start() From 019277ff396b80583d081f91cb1ef848c8a4e7f8 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Feb 2026 16:55:04 -0800 Subject: [PATCH 21/26] added toStringAll(), clear(), and size() to the command processing; now they can be called through terminal --- client/include/client/DataBuffer.hpp | 4 ---- client/src/DataBuffer.cpp | 32 +++++++++++----------------- client/src/command_processor.cpp | 1 + tools/pserver/pserver.py | 4 ++-- 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/client/include/client/DataBuffer.hpp b/client/include/client/DataBuffer.hpp index 26200100..8e70eb3f 100644 --- a/client/include/client/DataBuffer.hpp +++ b/client/include/client/DataBuffer.hpp @@ -67,10 +67,6 @@ class DataBuffer : public BufferBase { void exportBuffer(std::string exportPath); - void convertData(std::string dataType, std::string targetUnit); - - void addOffset(std::string dataType, float offsetValue); - private: // Raw buffer storing incoming data std::list buffer_; diff --git a/client/src/DataBuffer.cpp b/client/src/DataBuffer.cpp index e9cb9f63..c0bba6f5 100644 --- a/client/src/DataBuffer.cpp +++ b/client/src/DataBuffer.cpp @@ -166,38 +166,30 @@ void DataBuffer::exportBuffer(std::string exportPath) { return; } -void DataBuffer::convertData(std::string dataType, std::string targetUnit) { - //convert all data of type dataType in buffer_ to targetUnit - //e.g. if dataType = "temperature" and targetUnit = "F", convert all temperature data in buffer_ to Fahrenheit +/* +void DataBuffer::replaceData(std::string dataType, float targetValue) { + //replace all data of type dataType in buffer_ with targetValue std::lock_guard lock(mutex_); - for (auto& item : buffer_) { + std::cout << "Replacing data of type " << dataType << " with value " << targetValue << std::endl; + std::cout << "Buffer before replaceData: " << toStringAll() << std::endl; + + for (auto& item : readAll()) { if (item.datatype == dataType) { - if (dataType == "temperature") { - if (targetUnit == "farenheit") { - // Convert Celsius to Fahrenheit - item.data = item.data * 9.0 / 5.0 + 32; - item.dataunit = "farenheit"; - } else if (targetUnit == "celcius") { - // Convert Fahrenheit to Celsius - item.data = (item.data - 32) * 5.0 / 9.0; - item.dataunit = "celcius"; - } - } - // Add more data types and unit conversions as needed + item.data = targetValue; + std::cout << "Replaced data of type " << dataType << " with value " << targetValue << std::endl; } } } void DataBuffer::addOffset(std::string dataType, float offsetValue) { //add offsetValue to all data of type dataType in buffer_ - //e.g. if dataType = "temperature" and offsetValue = "5", add 5 to all temperature data in buffer_ std::lock_guard lock(mutex_); - //TO DO: print to the screen the buffer data after adding offset for debugging - for (auto& item : buffer_) { + for (auto& item : readAll()) { if (item.datatype == dataType) { item.data += offsetValue; } } -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp index e48d1bbe..80cd78c0 100644 --- a/client/src/command_processor.cpp +++ b/client/src/command_processor.cpp @@ -66,6 +66,7 @@ void CommandProcessor::processCommand(const std::string& command) { } else { std::cout << "Unknown command: " << command << std::endl; + } //add more commands as needed diff --git a/tools/pserver/pserver.py b/tools/pserver/pserver.py index c44ef5d2..6eaafe77 100644 --- a/tools/pserver/pserver.py +++ b/tools/pserver/pserver.py @@ -71,7 +71,7 @@ def send_stream(self, client_socket, client_address, streamer: PStreamer): data = streamer.get_data(timeout=2.0) if data is None: - #pwarning("PServer", "No data available from streamer") + pwarning("PServer", "No data available from streamer") continue client_socket.sendall(data) @@ -95,7 +95,7 @@ def main(): server = PServer() streamer = PStreamer() - streamer.build_stream(PStreamJSON()).set_interval(10.0) # Set interval to 10 seconds for testing + streamer.build_stream(PStreamJSON()).set_interval(0.4) streamer.start() From 18f521423f021f5fdf029690a268b65ad24e200a Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Mar 2026 15:00:14 -0700 Subject: [PATCH 22/26] Implemented adding offset and scaling through terminal --- client/include/client/graph_panel.hpp | 4 ++++ client/src/command_processor.cpp | 1 - client/src/graph_panel.cpp | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/client/include/client/graph_panel.hpp b/client/include/client/graph_panel.hpp index 35b4e141..52da407c 100644 --- a/client/include/client/graph_panel.hpp +++ b/client/include/client/graph_panel.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "client/post_processing.hpp" class GraphPanel : public wxPanel { @@ -35,4 +36,7 @@ class GraphPanel : public wxPanel { std::shared_ptr postProcessor_; wxDECLARE_EVENT_TABLE(); + + std::shared_ptr postProcessor_; + std::mutex dataMutex_; }; \ No newline at end of file diff --git a/client/src/command_processor.cpp b/client/src/command_processor.cpp index 80cd78c0..e48d1bbe 100644 --- a/client/src/command_processor.cpp +++ b/client/src/command_processor.cpp @@ -66,7 +66,6 @@ void CommandProcessor::processCommand(const std::string& command) { } else { std::cout << "Unknown command: " << command << std::endl; - } //add more commands as needed diff --git a/client/src/graph_panel.cpp b/client/src/graph_panel.cpp index 653c21bf..02768246 100644 --- a/client/src/graph_panel.cpp +++ b/client/src/graph_panel.cpp @@ -130,6 +130,8 @@ void GraphPanel::DrawAxes(wxDC& dc){ } void GraphPanel::AddDataPoint(const std::string& sensorName, double value, double timestamp){ + std::lock_guard lock(dataMutex_); + sensorData_[sensorName].push_back({timestamp, value}); // Keeps the first 100 data points @@ -140,6 +142,8 @@ void GraphPanel::AddDataPoint(const std::string& sensorName, double value, doubl } void GraphPanel::UpdateGraph(){ + std::lock_guard lock(dataMutex_); + for (auto& pair : sensorLayers_){ m_plot->DelLayer(pair.second,true); } From c01e70670a8ca4d748e1ae02d7f79616a93e1994 Mon Sep 17 00:00:00 2001 From: annhypen Date: Sat, 28 Mar 2026 15:44:48 -0700 Subject: [PATCH 23/26] Fixed a deadlock in reset method --- client/include/client/graph_panel.hpp | 2 -- client/src/graph_panel.cpp | 4 ---- 2 files changed, 6 deletions(-) diff --git a/client/include/client/graph_panel.hpp b/client/include/client/graph_panel.hpp index 52da407c..6626f498 100644 --- a/client/include/client/graph_panel.hpp +++ b/client/include/client/graph_panel.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include "client/post_processing.hpp" class GraphPanel : public wxPanel { @@ -38,5 +37,4 @@ class GraphPanel : public wxPanel { wxDECLARE_EVENT_TABLE(); std::shared_ptr postProcessor_; - std::mutex dataMutex_; }; \ No newline at end of file diff --git a/client/src/graph_panel.cpp b/client/src/graph_panel.cpp index 02768246..653c21bf 100644 --- a/client/src/graph_panel.cpp +++ b/client/src/graph_panel.cpp @@ -130,8 +130,6 @@ void GraphPanel::DrawAxes(wxDC& dc){ } void GraphPanel::AddDataPoint(const std::string& sensorName, double value, double timestamp){ - std::lock_guard lock(dataMutex_); - sensorData_[sensorName].push_back({timestamp, value}); // Keeps the first 100 data points @@ -142,8 +140,6 @@ void GraphPanel::AddDataPoint(const std::string& sensorName, double value, doubl } void GraphPanel::UpdateGraph(){ - std::lock_guard lock(dataMutex_); - for (auto& pair : sensorLayers_){ m_plot->DelLayer(pair.second,true); } From b2adc45598e3071198acba0f466326a34c02682d Mon Sep 17 00:00:00 2001 From: annhypen Date: Fri, 10 Apr 2026 20:31:18 -0700 Subject: [PATCH 24/26] fixed redeclarations errors --- client/include/client/graph_panel.hpp | 3 --- client/src/main.cpp | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/client/include/client/graph_panel.hpp b/client/include/client/graph_panel.hpp index 6626f498..3c655b95 100644 --- a/client/include/client/graph_panel.hpp +++ b/client/include/client/graph_panel.hpp @@ -33,8 +33,5 @@ class GraphPanel : public wxPanel { wxDECLARE_EVENT_TABLE(); - std::shared_ptr postProcessor_; - wxDECLARE_EVENT_TABLE(); - std::shared_ptr postProcessor_; }; \ No newline at end of file diff --git a/client/src/main.cpp b/client/src/main.cpp index bc103ac5..58a80ab1 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -220,7 +220,7 @@ class PanoramaClient : public wxApp { jsonWriterThread_->join(); } - + // Clean shutdown of command processor if (cmdProcessor_) { cmdProcessor_->stop(); } @@ -237,8 +237,6 @@ class PanoramaClient : public wxApp { std::shared_ptr dataLogger_; std::shared_ptr dataBuffer_; std::unique_ptr tcpClient_; - std::shared_ptr cmdProcessor_; - std::unique_ptr cmdThread_; std::shared_ptr jsonWriter_; std::unique_ptr jsonWriterThread_; std::shared_ptr cmdProcessor_; From 2f2e07c62e9f4d0079db2e2f50c0da7bfc23ee22 Mon Sep 17 00:00:00 2001 From: annhypen Date: Fri, 10 Apr 2026 22:54:46 -0700 Subject: [PATCH 25/26] fixed merge conflicts --- client/include/client/tcp_client.hpp | 1 + client/src/main.cpp | 20 +------------------- client/src/mainframe.cpp | 15 +++++++++------ client/src/tcp_client.cpp | 10 ++++++++++ 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/client/include/client/tcp_client.hpp b/client/include/client/tcp_client.hpp index 981d76c4..e6b6a744 100644 --- a/client/include/client/tcp_client.hpp +++ b/client/include/client/tcp_client.hpp @@ -18,6 +18,7 @@ class TcpClient { void start(); void stop(); void reconnectWith(const std::string& host, int port); + void sendCommand(const std::string& cmd); private: void run(); diff --git a/client/src/main.cpp b/client/src/main.cpp index 58a80ab1..f8746c9b 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -137,11 +137,6 @@ class PanoramaClient : public wxApp { dataBuffer_ = std::make_shared(runtimeDir + "/data"); - // --- Create CommandProcessor on a separate thread--- - cmdProcessor_ = std::make_shared(dataBuffer_); - cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); - - // --- Create and start TCP client on separate thread --- std::string tcpHost; int tcpPort; @@ -165,10 +160,6 @@ class PanoramaClient : public wxApp { return true; } - // --- Create CommandProcessor on a separate thread--- - cmdProcessor_ = std::make_shared(dataBuffer_); - cmdThread_ = std::make_unique(&CommandProcessor::start, cmdProcessor_); - // --- Create and start JSON writer on separate thread --- jsonWriter_ = std::make_shared(dataBuffer_, runtimeDir); jsonWriterThread_ = std::make_unique(&JsonWriter::start, jsonWriter_); @@ -204,7 +195,7 @@ class PanoramaClient : public wxApp { tcpClient_->stop(); } - //clean shutdown of command processor + //Clean shutdown of command processor if (cmdProcessor_) { cmdProcessor_->stop(); } @@ -220,15 +211,6 @@ class PanoramaClient : public wxApp { jsonWriterThread_->join(); } - // Clean shutdown of command processor - if (cmdProcessor_) { - cmdProcessor_->stop(); - } - - if (cmdThread_ && cmdThread_->joinable()) { - cmdThread_->join(); - } - return wxApp::OnExit(); } diff --git a/client/src/mainframe.cpp b/client/src/mainframe.cpp index 55166c5c..63f29ad4 100644 --- a/client/src/mainframe.cpp +++ b/client/src/mainframe.cpp @@ -18,10 +18,8 @@ MainFrame::MainFrame(const wxString& title, std::shared_ptr model, std::shared_ptr dataBuffer, std::shared_ptr postProcessor, TcpClient* tcpClient, - TcpClient* tcpClient, const wxPoint& pos, const wxSize& size) : wxFrame(nullptr, wxID_ANY, title, pos, size), model_(model), dataBuffer_(dataBuffer), postProcessor_(postProcessor), tcpClient_(tcpClient) { - : wxFrame(nullptr, wxID_ANY, title, pos, size), model_(model), dataBuffer_(dataBuffer), postProcessor_(postProcessor), tcpClient_(tcpClient) { CreateMenuBar(); @@ -336,10 +334,15 @@ void MainFrame::OnSettingsOpen(wxCommandEvent& event) { } } -void MainFrame::OnUpdateTimer(wxTimerEvent&) { - if (updatePending_.exchange(false)) { - updateMessageDisplay(); - updateDataPanel(); +void MainFrame::OnStartStream(wxCommandEvent& event) { + if (tcpClient_) { + tcpClient_->sendCommand("START"); + } +} + +void MainFrame::OnStopStream(wxCommandEvent& event) { + if (tcpClient_) { + tcpClient_->sendCommand("STOP"); } } diff --git a/client/src/tcp_client.cpp b/client/src/tcp_client.cpp index ed854db9..039c39a6 100644 --- a/client/src/tcp_client.cpp +++ b/client/src/tcp_client.cpp @@ -101,6 +101,16 @@ void TcpClient::run() { } } +void TcpClient::sendCommand(const std::string& cmd) { + if (socket_ != INVALID_SOCKET) { + std::string msg = cmd + "\n"; + send(socket_, msg.c_str(), msg.size(), 0); + model_->addMessage("Sent command: " + cmd); + } else { + model_->addMessage("Error: Not connected, cannot send command"); + } +} + void TcpClient::reconnectWith(const std::string& host, int port) { host_ = host; port_ = port; From 404d914449dab1941a0355b21917decee4008ae8 Mon Sep 17 00:00:00 2001 From: annhypen Date: Fri, 10 Apr 2026 23:18:01 -0700 Subject: [PATCH 26/26] Fixed redeclarations --- client/include/client/mainframe.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/include/client/mainframe.hpp b/client/include/client/mainframe.hpp index f73f119b..c08ee9e3 100644 --- a/client/include/client/mainframe.hpp +++ b/client/include/client/mainframe.hpp @@ -100,7 +100,7 @@ class MainFrame : public wxFrame { wxPanel* esp32Banner_ = nullptr; wxBoxSizer* mainSizer_ = nullptr; std::atomic esp32BannerPending_{false}; - bool esp32BannerVisible_ = false; std::shared_ptr postProcessor_; + bool esp32BannerVisible_ = false; };