The standard defines primitive data types to describe most of the components of objects and protocol data units.
This is a special class that holds a unique value to signify special values in objects and special values for APDU’s:
:Null
rdf:type owl:Class ;
owl:equivalentClass
[ rdf:type owl:Class ;
owl:oneOf (null:null)
] .
This class has only one named individual:
null:null
rdf:type :Null , owl:NamedIndividual .
Boolean values are pretty straight forward:
:Boolean
rdf:type rdfs:Datatype ;
owl:equivalentClass xsd:boolean .
Most of the unsigned values used in the standard have no specific value limit so the ontology specifies them as long:
:Unsigned
rdf:type rdfs:Datatype ;
owl:equivalentClass xsd:unsignedLong .
Integers are very similar to unsigned values:
:Integer
rdf:type rdfs:Datatype ;
owl:equivalentClass xsd:long .
This datatype is identical to the IEEE single precision floating point:
:Real
rdf:type rdfs:Datatype ;
owl:equivalentClass xsd:float .
This datatype is identical to the IEEE double precision floating point:
:Double
rdf:type rdfs:Datatype ;
owl:equivalentClass xsd:double .
Octet strings are used to transfer blobs of information that is not otherwise specified as structured content:
:OctetString
rdf:type rdfs:Datatype ;
owl:equivalentClass xsd:base64Binary .
Character strings are This is a long line of text:
:CharacterString
rdf:type rdfs:Datatype ;
owl:equivalentClass xsd:string .
Character strings have an “encoding” when it appears in an APDU, and some devices have restrictions on what encoding formats they support.
Here is an ASN.1 example of a bit string:
BACnetEventTransitionBits ::= BIT STRING {
to-offnormal (0),
to-fault (1),
to-normal (2)
}
Bit strings are packed arrays of named bits, this ontology uses rdf:Bag to note the bit definitions can be unordered:
:BitString
rdf:type owl:Class ;
rdfs:subClassOf rdf:Bag .
The bag contains bits, each of which has a specific nominal value:
:Bit rdf:type owl:Class .
bit:nominal
rdf:type owl:DatatypeProperty , owl:FunctionalProperty ;
rdfs:domain :Bit ;
rdfs:range xsd:nonNegativeInteger .
How can the ontology restrict the “bag” contents to only contain named individual bits that are a subclass of BitString?
This is the ANS.1 definition of an enumeration:
BACnetFileAccessMethod ::= ENUMERATED {
record-access (0),
stream-access (1)
}
The Enumeration class is a way to group all labeled values together. There are two types of enumerations, those defined in the standard and those that can be defined by vendors with their own names and values:
:Enumeration
rdf:type owl:Class ;
owl:disjointUnionOf (:ProprietaryEnumeration :StandardEnumeration) .
Some enumerations (see below) can have additional values added to them by vendors which are called extended enumerations:
:ExtendedEnumeration
rdf:type owl:Class ;
rdfs:subClassOf :StandardEnumeration .
Every enumerated value is a named individual object that has a nominal value:
enum:nominal
rdf:type owl:DatatypeProperty , owl:FunctionalProperty ;
rdfs:domain :Enumerated ;
rdfs:range xsd:nonNegativeInteger .
Enumerations that are defined by a vendor have a relationship to the Vendor object, and this applies to both extended enumerations and proprietary enumerations:
enum:vendor
rdf:type owl:FunctionalProperty , owl:ObjectProperty ;
rdfs:domain
[ rdf:type owl:Class ;
owl:unionOf (:ExtendedEnumeration :ProprietaryEnumeration)
] ;
rdfs:range :Vendor .
This is an example of an extended enumeration definition.
Property identifiers are used to describe properties or components of objects. Vendors can extend objects by adding their own properties which are distinguished from those in the standard by using values above those reserved to ASHRAE.
The standard defines some property identifiers:
:PropertyIdentifier
rdf:type owl:Class ;
rdfs:subClassOf :StandardEnumeration ;
owl:equivalentClass
[ rdf:type owl:Class ;
owl:oneOf (pi:ackedTransitions pi:ackRequired pi:action ... )
] .
Because the PropertyIdentifier class is restricted to the named individuals, there is a subclass for those that the vendor wants to define. This is a subclass so that every place where a PropertyIdentifier is required, an ExtendedPropertyIdentifier can be used. The upper bound is the maximum value the enumeration can have so vendors know an appropriate integer size (short, long, etc.) in their implementation:
:ExtendedPropertyIdentifier
rdf:type owl:Class ;
rdfs:subClassOf :PropertyIdentifier , :ExtendedEnumeration ;
rdfs:subClassOf
[ rdf:type owl:Restriction ;
owl:onProperty enum:nominal ;
owl:someValuesFrom
[ rdf:type rdfs:Datatype ;
owl:onDatatype xsd:nonNegativeInteger ;
owl:withRestrictions
( [ xsd:minInclusive 512 ]
[ xsd:maxInclusive 4194303 ]
)
]
] .
The property identifiers that are defined in the standard are defined in the ontology as named individuals:
pi:ackedTransitions
rdf:type :PropertyIdentifier , owl:NamedIndividual ;
enum:nominal 0 .
pi:ackRequired
rdf:type :PropertyIdentifier , owl:NamedIndividual ;
enum:nominal 1 .
pi:action
rdf:type :PropertyIdentifier , owl:NamedIndividual ;
enum:nominal 2 .
I have a suspicion that I can’t restrict the standard enumerations to some named individuals and yet open up a subclass to vendor values.
The date value in the standard has four parts; year, month, day and day-of-week. Each of these values are unsigned octets that can have the value 255 meaning “wild card” value. The ontology defines this as a patterned string of four components similar to other date formats, but cannot use standard date formats because of the wild card value:
:Date
rdf:type rdfs:Datatype ;
owl:equivalentClass
[ rdf:type rdfs:Datatype ;
owl:onDatatype xsd:string ;
owl:withRestrictions
([ xsd:pattern "([0-9]{4}|[*])-([0-9]{2}|[*])-([0-9]{2}|[*])-([0-9]{2}|[*])"
])
] .
The time value is very similar to the date format with four parts; hours, minutes, seconds, and hundredths. It can also contian wild card values:
:Time
rdf:type rdfs:Datatype ;
owl:equivalentClass
[ rdf:type rdfs:Datatype ;
owl:onDatatype xsd:string ;
owl:withRestrictions
([ xsd:pattern "([0-9]{2}|[*]):([0-9]{2}|[*]):([0-9]{2}|[*])[.]([0-9]{2}|[*])"
])
] .
Every object in the standard has a unique object identifier, a 32-bit packed value which contains a 10-bit object type and 22-bit instances number:
:ObjectIdentifier
rdf:type owl:Class .
Rather than specifying the object type as an unsigned integer, the ontology defines it as a relationship to a named individual of type ObjectIdentifier, which is itself an enumerated value:
oid:type
rdf:type owl:FunctionalProperty , owl:ObjectProperty ;
rdfs:domain :ObjectIdentifier ;
rdfs:range :ObjectType .
The instance number is just a non-negative integer with a limited range:
oid:instance
rdf:type owl:DatatypeProperty , owl:FunctionalProperty ;
rdfs:domain :ObjectIdentifier ;
rdfs:range
[ rdf:type rdfs:Datatype ;
owl:onDatatype xsd:nonNegativeInteger ;
owl:withRestrictions
([ xsd:minInclusive 0
] [ xsd:maxInclusive 4194302
])
] .
Most of the character strings in the standard can be empty, but object names must have at least one character:
:ObjectName
rdf:type rdfs:Datatype ;
owl:equivalentClass
[ rdf:type rdfs:Datatype ;
owl:onDatatype xsd:string ;
owl:withRestrictions
([ xsd:minLength 1
])
] .
Assuming there is a Device class, and that devices contain objects, how can the ontology specify that the object name for each object must be unique within the device?