-
Notifications
You must be signed in to change notification settings - Fork 0
Nodes
Link developers will mostly be creating org.iot.dsa.node.DSNode subclasses. DSNode is the primary organizational unit of the SDK. Subclasses will:
- Ensure there is a public no-arg constructor.
- Declare default children. These can be values, actions and additional nodes.
- Override the various DSNode callbacks (onXXX) to trigger application logic.
DSNode subclasses must support the public no-arg constructor. This is so the node can be serialized and deserialized.
DSNode uses a hybrid inheritance scheme that combines normal Java inheritance with instance based inheritance. The primary purpose of this is to reduce the number of objects in memory by utilizing default values.
Every subclass of DSNode has a private default instance, all other instances are copies of the default instance. Since there is a default instance in memory, you should never trigger application logic from a constructor. Application logic should be triggered by a lifecycle callback.
Instead of declaring children as compiled Java fields, DSNode subclasses override the declareDefaults method. The method should:
- Call super.declareDefaults(). NOT DOING THIS IS DANGEROUS, unless you have a lot of experience with the sdk and know what you are doing.
- Call DSNode.declareDefault(String name, DSIObject default, String help) for each non-removable / non-renamable child. Do not add dynamic children in declareDefaults, because if they are removed, they will be re-added the next time the configuration database is deserialized or the type instantiated (unless that is desired).
@Override
protected void declareDefaults() {
super.declareDefaults();
declareDefault("Do Something", DSAction.DEFAULT, "A non-virtual action");
declareDefault("An Integer Value", DSInt.valueOf(0), "Real numbers suck.");
declareDefault("Child Node", new DSNode(), "It is what it is");
}
During node serialization (configuration database, not DSA interop), children that match their declared default are omitted. This has two benefits:
- A smaller node database means faster serialization / deserialization.
- Default values can be modified in code and all existing databases with values at the default will be automatically upgraded the next time the updated class is loaded. You will learn to love this.
It is important to know the application life cycle. Use life cycle callbacks to trigger custom link functionality.
Because of Defaults, nodes should not execute any application logic unless they are running (started or stable).
Stopped
A node is instantiated in the stopped state. If a node tree has been persisted, it will be be fully restored in the stopped state. DSNode.onStopped will not be called, it is only called when nodes transition from running to stopped.
When nodes are removed from a running parent node, they will be stopped. DSNode.onStopped will be called after all child nodes have been stopped.
When a link is stopped, an attempt to stop the tree will be made, but it cannot be guaranteed.
Started
After the node tree is fully deserialized it will be started. A node's onStart method will be called after all of its child nodes have been started. The only guarantee is that all child nodes have been started.
Nodes will also started when they are added to an already running parent node.
Nodes that provide static facilities should initialized them here. Static facilities should not be accessed until the accessor is stable.
Stable
Stable is called after the entire tree has been started. The first time the node tree is loaded, there is a stable delay of 5 seconds.
Nodes added to an already stable parent will have onStart and onStable called immediately.
When in doubt of whether to use onStarted or onStable, use onStable.
Only access static facilities while in the stable state.
Additional Callbacks
When a node is stable, there are several callbacks for various state changes. All callbacks begin with on such as onChildAdded(). See the javadoc for complete list.
Nodes should suspend, or minimize activity when nothing is interested in them. For example, if nothing is interested in a point, it is best to not poll the point on the foreign system.
To do this you use the following APIs:
- DSNode.onSubscribed() - Called when the node transitions from unsubscribed to subscribed. This is not called for subsequent subscribers once in the subscribed state.
- DSNode.onUnsubscribed() - Called when the node transitions from subscribed to unsubscribed. If there are multiple subscribers, this is only called when the last one unsubscribes.
- DSNode.isSubscribed() - Tells the caller whether or not the node is subscribed.
All DSNode children have a corresponding DSInfo instance. This type serves serves two purposes:
- It carries some meta-data about the relationship between the parent node and the child.
- It tracks whether or not the child matches a declared default.
Important things for developers to know about DSInfo are:
- You can configure state such as transient, readonly and hidden.
- You can declare fields in the your Java class for info instances to avoid looking up the child every time it is needed. This is can be used to create fast getters and setters.
Without declaring fields (lookups required):
public void declareDefaults() {
super.declareDefaults();
declareDefault("The Int", DSInt.valueOf(0));
}
public int getTheInt() {
DSInt theInt = (DSInt) get("The Int"); //map lookup
return theInt.toInt();
}
public void setTheInt(int value) {
put("The Int", DSInt.valueOf(value)); //map lookup
}
With declared fields:
private DSInfo theInt = getInfo("The Int"); //will be null in the default instance
public void declareDefaults() {
super.declareDefaults();
declareDefault("The Int", DSInt.valueOf(0));
}
public int getTheInt() {
return theInt.toInt(); //no lookup
}
public void setTheInt(int value) {
put(theInt, DSInt.valueOf(value)); //no lookup
}
Actions allow allow responders to expose functionality in DSA that can't be modeled as values. To learn more about actions, visit actions.
Values mostly represent leaf members of the node tree. This is where defaults have an impact. To learn more about values, visit values.