BACnet.Protocol.BACnetURI (bacstack v0.1.0-dev.1)

Copy Markdown View Source

ASHRAE 135 Annex Q.8 defines a BACnet URI scheme, in cases where a URI is needed to refer to data that is accessible using BACnet services.

A BACnet URI is used to refer to BACnet objects, similar to BACnet.Protocol.DeviceObjectPropertyRef.

The format looks like this:

bacnet://<device>/<object>[/<property>[/<index>]]

Where angle brackets indicate variable text and square brackets indicate optionality.

The <device> segment is the device instance number in decimal. A <device> identifier of .this means 'this device' so that it can be used in static files that do not need to be changed when the device identifier changes.

The <object> identifier is in the form <type>,<instance> where <type> is either a decimal number or BACnet.Protocol.Constants.object_type/0, and <instance> is a decimal number.

The <property> identifier is either a decimal number or BACnet.Protocol.Constants.property_identifier/0. If it is omitted, it defaults to :present_value except for BACnet File objects, where absence of <property> refers to the entire content of the file accessed with Stream Access. In that special case, property is set to nil.

The <index> is the decimal number for the index of an array property.

Summary

Types

Available options for encode/2.

t()

Represents a BACnet URI.

Functions

Encodes a BACnetURI struct into a BACnet URI string.

Parses a BACnet URI string into a BACnetURI struct.

Returns true if the BACnetURI struct contains valid data according to the BACnet URI scheme rules (Annex Q.8).

Returns true if the given BACnet URI string is valid.

Types

encode_opt()

@type encode_opt() :: {:always_decimal, boolean()}

Available options for encode/2.

The always_decimal option will force the function to always use decimal numbers in the URI output for better compatibility. Otherwise it will by default try to use the Clause 21 definition text, where possible.

t()

@type t() :: %BACnet.Protocol.BACnetURI{
  device_identifier: BACnet.Protocol.ObjectIdentifier.t() | nil,
  object_identifier: BACnet.Protocol.ObjectIdentifier.t(),
  property_array_index: non_neg_integer() | nil,
  property_identifier:
    BACnet.Protocol.Constants.property_identifier() | non_neg_integer() | nil
}

Represents a BACnet URI.

Functions

encode(uri, opts \\ [])

@spec encode(t(), [encode_opt()]) :: {:ok, String.t()} | {:error, term()}

Encodes a BACnetURI struct into a BACnet URI string.

When the property is nil (for File objects), the property segment is omitted. Otherwise the property is always included.

This function will use Clause 21 text for the URI encoding where possible and fallback to use the decimal number where necessary. It can be configured to always use the decimal number for better compability. If the object type or property identifier is a number, it will also be encoded as a number. It will not be converted to text.

Examples

Local object:

iex> object = %BACnet.Protocol.ObjectIdentifier{type: :analog_output, instance: 5}
iex> BACnetURI.encode(%BACnetURI{
...>   device_identifier: nil, object_identifier: object,
...>   property_identifier: :present_value, property_array_index: nil
...> })
{:ok, "bacnet://.this/analog-output,5/present-value"}
iex> BACnetURI.encode(%BACnetURI{
...>   device_identifier: nil, object_identifier: object,
...>   property_identifier: :present_value, property_array_index: nil
...> }, always_decimal: true)
{:ok, "bacnet://.this/1,5/85"}

Remote object with array index:

iex> device = %BACnet.Protocol.ObjectIdentifier{type: :device, instance: 114705}
iex> object = %BACnet.Protocol.ObjectIdentifier{type: :binary_output, instance: 15555}
iex> BACnetURI.encode(%BACnetURI{
...>   device_identifier: device, object_identifier: object,
...>   property_identifier: :priority_array, property_array_index: 16
...> }, always_decimal: true)
{:ok, "bacnet://114705/4,15555/87/16"}

parse(uri)

@spec parse(String.t()) :: {:ok, t()} | {:error, term()}

Parses a BACnet URI string into a BACnetURI struct.

Returns {:ok, uri} on success or {:error, reason} on failure.

This function is more permissive than the specification, as in allows different casing and _ for object types and property identifiers. Not only analog-value is allowed, but also Analog_Value or analog_value.

Examples

Local object:

iex> BACnetURI.parse("bacnet://.this/1,5/85")
{:ok, %BACnetURI{
  device_identifier: nil,
  object_identifier: %BACnet.Protocol.ObjectIdentifier{type: :analog_output, instance: 5},
  property_identifier: :present_value,
  property_array_index: nil
}}

iex> BACnetURI.parse("bacnet://.this/analog-output,5")
{:ok, %BACnetURI{
  device_identifier: nil,
  object_identifier: %BACnet.Protocol.ObjectIdentifier{type: :analog_output, instance: 5},
  property_identifier: :present_value,
  property_array_index: nil
}}

Remote object with array index:

iex> BACnetURI.parse("bacnet://114705/4,15555/priority-array/16")
{:ok, %BACnetURI{
  device_identifier: %BACnet.Protocol.ObjectIdentifier{type: :device, instance: 114705},
  object_identifier: %BACnet.Protocol.ObjectIdentifier{type: :binary_output, instance: 15555},
  property_identifier: :priority_array,
  property_array_index: 16
}}

valid?(ba_cnet_uri)

@spec valid?(t()) :: boolean()

Returns true if the BACnetURI struct contains valid data according to the BACnet URI scheme rules (Annex Q.8).

valid_str?(uri)

@spec valid_str?(binary()) :: boolean()

Returns true if the given BACnet URI string is valid.

This function will attempt to parse the string and then validate it.