Files
BloomPatched/src/Application.hpp
Nav 7a6dcdcbfd Fixed bug with Insight QApplication object being created too soon, which in turn caused issues with signal capturing.
Also ensured that we only initialise the Insight object when the user has enabled Insight in their config.
2021-08-11 00:07:12 +01:00

266 lines
9.2 KiB
C++

#pragma once
#include <memory>
#include <map>
#include <string>
#include <functional>
#include <QtCore/QtCore>
#include <thread>
#include "src/SignalHandler/SignalHandler.hpp"
#include "src/TargetController/TargetController.hpp"
#include "src/DebugServers/GdbRsp/AvrGdbRsp/AvrGdbRsp.hpp"
#include "src/Insight/Insight.hpp"
#include "src/Helpers/Thread.hpp"
#include "src/Logger/Logger.hpp"
#include "src/ApplicationConfig.hpp"
#include "src/Exceptions/Exception.hpp"
#include "src/EventManager/EventListener.hpp"
#include "src/EventManager/Events/Events.hpp"
namespace Bloom
{
/**
* Bloom - a debug interface for embedded systems development on Linux.
*
* This is the main entry-point of execution for the Bloom program. The methods within will run on the main
* thread. If Insight is enabled, execution will be passed over to Insight::run() upon startup.
*/
class Application: public Thread
{
public:
static const inline std::string VERSION_STR = "0.3.0";
private:
/**
* This is the application wide event manager. It should be the only instance of the EventManager class at
* any given time.
*
* It should be injected (by reference) into any other instances of classes that require use
* of the EventManager.
*/
EventManager eventManager = EventManager();
EventListenerPointer applicationEventListener = std::make_shared<EventListener>("ApplicationEventListener");
/**
* The SignalHandler deals with any UNIX signals. It runs on a dedicated thread. All other threads
* ignore UNIX signals.
*
* See the SignalHandler class for more on this.
*/
SignalHandler signalHandler = SignalHandler(this->eventManager);
std::thread signalHandlerThread;
/**
* The TargetController possesses full control of the connect debug tool and target. It runs on a
* dedicated thread.
*
* See the TargetController class for more on this.
*/
TargetController targetController = TargetController(this->eventManager);
std::thread targetControllerThread;
/**
* The DebugServer exposes an interface to the connected target, to third-party software such as IDEs. It runs
* on a dedicated thread.
*
* See the DebugServer and GdbRspDebugServer class for more on this.
*/
std::unique_ptr<DebugServers::DebugServer> debugServer = nullptr;
std::thread debugServerThread;
/**
* Insight is, effectively, a small Qt application that serves a GUI to the user. It occupies the main thread,
* as well as a single worker thread, and possibly other threads created by Qt.
*
* Insight is optional - users can disable it via their project configuration.
*
* When the user closes the Insight GUI, control of the main thread is returned to Application::run(). How we
* deal with the GUI being closed at this point depends on user configuration.
*
* See the Insight class for more on this.
*
* Because Insight is optional, we only construct the Insight object when we need it. We can't use
* std::optional here because the Insight class extends QObject, which disables the copy constructor and
* the assignment operator. So we use an std::unique_ptr instead, which is perfectly fine for this use case,
* as we want to manage the lifetime of the object here.
*/
std::unique_ptr<Insight> insight = nullptr;
/**
* Configuration extracted from the user's project configuration file.
*
* See ApplicationConfig.hpp for more on this.
*/
ApplicationConfig applicationConfig;
EnvironmentConfig environmentConfig;
DebugServerConfig debugServerConfig;
InsightConfig insightConfig;
/**
* The project environment selected by the user.
*
* If an environment name is not provided as an argument when running Bloom, Bloom will fallback to the
* environment named "default".
*/
std::string selectedEnvironmentName = "default";
/**
* Some CLI arguments are interpreted as commands and thus require specific handler methods to be called.
* This mapping maps command strings to the appropriate handler methods. The mapped handler method is invoked
* when the command is provided as an argument from the CLI.
*
* See Application::run() for more on this.
*
* @return
*/
auto getCommandToHandlerMapping() {
return std::map<std::string, std::function<int()>> {
{
"--help",
std::bind(&Application::presentHelpText, this)
},
{
"-h",
std::bind(&Application::presentHelpText, this)
},
{
"--version",
std::bind(&Application::presentVersionText, this)
},
{
"-v",
std::bind(&Application::presentVersionText, this)
},
{
"init",
std::bind(&Application::initProject, this)
},
};
}
/**
* Extracts config from the user's JSON config file and generates an ApplicationConfig object.
*
* @see ApplicationConfig declaration for more on this.
* @return
*/
static ApplicationConfig extractConfig();
/**
* Presents application help text to user.
*
* @return
*/
int presentHelpText();
/**
* Presents the current Bloom version number to user.
*
* @return
*/
int presentVersionText();
/**
* Initialises a project in the user's working directory.
*
* @return
*/
int initProject();
/**
* Kicks off the application.
*
* Will start the TargetController and DebugServer. And register the main application's event handlers.
*/
void startup();
/**
* Will cleanly shutdown the application. This should never fail.
*/
void shutdown();
/**
* Prepares a dedicated thread for the TargetController and kicks it off.
*/
void startTargetController();
/**
* Invokes a clean shutdown of the TargetController. The TargetController should disconnect from the target
* and debug tool in a clean and safe manner, ensuring that both are left in a sensible state.
*
* This will join the TargetController thread.
*/
void stopTargetController();
/**
* Prepares a dedicated thread for the DebugServer and kicks it off with a call to DebugServer::run().
*/
void startDebugServer();
/**
* Sends a shutdown request to the DebugServer thread and waits on it to exit.
*/
void stopDebugServer();
public:
explicit Application() = default;
/**
* This mapping is used to map debug server names from project configuration files to polymorphic instances of
* the DebugServer class.
*
* See Application::startDebugServer() for more on this.
*
* @return
*/
auto getSupportedDebugServers() {
return std::map<std::string, std::function<std::unique_ptr<DebugServers::DebugServer>()>> {
{
"avr-gdb-rsp",
[this]() -> std::unique_ptr<DebugServers::DebugServer> {
return std::make_unique<DebugServers::Gdb::AvrGdbRsp>(this->eventManager);
}
},
};
};
/**
* Main entry-point for the Bloom program.
*
* @param arguments
* A vector of string arguments passed from the user via the cli.
*
* @return
*/
int run(const std::vector<std::string>& arguments);
/**
* If the TargetController unexpectedly shuts down, the rest of the application will follow.
*
* @param event
*/
void onTargetControllerThreadStateChanged(const Events::TargetControllerThreadStateChanged& event);
/**
* Same goes for the DebugServer - it should never shutdown unless a shutdown request was issued. If it does,
* something horrible has happened and so we shutdown the rest of the application in response.
*
* @param event
*/
void onDebugServerThreadStateChanged(const Events::DebugServerThreadStateChanged& event);
/**
* Triggers a shutdown of Bloom and all of its components.
*/
void onShutdownApplicationRequest(const Events::ShutdownApplication&);
/**
* Checks if the current effective user running Bloom has root privileges.
*
* @return
*/
static bool isRunningAsRoot();
};
}