diff --git a/src/PowerPlant.cpp b/src/PowerPlant.cpp index f4f271e8..15ecc76f 100644 --- a/src/PowerPlant.cpp +++ b/src/PowerPlant.cpp @@ -22,11 +22,49 @@ #include "PowerPlant.hpp" +#include +#include + +#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(args)); +} + PowerPlant::~PowerPlant() { // Make sure reactors are destroyed before anything else while (!reactors.empty()) { @@ -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&& 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, @@ -75,14 +123,19 @@ void PowerPlant::submit(std::unique_ptr&& task, const b } } -void PowerPlant::add_idle_task(const NUClear::id_t& id, - const util::ThreadPoolDescriptor& pool_descriptor, - std::function&& 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(std::make_unique( + 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() { @@ -101,4 +154,5 @@ void PowerPlant::shutdown() { bool PowerPlant::running() const { return is_running.load(); } + } // namespace NUClear diff --git a/src/PowerPlant.hpp b/src/PowerPlant.hpp index 27270e0f..5b5cfd52 100644 --- a/src/PowerPlant.hpp +++ b/src/PowerPlant.hpp @@ -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 -#include -#include +#include #include -#include -#include -#include #include -#include -#include -#include +#include #include // 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 + 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. @@ -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 + struct EmitCaller { + template + 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(args)...), true) { + Handler::emit(std::forward(args)...); + return true; + } + }; + public: // There can only be one powerplant, so this is it static PowerPlant* powerplant; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) @@ -104,7 +124,7 @@ 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; @@ -112,8 +132,7 @@ class PowerPlant { * 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 @@ -124,7 +143,17 @@ class PowerPlant { * @return A reference to the installed reactor */ template - 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::value, "You must install Reactors"); + + // The reactor constructor should handle subscribing to events + reactors.push_back(std::make_unique(std::make_unique(*this, util::demangle(typeid(T).name())), + std::forward(args)...)); + + return static_cast(*reactors.back()); + } /** * Adds an idle task to the task scheduler. @@ -189,7 +218,26 @@ class PowerPlant { * @param args The arguments we are logging */ template - static void log(Arguments&&... args); + void log(Arguments&&... args) { + log(level, std::forward(args)...); + } + template + void log(const LogLevel& level, Arguments&&... args) { + std::stringstream ss; + log(level, ss, std::forward(args)...); + } + template + void log(const LogLevel& level, std::stringstream& ss, First&& first, Arguments&&... args) { + ss << std::forward(first) << " "; + log(level, ss, std::forward(args)...); + } + template + void log(const LogLevel& level, std::stringstream& ss, Last&& last) { + ss << std::forward(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. @@ -203,9 +251,38 @@ class PowerPlant { * @param data The data we are emitting */ template - void emit(std::unique_ptr&& data); + void emit(std::unique_ptr&& data) { + emit(std::move(data)); + } template - void emit(std::unique_ptr& data); + void emit(std::unique_ptr& data) { + emit(std::move(data)); + } + template