blob: cc51a8b599e262ef2eae0b7940da5f748115e814 [file] [log] [blame]
#ifndef Y2016_DASHBOARD_DASHBOARD_H_
#define Y2016_DASHBOARD_DASHBOARD_H_
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <thread>
#include <atomic>
#include <vector>
#include "seasocks/PageHandler.h"
#include "seasocks/PrintfLogger.h"
#include "seasocks/StringUtil.h"
#include "seasocks/WebSocket.h"
#include "aos/events/event_loop.h"
#include "aos/mutex/mutex.h"
#include "aos/time/time.h"
#include "frc971/autonomous/auto_mode_generated.h"
#include "y2016/control_loops/superstructure/superstructure_status_generated.h"
#include "y2016/queues/ball_detector_generated.h"
#include "y2016/vision/vision_generated.h"
namespace y2016 {
namespace dashboard {
// Dashboard is a webserver that opens a socket and stream data from the robot
// to the client. It is divided between the DataCollector, which polls
// RunIteration to determine what to send to the client, and an instance of a
// Seasocks server, which initiates a webserver on a port and opens a socket
// for streaming data.
// It is an adaption of http_status, which was a 2015 project
// that plotted live position data from the robot queues on a webpage for
// debugging.
class DataCollector {
public:
DataCollector(::aos::EventLoop *event_loop);
void RunIteration();
// Store a datapoint. In this case, we are reading data points to determine
// what color to display on the webpage indicators. Traditionally, this would
// be used to plot live data on a graph on the page.
void AddPoint(const ::std::string &name, double value);
// Method called by the websocket to get a JSON-packaged string containing,
// at most, a constant number of samples, starting at from_sample.
::std::string Fetch(int32_t from_sample);
void operator()();
void Quit() { run_ = false; }
private:
// Returns a wrapped index based on the overflow size.
size_t GetIndex(size_t sample_id);
struct ItemDatapoint {
double value;
::aos::monotonic_clock::time_point time;
};
struct SampleItem {
::std::string name;
::std::vector<ItemDatapoint> datapoints;
};
::aos::EventLoop *event_loop_;
::aos::Fetcher<::y2016::vision::VisionStatus> vision_status_fetcher_;
::aos::Fetcher<::y2016::sensors::BallDetector> ball_detector_fetcher_;
::aos::Fetcher<::frc971::autonomous::AutonomousMode> autonomous_mode_fetcher_;
::aos::Fetcher<::y2016::control_loops::superstructure::Status>
superstructure_status_fetcher_;
// Storage vector that is written and overwritten with data in a FIFO fashion.
::std::vector<SampleItem> sample_items_;
::std::string cur_raw_data_;
int32_t sample_id_; // Last sample id used.
size_t measure_index_; // Last measure index used.
const int32_t overflow_id_; // Vector wrapping size.
::std::atomic<bool> run_{true};
::aos::Mutex mutex_;
};
class SocketHandler : public seasocks::WebSocket::Handler {
public:
SocketHandler(::aos::EventLoop *event_loop);
void onConnect(seasocks::WebSocket* connection) override;
void onData(seasocks::WebSocket* connection, const char* data) override;
void onDisconnect(seasocks::WebSocket* connection) override;
void Quit();
private:
::std::set<seasocks::WebSocket*> connections_;
DataCollector data_collector_;
::std::thread data_collector_thread_;
};
class SeasocksLogger : public seasocks::PrintfLogger {
public:
SeasocksLogger(Level min_level_to_log);
void log(Level level, const char* message) override;
};
} // namespace dashboard
} // namespace y2016
#endif // Y2016_DASHBOARD_DASHBOARD_H_