Graphs
NoFlo graph file format
JSON
In addition to using NoFlo in embedded mode where you create the FBP graph programmatically (see example), you can also initialize and run graphs defined using a JSON file.
The NoFlo JSON files declare the processes used in the FBP graph, and the connections between them. They look like the following:
{
"properties": {
"name": "Count lines in a file"
},
"processes": {
"Read File": {
"component": "ReadFile",
"metadata": {
...
}
},
"Split by Lines": {
"component": "SplitStr"
},
...
},
"connections": [
{
"data": "package.json",
"tgt": {
"process": "Read File",
"port": "source"
}
},
{
"src": {
"process": "Read File",
"port": "out"
},
"tgt": {
"process": "Split by Lines",
"port": "in"
}
},
...
]
}
To run a graph file, you can either use the load command of the NoFlo shell, or do it programmatically:
noflo = require "noflo"
noflo.loadFile "example.json", (network) ->
console.log "Graph loaded"
console.log network.graph.toDOT()
var noflo = require("noflo");
noflo.loadFile("example.json", function(network) {
console.log("Graph loaded");
console.log(network.graph.toDOT());
});
FBP
In addition to the JSON format described above, FBP has its own Domain-Specific Language (DSL) for easy graph definition. The syntax is the following:
'somedata' -> PORT Process(Component)
sends initial data somedata to port PORT of process Process that runs component ComponentA(Component1) X -> Y B(Component2)
sets up a connection between port X of process A that runs component Component1 and port Y of process B that runs component Component2
You can connect multiple components and ports together on one line, and separate connection definitions with a newline or a comma (,
).
You can find more information in the README of the stand-alone FBP parser.
Example:
'somefile.txt' -> SOURCE Read(ReadFile) OUT -> IN Split(SplitStr)
Split OUT -> IN Count(Counter) COUNT -> IN Display(Output)
Read ERROR -> IN Display
Loading and running graphs
NoFlo supports the FBP language fully. You can either load a graph with a string of FBP-language commands with:
fbpData = "<some FBP language connections>"
noflo = require "noflo"
noflo.graph.loadFbp fbpData, (graph) ->
console.log "Graph loaded"
console.log graph.toDOT()
var fbpData = "<some FBP language connections>";
var noflo = require("noflo");
noflo.graph.loadFbp(fbpData, function(graph) {
console.log("Graph loaded");
console.log(graph.toDOT());
});
The .fbp
file suffix is used for files containing FBP language. This means you can load them also the same way as you load JSON files, using the noflo.loadFile
method, or the NoFlo shell. Example:
$ noflo examples/linecount/count.fbp
FBP to JSON
flowbased can be used to convert your FBP to JSON.
Inline FBP in web applications
In addition to separate files, FBP graph definitions can also be defined inline in HTML. The typical way to do this is to utilize a script
tag. For example, the following would change the contents of an element identified with the ID header
to say Hello World:
<script type="application/fbp" id="main">
'#header' -> SELECTOR GetHeader(dom/GetElement)
'Hello World' -> HTML WriteContent(dom/WriteHTML)
GetHeader ELEMENT -> CONTAINER WriteContent
</script>
Loading and running inline FBP is quite simple:
# Get the FBP contents
fbp = document.getElementById('main').textContent.trim()
# Load the NoFlo graph based on the FBP string
noflo.graph.loadFBP fbp, (graph) ->
# Run the graph
noflo.createNetwork graph
// Get the FBP contents
var fbp = document.getElementById('main').textContent.trim();
// Load the NoFlo graph based on the FBP string
noflo.graph.loadFBP(fbp, function (graph) {
// Run the graph
noflo.createNetwork(graph);
});
Subgraphs
A NoFlo graph may contain multiple subgraphs, managed by instances of the Graph
component. Subgraphs are useful for packaging particular flows to be used as a "new component" by other flows. This allows building more advanced functionality by creating reusable graphs of connected components.
The Graph component loads the graph given to it as a new NoFlo network, and looks for unattached ports in it. It then exposes these ports as its own inports or outports. This way a graph containing subgraphs can easily connect data between the main graph and the subgraph.
Unattached ports from the subgraph will be available through naming ProcessName.port
on the Graph component instance.
A simple example, using a subgraph as a component by splitting linecount example fbp into two files:
Count
# File: graphs/count.fbp
INPORT=Read.IN:FILENAME
OUTPORT=Count.COUNT:COUNT
OUTPORT=Read.ERROR:ERROR
# Split the file contents by newlines
Read(ReadFile) OUT -> IN Split(SplitStr)
# Count the packets
Split() OUT -> IN Count(Counter)
Main
# File: graphs/main.fbp
# Read a file (using our subgraph defined above)
'package.json' -> IN Count(Count)
Count() COUNT -> IN Display(Output)
# Display also file read errors
Count() ERROR -> IN Display()
A more advanced example, specifying what file a spreadsheet-parsing subgraph should run with:
# Load a subgraph as a new process
'examples/spreadsheet/parse.fbp' -> GRAPH Reader(Graph)
# Send the filename to the component (subgraph)
'somefile.xls' -> READ.SOURCE Reader()
# Display the results
Reader() ENTITIZE.OUT -> IN Display(Output)
Just like with components, it is possible to share subgraphs via NPM. You have to register them in your package.json
, for example:
{
"name": "noflo-spreadsheet",
"noflo": {
"graphs": {
"Parse": "./graphs/parse.fbp"
}
}
}
After this the subgraph is available as a "virtual component" with the name spreadsheet/Parse
and can be used just like any other component. Subgraphs exported in this manner can be in either JSON or the .fbp
format.