Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ set(CLIENT_HEADERS
include/client/message_model.hpp
include/client/tcp_client.hpp
include/client/json_reader.hpp
include/client/buffer_base.hpp
include/client/DataBuffer.hpp
include/client/sensor_manager.hpp
include/client/sensor_data_panel.h
Expand Down
12 changes: 9 additions & 3 deletions client/include/client/DataBuffer.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#pragma once
#ifndef __DATABUFFER__
#define __DATABUFFER__
#include <string>
#include <vector>
#include <mutex>
#include "common/panorama_defines.hpp"
#include "client/buffer_base.hpp"
#include <list>

class DataBuffer {
class DataBuffer : public BufferBase<buffer_data_t> {
public:
DataBuffer();
~DataBuffer();
Expand Down Expand Up @@ -35,6 +37,7 @@ class DataBuffer {
// Clear the buffer
void clear();


// Extract the next complete JSON object from buffer
// Removes extracted portion from buffer
std::string extractNextJson();
Expand All @@ -47,7 +50,7 @@ class DataBuffer {
// Parse the next complete JSON object in the buffer.
// Returns success/failure depending on whether parsing succeeded.
// The parsed result can be returned as a variant, struct, or any user-defined type.
bool parseNextJson(/* ParsedData &out */);
std::string parseNextJson(/* ParsedData &out */);

// Parse *all* complete JSON objects currently in the buffer.
// Useful if the buffer contains multiple messages.
Expand All @@ -57,6 +60,8 @@ class DataBuffer {

std::string toStringAll();

void exportBuffer();

private:
// Raw buffer storing incoming data
std::list<buffer_data_t> buffer_;
Expand All @@ -78,3 +83,4 @@ class DataBuffer {
// This keeps parsing logic isolated from buffer logic
bool decodeJson(const std::string& jsonStr /*, ParsedData &out */);
};
#endif // __DATABUFFER__
102 changes: 102 additions & 0 deletions client/include/client/buffer_base.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// #pragma once
#include <list>
#include <mutex>
#include <cstddef>

/**
* @brief Base class for buffers with templated data type
* @tparam T The type of data stored in the buffer
*
* This template class provides thread-safe buffer operations
* (e.g., writes, reads ...)
* that can be customized by child classes
* to work with any data type.
*/
template <typename T>
class BufferBase {
public:
BufferBase() = default;
virtual ~BufferBase() = default;

@aexzhou aexzhou Jan 17, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Info: the virtual keyword here allows for class methods for this buffer_base class to be inherited to children classes

Learn more about virtual:
https://www.geeksforgeeks.org/cpp/virtual-function-cpp/


/**
* @brief Write data to the buffer
* @param data The data to append to the buffer
*/
virtual void write(T data) {
std::lock_guard<std::mutex> lock(mutex_);
buffer_.push_back(std::move(data));

}

/**
* @brief Replace the entire buffer with new data
* @param data The data to set as the new buffer content
*/
virtual void setData(T data) {
std::lock_guard<std::mutex> lock(mutex_);
buffer_.clear();
buffer_.push_back(std::move(data));
}

/**
* @brief Read all data from the buffer without clearing it
* @return A copy of all data in the buffer
*/
virtual std::list<T> readAll() const {
std::lock_guard<std::mutex> lock(mutex_);
return buffer_;
}

/**
* @brief Read and clear all data from the buffer
* @return All data that was in the buffer
*/
virtual std::list<T> consume() {
std::lock_guard<std::mutex> lock(mutex_);
std::list<T> result = std::move(buffer_);
buffer_.clear();
return result;
}

// Returns the first element of the buffer and removes it from the buffer
virtual T extractNextBuffer() {
buffer_data_t ret = buffer_.front();
buffer_.pop_front();
return ret;
}

virtual void popFront() {
buffer_.pop_front();
}

/**
* @brief Get the number of elements in the buffer
* @return The number of elements
*/
virtual size_t size() const {
std::lock_guard<std::mutex> lock(mutex_);
return buffer_.size();
}

/**
* @brief Check if the buffer is empty
* @return true if the buffer is empty, false otherwise
*/
virtual bool empty() const {
std::lock_guard<std::mutex> lock(mutex_);
return buffer_.empty();
}

/**
* @brief Clear all data from the buffer
*/
virtual void clear() {
std::lock_guard<std::mutex> lock(mutex_);
buffer_.clear();
}

protected:
std::list<T> buffer_;
mutable std::mutex mutex_;
int MAX_BUFFER_SIZE = 5;
};
8 changes: 7 additions & 1 deletion client/include/client/mainframe.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#ifndef __MAINFRAME__
#define __MAINFRAME__
#include <wx/wx.h>
#include <wx/splitter.h>
#include <wx/notebook.h>
Expand All @@ -13,20 +14,25 @@


class MessageModel;
class DataBuffer;
//class GraphPanel;
class SensorDataManager;
//class SensorDataFrame; // Add this

class MainFrame : public wxFrame {
public:
MainFrame(const wxString& title, std::shared_ptr<MessageModel> model,
std::shared_ptr<DataBuffer> dataBuffer,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxSize(1200, 800));

private:
void updateMessageDisplay();

std::shared_ptr<MessageModel> model_;
std::shared_ptr<DataBuffer> dataBuffer_;
wxTextCtrl* messageDisplay_;

};

#endif // __MAINFRAME__
14 changes: 10 additions & 4 deletions client/include/client/tcp_client.hpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#pragma once
#ifndef __TCP_CLIENT__
#define __TCP_CLIENT__

#include <string>
#include <atomic>
#include <thread>
#include <memory>

class MessageModel;
class DataBuffer;
class DataLogger;

class TcpClient {
public:
TcpClient(const std::string& host, int port, std::shared_ptr<MessageModel> model, std::shared_ptr<DataLogger> logger = nullptr);
TcpClient(const std::string& host, int port, std::shared_ptr<MessageModel> model, std::shared_ptr<DataBuffer> dataBuffer, std::shared_ptr<DataLogger> logger = nullptr);
~TcpClient();

void start();
Expand All @@ -24,12 +27,15 @@ class TcpClient {
int port_;
std::shared_ptr<MessageModel> model_;
std::shared_ptr<DataLogger> logger_;
std::shared_ptr<DataBuffer> dataBuffer_;
std::atomic<bool> running_;
std::thread clientThread_;

#ifdef _WIN32
unsigned long long socket_; // SOCKET type on Windows
#else
int socket_;
#endif
};
};

#endif
12 changes: 8 additions & 4 deletions client/include/common/panorama_defines.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#pragma once
#include <ctime>

typedef struct {
char a;
float a_data;
char b;
float b_data;
float data; // actual value
std::time_t timestamp; // date recorded
const char * dataunit; // e.g. "kPa", "mL"
const char * datatype; // e.g. "temperature", "sound"

} buffer_data_t;
13 changes: 10 additions & 3 deletions client/include/common/panorama_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

// #endif

#ifndef PANORAMA_UTILS_HPP
#define PANORAMA_UTILS_HPP
#ifndef __PANORAMA_UTILS_HPP__
#define __PANORAMA_UTILS_HPP__

#include <iostream>

Expand All @@ -22,4 +22,11 @@ void pinfo(const Args&... args) {
std::cout << std::endl;
}

#endif // PANORAMA_UTILS_HPP
template <typename... Args>
void pdebug(const Args&... args) {
std::cout << "[DEBUG][Client]\t";
(std::cout << ... << args); // fold expression over operator<<
std::cout << std::endl;
}

#endif // __PANORAMA_UTILS_HPP__
Loading