#pragma once /** * @file module.h */ #include "nlohmann/json.hpp" #include #include #include #include #include "core/style.h" #include "imgui/imgui_flags.h" #include "common/dsp/buffer.h" // TODOREWORK ditch #define UITO_C_STR(input) "%s", std::to_string(input).c_str() // TODOREWORK? #define REGISTER_MODULE(module) modules_registry.push_back(satdump::pipeline::ModuleEntry{module::getID(), module::getParams(), module::getInstance}) #define REGISTER_MODULE_EXTERNAL(registry, module) registry.push_back(satdump::pipeline::ModuleEntry{module::getID(), module::getParams(), module::getInstance}) namespace satdump { namespace pipeline { enum ModuleDataType { DATA_FILE, // Just generic data from a file DATA_STREAM, // Generic data, for which a circular buffer will be used DATA_DSP_STREAM, // DSP Data using the specialized buffer and complex floats DATA_NDSP_STREAM, // TODOREWORK }; /** * @brief Core SatDump Pipeline module class * * A pipeline is composed of several modules each running * one after the other. Each module needs to take input data, * process it, and pass it on to the next. * * Modules can always process from a file, sometimes take an input * from a FIFO or DSP Stream, and sometimes also output in one of * those formats as well to allow them to run in realtime. * * Modules should be used in the following way : * - ProcessingModule() : Call the constructor * - setInputType() / setOutputType() : Set input/output types * - init() : Prepares some things so process() can run * - process() : Run the actual processing * - stop() : Optional, if running from a stream, this must be * called to make process() exit TODOREWORK maybe use a FIFO marker to stop it? * - getOutput() : If the output type was a file, get its path * to pass it on to the next module in the pipeline * * getOutput may return nothing for modules meant as final pipeline * steps. */ class ProcessingModule { protected: // TODOREWORK document nlohmann::json d_parameters; // TODOREWORK enforce const again? const std::string d_input_file; const std::string d_output_file_hint; std::string d_output_file; ModuleDataType input_data_type, output_data_type; bool d_is_streaming_input; public: /** * @brief Constructor. * * @param input_file the input file to process (doesn't matter if later setting up for streaming input) TODOREWORK? * @param output_file_hint output file path hint (most modules just add an extension to it) TODOREWORK? * @param parameters module settings TODOREWORK? */ ProcessingModule(std::string input_file, std::string output_file_hint, nlohmann::json parameters); public: /** * @brief Get supported input types for this module * * @return list of supported inputs */ virtual std::vector getInputTypes() { return {DATA_FILE}; } /** * @brief Get supported output types for this module * * @return list of supported outputs */ virtual std::vector getOutputTypes() { return {DATA_FILE}; } /** * @brief Set input type to use * * @param type the type */ void setInputType(ModuleDataType type); /** * @brief Set output type to use * * @param type the type */ void setOutputType(ModuleDataType type); /** * @brief Get input type in use * * @return the type */ ModuleDataType getInputType(); /** * @brief Get output type in use * * @return the type */ ModuleDataType getOutputType(); /** * @brief If available, provides the name of the * produced output file * * @return path of the generated file */ std::string getOutput(); public: /** * @brief Initialize the module, making it ready to call * process(). * This must be called after setting input/output types, * and before calling process() */ virtual void init(); /** * @brief If running from a streaming input, this must be called * to make process() quit. * * TODOREWORK maybe use a FIFO marker to stop it? */ virtual void stop(); /** * @brief Implements actual processing. * Usually, this will include an internal loop reading from a * file/fifo, doing some stuff, and writing it out again. * * Once this exits, getOutput should be usable. */ virtual void process() = 0; /** * @brief Draw the module's GUI, show live stats of what it's * currently doing. * * @param window set to true to render the module in its own ImGui Window */ virtual void drawUI(bool window) = 0; public: // TODOREWORK document std::shared_ptr> input_fifo; std::shared_ptr> output_fifo; std::shared_ptr> input_stream; std::shared_ptr> output_stream; std::atomic input_active; public: /** * @brief Get the module's stats. * * Stats must be a list of JSON values representing various stats you * wish to make available. For example, this can be "synced" : true * or similar. * * @return JSON stats */ virtual nlohmann::json getModuleStats() { return {}; } public: // static std::string getID(); virtual std::string getIDM() = 0; // static nlohmann::json getParams() = 0; // static std::shared_ptr getInstance(std::string input_file, std::string output_file_hint, nlohmann::json parameters); }; /** * @brief Module registry entry * * @param id module string ID * @param params available module parameters and their defaults * @param inst function to create an instance of this module */ struct ModuleEntry { std::string id; nlohmann::json params; std::function(std::string, std::string, nlohmann::json)> inst; }; /** * @brief Actual module registry */ SATDUMP_DLL extern std::vector modules_registry; // Event where modules are registered, so plugins can load theirs TODOREWORK struct RegisterModulesEvent { std::vector &modules_registry; }; /** * @brief Setup module registry. This must be called once at initialization. */ void registerModules(); /** * @brief Get an instance of a module by ID * * @return new module instance */ std::shared_ptr getModuleInstance(std::string id, std::string input_file, std::string output_file_hint, nlohmann::json parameters); /** * @brief Check if a module exists * * @return true if it exists */ bool moduleExists(std::string id); } // namespace pipeline } // namespace satdump