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
5 changes: 3 additions & 2 deletions client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ endif()
# Panorama Includes
target_include_directories(panorama-client
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/external/wxMathPlot_0.2.0/mathplot
)


Expand Down Expand Up @@ -74,7 +75,7 @@ if(APPLE)

# Apply wxWidgets linker flags - don't use separate_arguments to preserve framework syntax
string(STRIP "${WX_LINK_FLAGS}" WX_LINK_FLAGS)
target_link_libraries(panorama-client PRIVATE ${WX_LINK_FLAGS})
target_link_libraries(panorama-client PRIVATE ${WX_LINK_FLAGS} mathplot)

message(STATUS "Using wxWidgets prebuilt from ${WXWIDGETS_PREBUILT_PATH}")
else() # linux
Expand All @@ -100,7 +101,7 @@ else() # linux

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WX_CXX_FLAGS}")
set(WX_LIBRARIES "${WX_LINK_FLAGS}")
target_link_libraries(panorama-client "${WX_LIBRARIES}")
target_link_libraries(panorama-client "${WX_LIBRARIES}" mathplot)

message(STATUS "Using locally built wxWidgets via wx-config: ${wxWidgets_CONFIG_EXECUTABLE}")
endif()
Expand Down
2 changes: 2 additions & 0 deletions client/include/client/DataBuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class DataBuffer : public BufferBase<buffer_data_t> {
// Removes extracted portion from buffer
std::string extractNextJson();

buffer_data_t consumeFront();


// ---------------------------
// JSON PARSING
Expand Down
2 changes: 1 addition & 1 deletion client/include/client/buffer_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,6 @@ class BufferBase {
protected:
std::list<T> buffer_;
mutable std::mutex mutex_;
int MAX_BUFFER_SIZE = 5;
int MAX_BUFFER_SIZE = 500;
int FLUSH_THRESHOLD = 50; //percentage
};
27 changes: 15 additions & 12 deletions client/include/client/graph_panel.hpp
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@

#pragma once
#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/window.h>
#include <vector>
#include <mathplot.h>
#include <map>
#include <vector>
#include <set>
#include <string>

class GraphPanel : public wxPanel {
public:
GraphPanel(wxWindow* parent);

void AddDataPoint(const std::string& sensorName, double value, double timestamp);
void SetVisibleSensors(const std::set<std::string>& visisble);

private:
// Event handlers
void OnPaint(wxPaintEvent& event);
void OnSize(wxSizeEvent& event);
mpWindow* m_plot;

//store data for each sensor
std::map<std::string, std::vector<std::pair<double, double>>> sensorData_;
std::map<std::string, mpFXYVector*> sensorLayers_;

// Drawing functions
void DrawBackground(wxDC& dc);
void DrawGrid(wxDC& dc);
void DrawAxes(wxDC& dc);
std::set<std::string> visibleSensors_;

wxDECLARE_EVENT_TABLE();
void UpdateGraph();
};
38 changes: 38 additions & 0 deletions client/include/client/json_writer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

#include "common/panorama_defines.hpp"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/filewritestream.h" // For writing directly to a FILE* stream
#include <iostream>
#include <fstream>
#include <cstdio> // For fopen, fclose

#include "client/DataBuffer.hpp"
#include <memory>
#include <atomic>
#include <thread>
#include <chrono>


class DataBuffer;

class JsonWriter {
public:
JsonWriter(std::shared_ptr<DataBuffer> dataBuffer, const std::string& exportPath); // constructor

void start();
void stop();

// returns boolean if has been written to
bool writeToJson(buffer_data_t data);

rapidjson::Document getDocumentFromData(buffer_data_t data);

private:
std::time_t previousTimestamp = 0;
std::atomic<bool> running_{true};
std::shared_ptr<DataBuffer> dataBuffer_;
std::string exportPath;
};
11 changes: 11 additions & 0 deletions client/include/client/mainframe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
#include <vector>
#include <memory>
#include <map>
#include <atomic>
#include "client/sensor_data_panel.h"
#include "client/sensor_manager.hpp"
#include "client/sensor.hpp"
#include "client/graph_panel.hpp"
#include <set>


Expand Down Expand Up @@ -68,6 +70,15 @@ class MainFrame : public wxFrame {
std::shared_ptr<DataBuffer> dataBuffer_;
wxTextCtrl* messageDisplay_;
wxPanel* consolePanel_;
GraphPanel* graphPanel_;

wxTimer updateTimer_;
std::atomic<bool> updatePending_{false};
size_t displayedMessageCount_ = 0;
size_t displayedBufferCount_ = 0;

void OnUpdateTimer(wxTimerEvent& event);


};

Expand Down
5 changes: 4 additions & 1 deletion client/include/common/panorama_defines.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#pragma once
#include <ctime>
#include <string>

typedef struct {
float data; // actual value
std::time_t timestamp; // date recorded
std::string dataunit; // e.g. "kPa", "mL"
std::string datatype; // e.g. "temperature", "sound"
std::string sensor;
int sensorID;

} buffer_data_t;
} buffer_data_t;
9 changes: 9 additions & 0 deletions client/src/DataBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ size_t DataBuffer::size() const {
return BufferBase<buffer_data_t>::size();
}

buffer_data_t DataBuffer::consumeFront() {
return BufferBase<buffer_data_t>::extractNextBuffer();
}

void DataBuffer::clear() {
BufferBase<buffer_data_t>::clear();
}
Expand Down Expand Up @@ -114,11 +118,16 @@ void DataBuffer::parseAll(/* std::vector<ParsedData> &out */) {

std::string DataBuffer::toString(const buffer_data_t& buffer_item) {
//convert one struct of buffer_ into string
//bool hasUnit = buffer_item.dataunit != "\0";

std::string temp = "{";

temp = temp + "\"datatype\": \"" + buffer_item.datatype + "\", \"data\": " + std::to_string(buffer_item.data) + ", ";

//if (hasUnit) {
temp = temp + "\"dataunit\": \"" + buffer_item.dataunit + "\", ";
//}

temp = temp + "\"timestamp\": " + std::to_string(buffer_item.timestamp);

temp += "}";
Expand Down
159 changes: 70 additions & 89 deletions client/src/graph_panel.cpp
Original file line number Diff line number Diff line change
@@ -1,109 +1,90 @@
#include "client/graph_panel.hpp"
#include <algorithm>
#include <cmath>

wxBEGIN_EVENT_TABLE(GraphPanel, wxPanel)
EVT_PAINT(GraphPanel::OnPaint)
EVT_SIZE(GraphPanel::OnSize)
wxEND_EVENT_TABLE()

GraphPanel::GraphPanel(wxWindow* parent)
: wxPanel(parent, wxID_ANY) {

SetBackgroundColour(wxColour(255, 255, 255));
SetBackgroundStyle(wxBG_STYLE_PAINT); // double buffering so smoother
}

// redraw the panel
void GraphPanel::OnPaint(wxPaintEvent& event) {
wxAutoBufferedPaintDC dc(this);
dc.Clear();

DrawBackground(dc);
DrawGrid(dc);
DrawAxes(dc);
}
m_plot = new mpWindow(this, wxID_ANY);
m_plot-> EnableDoubleBuffer(true);
m_plot-> EnableMousePanZoom(true);

// Function: used when the panel is resized to redraw
void GraphPanel::OnSize(wxSizeEvent& event) {
Refresh();
Update();
// Create X & Y axes
mpScaleX* xAxis = new mpScaleX(wxT("Time (seconds)"), mpALIGN_BORDER_BOTTOM, true);
mpScaleY* yAxis = new mpScaleY(wxT("value"), mpALIGN_BORDER_LEFT, true);

event.Skip();
}
xAxis->SetDrawOutsideMargins(false);
yAxis->SetDrawOutsideMargins(false);

// Drawing functions
void GraphPanel::DrawBackground(wxDC& dc) {
wxSize size = GetClientSize();

dc.SetBrush(wxBrush(wxColour(220, 220, 220)));
m_plot->AddLayer(yAxis);
m_plot->AddLayer(xAxis);

dc.SetPen(*wxTRANSPARENT_PEN);
m_plot->SetMargins(30, 30, 85, 60);
m_plot->Fit();

dc.DrawRectangle(0, 0, size.GetWidth(), size.GetHeight());
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(m_plot, 1, wxEXPAND);
SetSizer(sizer);
}

// Function: to draw grid lines
void GraphPanel::DrawGrid(wxDC& dc) {
wxSize size = GetClientSize();

int leftMargin = 60;
int bottomMargin = 40;
int topMargin = 20;
int graphHeight = size.GetHeight() - topMargin - bottomMargin;
int graphWidth = size.GetWidth() - leftMargin - 20;

dc.SetPen(wxPen(wxColour(90, 90, 90), 1));

// horizontal grid lines
for (int i = 0; i <= 4; i++) {
int y = topMargin + (graphHeight * i / 4);
dc.DrawLine(leftMargin, y, leftMargin + graphWidth, y);
}
void GraphPanel::AddDataPoint(const std::string& sensorName, double value, double timestamp){
sensorData_[sensorName].push_back({timestamp, value});

// vertical grid lines
for (int i = 0; i <=6; i++) {
int x = leftMargin + (graphWidth * i / 6);
dc.DrawLine(x, topMargin, x, topMargin + graphHeight);
// Keeps the first 100 data points
if (sensorData_[sensorName].size() > 100) {
sensorData_[sensorName].erase(sensorData_[sensorName].begin());
}
}


void GraphPanel::DrawAxes(wxDC& dc){
wxSize size = GetClientSize();

int leftMargin = 60;
int bottomMargin = 40;
int topMargin = 20;
int graphHeight = size.GetHeight() - topMargin - bottomMargin;
int graphWidth = size.GetWidth() - leftMargin - 20;
UpdateGraph();
}

dc.SetPen(wxPen(wxColour(0, 0, 0), 2));

// draw y-axis
dc.DrawLine(leftMargin, topMargin, leftMargin, topMargin + graphHeight);

// draw x-axis
dc.DrawLine(leftMargin, topMargin + graphHeight, leftMargin + graphWidth, topMargin + graphHeight);

//
//x axis label
for (int i = 0; i < 6; i++) {
int x = leftMargin + (graphWidth * i / 6);
int value = i;
wxString label = wxString::Format("%d", value);
wxSize textSize = dc.GetTextExtent(label);
dc.DrawText(label, x - textSize.GetWidth() / 2, size.GetHeight() - bottomMargin + 5);
void GraphPanel::UpdateGraph(){
for (auto& pair : sensorLayers_){
m_plot->DelLayer(pair.second,true);
}

//y axis label
for (int i = 0; i <= 4; i++) {
int y = (topMargin + graphHeight) - (graphHeight * i / 4);
int value = i * 25;
wxString label = wxString::Format("%d", value);
wxSize textSize = dc.GetTextExtent(label);
dc.DrawText(label, leftMargin - textSize.GetWidth() - 5, y - textSize.GetHeight() / 2);
sensorLayers_.clear();

// colours for the graph
wxColour colours[] = {
wxColour(255, 0, 0),
wxColour(0, 255, 0),
wxColour(0, 0, 255),
wxColour(255, 102, 178),
wxColour(178, 102, 255)
};
int colourIndex = 0;

for(auto& sensorPair : sensorData_){
const std::string& sensorName = sensorPair.first;
auto& data = sensorPair.second;

if(!visibleSensors_.empty() && visibleSensors_.count(sensorName) == 0)
continue;

if (data.empty()) continue;

std::vector<double> xs, ys;
for(const auto& point : data){
xs.push_back(point.first); // timestamp
ys.push_back(point.second); // value
}

mpFXYVector* layer = new mpFXYVector(wxString(sensorName));
layer->SetData(xs, ys);
layer->SetContinuity(true);

wxPen pen(colours[colourIndex % 5], 2);
layer->SetPen(pen);

m_plot->AddLayer(layer);
sensorLayers_[sensorName] = layer;

colourIndex++;
}

m_plot->Fit();
m_plot->Refresh();
m_plot->Update();
}

void GraphPanel::SetVisibleSensors(const std::set<std::string>& visible) {
visibleSensors_ = visible;
UpdateGraph();
}
Loading
Loading