diff --git a/docs/extension.rst b/docs/extension.rst index a53dccc71..d951e97e2 100644 --- a/docs/extension.rst +++ b/docs/extension.rst @@ -43,7 +43,7 @@ Bind .. codeblock:: c++ template - static inline void bind(const std::shared_ptr& reaction, /*More arguments can be declared*/) + static void bind(const std::shared_ptr& reaction, /*More arguments can be declared*/) This function is called when the reaction is bound, it should be thought of as the constructor. It is used to setup anything that is required by the DSL word. @@ -68,7 +68,7 @@ Get .. codeblock:: c++ template - static inline T get(threading::Reaction&) + static T get(threading::Reaction& reaction) This is used to get the data for the callback. The returned value is passed to the callback. @@ -84,7 +84,7 @@ Precondition .. codeblock:: c++ template - static inline bool precondition(threading::Reaction& reaction) + static bool precondition(threading::Reaction& reaction) A precondition is used to test if the reaction should run. On a true return the reaction will run as normal. On a false return the reaction will be dropped. @@ -103,7 +103,7 @@ Reschedule .. codeblock:: c++ template - static inline std::unique_ptr reschedule(std::unique_ptr&& task) + static std::unique_ptr reschedule(std::unique_ptr&& task) The ownership of the reaction task is passed to the DSL word. The task returned will be run instead of the passed in reaction task. If the returned task is the one passed in the task will be run normally. @@ -167,7 +167,7 @@ Now we define the `reschedule` to interrupt any new tasks if we are currently ru multithreaded so a mutex is needed when accessing the static members. .. codeblock:: c++ template - static inline std::unique_ptr reschedule( + static std::unique_ptr reschedule( std::unique_ptr&& task) { // Lock our mutex diff --git a/docs/networking.rst b/docs/networking.rst index f80a561ae..05d0c8edf 100644 --- a/docs/networking.rst +++ b/docs/networking.rst @@ -43,8 +43,8 @@ checks explicitly for an explicit type. Be careful about multiple declarations. For this partial specialisation three static methods need to be defined. .. codeblock:: c++ - static inline std::vector serialise(const T& in) + static std::vector serialise(const T& in) - static inline T deserialise(const std::vector& in) + static T deserialise(const std::vector& in) - static inline uint64_t hash() + static uint64_t hash() diff --git a/src/PowerPlant.cpp b/src/PowerPlant.cpp index f4f271e8b..15ecc76f0 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 27270e0f6..5b5cfd526 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