Skip to content

Commit

Permalink
Split more things into cpp files
Browse files Browse the repository at this point in the history
  • Loading branch information
TrentHouliston committed Aug 13, 2024
1 parent 0389281 commit dada65b
Show file tree
Hide file tree
Showing 13 changed files with 448 additions and 430 deletions.
68 changes: 61 additions & 7 deletions src/PowerPlant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,49 @@

#include "PowerPlant.hpp"

#include <exception>
#include <tuple>

#include "Reactor.hpp"
#include "dsl/store/DataStore.hpp"
#include "dsl/word/Shutdown.hpp"
#include "dsl/word/Startup.hpp"
#include "dsl/word/emit/Direct.hpp"
#include "message/CommandLineArguments.hpp"
#include "message/LogMessage.hpp"
#include "threading/ReactionTask.hpp"

namespace NUClear {
namespace util {
struct GroupDescriptor;
struct ThreadPoolDescriptor;
} // namespace util

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
PowerPlant* PowerPlant::powerplant = nullptr;

// This is taking argc and argv as given by main so this should not take an array
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays)
PowerPlant::PowerPlant(Configuration config, int argc, const char* argv[]) : scheduler(config.thread_count) {

// Stop people from making more then one powerplant
if (powerplant != nullptr) {
throw std::runtime_error("There is already a powerplant in existence (There should be a single PowerPlant)");
}

// Store our static variable
powerplant = this;

// Emit our arguments if any.
message::CommandLineArguments args;
for (int i = 0; i < argc; ++i) {
args.emplace_back(argv[i]);
}

// Emit our command line arguments
emit(std::make_unique<message::CommandLineArguments>(args));
}

PowerPlant::~PowerPlant() {
// Make sure reactors are destroyed before anything else
while (!reactors.empty()) {
Expand All @@ -50,6 +88,16 @@ void PowerPlant::start() {
scheduler.start();
}

void PowerPlant::add_idle_task(const NUClear::id_t& id,
const util::ThreadPoolDescriptor& pool_descriptor,
std::function<void()>&& task) {
scheduler.add_idle_task(id, pool_descriptor, std::move(task));
}

void PowerPlant::remove_idle_task(const NUClear::id_t& id, const util::ThreadPoolDescriptor& pool_descriptor) {
scheduler.remove_idle_task(id, pool_descriptor);
}

void PowerPlant::submit(const NUClear::id_t& id,
const int& priority,
const util::GroupDescriptor& group,
Expand All @@ -75,14 +123,19 @@ void PowerPlant::submit(std::unique_ptr<threading::ReactionTask>&& task, const b
}
}

void PowerPlant::add_idle_task(const NUClear::id_t& id,
const util::ThreadPoolDescriptor& pool_descriptor,
std::function<void()>&& task) {
scheduler.add_idle_task(id, pool_descriptor, std::move(task));
}
void PowerPlant::log(const LogLevel& level, std::string message) {
// Get the current task
const auto* current_task = threading::ReactionTask::get_current_task();

void PowerPlant::remove_idle_task(const NUClear::id_t& id, const util::ThreadPoolDescriptor& pool_descriptor) {
scheduler.remove_idle_task(id, pool_descriptor);
// Direct emit the log message so that any direct loggers can use it
emit<dsl::word::emit::Direct>(std::make_unique<message::LogMessage>(
level,
current_task != nullptr ? current_task->parent.reactor.log_level : LogLevel::UNKNOWN,
std::move(message),
current_task != nullptr ? current_task->stats : nullptr));
}
void PowerPlant::log(const LogLevel& level, std::stringstream& message) {
log(level, message.str());
}

void PowerPlant::shutdown() {
Expand All @@ -101,4 +154,5 @@ void PowerPlant::shutdown() {
bool PowerPlant::running() const {
return is_running.load();
}

} // namespace NUClear
189 changes: 137 additions & 52 deletions src/PowerPlant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,41 @@
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef NUCLEAR_POWERPLANT_HPP
#define NUCLEAR_POWERPLANT_HPP
#ifndef NUCLEAR_POWER_PLANT_HPP
#define NUCLEAR_POWER_PLANT_HPP

#include <atomic>
#include <iostream>
#include <map>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <set>
#include <sstream>
#include <string>
#include <thread>
#include <typeindex>
#include <utility>
#include <vector>

// Utilities
#include "Configuration.hpp"
#include "Environment.hpp"
#include "LogLevel.hpp"
#include "id.hpp"
#include "message/LogMessage.hpp"
#include "threading/ReactionTask.hpp"
#include "threading/TaskScheduler.hpp"
#include "util/FunctionFusion.hpp"
#include "util/demangle.hpp"
#include "util/main_thread_id.hpp"
#include "util/unpack.hpp"

namespace NUClear {

// Forward declare reactor
// Forward declarations
class Reactor;
namespace util {
struct ThreadPoolDescriptor;
} // namespace util
namespace dsl {
namespace word {
namespace emit {
template <typename T>
struct Local;
} // namespace emit
} // namespace word
} // namespace dsl

/**
* The PowerPlant is the core of a NUClear system. It holds all Reactors in it and manages their communications.
Expand All @@ -64,6 +67,23 @@ class PowerPlant {
// Reactors and PowerPlants are very tightly linked
friend class Reactor;

/**
* This is our Function Fusion wrapper class that allows it to call emit functions
*
* @tparam Handler The emit handler that we are wrapping for
*/
template <typename Handler>
struct EmitCaller {
template <typename... Arguments>
static auto call(Arguments&&... args)
// THIS IS VERY IMPORTANT, the return type must be dependent on the function call
// otherwise it won't check it's valid in SFINAE (the comma operator does it again!)
-> decltype(Handler::emit(std::forward<Arguments>(args)...), true) {
Handler::emit(std::forward<Arguments>(args)...);
return true;
}
};

public:
// There can only be one powerplant, so this is it
static PowerPlant* powerplant; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
Expand Down Expand Up @@ -104,16 +124,15 @@ class PowerPlant {
/**
* Gets the current running state of the PowerPlant.
*
* @return True if the PowerPlant is running, false if it is shut down, or is in the process of shutting down.
* @return `true` if the PowerPlant is running, `false` if it is shut down, or is in the process of shutting down.
*/
bool running() const;

/**
* Installs a reactor of a particular type to the system.
*
* This function constructs a new Reactor of the template type.
* It passes through the specified LogLevel
* in the environment of that reactor so that it can be used to filter logs.
* It passes the specified LogLevel in the environment of that reactor so that it can be used to filter logs.
*
* @tparam T The type of the reactor to build and install
* @tparam Args The types of the extra arguments to pass to the reactor constructor
Expand All @@ -124,7 +143,17 @@ class PowerPlant {
* @return A reference to the installed reactor
*/
template <typename T, typename... Args>
T& install(Args&&... args);
T& install(Args&&... args) {

// Make sure that the class that we received is a reactor
static_assert(std::is_base_of<Reactor, T>::value, "You must install Reactors");

// The reactor constructor should handle subscribing to events
reactors.push_back(std::make_unique<T>(std::make_unique<Environment>(*this, util::demangle(typeid(T).name())),
std::forward<Args>(args)...));

return static_cast<T&>(*reactors.back());
}

/**
* Adds an idle task to the task scheduler.
Expand Down Expand Up @@ -189,7 +218,26 @@ class PowerPlant {
* @param args The arguments we are logging
*/
template <enum LogLevel level, typename... Arguments>
static void log(Arguments&&... args);
void log(Arguments&&... args) {
log(level, std::forward<Arguments>(args)...);
}
template <typename... Arguments>
void log(const LogLevel& level, Arguments&&... args) {
std::stringstream ss;
log(level, ss, std::forward<Arguments>(args)...);
}
template <typename First, typename... Arguments>
void log(const LogLevel& level, std::stringstream& ss, First&& first, Arguments&&... args) {
ss << std::forward<First>(first) << " ";
log(level, ss, std::forward<Arguments>(args)...);
}
template <typename Last>
void log(const LogLevel& level, std::stringstream& ss, Last&& last) {
ss << std::forward<Last>(last);
log(level, ss);
}
void log(const LogLevel& level, std::stringstream& message);
void log(const LogLevel& level, std::string message);

/**
* Emits data to the system and routes it to the other systems that use it.
Expand All @@ -203,9 +251,38 @@ class PowerPlant {
* @param data The data we are emitting
*/
template <typename T>
void emit(std::unique_ptr<T>&& data);
void emit(std::unique_ptr<T>&& data) {
emit<dsl::word::emit::Local>(std::move(data));
}
template <typename T>
void emit(std::unique_ptr<T>& data);
void emit(std::unique_ptr<T>& data) {
emit<dsl::word::emit::Local>(std::move(data));
}
template <template <typename> class First,
template <typename>
class... Remainder,
typename T,
typename... Arguments>
void emit(std::unique_ptr<T>& data, Arguments&&... args) {
emit<First, Remainder...>(std::move(data), std::forward<Arguments>(args)...);
}

template <template <typename> class First,
template <typename>
class... Remainder,
typename T,
typename... Arguments>
void emit(std::unique_ptr<T>&& data, Arguments&&... args) {

// Release our data from the pointer and wrap it in a shared_ptr
emit_shared<First, Remainder...>(std::shared_ptr<T>(std::move(data)), std::forward<Arguments>(args)...);
}

template <template <typename> class First, template <typename> class... Remainder, typename... Arguments>
void emit(Arguments&&... args) {

emit_shared<First, Remainder...>(std::forward<Arguments>(args)...);
}

/**
* Emits data to the system and routes it to the other systems that use it.
Expand Down Expand Up @@ -234,29 +311,41 @@ class PowerPlant {
class... Remainder,
typename T,
typename... Arguments>
void emit_shared(std::shared_ptr<T> data, Arguments&&... args);
void emit_shared(std::shared_ptr<T> data, Arguments&&... args) {

template <template <typename> class First, template <typename> class... Remainder, typename... Arguments>
void emit_shared(Arguments&&... args);
using Functions = std::tuple<First<T>, Remainder<T>...>;
using ArgumentPack = decltype(std::forward_as_tuple(*this, data, std::forward<Arguments>(args)...));
using CallerArgs = std::tuple<>;
using FusionFunction = util::FunctionFusion<Functions, ArgumentPack, EmitCaller, CallerArgs, 2>;

template <template <typename> class First,
template <typename>
class... Remainder,
typename T,
typename... Arguments>
void emit(std::unique_ptr<T>&& data, Arguments&&... args);
// Provide a check to make sure they are passing us the right stuff
static_assert(
FusionFunction::value,
"There was an error with the arguments for the emit function, Check that your scope and arguments "
"match what you are trying to do.");

template <template <typename> class First,
template <typename>
class... Remainder,
typename T,
typename... Arguments>
void emit(std::unique_ptr<T>& data, Arguments&&... args);
// Fuse our emit handlers and call the fused function
FusionFunction::call(*this, data, std::forward<Arguments>(args)...);
}

template <template <typename> class First, template <typename> class... Remainder, typename... Arguments>
void emit(Arguments&&... args);
void emit_shared(Arguments&&... args) {

using Functions = std::tuple<First<void>, Remainder<void>...>;
using ArgumentPack = decltype(std::forward_as_tuple(*this, std::forward<Arguments>(args)...));
using CallerArgs = std::tuple<>;
using FusionFunction = util::FunctionFusion<Functions, ArgumentPack, EmitCaller, CallerArgs, 1>;

// Provide a check to make sure they are passing us the right stuff
static_assert(
FusionFunction::value,
"There was an error with the arguments for the emit function, Check that your scope and arguments "
"match what you are trying to do.");

// Fuse our emit handlers and call the fused function
FusionFunction::call(*this, std::forward<Arguments>(args)...);
}

private:
/// A list of tasks that must be run when the powerplant starts up
std::vector<std::function<void()>> tasks;
/// Our TaskScheduler that handles distributing task to the pool threads
Expand All @@ -281,21 +370,17 @@ class PowerPlant {
*/
template <enum LogLevel level = NUClear::DEBUG, typename... Arguments>
void log(Arguments&&... args) {
PowerPlant::log<level>(std::forward<Arguments>(args)...);
if (PowerPlant::powerplant != nullptr) {
PowerPlant::powerplant->log<level>(std::forward<Arguments>(args)...);
}
}
template <typename... Arguments>
void log(const LogLevel& level, Arguments&&... args) {
if (PowerPlant::powerplant != nullptr) {
PowerPlant::powerplant->log(level, std::forward<Arguments>(args)...);
}
}

} // namespace NUClear

// Include our Reactor.h first as the tight coupling between powerplant and reactor requires a specific include order
#include "Reactor.hpp"
#include "dsl/word/emit/Direct.hpp"
#include "dsl/word/emit/Initialise.hpp"
#include "dsl/word/emit/Local.hpp"
#include "message/CommandLineArguments.hpp"
#include "message/NetworkConfiguration.hpp"
#include "message/NetworkEvent.hpp"

// Include all of our implementation files (which use the previously included reactor.h)
#include "PowerPlant.ipp"

#endif // NUCLEAR_POWERPLANT_HPP
#endif // NUCLEAR_POWER_PLANT_HPP
Loading

0 comments on commit dada65b

Please sign in to comment.