BACnet (bacstack v0.0.1)

View Source

BACstack is a low-level Elixir implementation for the ASHRAE standard 135, BACnet - Building Automation and Controller network. This implementation supports ASHRAE 135-xxxx<!-- TODO --> and BACnet/IPv4. Support for other transport layers (such as BACnet/SC, BACnet/MSTP) can be straight forward added on top of it.

As this is a low-level implementation, users of this library are required to do the heavy-lifting of the BACnet stack, such as automatically replying to Who-Is services, applying hard application timeout constraints, synchronizing time, etc.

Installation

While v0.0.1 has been released to Hex to provide a minimal BACnet client featureset, it is recommended to install it through GitHub (you can also pin it to a commit hash). New changes, bugfixes and features are not released to Hex until bacstack has reached a more stable and complete featureset.

The package can be installed by adding bacstack to your list of dependencies in mix.exs:

def deps do
  [
    {:bacstack, github: "bacnet-ex/bacstack"}
  ]
end

The documentation can be found at https://bacnet-ex.github.io/bacstack/.

Getting Started

First start a transport (specify callback), start Segmentator, start SegmentsStore and finally start Client, specifying all three previously started processes.

In the following example, we will use registered processes for ease of use. You can also use :via tuple for an alternative to locally registered names (or simply no registration at all). However since we need to "link" the processes, names make it a lot easier. Though the transport module does allow to use a function or MFA callback instead, so "linking" can be implementation by the user as desired.

We use the IPv4 transport and it will automatically determine a network interface to use. You can also specify the network interface directly, if you want to or the wrong interface is selected. Refer to the documentation for more information.

alias BACnet.Stack.Client
alias BACnet.Stack.Segmentator
alias BACnet.Stack.SegmentsStore
alias BACnet.Stack.Transport.IPv4Transport

IPv4Transport.open(Client, name: IPv4Transport)
Segmentator.start_link(name: Segmentator)
SegmentsStore.start_link(name: SegmentsStore)
Client.start_link(
  name: Client,
  segmentator: Segmentator,
  segments_store: SegmentsStore,
  transport: IPv4Transport
)

In a more production environment these processes should be started under a supervisor and also where possible, you should specify the supervisor for the modules to use (i.e. IPv4 transport).

You can now use the Client module to interact with the BACnet network (i.e. send Who-Is messages). You can also receive BACnet traffic by specifying a notification receiver when starting Client.

See also the ClientHelper module for functions orientated around helping you to send BACnet service requests.

If you need to interact with a remote BACnet network, you may want to register yourself as a Foreign Device. For that case, you can start an additional process ForeignDevice and specify the BBMD. Please note that the Client needs to be started on the correct network interface where the BBMD is reachable.

alias BACnet.Stack.ForeignDevice

ForeignDevice.start_link(bbmd: {bbmd_ip, 0xBAC0}, client: Client)

Character Sets

This library uses the library Codepagex to implement character set conversion. By default, Codepagex only generates conversion code for fast conversion algorithms to and from UTF-8 (such as ISO-8859-1).

If you need to use other character sets, such as CP932 (JIS-X-0208) or CP850 (DBCS), you will have to configure Codepagex accordingly. Refer to their documentation for the necessary steps.

Time Zone

When using this library, there are some functions that take or return DateTime structs. Inside the library, there is a default timezone that will be used, when you don't particularily set/override the timezone. The default timezone is an application environment configuration that is used across the library and can be used to set a different timezone than the default Etc/UTC. Where applicable, you can still override the default timezone locally (i.e. BACnetDateTime.to_datetime/3).

config :bacstack, :default_timezone, "Etc/UTC"

Please note, for timezones other than UTC, you still need to set a timezone database for Elixir to work with. For example, with the library Tzdata it's just a matter of configuration:

config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase

Extensibility

Some parts of this library can be extended at compile-time to provide additional value. Let's explore them.

Property Identifiers

The known property identifiers can be extended at compile-time by the library user. This can be done to add new properties or even proprietary property identifiers.

Simply configure the key :additional_property_identifiers of the application :bacstack to be a keyword or map and use the name as key and the integer protocol value as value.

For example:

config :bacstack, :additional_property_identifiers, loop_enable: 523, loop_mode: 524

Object Properties

For each object the available properties can be extended at compile-time by the library user. This may be done to add new properties or even proprietary properties to an object type.

Configure the key :objects_additional_properties of the application :bacstack and supply proper quoted expression (Elixir AST) to a keyword list or map, with the object type as key.

Please note that the properties must be known (either already supplied by the library or added by the library user using additional_property_identifiers).

For example:

config :bacstack, :objects_additional_properties,
  loop:
    (quote do
        field(:loop_enable, boolean(), encode_as: :enumerated)

        field(:loop_mode, :bacnet_loop | :plc_loop,
          bac_type: {:in_list, [:bacnet_loop, :plc_loop]},
          annotation: [
            encoder: &{:enumerated, if(&1 == :plc_loop, do: 1, else: 0)},
            decoder: &if(&1.value == 1, do: :plc_loop, else: :bacnet_loop)
          ]
        )
      end)

See the documentation to ObjectsMacro.bac_object/2 for more information and the allowed/expected expression.

Object Types

The known object types can be extended at compile-time by the library. That does not automatically implement the new object types, however makes it possible to do so. It can be done to provide forward compatibility for new revisions at early stages.

Simply configure the key :additional_object_type of the application :bacstack to be a keyword or map and use the name as key and the integer protocol value as value.

You'll also want to extend object types supported through :additional_object_types_supported. The struct BACnet.Protocol.Device.ObjectTypesSupported is automatically adjusted and up-to-date at compile time.

Services

Both confirmed and unconfirmed service choices known can be extended at compile-time by the library user. That does not mean though, that the services are implemented. It can be done though, to provide forward compatibility for new revisions at early stages.

Simply configure the key :additional_confirmed_service_choice and/or :additional_unconfirmed_service_choice of the application :bacstack to be a keyword or map and use the name as key and the integer protocol value as value.

You'll also want to extend services supported through :additional_services_supported. The struct BACnet.Protocol.Device.ServicesSupported is automatically adjusted and up-to-date at compile time.

Errors

Abort Reason, Error Class, Error Code and Reject Reason can be extended at compile-time by the library user. This allows to provide forward compatibility for new revisions at early stages.

Simply configure the key :additional_abort_reason, :additional_error_class, :additional_error_code and/or :additional_reject_reason of the application :bacstack to be a keyword or map and use the name as key and the integer protocol value as value.

Status of BACnet objects implementation

Object TypeRepresentationExecuteIntrinsic Reporting
Accumulator135-2012--
Analog Input135-2012--
Analog Output135-2012--
Analog Value135-2012--
Averaging135-2012--
Binary Input135-2012--
Binary Output135-2012--
Binary Value135-2012--
Calendar135-2012--
Command135-2012--
Device135-2012--
Event Enrollment135-2012--
File135-2012--
Group135-2012--
Life Safety PointN/A--
Life Safety ZoneN/A--
Loop135-2012--
Multistate Input135-2012--
Multistate Output135-2012--
Multistate Value135-2012--
Notification Class135-2012--
Program135-2012--
Pulse Converter135-2012--
Schedule135-2012--
Event Log135-2012--
Trend Log135-2012--
Trend Log Multiple135-2012--
Load Control---
Access PointN/A--
Access ZoneN/A--
Access UserN/A--
Access RightsN/A--
Access CredentialN/A--
Credential Data InputN/A--
Structured View135-2012--
Character String Value135-2012--
DateTime Value135-2012--
Large Analog Value135-2012--
Bitstring135-2012--
Octet String Value135-2012--
Time Value135-2012--
Integer Value135-2012--
Positive Integer Value135-2012--
Date Value135-2012--
DateTime Pattern Value135-2012--
Time Pattern Value135-2012--
Date Pattern Value135-2012--
Network SecurityN/A--
Global Group---
Notification Forwarder---
Alert Enrollment---
Channel---
Lighting Output---

Status of BACnet services implementation

Service NameReceiveSendACK (Pos/Neg)
Confirmed COVxxSimple/Error
Unconfirmed COVxx
Confirmed EventxxSimple/Error
Unconfirmed Eventxx
Acknowledge AlarmxxSimple/Error
Get Alarm Summaryxxx/Error
Get Enrollment Summaryxxx/Error
Get Event Informationxxx/Error
Life Safety OperationxxSimple/Error
Subscribe COVxxSimple/Error
Subscribe COV PropertyxxSimple/Error
Atomic Read Filexxx/Error
Atomic Write Filexxx/Error
Add List ElementxxSimple/x
Remove List ElementxxSimple/x
Create Objectxxx/x
Delete ObjectxxSimple/x
Read Propertyxxx/Error
Read Property Multiplexxx/Error
Read Rangexxx/Error
Write PropertyxxSimple/Error
Write Property MultiplexxSimple/x
Write Groupxx
Device Communication ControlxxSimple/Error
Confirmed Private Transferxxx/x
Unconfirmed Private Transferxx
Reinitialize DevicexxSimple/Error
Confirmed Text MessagexxSimple/Error
Unconfirmed Text Messagexx
Time Synchronizationxx
UTC Time Synchronizationxx
Who-Hasxx
I-Havexx
Who-Isxx
I-Amxx
VT OpenN/AN/A
VT CloseN/AN/A
VT DataN/AN/A

Status of Event Algorithms Implementation

Event AlgorithmImplemented
Buffer Ready135-2012
Change Of Bitstring135-2012
Change Of Character String135-2012
Change Of Life Safety135-2012
Change Of State135-2012
Change Of Status Flags135-2012
Change Of Value135-2012
Command Failure135-2012
Complex Event TypeN/A
Double Out Of Range135-2012
ExtendedN/A
Floating Limit135-2012
Out Of Range135-2012
Signed Out Of Range135-2012
Unsigned Out Of Range135-2012
Unsigned Range135-2012

Status of Fault Algorithms Implementation

Fault AlgorithmImplemented
Fault Character String135-2012
Fault ExtendedN/A
Fault Life Safety135-2012
Fault State135-2012
Fault Status Flags135-2012