Node Prototypes
Basic Example
defmodule Exred.Node.HelloWorld do
@moduledoc """
Sends "Hello World" or any other configured greeting as payload
when it receives a message.
**Incoming message format**
Anything / ignored
**Outgoing message format**
msg = %{
payload :: string
}
"""
@name "Greeter"
@category "output"
@info @moduledoc
@config %{
name: %{
info: "Visible node name",
value: @name,
type: "string",
attrs: %{max: 20}
},
greeting: %{
info: "Greeting to be sent",
value: "Hello World",
type: "string",
attrs: %{max: 40}
}
}
@ui_attributes %{
left_icon: "face"
}
use Exred.Library.NodePrototype
@impl true
def handle_msg(msg, state) do
out = Map.put(msg, :payload, state.config.greeting.value)
{out, state}
end
end
Module Attributes
@name :: string
Name of the node. This will be the visible name in the UI.
@category :: string
Name of the category the node is in. Categories are the panel headers in the node selector on the left side of the UI.
The name of the category also determines the color of the node given in exred/ui/app/styles/app.scss
(see .exred-category-function, etc. CSS classes)
@info :: string
Description of the node. This is displayed in the Info tab in the UI.
It is usually a longer multi-line string and it is interpreted as markdown text.
@config :: map
This is a map of configurable values for the node.
These are displayed in the Config tab in the UI and the values are accessible in the state
argument in the node.
There are different types of config entries represented by different widgets in the UI.
Each type has its unique attributes.
Node types match the UI components in exred/ui/app/templates/components/x-config-tab/
Keys in the map are:
- type: type of the config item
- info: short descritpion of the config item (will be displayed as a tooltip)
- value: default value for the config item
- attrs :: map : map of attributes based on the type
type: “string”
config_item: %{
type: "string",
info: "Short description",
value: "default value",
attrs: %{max: 50}
}
Attributes:
- max : maximum length of the string
type: “number”
config_item: %{
type: "number",
info: "Short description",
value: 22,
attrs: %{min: 10, max: 50}
}
Attributes:
- max : maximum length of the string
type: “select”
config_item: %{
type: "select",
info: "Short description",
value: "GET",
attrs: %{options: ["GET", "POST", "PATCH"]}
}
Attributes:
- options : list of options for the selector
type: “list-singleselect”
config_item: %{
info: "Short description",
type: "list-singleselect",
value: [],
attrs: %{items: ["Display 1", "Display 2"]}
},
Attributes:
- items : list of items to select from
type: “list-multiselect”
config_item: %{
type: "list-multiselect",
info: "Short description",
value: [],
attrs: %{items: ["channel1", "channel2", "channel3"]}
}
Attributes:
- items : list of items to select from
type: “codeblock”
config_item: %{
type: "codeblock",
info: "Short description",
value: "default value"
}
No attributes
Example (@config map):
@config %{
name: %{
info: "Visible node name",
value: "GPIO In",
type: "string",
attrs: %{max: 20}
},
pin_number: %{
info: "GPIO pin number that the node will read",
value: 0,
type: "number",
attrs: %{min: 0}
},
mode: %{
info: "read_on_message or monitor",
type: "list-singleselect",
value: nil,
attrs: %{items: ["read_on_message", "monitor"]}
},
monitored_transition: %{
info: "send message in rising and/or falling edge",
type: "list-multiselect",
value: [],
attrs: %{items: ["rising", "falling"]}
}
}
@ui_attributes
Additional UI attributes for the node.
@ui_attributes %{
fire_button: false,
left_icon: nil,
right_icon: "send",
config_order: [:name,:pin_number,:mode]
}
- fire_button :: boolean : clickable button on the node in the UI. Sends a fire message to the node’s gen_server
- left_icon :: string, right_icon :: string : material design icon name (see Material Design Icons)
- config_order :: list : list of the config items in the order we want to display them in the UI
Module Callbacks
node_init(state)
node_init(state :: map) :: map | {map, integer}
This is called as the last step of the node’s init function. Needs to return a new state or a {state, timeout} tuple. (see GenServer documentation)
handle_msg(msg, state)
handle_msg(msg :: map, state :: map) :: {map, map}
Handles incoming messages sent to the node from another node.
Returns the outgoing message and a state.
If the outgoing message is nil
then no message will be sent from the node.
fire(state)
fire(state :: map) :: map`
Called when the fire button is pressed on the node in the UI. Needs to return the updated state.
If the fire action needs to send an outgoing message from the node that can be done with the standard send/2
function.
state.out_nodes
is a list of node PIDs that are connected to this node with outgoing edges.
Example:
def fire(state) do
Enum.each state.out_nodes, & send(&1, %{payload: "hello"})
state
end