Obix Specification

Working Draft


Version 0.2

22 Jan 04


This mission of the group is to define standard XML data representations and web services for data acquisition and control systems. The core of the standard will be a foundation of common types and services applicable to horizontal issues including discovery, points, historical trends, and alarming. Additional libraries may be defined upon this foundation for vertical domains (HVAC, lighting, security, etc) and heterogeneous protocols (BACnet, Lonworks, Mobus, etc).


The Obix architecture is built as a stack of data and service abstractions.

Note this was my original illustration, but is a bit obsolete.

The working group is currently focused on the following layers of the stack:

  • Type Library: The foundation of the stack is an XML data model which defines a core type system including points, historical trends, alarms, units, and status. These types allow normalized control system information to be expressed in XML documents.
  • Horizontal Services: A set of web services which are common to all application domains including discovery, points, historian, and alarm.

Future work entails:

  • Generic Object Model Language: A set of rules for building domain and vendor specific type systems using an extensible model.
  • Vertical Models: A library of data type models built using the generic object modeling language to standardize specific domain models including BACnet, Lonworks, and security systems.
  • Vertical Services: A set of web services for interacting with vertical domain applications.

1.2Horizontal Services

The first phase of Obix is focused on defining a horizontal set of services to enable basic interaction with data acquisition and control systems. These services are:

  • DiscoveryService: This service provides the fundamental bootstrap service used to discover other services which may be available. The DiscoveryService provides the ability to navigate the system’s logical model using a hierarchical structure and defines how this logical model maps to other plugin services.
  • PointService: The PointService is used to read and write data which fits the traditional definition of a “control point” – usually a sensor or actuator. A point provides a normalized representation of an atomic data value with its associated metadata.
  • HistoryService: The HistoryService is used to query a time sampled history of point data. It uses the same point data type models, but adds a temporal dimension.
  • AlarmService: This service provides a simple normalized representation for alarm queries and acknowledgement.

2Core Type Library

Before defining any services it is helpful to define a core set of types which provide common abstractions used by all services.

Note for now I am just using a extremely simple C/Java syntax for types – I expect that we formally define these things in XML Schema? The ^ means href or inlined and [] means list of.

2.1Primitive Types

The following built-in primitive types are used to define all other types. These types correspond exactly to XML Schema. Obix only uses a subset of the XML Schema predefined simple types.

  • boolean
  • int
  • double
  • dateTime
  • duration
  • anyURI
  • string
  • language

By convention primitive types will start with a lower case letter and Obix types with a capital letter.


The basic philosophy of Obix localization is that requests supply a desired localization language, and the service automatically localizes all strings which are human consumable. To make a clear distinction between machine consumable strings which will not be localized and human consumable strings which should be, we define a special string type called Text. Whenever a type declares a Text element, the implied semantics are that the service will attempt to localize when possible.

Text extends string

2.3Request / Response

The following abstract types are used for all service requests and responses.



lang: language // used for all Text elements




// standard exception reporting? do that at SOAP level?


2.4Indirect References

For types which map to data reused across many service operations, it is preferable to return a URI to the data, rather than having to inline it into every XML response. In these cases an href attribute may be used.

For my pseudo code I am using the ^ to indicate where an href may be used as an alternative to in-lining all the data.

3Discovery Service

The DiscoveryService is the bootstrap service of the Obix architecture. It allows clients to interrogate the logical structure of the system and discover other services available.


The basic type used in discovery is an hierarchical tree of DiscoveryNodes.



id: string // identifies node in DiscoveryService

displayName: Text // readable name for the node

documentationUri: uri // uri to additional documentation

icon: anyURI // href to 16x16 GIF or PNG file

hasChildren: boolean // true if children are available

children: DiscoveryNode[] // only when read with proper depth

pointId: string // if usable by PointService

historyId: string // if usable by HistoryService

alarmId: string // if usable by AlarmService


The DiscoveryNode provides a basic set of information to display information to the user including a name, description, and icon. Every node may contain zero or more children nodes which allows the Obix implementation the ability to expose any logical structure desirable. The id field is used to identify the node by the DiscoveryService itself.

Each DiscoveryNode also provides the ability to act as a naming service and jump point into other pluggable services. By convention each plugin service FooService declares a field called fooId in the DiscoveryNode which identifies the node to the service. If a DiscoveryNode is not designed to work with a specific service then the id field is omitted.

For example consider a node with a pointId of “pt20”. That would mean that the node can be accessed as a Point using the PointService. The id “pt20” would be used to read and write the node as a Point. If a historyId were provided, that would indicate that clients could use the HistoryService to access historical information about the node.

The format of ids is always a implementation matter. Clients should consider ids opaque strings.

How service bindings and ids are declared within their Nodes?

How the URI of the service itself is discovered?




read(DiscoveryReadReq) -> DiscoveryReadRes


DiscoveryReadReq extends Request


ids: string[] // list of ids to read

depth: int // number of children levels to include


DiscoveryReadRes extends Response


nodes: DiscoveryNode[] // each node matches request id


The DiscoveryService supports one operation called read. The read request takes one or more ids and returns their corresponding nodes. For bootstrap the root node is always identified with the id of “root”.

The depth argument specifies how many children levels to read. A value of zero indicates to just return the node itself. A value of one means return the node and its direct children, two means children and grandchildren. The children field of the DiscoveryNode is only applicable when children are read at the specified depth. The hasChildren field should always be supported so that clients performing a lazy discovery know whether to dive down into the node.


The PointService provides a normalized represented of information using a traditional control point paradigm. The PointService supports six types of points:

  • BooleanPoint: models digital boolean true/false values
  • DoublePoint: models numeric/analog values
  • MultistatePoint: models values with a discrete range using string keys
  • StringPoint: models data as open ended character strings
  • DateTimePoint: models an absolute point in time
  • DurationPoint: models a relative duration of time


All Points extend from a common abstract definition:



id: string // id used by PointService operations

value: PointValue // current value of point

valueText: Text // formatted representation of value

status: Status // current status of point

facets^: PointFacets // additional metadata (may be href)




choiceId: string // must be id of Choice OR scalar

value: * // value, but never both




displayName: Text // human readable name

documentationUri: uri // uri to additional documentation

writable: boolean // can this point be written

obselete: boolean // if point is deprecated

restrictToChoices: boolean // value must be in choice list

choices^: Choice[] // list of choices for value (or href)




id: string // the string key for the choice

value: * // the value of this choice

displayName: Text // the human text for this choice


I took the liberty of trying to nail down a precise vocabulary. I used the term Facets for metadata to be consistent with XML Schema – do we like that? I used the term Choice for value labels, range, etc.


BooleanPoint extends Point


value: BooleanValue

facets^: BooleanFacets


BooleanValue extends PointValue


value: boolean


BooleanFacets extends PointFacets




DoublePoint extends Point


value: DoubleValue

facets^: DoubleFacets


DoubleValue extends PointValue


value: double


DoubleFacets extends PointFacets


min: double // min value inclusive

max: double // max value inclusive

unit^: Unit // units of measurement (may be href)

resolution: double // resolution of value...

precision: int // num of digits to display after decimal



MultistatePoint extends Point


value: MultiStateValue

facets^: MultistateFacets


MultistateValue extends PointValue


value: // Can never be used - must use choice id


MulistateFacets extends PointFacets



Is the choices model sufficient for describing the range of discrete values? I feel so.


StringPoint extends Point


value: StringValue

facets^: StringFacets


StringValue extends PointValue


value: string


StringFacets extends PointFacets


min: int // min number of characters inclusive

max: int // max number of characters inclusive



DateTimePoint extends Point


value: DateTimeValue

facets^: DateTimeFacets


DateTimeValue extends PointValue


value: dateTime


DateTimeFacets extends PointFacets


min: dateTime // min value inclusive

max: dateTime // max value inclusive


Do we need some sort of resolution/precision?


DurationPoint extends Point


value: DurationValue

facets^: DurationFacets


DurationValue extends PointValue


value: duration


DurationFacets extends PointFacets


min: duration // min value inclusive

max: duration // max value inclusive


Do we need some sort of resolution/precision?


The Status type is used by all points to provide normalized status information. It is not intended to provide comprehensive status details, only a normalized summary.



down: boolean // network/communication problems

fault: boolean // software/hardware/config problems

inAlarm: boolean // if in the alarm condition

unackAlarm: boolean // if last alarm was never acknowledged

outOfService: boolean // if manually disabled

overridden: boolean // if manually overridden

null: boolean // used to indicate null/auto/void



Units of measurement is a thorny issue that has always plagued software that processes analog information from the real world. Obix provides a unit framework for mathematically defining units. An extensive database of unit objects is also predefined.

All units measure a specific quantity or dimension in the physical world. Every known dimension can be expressed as a ratio of the seven fundamental dimensions: length, mass, time, temperature, electrical current, amount of substance, and luminous intensity. These seven dimensions are represented in SI respectively as kilogram (kg), meter (m), second (sec), Kelvin (K), ampere (A), mole (mol), and candela (cd).

Obix defines the Dimension type as a ratio of the seven SI units using a positive or negative exponent. A value of zero indicates an absence of the base unit in the ratio.



kg: int // exponent for kilograms

m: int // exponent for meters

sec: int // exponent for seconds

K: int // exponent for Kelvin

A: int // exponent for amperes

mol: int // exponent for moles

cd: int // exponent for candela


Units with equal dimensions are considered to measure the same physical quantity. This is not always precisely true, but is good enough for practice. This means that units with the same dimension are convertible. Conversion can be expressed by specifying the formula required to convert the unit to the dimension’s normalized unit. The normalized unit for every dimension is the ratio of SI units itself. For example the normalized unit of energy is the joule m2kgs-2. The kilojoule is 1000 joules and the watt-hour is 3600 joules. Most units can be mathematically converted to their normalized unit and to other units using the linear equations:

unit = dimension  scale + offset

toNormal = scalar  scale + offset

fromNormal = (scalar - offset) / scale

toUnit = fromUnit.fromNormal( toUnit.toNormal(scalar) )

There are some units which don’t fit this model including logarithm units and units dealing with angles. But this model is good enough for practice. Units which don’t fit this model should use a dimension where every exponent is set to zero. Applications should not attempt conversions on these types of units.

The Unit type extends from Ref allowing a library of predefined Units with global URIs.

Unit extends Ref


name: Text // human name for unit

symbol: Text // human symbol for unit

dimension: Dimension // ratio of SI base units

scale: double // toNormal/fromNormal

offset: double // toNormal/fromNormal}




read(PointReadReq) -> PointReadRes

write(PointWriteReq) -> PointWriteRes


PointReadReq extends Request


ids: string[] // list of ids to read

facets: boolean // include the facets for each point

valueText: boolean // include valueText for each point

unit^: Unit // convert all DoublePoints to given unit


PointReadRes extends Response


points: Point[] // maps to ids in request


PointWriteReq extends Request


ids: string[] // ids to write

values: *[] // values to write

unit^: Unit // units when writing DoublePoints ???


PointWriteRes extends Response


// error conditions per write?


Subscription for change of value is definitely something we need eventually. Should we tackle that now or just take poll approach for Obix 1.0? Part of the problem is that async event web services standards are just emerging.


The HistoryService allows querying of historical time sampled data. Since historical data is typically collected on Points, there is a great deal of overlap in the types used.


All point historical data uses the following abstract type:



id: string // id used by HistoryService

name: Text // human readable name

description: Text // human readable description

facets^: PointFacets // metadata for samples

samples: HistorySample[] // list of samples for query




timestamp: dateTime // time sample was collected

value: * // value at sample time

valueText: Text // formatted text of value

status: Status // status at sample time



BooleanHistory extends PointHistory { facets^: BooleanFacets }

BooleanSample extends HistorySample { value: BooleanPointValue }


DoubleHistory extends PointHistory { facets^: DoubleFacets }

DoubleSample extends HistorySample { value: DoublePointValue }


MultistateHistory extends PointHistory

{ facets^: MulitstateFacets }

MultistateSample extends HistorySample

{ value: MultiStatePointValue }


StringHistory extends PointHistory { facets^: StringFacets }

StringSample extends HistorySample { value: StringPointValue }

Do we need DurationHistory and DateTimeHistory???




read(HistoryReadReq) -> HistoryReadRes


HistoryReadReq extends Request


ids: string[] // list of ids to query

facets: boolean // include the facets for each history

valueText: boolean // include valueText for each sample

unit^: Unit // convert DoubleHistories to given unit

minTime: dateTime // min inclusive range of query

maxTime: dateTime // max inclusive range of query


HistoryReadRes extends Response


histories: PointHistory[] // maps to ids in request



TODO - this will probably the hardest task to get normalization consensus

7Change Log

7.1Version 0.1 (14 Jan 03)

  • Original draft

7.2Version 0.2 (22 Jan 03)

  • Change to use “displayName” and “documentationUri” for DiscoveryNode, PointFacets, and Choice
  • Make point value’s a union of scalar or choice id by creating the following new types: PointValue, BooleanValue, DoubleValue, MultiStateValue, StringValue, DurationValue, and DateTimeValue