Packages

BACstack is a low-level Elixir implementation for the ASHRAE standard 135, BACnet - Building Automation and Controller network. In its current published state, v0.0.x, it can (only) act as a BACnet client for other BACnet devices.

Current section

Files

Jump to
bacstack lib bacnet protocol constants object.ex
Raw

lib/bacnet/protocol/constants/object.ex

defmodule BACnet.Protocol.Constants.Object do
  @moduledoc false
  # Can be extended through env :bacstack, :additional_object_type

  use ConstEnum, exception: BACnet.Protocol.Constants.ConstantError

  # The name usually matches the clause 21 definition and `-` translated to `_`.
  # Some exceptions were done for better readability (inserting `_`).
  #
  # When new object types are added that don't match with the clause 21,
  # with - translated to _, these need to be added to the `clause21_translation` map below.
  defconst(:object_type, :analog_input, 0x00)
  defconst(:object_type, :analog_output, 0x01)
  defconst(:object_type, :analog_value, 0x02)
  defconst(:object_type, :binary_input, 0x03)
  defconst(:object_type, :binary_output, 0x04)
  defconst(:object_type, :binary_value, 0x05)
  defconst(:object_type, :calendar, 0x06)
  defconst(:object_type, :command, 0x07)
  defconst(:object_type, :device, 0x08)
  defconst(:object_type, :event_enrollment, 0x09)
  defconst(:object_type, :file, 0x0A)
  defconst(:object_type, :group, 0x0B)
  defconst(:object_type, :loop, 0x0C)
  defconst(:object_type, :multi_state_input, 0x0D)
  defconst(:object_type, :multi_state_output, 0x0E)
  defconst(:object_type, :notification_class, 0x0F)
  defconst(:object_type, :program, 0x10)
  defconst(:object_type, :schedule, 0x11)
  defconst(:object_type, :averaging, 0x12)
  defconst(:object_type, :multi_state_value, 0x13)
  defconst(:object_type, :trend_log, 0x14)
  defconst(:object_type, :life_safety_point, 0x15)
  defconst(:object_type, :life_safety_zone, 0x16)
  defconst(:object_type, :accumulator, 0x17)
  defconst(:object_type, :pulse_converter, 0x18)
  defconst(:object_type, :event_log, 0x19)
  defconst(:object_type, :global_group, 0x1A)
  defconst(:object_type, :trend_log_multiple, 0x1B)
  defconst(:object_type, :load_control, 0x1C)
  defconst(:object_type, :structured_view, 0x1D)
  defconst(:object_type, :access_door, 0x1E)
  defconst(:object_type, :timer, 0x1F)
  defconst(:object_type, :access_credential, 0x20)
  defconst(:object_type, :access_point, 0x21)
  defconst(:object_type, :access_rights, 0x22)
  defconst(:object_type, :access_user, 0x23)
  defconst(:object_type, :access_zone, 0x24)
  defconst(:object_type, :credential_data_input, 0x25)
  defconst(:object_type, :network_security, 0x26)
  defconst(:object_type, :bitstring_value, 0x27)
  defconst(:object_type, :character_string_value, 0x28)
  defconst(:object_type, :date_pattern_value, 0x29)
  defconst(:object_type, :date_value, 0x2A)
  defconst(:object_type, :datetime_pattern_value, 0x2B)
  defconst(:object_type, :datetime_value, 0x2C)
  defconst(:object_type, :integer_value, 0x2D)
  defconst(:object_type, :large_analog_value, 0x2E)
  defconst(:object_type, :octet_string_value, 0x2F)
  defconst(:object_type, :positive_integer_value, 0x30)
  defconst(:object_type, :time_pattern_value, 0x31)
  defconst(:object_type, :time_value, 0x32)
  defconst(:object_type, :notification_forwarder, 0x33)
  defconst(:object_type, :alert_enrollment, 0x34)
  defconst(:object_type, :channel, 0x35)
  defconst(:object_type, :lighting_output, 0x36)
  defconst(:object_type, :binary_lighting_output, 0x37)
  defconst(:object_type, :network_port, 0x38)
  defconst(:object_type, :elevator_group, 0x39)
  defconst(:object_type, :escalator, 0x3A)
  defconst(:object_type, :lift, 0x3B)

  defconst_extend_env(
    :object_type,
    :bacstack,
    :additional_object_type,
    &(&1 != nil and is_integer(&2) and &2 >= 0)
  )

  @doc """
  Any object type names that dont match the Clause 21 definition,
  are to be listed here.

  Note that if the difference is only `-` translated to `_`, they are not listed here.
  """
  @spec clause21_translation() :: %{optional(atom()) => String.t()}
  def clause21_translation() do
    # Listed in order of the object type number
    %{
      character_string_value: "characterstring-value",
      date_pattern_value: "datepattern-value",
      datetime_pattern_value: "datetimepattern-value",
      octet_string_value: "octetstring-value",
      time_pattern_value: "timepattern-value"
    }
  end

  @doc """
  Translates a object type to the Clause 21 string.

  If the input is a number, it will be resolved to the atom and then translated.
  If the number can't be resolved, `nil` will be returned.
  """
  @spec translate_to_clause21(atom() | non_neg_integer()) ::
          String.t() | nil
  def translate_to_clause21(object_type)

  def translate_to_clause21(object_type)
      when is_atom(object_type) and object_type not in [true, false, nil] do
    case Map.fetch(clause21_translation(), object_type) do
      {:ok, str} ->
        str

      :error ->
        object_type
        |> Atom.to_string()
        |> String.replace("_", "-")
    end
  end

  def translate_to_clause21(object_type)
      when is_integer(object_type) and object_type >= 0 do
    case by_value(:object_type, object_type) do
      {:ok, id} -> translate_to_clause21(id)
      :error -> nil
    end
  end
end