Program Listing for File DataPlotter.h#
↰ Return to documentation for file (include/Karana/KUtils/DataPlotter.h)
/*
* Copyright (c) 2024-2026 Karana Dynamics Pty Ltd. All rights reserved.
*
* NOTICE TO USER:
*
* This source code and/or documentation (the "Licensed Materials") is
* the confidential and proprietary information of Karana Dynamics Inc.
* Use of these Licensed Materials is governed by the terms and conditions
* of a separate software license agreement between Karana Dynamics and the
* Licensee ("License Agreement"). Unless expressly permitted under that
* agreement, any reproduction, modification, distribution, or disclosure
* of the Licensed Materials, in whole or in part, to any third party
* without the prior written consent of Karana Dynamics is strictly prohibited.
*
* THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
* KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
* FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
* INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
* OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
*
* U.S. Government End Users: The Licensed Materials are a "commercial item"
* as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
* only as a commercial end item under the terms of this license.
*
* Any use of the Licensed Materials in individual or commercial software must
* include, in the user documentation and internal source code comments,
* this Notice, Disclaimer, and U.S. Government Use Provision.
*/
/**
* @file
* @brief Contains the declarations for the classes/structs that use and produce the data for
* dynamic plotting with the DashApp.
*/
#include "Karana/KCore/Base.h"
#include "Karana/Math/Defs.h"
#include <boost/asio.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <map>
#include <string>
namespace Karana::KUtils {
namespace km = Karana::Math;
/// The types of a data that can be plotted
using PlotDataType = std::variant<double, km::Vec3, km::Vec>;
/**
* @struct SinglePlotData
* @brief Structure that holds data update functions for a single plot.
*/
struct SinglePlotData {
public:
/// The title of the plot
std::string title;
/// The name of the x_data
std::string x_data_name;
/// The function that produces the x_data
std::function<double()> x_data;
/// The y_data names
std::vector<std::string> y_data_names;
/// The y_data functions
std::vector<std::function<PlotDataType()>> y_data_fns;
/**
* @brief Constructor.
*
* @param title The title of the plot.
* @param x_data_name The name for the x_data.
* @param x_data The function that produces the x_data.
* @param y_data A map whose keys are the y_data names and whose values are
* functions that produce the y_data.
*/
SinglePlotData(std::string_view title,
std::string_view x_data_name,
std::function<double()> x_data,
std::map<std::string, std::function<PlotDataType()>> y_data);
/**
* @brief Constructor.
*
* @param title The title of the plot.
* @param x_data_name The name for the x_data.
* @param x_data The function that produces the x_data.
* @param y_data_names The names of the y_data
* @param y_data_fns The functions that produce the y_data
*/
SinglePlotData(std::string_view title,
std::string_view x_data_name,
std::function<double()> x_data,
std::vector<std::string> y_data_names,
std::vector<std::function<PlotDataType()>> y_data_fns);
/**
* @brief Copy constructor.
*
* @param other The data to copy from.
*/
SinglePlotData(const SinglePlotData &other);
/**
* @brief Move constructor.
*
* @param other The data to move from.
*/
SinglePlotData(SinglePlotData &&other) noexcept;
/**
* @brief Copy assignment operator.
*
* @param other The data to copy from.
* @returns A reference to the newly assigned SinglePlotData.
*/
SinglePlotData &operator=(const SinglePlotData &other);
/**
* @brief Move assignment operator.
*
* @param other The data to copy from.
* @returns A reference to the newly assigned SinglePlotData.
*/
SinglePlotData &operator=(SinglePlotData &&other) noexcept;
private:
/**
* @brief Constructor.
*
* @param title The title of the plot.
* @param x_data_name The name for the x_data.
* @param x_data The function that produces the x_data.
* @param y_data The names and functions that produce the y_data.
*/
SinglePlotData(
std::string_view title,
std::string_view x_data_name,
std::function<double()> x_data,
std::pair<std::vector<std::string>, std::vector<std::function<PlotDataType()>>> y_data);
};
/**
* @class WsClient
* @brief Simple websocket client to connect to a websocket and send messages.
*/
class WsClient {
public:
/**
* @brief Constructor.
*
* @param host The host for the websocket.
* @param port The port for the websocket.
* @param target The target for the websocket.
*/
WsClient(std::string_view host, std::string_view port, std::string_view target);
/**
* @brief Start the websocket and create the connection.
*/
void start();
/**
* @brief Send a message over the websocket.
*
* @param msg The message to send.
*/
void send(std::string msg);
/**
* @brief Stop the websocket.
*/
void stop();
private:
/// The host for the websocket
std::string _host;
/// The port for the websocket
std::string _port;
/// The target of the websocket
std::string _target;
/// The IO context
boost::asio::io_context _ioc;
/// The TCP resolver
boost::asio::ip::tcp::resolver _resolver;
/// The websocket itself
boost::beast::websocket::stream<boost::asio::ip::tcp::socket> _ws;
/// The strand executor
boost::asio::strand<boost::asio::io_context::executor_type> _strand;
/// Stores whether the websocket is running or not
bool _running = false;
};
/**
* @class PlotData
* @brief Class that contains plot data and associated methods for potentially multiple plots.
*/
class PlotData : public Karana::Core::Base {
public:
/**
* @brief PlotData constructor. The constructor is not meant to be called directly.
* Please use the create(...) method instead to create an instance.
*
* @param plot_data The data for the plots.
* @param host The host for the websocket.
* @param port The port for the websocket.
* @param target The target for the websocket.
*/
PlotData(const std::vector<SinglePlotData> &plot_data,
std::string_view host,
std::string_view port,
std::string_view target);
/**
* @brief Create an instance of PlotData.
*
* @param plot_data The data for the plots.
* @param host The host for the websocket.
* @param port The port for the websocket.
* @param target The target for the websocket.
*
* @returns A pointer to the newly created PlotData.
*/
static Karana::Core::ks_ptr<PlotData> create(const std::vector<SinglePlotData> &plot_data,
std::string_view host,
std::string_view port,
std::string_view target);
/**
* @brief Destructor.
*/
~PlotData();
/**
* @brief Add a plot.
*
* @param plot_data The data for the plot to add.
*/
void addPlot(SinglePlotData plot_data);
/**
* @brief Remove the plot at index.
*
* @param index The index of the plot to remove.
*/
void removePlot(size_t index);
/**
* @brief Get data from the SinglePlotData and send it via a websocket to the server.
*/
void update();
/**
* @brief Send a message over the websocket. This should be used with caution and only if
* you know exactly what type of messages are allowed to be sent.
* @param msg The message to send.
*/
void sendWebsocketMessage(const std::string &msg);
/**
* @brief Shutdown the websocket client.
*/
void shutdown();
/// The data for the plots.
std::vector<SinglePlotData> plot_fns;
private:
/// The websocket client to use
WsClient _ws;
};
} // namespace Karana::KUtils