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 services get_event_information.ex
Raw

lib/bacnet/protocol/services/get_event_information.ex

defmodule BACnet.Protocol.Services.GetEventInformation do
@moduledoc """
This module represents the BACnet Get Event Information service.
The Get Event Information service is used to get a list of active event states from a device.
Active event states refers to all abnormal events of event-initiating objects.
Service Description (ASHRAE 135):
> The GetEventInformation service is used by a client BACnet-user to obtain a summary of all "active event states". The term
> "active event states" refers to all event-initiating objects that
> (a) have an Event_State property whose value is not equal to NORMAL, or
> (b) have an Acked_Transitions property, which has at least one of the bits (TO_OFFNORMAL, TO_FAULT, TO_NORMAL) set to FALSE.
> This service is intended to be implemented in all devices that generate event notifications.
"""
alias BACnet.Protocol
alias BACnet.Protocol.ApplicationTags
alias BACnet.Protocol.Constants
import Protocol.Utility, only: [pattern_extract_tags: 4]
require Constants
@behaviour Protocol.Services.Behaviour
# TODO: Docs
# TODO: Add Service Procedure to docs
@type t :: %__MODULE__{
last_received_object_identifier: Protocol.ObjectIdentifier.t() | nil
}
@fields [
:last_received_object_identifier
]
@enforce_keys @fields
defstruct @fields
@service_name Constants.macro_assert_name(
:confirmed_service_choice,
:get_event_information
)
@doc """
Get the service name atom.
"""
@spec get_name() :: atom()
def get_name(), do: @service_name
@doc """
Whether the service is of type confirmed or unconfirmed.
"""
@spec is_confirmed() :: true
def is_confirmed(), do: true
@doc """
Converts the given Confirmed Service Request into a Get Event Information Service.
"""
@spec from_apdu(Protocol.APDU.ConfirmedServiceRequest.t()) ::
{:ok, t()} | {:error, term()}
def from_apdu(
%Protocol.APDU.ConfirmedServiceRequest{
service: @service_name
} = request
) do
with {:ok, last_received_object_identifier, _rest} <-
pattern_extract_tags(
request.parameters,
{:tagged, {0, _t, _l}},
:object_identifier,
true
) do
event = %__MODULE__{
last_received_object_identifier: last_received_object_identifier
}
{:ok, event}
else
{:error, :missing_pattern} -> {:error, :invalid_request_parameters}
{:error, _err} = err -> err
end
end
def from_apdu(%Protocol.APDU.ConfirmedServiceRequest{} = _request) do
{:error, :invalid_request}
end
@doc """
Get the Confirmed Service request for the given Get Event Information Service.
See the `BACnet.Protocol.Services.Protocol` function documentation for more information.
"""
@spec to_apdu(t(), Keyword.t()) ::
{:ok, Protocol.APDU.ConfirmedServiceRequest.t()}
| {:error, term()}
def to_apdu(%__MODULE__{} = service, request_data) do
with {:ok, parameters} <-
(case service.last_received_object_identifier do
nil ->
{:ok, []}
_term ->
with {:ok, bytes, _header} <-
ApplicationTags.encode_value(
{:object_identifier, service.last_received_object_identifier}
) do
{:ok, [{:tagged, {0, bytes, byte_size(bytes)}}]}
end
end) do
req = %Protocol.APDU.ConfirmedServiceRequest{
segmented_response_accepted: request_data[:segmented_response_accepted] || true,
max_segments: request_data[:max_segments] || :more_than_64,
max_apdu:
request_data[:max_apdu] ||
Constants.macro_by_name(:max_apdu_length_accepted_value, :octets_1476),
invoke_id: request_data[:invoke_id] || 0,
sequence_number: nil,
proposed_window_size: nil,
service: @service_name,
parameters: parameters
}
{:ok, req}
end
end
defimpl Protocol.Services.Protocol do
alias BACnet.Protocol
alias BACnet.Protocol.Constants
require Constants
@spec get_name(@for.t()) :: atom()
def get_name(%@for{} = _service), do: @for.get_name()
@spec is_confirmed(@for.t()) :: boolean()
def is_confirmed(%@for{} = _service), do: @for.is_confirmed()
@spec to_apdu(@for.t(), Keyword.t()) ::
{:ok, Protocol.APDU.ConfirmedServiceRequest.t()}
| {:error, term()}
def to_apdu(%@for{} = service, request_data), do: @for.to_apdu(service, request_data)
end
end