Skip to content
This repository has been archived by the owner on Jul 4, 2022. It is now read-only.

Commit

Permalink
Add documentation for the remote control application
Browse files Browse the repository at this point in the history
  • Loading branch information
hypothermic committed Jun 6, 2022
1 parent 77f85f6 commit f940cf7
Show file tree
Hide file tree
Showing 13 changed files with 607 additions and 41 deletions.
42 changes: 39 additions & 3 deletions rc/rc_application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,91 @@

#include "rc_uart_packet.hpp"

/*
* @inheritDoc
*/
RemoteControlApplication::RemoteControlApplication()
: Gtk::Application("com.arobs.lane.RemoteControl"),
synchronizer(),
tty_thread(nullptr),
uart_manager(new UartManager()) {

// Register the callback for when UART Data has been received
this->synchronizer.connect(sigc::mem_fun(*this, &RemoteControlApplication::on_thread_data_renew));
}

/*
* @inheritDoc
*/
void RemoteControlApplication::on_activate() {
auto info_window = new InformationWindow(uart_manager);

this->add_window(*info_window);
this->info_window = info_window;

// Register the callback for when the user quits the application
info_window->signal_hide().connect(sigc::mem_fun(*this, &RemoteControlApplication::on_destroy));

// Register the callback for when the user clicks the "Connect" button
info_window->connect_request_signal.connect(sigc::mem_fun(*this, &RemoteControlApplication::on_connect));

// Register the callback for when the user clicks the "Disconnect" button
info_window->disconnect_request_signal.connect(sigc::mem_fun(*this, &RemoteControlApplication::on_disconnect));
info_window->present();

this->info_window = info_window;
// Show the window on the screen
info_window->present();
}

/*
* @inheritDoc
*/
void RemoteControlApplication::on_destroy() {
delete this->info_window;
}

/*
* @inheritDoc
*/
void RemoteControlApplication::on_connect(Glib::ustring tty_name) {
uart_manager->set_connection_target(tty_name);

// Run the UART receive loop on a new thread
this->tty_thread = new std::thread([this] {
uart_manager->main_loop(this);
});
}

/*
* @inheritDoc
*/
void RemoteControlApplication::on_disconnect() {
this->uart_manager->request_stop();
}

/*
* @inheritDoc
*/
void RemoteControlApplication::on_thread_sync() {
this->synchronizer.emit();
}

/*
* @inheritDoc
*/
void RemoteControlApplication::on_thread_data_renew() {
std::vector<UartPacket *> packets = this->uart_manager->get_new_packets();

// This code is run on the UI thread and we should have acquired the mutex (check the calling code to be sure)
// So we can read the UartManager's packets and connection state

std::vector<UartPacket *> packets = this->uart_manager->get_new_packets();

this->info_window->on_connection_state_update(this->uart_manager->get_connection_state());

// Send every packet that was received in this burst to the information window
for (UartPacket *packet : packets) {
this->info_window->on_data_update(packet);

// And then free the memory since we took ownership of it
delete packet;
}
}
Expand Down
73 changes: 69 additions & 4 deletions rc/rc_application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,97 @@
#include "rc_information_window.hpp"
#include "rc_uart_manager.hpp"

/**
* @see rc_information_window.hpp
*/
class InformationWindow;

/**
* @see rc_uart_manager.hpp
*/
class UartManager;

/**
* The main entry point of the program which handles the setup of
* the user interface and the creation of the UART manager class.
* It acts as a bridge between these by managing the data between
* the two threads
*/
class RemoteControlApplication : public Gtk::Application {
private:
/**
* A reference to the main window. It is
* initialized upon activation of the application
*/
InformationWindow *info_window;

// Messaging between threads
/**
* Allows for messaging between the UI thread
* and the serial reader thread by emitting a
* signal when data has been receiver
*/
Glib::Dispatcher synchronizer;

/**
* The system thread that the serial connection
* is accessed from
*/
std::thread *tty_thread;

/**
* Wrapper for handling the serial communication
*/
UartManager *uart_manager;

public:
/*
* @inheritDoc
*/
RemoteControlApplication();


// GTK event callbacks
// --- GTK event callbacks

/**
* Called upon startup of the application
*/
void on_activate() override;

/**
* Called when the user quits the application
*/
void on_destroy();

// UI connect button callback
// --- UI window action callbacks

/**
* Called when the user requests to connect to
* a device at port <i>tty_name</i>
*/
void on_connect(Glib::ustring tty_name);

/**
* Called when the user requests to disconnect
* from all devices
*/
void on_disconnect();

// UART thread callbacks
// --- UART data thread callbacks

/**
* Called when the state of the serial data thread
* has been updated. The new state can be read by
* claiming its mutex and reading its <i>state</i>
* member.
*/
void on_thread_sync();

/**
* Called when the serial data receiver thread has
* received new data. The new data can be read by
* claiming its mutex and using its
* <i>get_new_packets</i> function.
*/
void on_thread_data_renew();
};

6 changes: 6 additions & 0 deletions rc/rc_connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@

#include <glibmm/ustring.h>

/**
* The current state of a connection
*/
enum class ConnectionState {
DISCONNECTED,
CONNECTED,
};

/**
* The parameters used for connecting to a target device
*/
struct ConnectionTarget {
Glib::ustring tty_port;
//unsigned long baud_rate;
Expand Down
59 changes: 51 additions & 8 deletions rc/rc_information_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,37 @@

#include "rc_information_window.hpp"

#include <iostream>

/**
/*
* @internal
*
* Set the label text with a value suffix
*
* @tparam T the type of value which is supplied
*
* @param label the label which should be updated
* @param name the prefix of the label
* @param value the suffix of the label
*/
template<typename T>
static inline void set_label_text(Gtk::Label &label, const char *name, T value) {
Glib::ustring seg_thres_str;

label.set_markup(Glib::ustring::sprintf("%s: <b>%s</b>", name, std::to_string(value)));
}

/*
* @internal
* @inheritDoc
*
* Specialization for booleans because <i>std::to_string</i> can't handle them
* Specialization for booleans because <i>std::to_string</i> can't handle them...
* Each day I'm losing more faith in the STL, it's almost as bad as the IEEE VHDL STD
*/
template<>
inline void set_label_text(Gtk::Label &label, const char *name, bool value) {
Glib::ustring seg_thres_str;

label.set_markup(Glib::ustring::sprintf("%s: <b>%s</b>", name, (value ? "true" : "false")));
}

/*
* @inheritDoc
*/
InformationWindow::InformationWindow(UartManager *uart_manager) :
uart_manager(uart_manager),
main_container(Gtk::Orientation::VERTICAL),
Expand All @@ -48,9 +55,15 @@ InformationWindow::InformationWindow(UartManager *uart_manager) :
preview_aspect(Gtk::Align::CENTER, Gtk::Align::CENTER, (1280.0f/720.0f)),
preview_area() {

// Construct all the UI widgets.
// This could be done using Glade in the future
// It's an XML-like UI definition file that will help
// replace the hardcoded stuff below here

this->port_entry.set_text("/dev/ttyUSB0");
this->port_entry.set_hexpand(true);

// Connect the callback of the "Connect"/"Disconnect" button
this->connect_button.signal_clicked().connect(
sigc::mem_fun(*this, &InformationWindow::on_connect_request));

Expand All @@ -60,6 +73,8 @@ InformationWindow::InformationWindow(UartManager *uart_manager) :

this->connection_frame.set_child(this->connection_container);

// TODO use a for loop or macro or something

this->is_processing_label.set_halign(Gtk::Align::START);
this->is_processing_label.set_hexpand(false);
this->is_processing_label.set_text("");
Expand Down Expand Up @@ -104,13 +119,25 @@ InformationWindow::InformationWindow(UartManager *uart_manager) :
this->set_child(this->main_container);
}

/*
* @inheritDoc
*/
void InformationWindow::on_connection_state_update(ConnectionState state) {
this->port_entry.set_editable(state == ConnectionState::DISCONNECTED);
this->connect_button.set_label(state == ConnectionState::DISCONNECTED ? "Connect" : "Disconnect");
}

/*
* @inheritDoc
*/
void InformationWindow::on_data_update(UartPacket *packet) {

// Depending on which type of packet we have received,
// we want to update the relevant information widgets
switch (packet->get_type()) {

// The packet contains a new status and parameters,
// update the relevant fields in the Status frame;
case static_cast<uint8_t>(UartPacket::Type::STATUS_UPDATE): {
StatusUpdatePacket *sup = dynamic_cast<StatusUpdatePacket *>(packet);

Expand All @@ -123,11 +150,20 @@ void InformationWindow::on_data_update(UartPacket *packet) {

break;
}

// The packet contains line data, delegate it to the
// preview window so that it can plot the lines
case static_cast<uint8_t>(UartPacket::Type::FRAME_PROCESSED): {
this->preview_area.on_frame_update(dynamic_cast<FrameProcessedPacket *>(packet));

break;
}

// The packet contains a boolean which indicates
// whether the departure alarm has been activated.
//
// Update the status label and set the line plot
// color to make the lines red/blue.
case static_cast<uint8_t>(UartPacket::Type::DEPARTURE_WARNING): {
DepartureWarningPacket *dwp = dynamic_cast<DepartureWarningPacket *>(packet);

Expand All @@ -144,7 +180,14 @@ void InformationWindow::on_data_update(UartPacket *packet) {
}
}

/*
* @inheritDoc
*/
void InformationWindow::on_connect_request() {

// If the port text field is editable, the connection
// hasn't been established, so we emit the connect signal.
// Otherwise, emit the disconnect signal
if (this->port_entry.get_editable() == true) {
this->connect_request_signal(this->port_entry.get_text());
} else {
Expand Down
Loading

0 comments on commit f940cf7

Please sign in to comment.