BACnet.Stack.ClientHelper (bacstack v0.0.1)

View Source

BACnet stack client helper functions for executing commands/queries.

Summary

Functions

Sends an I-Am service request to the destination, or optionally using :broadcast (or the real broadcast address) as local broadcast.

Read a BACnet object from a remote BACnet device and transform it into an object.

Read a single property from a remote BACnet object and transform the value.

Read multiple properties from a remote BACnet object at once and transform each value.

Scan the given device for available objects and read all objects. A map of objects will be returned on success.

Send a (UTC) Time Synchronization service APDU to the destination.

Subscribes for COV notification for a remote BACnet object property.

Sends a Who-Is service request to the network (local broadcast). The I-Am responses will be collected and returned.

Write to a single property from a remote BACnet object.

Functions

i_am(server, destination, device, vendor_id, opts \\ [])

@spec i_am(
  GenServer.server(),
  term() | :broadcast,
  BACnet.Protocol.ObjectIdentifier.t(),
  non_neg_integer(),
  Keyword.t()
) :: :ok | {:error, term()}

Sends an I-Am service request to the destination, or optionally using :broadcast (or the real broadcast address) as local broadcast.

See also BACnet.Protocol.Services.IAm.

The Client.send/4 options are available.

read_object(server, destination, object, opts \\ [])

Read a BACnet object from a remote BACnet device and transform it into an object.

The required properties are always as a bare minimum read, only more properties can be read, never less.

The value is casted through the BACnet.Protocol.ObjectsUtility module based on the object modules. As such object types or properties that are not supported, will fail.

If you want to read a device object and don't know the proper device instance number, you can use 4_194_303 as instance number. By the BACnet specification that instance number will be treated by the remote BACnet device as if the instance number was locally correctly matched.

The following options are available:

properties and read_level are mutually excluse. If both are given, properties takes precedence.

read_property(server, destination, object, property, array_index \\ nil, opts \\ [])

Read a single property from a remote BACnet object and transform the value.

The value is casted through the BACnet.Protocol.ObjectsUtility module based on the object modules. As such object types or properties that are not supported, will fail, unless you specify the raw options, which will give you the Encoding struct (or list of) to handle yourself. Array indexes of 0 will return the array size as {:ok, non_neg_integer()}, if successfully read.

If you want to read a device object's property without needing to know before hand which instance number, you can use 4_194_303 as instance number. By the BACnet specification that instance number will be treated by the remote BACnet device as if the instance number was locally correctly matched.

The following options are available:

read_property_multiple(server, destination, object, properties, opts \\ [])

Read multiple properties from a remote BACnet object at once and transform each value.

The values are casted through the BACnet.Protocol.ObjectsUtility module based on the object modules. As such object types or properties that are not supported, will fail, unless you specify the raw options, which will give you a list of ReadAccessResults to handle yourself.

The following options are available:

reinitialize_device(server, destination, state \\ Constants.macro_assert_name(:reinitialized_state, :warmstart), password \\ nil, opts \\ [])

@spec reinitialize_device(
  GenServer.server(),
  term(),
  BACnet.Protocol.Constants.reinitialized_state(),
  String.t() | nil,
  Keyword.t()
) :: :ok | {:error, BACnet.Protocol.apdu()} | {:error, term()}

Send a Reinitialize-Device service request to a remote BACnet device.

Password must be an ASCII string between 1 to 20 characters, inclusive, or nil.

The following options are available:

scan_device(server, destination, device, opts \\ [])

Scan the given device for available objects and read all objects. A map of objects will be returned on success.

If you don't know the device object identifier of the BACnet device in question, but you know the BACnet network address (i.e. the IP address and port for BACnet/IP), you can use the Who-Is service with the destination address being the device's network address, to discover the object identifier. You can also use read_property/6 to read the :object_identifier property.

The scan process is parallelized through Task.async_stream/3 and thus the invoke_id is automatically being set. Since this implementation simply uses invoke_id in the range of 0..max_concurrency-1, it would be safest when the BACnet.Stack.Client implementation manages and overrides the invoke_id, so that an user does not have to care about possible collisions. The current "default" implementation of BACnet.Stack.Client does manage invoke_ids, but it can be deactivated, so care must be exercised if it done. You need to be aware to not invoke/have parallel other requests to the same destination, as the invoke_id could be duplicated.

The values are casted through the BACnet.Protocol.ObjectsUtility module based on the object modules. As such object types or properties that are not supported, will fail the operation.

The following options are available:

exit_on_error and ignore_errors are mutually excluse. ignore_errors takes precedence, if set to true.

send_time_synchronization(server, destination \\ :broadcast, opts \\ [])

@spec send_time_synchronization(
  GenServer.server(),
  term(),
  Keyword.t()
) :: :ok | {:error, term()}

Send a (UTC) Time Synchronization service APDU to the destination.

:broadcast will be resolved to the local broadcast address.

The following options are available:

  • All options from BACnet.Stack.Client.send/4.
  • All options from BACnet.Protocol.Services.TimeSynchronziation.to_apdu/2 respectively BACnet.Protocol.Services.UtcTimeSynchronziation.to_apdu/2.
  • datetime: DateTime.t() - Optional. The timestamp to use for synchronization. It will be automatically shifted to UTC, if necessary. If omitted, DateTime.now!/1 will be used with Time Synchronization - if the default timezone is "Etc/UTC", then UTC Time Synchronization will be used. The utc option overrides the behaviour of the default timezone - you may use a non-UTC timezone and still be able to use UTC.
  • utc: boolean() - Optional. Whether to use UTC Time Synchronization.

subscribe_cov_property(server, destination, object, property, opts \\ [])

Subscribes for COV notification for a remote BACnet object property.

When using confirmed COV notifications, the remote BACnet device requires you to send confirmations of the reception (BACnet.Protocol.Services.SimpleACK) - this is not done automatically.

The following options are available:

  • All options from BACnet.Stack.Client.send/4.
  • All options from BACnet.Protocol.Services.SubscribeCovProperty.to_apdu/2.
  • confirmed: boolean() - Optional. Request confirmed COV notifications. By default, COV notifications are requested to be unconfirmed.
  • cov_increment: float() - Optional. The COV increment to use for float properties.
  • lifetime: non_neg_integer() | nil - Optional. The COV subscription lifetime to use in seconds (defaults to 3600). To unsubscribe, use nil.

  • pid: non_neg_integer() - Optional. The process identifier to use. By default, this will be calculated from the caller PID (node bits 0-3 << 28 + pid_number << 13 + pid_serial).

who_is(server, timeout \\ 5000, opts \\ [])

@spec who_is(GenServer.server(), pos_integer(), Keyword.t()) ::
  {:ok, [{source_address :: term(), BACnet.Protocol.Services.IAm.t()}]}
  | {:error, term()}

Sends a Who-Is service request to the network (local broadcast). The I-Am responses will be collected and returned.

See also BACnet.Protocol.Services.WhoIs.

By default, it will collect all responses received until timeout. By using max opts, one can tell the function how many to receive and then stop prematurely. Either max or timeout will stop the collecting. Timeout must be minimum 10ms.

This function will by default spawn a new task and subscribe for BACnet notification messages and afterwards unsubscribe. This behaviour can be disabled through no_subscribe opts.

The following options are available, in addition the Client.send/4 options:

  • apdu_destination: term() - Optional. Overrides the APDU destination address.
  • high_limit: pos_integer() - Optional. The maximum BACnet device ID for the Who-Is query.
  • low_limit: pos_integer() - Optional. The minimum BACnet device ID for the Who-Is query.
  • max: pos_integer() - Optional. The maximum amount of IAm responses to collect.
  • no_subscribe: boolean() - Optional. Whether to spawn a new task.

write_property(server, destination, object, property, value, opts \\ [])

Write to a single property from a remote BACnet object.

Either the actual value of the property can be given and then the value will be automatically encoded through BACnet.Protocol.ObjectsUtility. Or an Encoding struct (or list of) can be given, which will be used directly without validation.

The following options are available: