7. Data-Distributor
The DataDistributor
essentially represents a DataPubSubSystem
. It distributes data over the NoPE network (or internally). Data is manipulated via JSON pointers (these are structured like MQTT topics; see above). In order to inform systems about changes, subscribers
can be informed about changes like in MQTT. The wildcards of MQTT can be used to get the relevant information. I.e.:
- The
DataDistributor
contains aroot
data object.- The
root
data object can be modified via paths (similar to topics) (see methodspatternBasedPush
orpushData
)
- The
- If the data object is manipulated, all changes are automatically forwarded to the interested
subscribers
. - The naming of the methods corresponds to the push and pull principle
DataPubSubSystem
A data based publish and subscribe system. It extends the PubSubSystem by providing the methods and properties:
pushData
to push data into the system.pullData
to pull data from the system. It always returns the current data or the default value if there is no data under the specified path.patternbasedPullData
to fetch data with a specific pattern. See the example for details.patternBasedPush
to push data with a given pattern into the system.data
: direct access to theroot
data object. This object is continuously present and always contains the most recent data manipulated by changes (seepatternBasedPush
orpushData
).
Interact with the DataDistributor
Firstly we have to create a Dispatcher, that we are using to access the dataDistributor
// First lets install nope using npm
import * as nope from "nope-js-node";
// Create a communicator:
// We will use the event layer (which just runs internally)
const communicator = nope.getLayer("event");
// Lets create our dispatcher
// 1. Dispatcher simulates our local system
const localDispatcher = nope.dispatcher.getDispatcher({
communicator,
id: "local"
}, {
singleton: false,
useBaseServices: false
});
// Lets wait for our element to be ready.
await localDispatcher.ready.waitFor();
// Create a short cut:
const dataDistributor = localDispatcher.dataDistributor;
We are now ready to manipulate some data in the system:
// Now we manipulate the data:
dataDistributor.pushData("", { robot: {status: {state:"waiting", speed:0}}});
// Render the Root Object. (Read only)
dataDistributor.data
The root object contains:
{ robot: { status: { state: 'waiting', speed: 0 } } }
Subscribing to data Changes
We are now interessed in data changes. We want allways be informed about the status of a specific object.
Therefore we create an subscription of on the topic robot/status
. By subscribing, we will immediately receive an update containing the current status:
const observer = dataDistributor.registerSubscription("robot/status", (data) => {
console.log("The Robot-Status is",data)
})
Executing the code above result in the following output:
The Robot-Status is { state: 'waiting', speed: 0 }
We now simulate a change of the robots speed:
// We create an extra Element describing the status.
// We use an observable for that purpose
const robotStatus = new nope.NopeObservable();
robotStatus.setContent({state:"waiting", speed:0});
dataDistributor.register(robotStatus, {
mode: "publish",
topic: "robot/status"
});
// Now we adapt the status
robotStatus.getContent().speed = 1;
robotStatus.getContent().state = "moving";
Registering the robotStatus
results in triggering the subscription:
The Robot-Status is { state: 'waiting', speed: 0 }
As we can see, we didnt receive an update. Thats based on the behavior of the observable. We have to trigger an update manually right now, or must use the method setContent
instead of adapting the data object directly.
robotStatus.forcePublish();
// Alternative:
robotStatus.setContent({state:"moving", speed:1});
The Robot-Status is { state: 'moving', speed: 1 }
But why didn't receive 2 updates? we used the method
forcePublish
andsetContent
and assigned a new status-object, but didnt receive 2 notifications. This is related to the behavior of observables. They only emit changes, if the data really changed (what wasnt the case)
Accessing specific datapoints
If we want to manipulate data we can use the method pushData
. Lets add an additional status of an sensor:
dataDistributor.pushData("sensor", {status: {state:"waiting"}});
The root object of the dataDistributor can be read with the property data
dataDistributor.data
The content of the root object is now:
{
robot: { status: { state: 'moving', speed: 1 } },
sensor: { status: { state: 'waiting' } }
}
If we want to access specific data points inside of the dataDistributor, we can use the function pullData
or patternBasedPullData
// pull the data of the sensor:
dataDistributor.pullData("sensor",false)
The pull commands extract the following data:
{ status: { state: 'waiting' } }
If the data is not present, you can provide an default value, which will be return instead. Otherwise an Error is thrown.
// Data that is not present
dataDistributor.pullData("not_contained", "default-value")
If no data is present, the default value is returned!
'default-value'
// Accessing data with a pattern
dataDistributor.patternbasedPullData("+/status/state")
Patterns can be used to pull data. The method will allways return a list (it might be empty.)
[
{ path: 'robot/status/state', data: 'moving' },
{ path: 'sensor/status/state', data: 'waiting' }
]
As you can see, we receive an array, which contains the object containing:
- the
path
of the data - the
data
Additionally you are able to manipulate data using a path with wildcards, which will trigger our subscriptions as well:
dataDistributor.patternBasedPush("+/status/state", "manipulated!")
The Robot-Status is { state: 'manipulated!', speed: 1 }
If we look at the data again, we have manipulated the state of the sensor and robot
dataDistributor.data
{
robot: { status: { state: 'manipulated!', speed: 1 } },
sensor: { status: { state: 'manipulated!' } }
}
Like the eventDistributor
the dataDistributor
shares those changes with all runtimes in the network. For this purpose please check the docu of the eventDistributor