Plugins
A Nodos plugin defines nodes and their behaviors, and pin data types. Nodos loads plugins dynamically and makes its nodes and data types available to be used in the node graph to perform tasks.
Nodos Package Manager CLI tool (nodos
) has a command create plugin
that can generate a simple plugin for Nodos. You can use nodos create --help
for detailed information about the command.
Create a plugin
Let's create our first Nodos plugin with command below.
It will output:
Creating a new Nodos module project of type Plugin
Plugin project created at "./Module/mycorp.myplugin"
Found 1 modules in C:/Nodos/Module/mycorp.myplugin
This will create a folder named mycorp.myplugin
under Module
folder of Nodos Workspace (where nodos
tool resides) with folder structure as below.
./Module/mycorp.myplugin/
├── CMakeLists.txt
├── Source
│ └── PluginMain.cpp
└── mycorp.myplugin.noscfg
This folder contains a CMake project file, a C++ source file with minimal code and a Nodos plugin manifest file .noscfg
.
Building
Now generate CMake project using our CMake helpers, which can be found in Toolchain/CMake
folder of Nodos Workspace and build it using commands run from workspace root:
This will result in a DLL file under Module/mycorp.myplugin/Binaries
folder, and Nodos will be able to load this plugin.
Loading a plugin
Open the editor and click Fetch button of Modules pane. This will scan the plugin and show it under uncategorized table on Plugins section. Click on the plugin and you'll see Load button at the bottom.
Info
A plugin should implement nosImportDependencies
, nosExportPlugin
& nosGetPluginAPIVersion
functions in Nodos C API for plugins (defined under Engine/<version>/SDK/include/PluginAPI.h
).
Defining a node
For simplicity, tutorial will be based on our C++ helpers. Create a struct PluginExtension
that is derived from nos::PluginFunctions publicly and override ExportNodeFunctions()
function.
When a plugin is loaded, ExportNodeFunctions()
is called twice by the engine. One for querying node count and another one for getting the node list. That means, your function should start something like,
nosNodeFunctions*
list.
For each node you want to register, you can implement a class derived from nos::NodeContext
. You should override the base class' functions you're going to use (OnPinValueChanged()
for example).
Example C++ code
Registering a node that gets float from input pin and prints it on Log pane#include <Nodos/PluginAPI.h>
#include <Nodos/PluginHelpers.hpp>
#include <Nodos/Helpers.hpp>
NOS_INIT() // Defines nosGetPluginAPIVersion
NOS_BEGIN_IMPORT_DEPS() // Defines nosImportDependencies and makes nosEngineServices available as 'nosEngine'.
// If you have dependencies, you can define them here like
// NOS_IMPORT_DEP("mycorp.somedep", "1.0.0"...)
NOS_END_IMPORT_DEPS()
struct PrintLogPaneNodeContext : nos::NodeContext
{
PrintLogPaneNodeContext(const nosFbNode* node) : nos::NodeContext(node)
{
}
void OnPinValueChanged(nos::Name pinName, nosUUID pinId, nosBuffer value) override
{
if (pinName == NOS_NAME_STATIC("Message"))
{
auto* floatInfo = nos::InterpretPinValue<float>(value);
nosEngine.LogI(std::to_string(*floatInfo).c_str());
}
}
};
nosResult RegisterPrintLogPaneNode(nosNodeFunctions* outFunctions)
{
NOS_BIND_NODE_CLASS(NOS_NAME("mycorp.myplugin.PrintLog"),
PrintLogPaneNodeContext,
outFunctions)
return NOS_RESULT_SUCCESS;
}
struct PluginExtension : public nos::PluginFunctions
{
virtual nosResult ExportNodeFunctions(size_t& outSize, nosNodeFunctions** outFunctions) override
{
outSize = 1;
if (!outFunctions)
return NOS_RESULT_SUCCESS;
NOS_RETURN_ON_FAILURE(RegisterPrintLogPaneNode(outFunctions[0]));
return NOS_RESULT_SUCCESS;
}
};
NOS_EXPORT_PLUGIN_FUNCTIONS(PluginExtension);
If you build the plugin and try to load it from the engine, you'll get "Plugin is trying to register a node that doesn't exist in its node definitions" error. This is because Nodos reads node configuration (.noscfg) and node definition (.nosdef) files to create node properties.
Configuration file describes the whole plugin such as; compiled binary path, node definition file paths, plugin's dependencies to subsystems and other plugins, custom data types (flatbuffers based).
To add a node, create a .nosdef file. It should have JSON schema. You can define multiple nodes in one .nosdef file.
Each node should have a class name, display name, content type (either a job that has no sub-nodes or a graph that has sub-graphs), user-friendly description text, pins & functions.
We're gonna use only a single pin today, so we don't need to define functions. After describing your nodes in the related .nosdef files, you should associate them with the plugin in .noscfg's associated_nodes
list.
Example .noscfg & .nosdef files
Registering a node that gets float from input pin and prints it on Log pane.nosdef
.noscfg
{
"info": {
"id": {
"name": "mycorp.myplugin",
"version": "0.1.0"
},
"display_name": "mycorp.myplugin",
"description": "",
"dependencies": []
},
"binary_path": "./Binaries/mycorp.myplugin",
"node_definitions": [
"PrintLog.nosdef"
],
"defaults": [],
"custom_types": [],
"associated_nodes": [
{
"category": "Sample",
"class_name": "PrintLog",
"display_name": "Print Log"
}
]
}
Now you should be able to see your first node in the node graph. In examples above, we created a node that prints to Log pane only if the pin's value changes. So create an Add node and sets its input values. After you connect it to the Message pin, OnPinValueChanged()
will be called and it will print the value. Everytime you change the pin's value by changing output value of the connection, it'll be printed.
Using nodos CLI to add a pin
You can use nodos pin
command to add a pin to a node in the workspace. If not all parameters are provided, it will enter interactive mode.
--show-as
parameter, it will ask for it. This is used to define the pin's current kind in the node. It can be an input pin, output pin or a property.
After this, it will ask for the can-show-as
parameter. This is used to define which show-as options are available for the pin.
? Select pin can-show-as:
[x] INPUT_PIN
[ ] OUTPUT_PIN
> [x] PROPERTY
[↑↓ to move, space to select one, → to all, ← to none, type to filter]
INPUT_PIN
and PROPERTY
as the options. You can select multiple options by pressing space.
This creates the possibility for this pin to be changed to a property in the editor by the end user.
After this, it will ask for the type-name
parameter. This parameter is used to define the data type of the pin.
You can use nodos pin --help
for detailed information about the command.