Plugins

Covers Engine Plugin SDK 2.0.0 · Interface Plugin SDK 0.2.0

SDK_API

Host API available to engine plugins and their module instances.

Registration functions qualify local identifiers as <plugin-id>.<local-id>. Channel authority calls require an active task or plugin loop controller.

Declaration

class SDK_API {
    public:
        SDK_API() = default;
        virtual ~SDK_API() = default;
        virtual double queryChannelField(const std::string& channel,
            ChannelField field,
            std::optional<ChannelValue> default_value) = 0;
        virtual void insertChannelField(const std::string& channel,
            ChannelField field,
            ChannelValue value,
            ChannelStorage storage = ChannelStorage::Dynamic) = 0;
        virtual void upsertChannelField(const std::string& channel,
            ChannelField field,
            ChannelValue value,
            ChannelStorage storage = ChannelStorage::Dynamic) = 0;
        virtual FixedChannelHandle createFixedChannel(const std::string& channel,
            double initial_value = 0.0) = 0;
        virtual double queryChannel(const FixedChannelHandle& handle,
            double default_value = 0.0) = 0;
        virtual bool upsertChannel(const FixedChannelHandle& handle, double value,
            std::optional<uint64_t> timestamp = std::nullopt) = 0;
        virtual bool removeChannel(const std::string& channel) = 0;
        virtual std::string registerModuleType(ModuleTypeDefinition definition) = 0;
        virtual std::string registerPeer(PeerDefinition definition) = 0;
        virtual std::string registerPeerTransport(PeerTransportDefinition definition) {
            PeerDefinition peer;
            peer.id=std::move(definition.id);peer.name=std::move(definition.name);
            peer.version=definition.version;peer.default_config=std::move(definition.default_config);
            peer.protocol_id=std::move(definition.protocol_id);peer.protocol_version=definition.protocol_version;
            peer.engine_protocol=definition.engine_protocol;peer.create_transport=std::move(definition.create);
            peer.configure=std::move(definition.configure);
            return registerPeer(std::move(peer));
        }
        virtual std::string registerTaskType(std::string local_id, std::string name, TaskTypeDefinition definition) = 0;
        virtual std::string registerOperation(OperationDefinition definition) = 0;
        // Convenience for operations without argument metadata. Delegates to the
        // registered definition and does not add a virtual slot to the SDK ABI.
        std::string registerOperation(std::string local_id, std::string name, OperationHandler handler) {
            OperationDefinition definition;
            definition.id = std::move(local_id);
            definition.name = std::move(name);
            definition.handler = std::move(handler);
            return registerOperation(std::move(definition));
        }
        virtual std::string registerTelemetry(TelemetryDefinition definition) = 0;
        virtual nlohmann::json callTempest(const std::string& node, const std::string& operation,
                                           const nlohmann::json& payload) {
            throw std::runtime_error("TEMPEST peer calls are unavailable in this SDK host.");
        }
        virtual void publishTelemetry(const std::string& topic, const nlohmann::json& payload) {
            throw std::runtime_error("TEMPEST telemetry is unavailable in this SDK host.");
        }
        virtual std::string registerDCodeFunction(
            std::string local_id,
            std::string name,
            DCodeFunctionHandler handler,
            std::string doc = {},
            std::vector<DCodeFunctionArgument> input_arguments = {},
            std::vector<DCodeFunctionArgument> output_arguments = {},
            DCodePreparedFunctionHandler prepared_handler = {}
        ) = 0;
        virtual std::string registerLoop(std::string local_id, std::string name, PluginLoopDefinition definition) = 0;
        virtual int message(const std::string& title, const std::string& description,
            nlohmann::json channels = nlohmann::json::array(), const std::string& resolution = "",
            int auto_acknowledge = 0,
            double firing_timeout_seconds = 5, const std::string& system = "SOFTWARE",
            const std::string& subsystem = "DCODE") {
            return reportEvent("message", title, description, channels, resolution, auto_acknowledge,
                firing_timeout_seconds, system, subsystem);
        }
        virtual int warning(const std::string& title, const std::string& description,
            nlohmann::json channels = nlohmann::json::array(), const std::string& resolution = "",
            int auto_acknowledge = 0,
            double firing_timeout_seconds = 5, const std::string& system = "SOFTWARE",
            const std::string& subsystem = "DCODE") {
            return reportEvent("warning", title, description, channels, resolution, auto_acknowledge,
                firing_timeout_seconds, system, subsystem);
        }
        virtual int error(const std::string& title, const std::string& description,
            nlohmann::json channels = nlohmann::json::array(), const std::string& resolution = "",
            int auto_acknowledge = 0,
            double firing_timeout_seconds = 5, const std::string& system = "SOFTWARE",
            const std::string& subsystem = "DCODE") {
            return reportEvent("error", title, description, channels, resolution, auto_acknowledge,
                firing_timeout_seconds, system, subsystem);
        }
        virtual int abort(const std::string& title, const std::string& description,
            nlohmann::json channels = nlohmann::json::array(), const std::string& resolution = "",
            int auto_acknowledge = 0,
            double firing_timeout_seconds = 5, const std::string& system = "SOFTWARE",
            const std::string& subsystem = "DCODE") {
            return reportEvent("abort", title, description, channels, resolution, auto_acknowledge,
                firing_timeout_seconds, system, subsystem);
        }
        virtual int hold(const std::string& title, const std::string& description,
            nlohmann::json channels = nlohmann::json::array(), const std::string& resolution = "",
            int auto_acknowledge = 0, const std::string& system = "SOFTWARE",
            const std::string& subsystem = "DCODE") {
            throw std::runtime_error("ARGUS hold is not supported by this host.");
        }
        virtual double promptNumerical(const std::string& title, const std::string& description,
            nlohmann::json channels = nlohmann::json::array(), const std::string& resolution = "",
            int auto_acknowledge = 0, const std::string& system = "SOFTWARE",
            const std::string& subsystem = "DCODE") {
            throw std::runtime_error("ARGUS promptNumerical is not supported by this host.");
        }
        virtual int promptYesNo(const std::string& title, const std::string& description,
            nlohmann::json channels = nlohmann::json::array(), const std::string& resolution = "",
            int auto_acknowledge = 0, const std::string& system = "SOFTWARE",
            const std::string& subsystem = "DCODE") {
            throw std::runtime_error("ARGUS promptYesNo is not supported by this host.");
        }
        virtual std::string promptString(const std::string& title, const std::string& description,
            nlohmann::json channels = nlohmann::json::array(), const std::string& resolution = "",
            int auto_acknowledge = 0, const std::string& system = "SOFTWARE",
            const std::string& subsystem = "DCODE") {
            throw std::runtime_error("ARGUS promptString is not supported by this host.");
        }
        virtual std::string promptOptions(const std::string& title, const std::string& description,
            const std::vector<std::string>& options,
            nlohmann::json channels = nlohmann::json::array(), const std::string& resolution = "",
            int auto_acknowledge = 0, const std::string& system = "SOFTWARE",
            const std::string& subsystem = "DCODE") {
            throw std::runtime_error("ARGUS promptOptions is not supported by this host.");
        }
        virtual int releaseHold(const std::string& title) {
            throw std::runtime_error("ARGUS releaseHold is not supported by this host.");
        }

    protected:
        static nlohmann::json eventGraphs(const nlohmann::json& channels) {
            if (channels.is_null() || (channels.is_array() && channels.empty())) return nlohmann::json::array();
            if (!channels.is_array()) throw std::invalid_argument("ARGUS channels must be an array.");
            const bool grouped = channels.front().is_array();
            for (const auto& group : channels) {
                if (grouped) {
                    if (!group.is_array()) throw std::invalid_argument("ARGUS graph groups must be arrays of strings.");
                    for (const auto& series : group)
                        if (!series.is_string()) throw std::invalid_argument("ARGUS graph series must be strings.");
                } else if (!group.is_string()) {
                    throw std::invalid_argument("ARGUS graph series must be strings.");
                }
            }
            return grouped ? channels : nlohmann::json::array({channels});
        }

    private:
        int reportEvent(const std::string& type, const std::string& title, const std::string& description,
            const nlohmann::json& channels, const std::string& resolution, int auto_acknowledge,
            double firing_timeout_seconds, const std::string& system, const std::string& subsystem) {
            const auto result = recordEvent({{"type", type}, {"title", title}, {"description", description},
                {"graphs", eventGraphs(channels)}, {"resolution", resolution},
                {"auto_acknowledge_seconds", auto_acknowledge}, {"firing_timeout_seconds", firing_timeout_seconds},
                {"system", system}, {"subsystem", subsystem},
                {"actions", type == "abort" ? std::vector<std::string>{"acknowledge", "delete"}
                    : std::vector<std::string>{"acknowledge", "silence", "delete"}}});
            return result.is_object() && !result.empty() ? 1 : 0;
        }

    public:
        virtual std::shared_ptr<Modules::BaseModule> getModuleInstance(const std::string& instance_name) = 0;
        virtual std::vector<ModuleInstanceSummary> getModuleInstances(const std::string& plugin_id = "") = 0;
        virtual void upsertChannelValueBulk(const std::string& channel,
            const std::vector<std::pair<double, uint64_t>>& data) = 0;
        virtual nlohmann::json callDCodeFunction(const std::string& function_name, const nlohmann::json& payload) = 0;
        virtual void commandChannel(const std::string& channel, ChannelValue value) = 0;
        virtual void setChannel(const std::string& channel, std::optional<ChannelValue> value = std::nullopt) = 0;
        virtual void freeChannel(const std::string& channel) = 0;
        virtual nlohmann::json recordEvent(nlohmann::json event) { return nlohmann::json::object(); }
        virtual bool stop_event_firing(const std::string& title, const std::string& system = "SOFTWARE",
            const std::string& subsystem = "DCODE") { return false; }
        virtual bool updateEventStatus(const std::string& event_id, const std::string& status) {
            (void)event_id;
            (void)status;
            return false;
        }
        virtual nlohmann::json requestInterfaceUi(
            const std::string& ui_id,
            nlohmann::json payload,
            nlohmann::json options = nlohmann::json::object()) {
            (void)ui_id;
            (void)payload;
            (void)options;
            return nlohmann::json::object();
        }
        virtual nlohmann::json getInterfaceUiRequest(const std::string& request_id) {
            (void)request_id;
            return nlohmann::json::object();
        }
        virtual nlohmann::json announceDiscoveredDevice(nlohmann::json candidate) {
            (void)candidate;
            return nlohmann::json::object();
        }
        virtual bool isNotificationMuted(const std::string& notification_id) {
            (void)notification_id;
            return false;
        }
        virtual bool writeLog(const std::string& stream, const std::string& text,
            const std::string& channel = "stdout", const std::string& level = "info") {
            (void)stream;
            (void)text;
            (void)channel;
            (void)level;
            return false;
        }
        bool log(const std::string& stream, const std::string& text) {
            return writeLog(stream, text + "\n", "stdout", "info");
        }
        bool log_warning(const std::string& stream, const std::string& text) {
            return writeLog(stream, text + "\n", "stdout", "warning");
        }
        bool log_error(const std::string& stream, const std::string& text) {
            return writeLog(stream, text + "\n", "stderr", "error");
        }
        virtual void registerChannelReferenceSource(std::string local_id,
            std::function<void(ChannelReferenceSink&)> collect) {
            (void)local_id;
            (void)collect;
            throw std::runtime_error("Channel reference sources are unavailable in this SDK host.");
        }
        virtual nlohmann::json callPinnedTempest(const std::string& node, const std::string& preset,
                                                  const nlohmann::json& overrides = nlohmann::json::object()) {
            throw std::runtime_error("Pinned TEMPEST peer calls are unavailable in this SDK host.");
        }
        std::string readWorkspaceAsset(const std::string& node, const std::string& asset_namespace, const std::string& filename) {
            return callTempest(node, "dartwic/get-file", {{"rootDir", "workspace_assets_directory"},
                {"filePath", workspaceAssetPath(asset_namespace, filename).substr(7)}}).at("content").get<std::string>();
        }
        std::string saveWorkspaceAsset(const std::string& node, const std::string& asset_namespace,
                                      const std::string& filename, const std::string& content) {
            const auto path = workspaceAssetPath(asset_namespace, filename);
            callTempest(node, "dartwic/save-file", {{"rootDir", "workspace_assets_directory"}, {"path", path.substr(7)}, {"content", content}});
            return path;
        }
        std::string readPluginWorkspaceAsset(const std::string& node, const std::string& plugin_id, const std::string& relative) {
            return callTempest(node, "dartwic/get-file", {{"rootDir", "workspace_assets_directory"}, {"filePath", pluginWorkspaceAssetPath(plugin_id, relative).substr(7)}}).at("content").get<std::string>();
        }
        std::string savePluginWorkspaceAsset(const std::string& node, const std::string& plugin_id, const std::string& relative, const std::string& content) {
            const auto path = pluginWorkspaceAssetPath(plugin_id, relative);
            callTempest(node, "dartwic/save-file", {{"rootDir", "workspace_assets_directory"}, {"path", path.substr(7)}, {"content", content}});
            return path;
        }
        std::filesystem::path pluginAssetPath(const std::string& plugin_id, const std::string& relative) {
            pluginWorkspaceAssetPath(plugin_id, relative);
            return installationPath("plugins/" + plugin_id + "/assets/" + relative);
        }
        virtual std::string storagePath(const std::string& scope, const std::string& relative,
                                        const std::string& project = "") {
            throw std::runtime_error("Storage path helpers are unavailable in this host.");
        }
        std::filesystem::path installationPath(const std::string& path = "") { return storagePath("installation", path); }
        std::filesystem::path instancePath(const std::string& path = "") { return storagePath("instance", path); }
        std::filesystem::path workspacePath(const std::string& path = "") { return storagePath("workspace", path); }
        std::filesystem::path projectPath(const std::string& path = "", const std::string& project = "") { return storagePath("project", path, project); }
        std::filesystem::path projectSettingsPath(const std::string& path = "", const std::string& project = "") { return storagePath("settings", path, project); }
        std::filesystem::path projectRuntimePath(const std::string& path = "", const std::string& project = "") { return storagePath("runtime", path, project); }
        std::filesystem::path projectCachePath(const std::string& path = "", const std::string& project = "") { return storagePath("cache", path, project); }
        virtual nlohmann::json readEffectiveSettings(const nlohmann::json& defaults = nlohmann::json::object(), const std::string& project = "") {
            throw std::runtime_error("Settings helpers are unavailable in this host.");
        }
        virtual nlohmann::json writeSettingsOverride(const std::string& scope, const nlohmann::json& patch,
            const nlohmann::json& reset = nlohmann::json::array(), const std::string& project = "", const std::string& revision = "") {
            throw std::runtime_error("Settings helpers are unavailable in this host.");
        }
        nlohmann::json resetSettingsOverride(const std::string& scope, const nlohmann::json& pointers,
            const std::string& project = "", const std::string& revision = "") {
            return writeSettingsOverride(scope, nlohmann::json::object(), pointers, project, revision);
        }
        virtual nlohmann::json readStorageJson(const std::string& scope, const std::string& path,
            const nlohmann::json& missing = nullptr, const std::string& project = "") {
            throw std::runtime_error("JSON storage helpers are unavailable in this host.");
        }
        virtual void writeStorageJson(const std::string& scope, const std::string& path,
            const nlohmann::json& value, const std::string& project = "") {
            throw std::runtime_error("JSON storage helpers are unavailable in this host.");
        }
        void commandChannel(const std::string& channel) { applyAuthorityByName(0, channel, std::nullopt); }
        void overrideChannel(const std::string& channel) { applyAuthorityByName(2, channel, std::nullopt); }
        void overrideChannel(const std::string& channel, ChannelValue value) { applyAuthorityByName(2, channel, std::move(value)); }
        void freeChannel(const std::string& channel, ChannelValue value) { applyAuthorityByName(3, channel, std::move(value)); }
        void commandChannel(const FixedChannelHandle& channel, std::optional<ChannelValue> value = std::nullopt) {
            applyAuthorityByHandle(0, channel, std::move(value));
        }
        void setChannel(const FixedChannelHandle& channel, std::optional<ChannelValue> value = std::nullopt) {
            applyAuthorityByHandle(1, channel, std::move(value));
        }
        void overrideChannel(const FixedChannelHandle& channel, std::optional<ChannelValue> value = std::nullopt) {
            applyAuthorityByHandle(2, channel, std::move(value));
        }
        void freeChannel(const FixedChannelHandle& channel, std::optional<ChannelValue> value = std::nullopt) {
            applyAuthorityByHandle(3, channel, std::move(value));
        }
        virtual void applyAuthorityByName(uint32_t operation, const std::string& channel,
            std::optional<ChannelValue> value) = 0;
        virtual void applyAuthorityByHandle(uint32_t operation, const FixedChannelHandle& channel,
            std::optional<ChannelValue> value) = 0;

    };