What is an Entity? An Entity is a thing with a distinct and independent existence. In the Niagara world, this is anything that implements the Entity interface. Right now, all station components are Entities - all instances of BComponent and its subclasses. In the future, we may add other Entity implementations. Since Entities must be unique within a system, we base this uniqueness off their Entity ORD - or in terms of BComponent, its absolute ord.
We can describe Entities by assigning them attributes (aka Tags) and describing their relations with other Entities.
Tags have an Id and optionally a value. This value must be a (BIDataValue). Niagara 4 introduced a new BIDataValue, (BMarker) that is used when just the presence of the Tag is sufficient to convey it’s meaning.
A Tag Id is the combination of the Tag’s namespace and its name. A TagDictionary to define a namespace of Tags. Namespaces allow two Tag Dictionaries to define Tags with the same name but different meanings or value types.
Tags may be placed directly on an Entity (Direct Tags) or Implied by a SmartTagDictionary using a set of rules.
Some examples include the “n:vendor” Tag from the Niagara Tag Dictionary. This is used as a Direct Tag and has a BString value. “n:point” is a Marker Tag implied on all Niagara BControlPoints by the Niagara Tag Dictionary. “hs:chiller”, is a direct Marker Tag from the Haystack Tag Dictionary to mark chiller equipment.
Relations relate two Entities to each other. Like Tags, they have a namespaced ID and an Entity ORD to define the Entity at the other end of the Relation. Relations also have a direction that we call either Inbound or Outbound. This defines how we view the relationship. This allows us to say that a Device has an outbound “n:childPoint” Relation to each of it points. If we look at the relation from the perspective of the points, each point has an Inbound “n:childPoint” Relation from the device. BLink is a special type of relation that passes data. Like tags, in Niagara relations can be direct - living directly on a BComponent - or implied by a SmartTagDictionary.
The javax.baja.tag package contains the Entity API. There are a number of classes in it, but these are the basics:
public interface Entity extends Taggable
{
Tags tags();
Relations relations();
Optional<BOrd> getOrdToEntity();
}
public final class Tag
{
public Tag(Id id, BIDataValue value);
public Id getId();
public BIDataValue getValue();
}
public interface Relation extends Taggable
{
Id getId();
boolean isInbound();
boolean isOutbound();
Entity getEndpoint();
BOrd getEndpointOrd();
Tags tags();
}
Adding a Tag
Using the Component Model, this is simply adding a BIDataValue Property with the METADATA Flag set - remember to escape the Tag’s Id.
//Component Model
myComponent.add(SlotPath.escape("b:floor"), BInteger.make(1),
Flags.METADATA);
Using the Entity API, it’s pretty similar. We can create a new Tag and set it on the Entity or use an alternate version of the set method that takes an Id and a BIDataValue.
//Entity API
Tag floorTag = Tag.newTag("b:floor", BInteger.make(1));
entity.tags().set(floorTag);
//or
entity.tags().set(Id.newId("b:floor"), BInteger.make(1));
The call to set() will add the tag if it does not already exist or overwrite an existing tag with the same Id.
Adding a Relation
Using the Component Model, a new method was added to BComponent in Niagara 4 - makeRelation(). This looks a lot like makeLink(). We pass in an Id, a BComponent endpoint and a Context. This will create an Outbound Relation from our Component to the targetEndpoint.
//Component Model
BRelation r = myComponent.makeRelation(Id.newId("hs:ahuRef"),
targetEndpoint,
cx);
Using the Entity API, it looks pretty similar to the Tag example above. We call an add() method and pass in the Relation’s Id and the otherEntity that we’re creating the relation to. We can also pass in a direction (defaulting to Outbound if none is supplied).
//Entity API
Relation r = entity.relations().add(Id.newId("hs:ahuRef"),
otherEntity);
Why does adding a Relation call add() instead of set()? It’s quite common for an Entity to have multiple relations with the same Id but different endpoints. A very common case for this is the “n:childPoint” relation - a BDevice will have multiple “n:childPoint” relations, one to each of the points under its BPointDeviceExt or subfolders.
Checking if a Tag Exists
Using the Component Model, we can simply check if a property with our Tag’s Id exists.
//Component Model (only works for Direct Tags)
myComponent.get(SlotPath.escape("n:point"));
But, this only works when our Tag is a property of our component - a Direct Tag. If we want to check is a component has a Tag implied on it, we have to use the Entity API. Using this will check for both Direct and Implied tags.
//Entity API
entity.tags().get(Id.newId("n:point")).isPresent();
//or
entity.tags().contains(Id.newId("n:point"));
Traversing a Relation
Let’s say we have our system setup using the Haystack Tag Dictionary and we want to find the chilled water plant of a chiller. First, we define our Relation Id - “hs:chilledWaterPlantRef”. Then we get the Outbound Relation on our chiller with this Id. If it’s present, we can traverse to its endpoint and we will have found our chilled water plant.
/* Find the chilledWaterPlant of a chiller */
Id plantRef = Id.newId("hs", "chilledWaterPlantRef");
chiller.relations().get(plantRef, Relations.OUT).ifPresent(
relation -> {
Entity chilledWaterPlant = relation.getEndpoint();
}
);
The call to get() will return the first relation with the hs:chilledWaterPlantRef Id, so if we find one, this is our chilled water plant.
Traversing all Relations
We can also traverse all the Relations with a given Id. Let’s say we want to find all the chillers in a given device network. Like the previous example, we’ll start by defining an Id. This time instead of the relation Id, it’s the Id defining the equipment that we want to find - “hs:chiller.” Starting at the device network, we’ll get all of the Relations with the “n:childDevice” Id, stream them and map the Relations to their endpoints. From the endpoint entities, we filter and return true for the ones tagged with the Haystack chiller tag, then collect them as a List.
/* Get all device in network with an hs:chiller tag */
Id chiller = Id.newId("hs:chiller");
List<Entity> chillers = network.relations()
.getAll(Id.newId("n:childDevice"))
.stream()
.map(Relation::getEndpoint)
.filter(entity -> entity.tags().get(chiller).isPresent())
.collect(Collectors.toList());
Copyright © 2000-2019 Tridium Inc. All rights reserved.