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 notification_parameters.ex
Raw

lib/bacnet/protocol/notification_parameters.ex

defmodule BACnet.Protocol.NotificationParameters do
@moduledoc """
BACnet has various different types of notification parameters.
Each of them is represented by a different module.
The event algorithm `AccessEvent` is not supported.
Consult the module `BACnet.Protocol.EventAlgorithms` for
details about each event's algorithms.
Consult the module `BACnet.Protocol.EventParameters` for
details about each event's parameters.
"""
alias BACnet.Protocol.ApplicationTags
alias BACnet.Protocol.Constants
alias BACnet.Protocol.DeviceObjectPropertyRef
alias BACnet.Protocol.PropertyState
alias BACnet.Protocol.StatusFlags
alias BACnet.Protocol.Utility
import Utility, only: [pattern_extract_tags: 4]
require Constants
# TODO: Docs
# TODO: Throw argument error in encode if not valid
@typedoc """
Possible BACnet notification parameters.
"""
@type notification_parameter ::
__MODULE__.ChangeOfBitstring.t()
| __MODULE__.ChangeOfState.t()
| __MODULE__.ChangeOfValue.t()
| __MODULE__.CommandFailure.t()
| __MODULE__.FloatingLimit.t()
| __MODULE__.OutOfRange.t()
| __MODULE__.ComplexEventType.t()
| __MODULE__.ChangeOfLifeSafety.t()
| __MODULE__.Extended.t()
| __MODULE__.BufferReady.t()
| __MODULE__.UnsignedRange.t()
| __MODULE__.DoubleOutOfRange.t()
| __MODULE__.SignedOutOfRange.t()
| __MODULE__.UnsignedOutOfRange.t()
| __MODULE__.ChangeOfCharacterString.t()
| __MODULE__.ChangeOfStatusFlags.t()
| __MODULE__.ChangeOfReliability.t()
defmodule ChangeOfBitstring do
@moduledoc """
Represents the BACnet event algorithm `ChangeOfBitstring` notification parameters.
The ChangeOfBitstring event algorithm detects whether the monitored value of type BIT STRING equals a value
that is listed as an alarm value, after applying a bitmask.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.1.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:referenced_bitstring, tuple(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
end
@doc false
def get_tag_number(), do: 0
end
defmodule ChangeOfState do
@moduledoc """
Represents the BACnet event algorithm `ChangeOfState` notification parameters.
The ChangeOfState event algorithm detects whether the monitored value equals a value that is listed as an alarm
value. The monitored value may be of any discrete or enumerated datatype, including Boolean.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.2.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:new_state, PropertyState.t(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
end
@doc false
def get_tag_number(), do: 1
end
defmodule ChangeOfValue do
@moduledoc """
Represents the BACnet event algorithm `ChangeOfValue` notification parameters.
The ChangeOfValue event algorithm, for monitored values of datatype REAL, detects whether the absolute value of
the monitored value changes by an amount equal to or greater than a positive REAL increment.
The ChangeOfValue event algorithm, for monitored values of datatype BIT STRING, detects whether the monitored
value changes in any of the bits specified by a bitmask.
For detection of change, the value of the monitored value when a transition to NORMAL is indicated shall be used in
evaluation of the conditions until the next transition to NORMAL is indicated. The initialization of the value used in
evaluation before the first transition to NORMAL is indicated is a local matter.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.3.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:changed_bits, tuple(), required: false)
field(:changed_value, float(), required: false)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
end
@doc false
def get_tag_number(), do: 2
end
defmodule CommandFailure do
@moduledoc """
Represents the BACnet event algorithm `CommandFailure` notification parameters.
The CommandFailure event algorithm detects whether the monitored value and the feedback value disagree for a time
period. It may be used, for example, to verify that a process change has occurred after writing a property.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.4.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:command_value, BACnet.Protocol.ApplicationTags.Encoding.t(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
field(:feedback_value, BACnet.Protocol.ApplicationTags.Encoding.t(), required: true)
end
@doc false
def get_tag_number(), do: 3
end
defmodule FloatingLimit do
@moduledoc """
Represents the BACnet event algorithm `FloatingLimit` notification parameters.
The FloatingLimit event algorithm detects whether the monitored value exceeds a range defined by a setpoint, a high
difference limit, a low difference limit and a deadband.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.5.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:reference_value, float(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
field(:setpoint_value, float(), required: true)
field(:error_limit, float(), required: true)
end
@doc false
def get_tag_number(), do: 4
end
defmodule OutOfRange do
@moduledoc """
Represents the BACnet event algorithm `OutOfRange` notification parameters.
The OutOfRange event algorithm detects whether the monitored value exceeds a range defined by a high limit and a
low limit. Each of these limits may be enabled or disabled. If disabled, the normal range has no higher limit or no lower limit.
In order to reduce jitter of the resulting notification state, a deadband is applied when the value is in the process of returning to the
normal range.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.6.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:exceeding_value, float(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
field(:deadband, float(), required: true)
field(:exceeded_limit, float(), required: true)
end
@doc false
def get_tag_number(), do: 5
end
defmodule ComplexEventType do
@moduledoc """
Represents the BACnet event algorithm `ComplexEventType` notification parameters.
The `ComplexEventType` algorithm is introduced to allow the addition of proprietary event algorithms
whose notification parameters are not necessarily network-visible.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:property_values, [BACnet.Protocol.PropertyValue.t()], required: true)
end
@doc false
def get_tag_number(), do: 6
end
defmodule ChangeOfLifeSafety do
@moduledoc """
Represents the BACnet event algorithm `ChangeOfLifeSafety` notification parameters.
The ChangeOfLifeSafety event algorithm detects whether the monitored value equals a value that is listed as an
alarm value or life safety alarm value. Event state transitions are also indicated if the value of the mode parameter changed
since the last transition indicated. In this case, any time delays are overridden and the transition is indicated immediately.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.8.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:new_state, BACnet.Protocol.Constants.life_safety_state(), required: true)
field(:new_mode, BACnet.Protocol.Constants.life_safety_mode(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
field(:operation_expected, BACnet.Protocol.Constants.life_safety_operation(),
required: true
)
end
@doc false
def get_tag_number(), do: 8
end
defmodule Extended do
@moduledoc """
Represents the BACnet event algorithm `Extended` notification parameters.
The Extended event algorithm detects notification conditions based on a proprietary event algorithm. The proprietary notification
algorithm uses parameters and conditions defined by the vendor. The algorithm is identified by a vendor-specific notification type
that is in the scope of the vendor's vendor identification code. The algorithm may, at the vendor's discretion, indicate a new
notification state, a transition to the same notification state, or no transition to the Event-State-Detection. The indicated new notification states
may be NORMAL, and any OffNormal notification state. FAULT notification state may not be indicated by this algorithm. For the
purpose of proprietary evaluation of unreliability conditions that may result in FAULT notification state, a FAULT_EXTENDED
fault algorithm shall be used.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.10.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:vendor_id, BACnet.Protocol.ApplicationTags.unsigned16(), required: true)
field(:extended_notification_type, non_neg_integer(), required: true)
field(:parameters, [BACnet.Protocol.ApplicationTags.Encoding.t()], required: true)
end
@doc false
def get_tag_number(), do: 9
end
defmodule BufferReady do
@moduledoc """
Represents the BACnet event algorithm `BufferReady` notification parameters.
The BufferReady event algorithm detects whether a defined number of records have been added to a log buffer since
start of operation or the previous notification, whichever is most recent.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.7.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:buffer_property, DeviceObjectPropertyRef.t(), required: true)
field(:previous_notification, BACnet.Protocol.ApplicationTags.unsigned32(), required: true)
field(:current_notification, BACnet.Protocol.ApplicationTags.unsigned32(), required: true)
end
@doc false
def get_tag_number(), do: 10
end
defmodule UnsignedRange do
@moduledoc """
Represents the BACnet event algorithm `UnsignedRange` notification parameters.
The UnsignedRange event algorithm detects whether the monitored value exceeds a range defined by a high limit and
a low limit.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.9.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:exceeding_value, non_neg_integer(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
field(:exceeded_limit, non_neg_integer(), required: true)
end
@doc false
def get_tag_number(), do: 11
end
# AccessEvent (13, 13.3.12) not implemented
defmodule DoubleOutOfRange do
@moduledoc """
Represents the BACnet event algorithm `DoubleOutOfRange` notification parameters.
The DoubleOutOfRange event algorithm detects whether the monitored value exceeds a range defined by a high
limit and a low limit. Each of these limits may be enabled or disabled. If disabled, the normal range has no lower limit or no
higher limit respectively. In order to reduce jitter of the resulting notification state, a deadband is applied when the value is in the
process of returning to the normal range.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.13.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:exceeding_value, float(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
field(:deadband, float(), required: true)
field(:exceeded_limit, float(), required: true)
end
@doc false
def get_tag_number(), do: 14
end
defmodule SignedOutOfRange do
@moduledoc """
Represents the BACnet event algorithm `SignedOutOfRange` notification parameters.
The SignedOutOfRange event algorithm detects whether the monitored value exceeds a range defined by a high
limit and a low limit. Each of these limits may be enabled or disabled. If disabled, the normal range has no lower limit or no
higher limit respectively. In order to reduce jitter of the resulting notification state, a deadband is applied when the value is in the
process of returning to the normal range.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.14.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:exceeding_value, integer(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
field(:deadband, non_neg_integer(), required: true)
field(:exceeded_limit, integer(), required: true)
end
@doc false
def get_tag_number(), do: 15
end
defmodule UnsignedOutOfRange do
@moduledoc """
Represents the BACnet event algorithm `UnsignedOutOfRange` notification parameters.
The UnsignedOutOfRange event algorithm detects whether the monitored value exceeds a range defined by a high
limit and a low limit. Each of these limits may be enabled or disabled. If disabled, the normal range has no lower limit or no
higher limit respectively. In order to reduce jitter of the resulting notification state, a deadband is applied when the value is in the
process of returning to the normal range.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.15.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:exceeding_value, non_neg_integer(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
field(:deadband, non_neg_integer(), required: true)
field(:exceeded_limit, non_neg_integer(), required: true)
end
@doc false
def get_tag_number(), do: 16
end
defmodule ChangeOfCharacterString do
@moduledoc """
Represents the BACnet event algorithm `ChangeOfCharacterString` notification parameters.
The ChangeOfCharacterString event algorithm detects whether the monitored value matches a character string
that is listed as an alarm value. Alarm values are of type BACnetOptionalCharacterString, and may also be NULL or an
empty character string.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.16.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:changed_value, String.t(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
field(:alarm_value, String.t(), required: true)
end
@doc false
def get_tag_number(), do: 17
end
defmodule ChangeOfStatusFlags do
@moduledoc """
Represents the BACnet event algorithm `ChangeOfStatusFlags` notification parameters.
The ChangeOfStatusFlags event algorithm detects whether a significant flag of the monitored value of type
BACnetStatusFlags has the value TRUE.
For more specific information about the event algorithm, consult ASHRAE 135 13.3.11.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:present_value, BACnet.Protocol.ApplicationTags.Encoding.t())
field(:referenced_flags, BACnet.Protocol.StatusFlags.t(), required: true)
end
@doc false
def get_tag_number(), do: 18
end
defmodule ChangeOfReliability do
@moduledoc """
Represents the BACnet event algorithm `ChangeOfReliability` notification parameters.
For all transitions to, or from, the FAULT state, the corresponding notification notification shall use the Event Type
ChangeOfReliability.
For more specific information about the fault notification event algorithm, consult ASHRAE 135 13.2.5.3.
"""
import BACnet.Macro
@typedoc """
Representative type for the notification parameter.
"""
typedstruct do
field(:reliability, BACnet.Protocol.Constants.reliability(), required: true)
field(:status_flags, BACnet.Protocol.StatusFlags.t(), required: true)
field(:property_values, [BACnet.Protocol.PropertyValue.t()], required: true)
end
@doc false
def get_tag_number(), do: 19
end
# TODO: Docs
@spec parse(ApplicationTags.encoding()) :: {:ok, notification_parameter()} | {:error, term()}
def parse(notification_values_tag)
# 0 = Change Of Bitstring
def parse({:constructed, {0, notification_values, 0}}) do
case notification_values do
[
tagged: {0, bitstring_raw, _length},
tagged: {1, status_raw, _length2}
] ->
with {:ok, {:bitstring, bitstring}} <-
ApplicationTags.unfold_to_type(:bitstring, bitstring_raw),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs) do
notification = %ChangeOfBitstring{
referenced_bitstring: bitstring,
status_flags: status_flags
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 1 = Change Of State
def parse({:constructed, {1, notification_values, 0}}) do
case notification_values do
[
constructed: {0, new_state_raw, _length},
tagged: {1, status_raw, _length2}
] ->
with {:ok, {new_state, _rest}} <-
PropertyState.parse([new_state_raw]),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs) do
notification = %ChangeOfState{
new_state: new_state,
status_flags: status_flags
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 2 = Change Of Value
def parse({:constructed, {2, notification_values, 0}}) do
case notification_values do
[
constructed: {0, new_value_raw, _length},
tagged: {1, status_raw, _length2}
] ->
with {:ok, new_value_bits, new_value_real} <-
(case new_value_raw do
{:tagged, {0, term, _len}} ->
with {:ok, {:bitstring, bits}} <-
ApplicationTags.unfold_to_type(:bitstring, term) do
{:ok, bits, nil}
end
{:tagged, {1, term, _len}} ->
with {:ok, {:real, real}} <- ApplicationTags.unfold_to_type(:real, term) do
{:ok, nil, real}
end
end),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs) do
notification = %ChangeOfValue{
changed_bits: new_value_bits,
changed_value: new_value_real,
status_flags: status_flags
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 3 = Command Failure
def parse({:constructed, {3, notification_values, 0}}) do
case notification_values do
[
constructed: {0, command_value, _length},
tagged: {1, status_raw, _length2},
constructed: {2, feedback_value, _length3}
] ->
with {:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs) do
notification = %CommandFailure{
command_value: ApplicationTags.Encoding.create!(command_value),
status_flags: status_flags,
feedback_value: ApplicationTags.Encoding.create!(feedback_value)
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 4 = Floating Limit
def parse({:constructed, {4, notification_values, 0}}) do
case notification_values do
[
tagged: {0, ref_value_raw, _length},
tagged: {1, status_raw, _length2},
tagged: {2, setpoint_value_raw, _length3},
tagged: {3, error_limit_raw, _length4}
] ->
with {:ok, {:real, ref_value}} <-
ApplicationTags.unfold_to_type(:real, ref_value_raw),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs),
{:ok, {:real, setpoint_value}} <-
ApplicationTags.unfold_to_type(:real, setpoint_value_raw),
{:ok, {:real, error_limit}} <-
ApplicationTags.unfold_to_type(:real, error_limit_raw) do
notification = %FloatingLimit{
reference_value: ref_value,
status_flags: status_flags,
setpoint_value: setpoint_value,
error_limit: error_limit
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 5 = Out Of Range
def parse({:constructed, {5, notification_values, 0}}) do
case notification_values do
[
tagged: {0, exceeding_value_raw, _length},
tagged: {1, status_raw, _length2},
tagged: {2, deadband_raw, _length3},
tagged: {3, exceeded_limit_raw, _length4}
] ->
with {:ok, {:real, exceeding_value}} <-
ApplicationTags.unfold_to_type(:real, exceeding_value_raw),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs),
{:ok, {:real, deadband}} <-
ApplicationTags.unfold_to_type(:real, deadband_raw),
{:ok, {:real, exceeded_limit}} <-
ApplicationTags.unfold_to_type(:real, exceeded_limit_raw) do
notification = %OutOfRange{
exceeding_value: exceeding_value,
status_flags: status_flags,
deadband: deadband,
exceeded_limit: exceeded_limit
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 6 = Complex Event Type
def parse({:constructed, {6, [], 0}}) do
notification = %ComplexEventType{
property_values: []
}
{:ok, notification}
end
# 6 = Complex Event Type
def parse({:constructed, {6, notification_values, 0}}) when is_list(notification_values) do
case BACnet.Protocol.PropertyValue.parse_all(notification_values) do
{:ok, prop_value} ->
notification = %ComplexEventType{
property_values: prop_value
}
{:ok, notification}
{:error, _term} = term ->
term
end
end
# 8 = Change Of Life Safety
def parse({:constructed, {8, notification_values, 0}}) do
case notification_values do
[
tagged: {0, life_safety_state_raw, _length},
tagged: {1, life_safety_mode_raw, _length2},
tagged: {2, status_raw, _length3},
tagged: {3, life_safety_operation_raw, _length4}
] ->
with {:ok, {:enumerated, life_safety_state}} <-
ApplicationTags.unfold_to_type(:enumerated, life_safety_state_raw),
{:ok, ls_state_c} <-
Constants.by_value_with_reason(
:life_safety_state,
life_safety_state,
{:unknown_life_safety_state, life_safety_state}
),
{:ok, {:enumerated, life_safety_mode}} <-
ApplicationTags.unfold_to_type(:enumerated, life_safety_mode_raw),
{:ok, ls_mode_c} <-
Constants.by_value_with_reason(
:life_safety_mode,
life_safety_mode,
{:unknown_life_safety_mode, life_safety_mode}
),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs),
{:ok, {:enumerated, life_safety_operation}} <-
ApplicationTags.unfold_to_type(:enumerated, life_safety_operation_raw),
{:ok, ls_operation_c} <-
Constants.by_value_with_reason(
:life_safety_operation,
life_safety_operation,
{:unknown_life_safety_operation, life_safety_operation}
) do
notification = %ChangeOfLifeSafety{
new_state: ls_state_c,
new_mode: ls_mode_c,
status_flags: status_flags,
operation_expected: ls_operation_c
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 9 = Extended
def parse({:constructed, {9, notification_values, 0}}) do
case notification_values do
[
tagged: {0, vendor_id_raw, _length},
tagged: {1, ext_notification_raw, _length2},
constructed: {2, parameters, _length3}
] ->
with {:ok, {:unsigned_integer, vendor_id}} <-
ApplicationTags.unfold_to_type(:unsigned_integer, vendor_id_raw),
:ok <-
if(ApplicationTags.valid_int?(vendor_id, 16),
do: :ok,
else: {:error, :invalid_vendor_id_value}
),
{:ok, {:unsigned_integer, ext_notification}} <-
ApplicationTags.unfold_to_type(:unsigned_integer, ext_notification_raw) do
notification = %Extended{
vendor_id: vendor_id,
extended_notification_type: ext_notification,
parameters: Enum.map(List.wrap(parameters), &ApplicationTags.Encoding.create!/1)
}
{:ok, notification}
else
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 10 = Buffer Ready
def parse({:constructed, {10, notification_values, 0}}) do
case notification_values do
[
constructed: {0, devobjref_raw, _length},
tagged: {1, prev_notification_raw, _length2},
tagged: {2, current_notification_raw, _length3}
] ->
with {:ok, {dev_obj_ref, _rest}} <- DeviceObjectPropertyRef.parse(devobjref_raw),
{:ok, {:unsigned_integer, prev_notification}} <-
ApplicationTags.unfold_to_type(:unsigned_integer, prev_notification_raw),
:ok <-
if(ApplicationTags.valid_int?(prev_notification, 32),
do: :ok,
else: {:error, :invalid_previous_notification_value}
),
{:ok, {:unsigned_integer, current_notification}} <-
ApplicationTags.unfold_to_type(:unsigned_integer, current_notification_raw),
:ok <-
if(ApplicationTags.valid_int?(current_notification, 32),
do: :ok,
else: {:error, :invalid_current_notification_value}
) do
notification = %BufferReady{
buffer_property: dev_obj_ref,
previous_notification: prev_notification,
current_notification: current_notification
}
{:ok, notification}
else
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 11 = Unsigned Range
def parse({:constructed, {11, notification_values, 0}}) do
case notification_values do
[
tagged: {0, exceeding_value_raw, _length},
tagged: {1, status_raw, _length2},
tagged: {2, exceeded_limit_raw, _length4}
] ->
with {:ok, {:unsigned_integer, exceeding_value}} <-
ApplicationTags.unfold_to_type(:unsigned_integer, exceeding_value_raw),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs),
{:ok, {:unsigned_integer, exceeded_limit}} <-
ApplicationTags.unfold_to_type(:unsigned_integer, exceeded_limit_raw) do
notification = %UnsignedRange{
exceeding_value: exceeding_value,
status_flags: status_flags,
exceeded_limit: exceeded_limit
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 13 = Access Event
def parse({:constructed, {13, _notification_values, 0}}) do
{:error, :not_supported_notification_type}
end
# 14 = Double Out Of Range
def parse({:constructed, {14, notification_values, 0}}) do
case notification_values do
[
tagged: {0, exceeding_value_raw, _length},
tagged: {1, status_raw, _length2},
tagged: {2, deadband_raw, _length3},
tagged: {3, exceeded_limit_raw, _length4}
] ->
with {:ok, {:double, exceeding_value}} <-
ApplicationTags.unfold_to_type(:double, exceeding_value_raw),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs),
{:ok, {:double, deadband}} <-
ApplicationTags.unfold_to_type(:double, deadband_raw),
{:ok, {:double, exceeded_limit}} <-
ApplicationTags.unfold_to_type(:double, exceeded_limit_raw) do
notification = %DoubleOutOfRange{
exceeding_value: exceeding_value,
status_flags: status_flags,
deadband: deadband,
exceeded_limit: exceeded_limit
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 15 = Signed Out Of Range
def parse({:constructed, {15, notification_values, 0}}) do
case notification_values do
[
tagged: {0, exceeding_value_raw, _length},
tagged: {1, status_raw, _length2},
tagged: {2, deadband_raw, _length3},
tagged: {3, exceeded_limit_raw, _length4}
] ->
with {:ok, {:signed_integer, exceeding_value}} <-
ApplicationTags.unfold_to_type(:signed_integer, exceeding_value_raw),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs),
{:ok, {:unsigned_integer, deadband}} <-
ApplicationTags.unfold_to_type(:unsigned_integer, deadband_raw),
{:ok, {:signed_integer, exceeded_limit}} <-
ApplicationTags.unfold_to_type(:signed_integer, exceeded_limit_raw) do
notification = %SignedOutOfRange{
exceeding_value: exceeding_value,
status_flags: status_flags,
deadband: deadband,
exceeded_limit: exceeded_limit
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 16 = Unsigned Out Of Range
def parse({:constructed, {16, notification_values, 0}}) do
case notification_values do
[
tagged: {0, exceeding_value_raw, _length},
tagged: {1, status_raw, _length2},
tagged: {2, deadband_raw, _length3},
tagged: {3, exceeded_limit_raw, _length4}
] ->
with {:ok, {:unsigned_integer, exceeding_value}} <-
ApplicationTags.unfold_to_type(:unsigned_integer, exceeding_value_raw),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs),
{:ok, {:unsigned_integer, deadband}} <-
ApplicationTags.unfold_to_type(:unsigned_integer, deadband_raw),
{:ok, {:unsigned_integer, exceeded_limit}} <-
ApplicationTags.unfold_to_type(:unsigned_integer, exceeded_limit_raw) do
notification = %UnsignedOutOfRange{
exceeding_value: exceeding_value,
status_flags: status_flags,
deadband: deadband,
exceeded_limit: exceeded_limit
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 17 = Change Of Character String
def parse({:constructed, {17, notification_values, 0}}) do
case notification_values do
[
tagged: {0, changed_value_raw, _length},
tagged: {1, status_raw, _length2},
tagged: {2, alarm_value_raw, _length3}
] ->
with {:ok, {:character_string, changed_value}} <-
ApplicationTags.unfold_to_type(:character_string, changed_value_raw),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs),
{:ok, {:character_string, alarm_value}} <-
ApplicationTags.unfold_to_type(:character_string, alarm_value_raw) do
notification = %ChangeOfCharacterString{
changed_value: changed_value,
status_flags: status_flags,
alarm_value: alarm_value
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
# 18 = Change Of Status Flags
def parse({:constructed, {18, notification_values, 0}}) do
with {:ok, present_value, rest} <-
(case pattern_extract_tags(notification_values, {:constructed, {0, _v, _l}}, nil, true) do
{:ok, {:constructed, {0, term, _l}}, rest} ->
{:ok, ApplicationTags.Encoding.create!(term), rest}
term ->
term
end),
{:ok, statusflags_bs, _rest} when tuple_size(statusflags_bs) == 4 <-
pattern_extract_tags(rest, {:tagged, {1, _v, _l}}, :bitstring, false),
%StatusFlags{} = ref_flags <- StatusFlags.from_bitstring(statusflags_bs) do
notification = %ChangeOfStatusFlags{
present_value: present_value,
referenced_flags: ref_flags
}
{:ok, notification}
else
{:ok, _value, _rest} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
end
# 19 = Change Of Reliability
def parse({:constructed, {19, notification_values, 0}}) do
case notification_values do
[
tagged: {0, reliability_raw, _length},
tagged: {1, status_raw, _length2},
# TODO: May be not constructed (tagged)
constructed: {2, prop_value_raw, _length3}
] ->
with {:ok, {:enumerated, reliability}} <-
ApplicationTags.unfold_to_type(:enumerated, reliability_raw),
{:ok, reliability_c} <-
Constants.by_value_with_reason(
:reliability,
reliability,
{:unknown_reliability, reliability}
),
{:ok, {:bitstring, statusflags_bs}} when tuple_size(statusflags_bs) == 4 <-
ApplicationTags.unfold_to_type(:bitstring, status_raw),
%StatusFlags{} = status_flags <- StatusFlags.from_bitstring(statusflags_bs),
{:ok, prop_value} <-
if(prop_value_raw == [],
do: {:ok, []},
else: BACnet.Protocol.PropertyValue.parse_all(prop_value_raw)
) do
notification = %ChangeOfReliability{
reliability: reliability_c,
status_flags: status_flags,
property_values: prop_value
}
{:ok, notification}
else
{:ok, _value} -> {:error, :invalid_notification_values}
{:error, _err} = err -> err
end
_term ->
{:error, :invalid_notification_values}
end
end
def parse(_notification_values_tag) do
{:error, :invalid_tag}
end
# TODO: Docs
@spec encode(notification_parameter(), Keyword.t()) ::
{:ok, ApplicationTags.encoding()} | {:error, term()}
def encode(notification_params, opts \\ [])
def encode(%ChangeOfBitstring{} = params, opts) do
with true <- is_tuple(params.referenced_bitstring),
true <- is_struct(params.status_flags, StatusFlags),
{:ok, bits, _header} <-
ApplicationTags.encode_value({:bitstring, params.referenced_bitstring}, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts) do
{:ok,
{:constructed,
{0,
[
tagged: {0, bits, byte_size(bits)},
tagged: {1, flags, byte_size(flags)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%ChangeOfState{} = params, opts) do
with true <- is_struct(params.new_state, PropertyState),
true <- is_struct(params.status_flags, StatusFlags),
{:ok, [state]} <-
PropertyState.encode(params.new_state, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts) do
{:ok,
{:constructed,
{1,
[
constructed: {0, state, 0},
tagged: {1, flags, byte_size(flags)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%ChangeOfValue{} = params, opts) do
with true <- is_tuple(params.changed_bits) or is_nil(params.changed_bits),
true <- is_float(params.changed_value) or is_nil(params.changed_value),
true <- :erlang.xor(is_nil(params.changed_bits), is_nil(params.changed_value)),
true <- is_struct(params.status_flags, StatusFlags),
{:ok, bits} <-
(if params.changed_bits do
with {:ok, bytes, _header} <-
ApplicationTags.encode_value({:bitstring, params.changed_bits}) do
{:ok, {:tagged, {0, bytes, byte_size(bytes)}}}
end
else
{:ok, nil}
end),
{:ok, float} <-
(if params.changed_value do
with {:ok, bytes, _header} <-
ApplicationTags.encode_value({:real, params.changed_value}) do
{:ok, {:tagged, {1, bytes, byte_size(bytes)}}}
end
else
{:ok, nil}
end),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts) do
{:ok,
{:constructed,
{2,
[
constructed: {0, bits || float, 0},
tagged: {1, flags, byte_size(flags)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%CommandFailure{} = params, opts) do
with true <- is_struct(params.command_value, ApplicationTags.Encoding),
true <- is_struct(params.status_flags, StatusFlags),
true <- is_struct(params.feedback_value, ApplicationTags.Encoding),
{:ok, command} <-
ApplicationTags.Encoding.to_encoding(params.command_value),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts),
{:ok, feedback} <-
ApplicationTags.Encoding.to_encoding(params.feedback_value) do
{:ok,
{:constructed,
{3,
[
constructed: {0, command, 0},
tagged: {1, flags, byte_size(flags)},
constructed: {2, feedback, 0}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%FloatingLimit{} = params, opts) do
with true <- is_float(params.reference_value),
true <- is_struct(params.status_flags, StatusFlags),
true <- is_float(params.setpoint_value),
true <- is_float(params.error_limit),
{:ok, refvalue, _header} <-
ApplicationTags.encode_value({:real, params.reference_value}, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts),
{:ok, setvalue, _header} <-
ApplicationTags.encode_value({:real, params.setpoint_value}, opts),
{:ok, errlimit, _header} <-
ApplicationTags.encode_value({:real, params.error_limit}, opts) do
{:ok,
{:constructed,
{4,
[
tagged: {0, refvalue, byte_size(refvalue)},
tagged: {1, flags, byte_size(flags)},
tagged: {2, setvalue, byte_size(setvalue)},
tagged: {3, errlimit, byte_size(errlimit)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%OutOfRange{} = params, opts) do
with true <- is_float(params.exceeding_value),
true <- is_struct(params.status_flags, StatusFlags),
true <- is_float(params.deadband),
true <- is_float(params.exceeded_limit),
{:ok, excvalue, _header} <-
ApplicationTags.encode_value({:real, params.exceeding_value}, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts),
{:ok, deadband, _header} <-
ApplicationTags.encode_value({:real, params.deadband}, opts),
{:ok, exclimit, _header} <-
ApplicationTags.encode_value({:real, params.exceeded_limit}, opts) do
{:ok,
{:constructed,
{5,
[
tagged: {0, excvalue, byte_size(excvalue)},
tagged: {1, flags, byte_size(flags)},
tagged: {2, deadband, byte_size(deadband)},
tagged: {3, exclimit, byte_size(exclimit)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%ComplexEventType{} = params, opts) do
with true <- is_list(params.property_values),
{:ok, propval} <-
BACnet.Protocol.PropertyValue.encode_all(params.property_values, opts) do
{:ok, {:constructed, {6, propval, 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%ChangeOfLifeSafety{} = params, opts) do
with true <- is_struct(params.status_flags, StatusFlags),
{:ok, new_state_c} <-
Constants.by_name_with_reason(
:life_safety_state,
params.new_state,
{:unknown_life_safety_state, params.new_state}
),
{:ok, new_state, _header} <-
ApplicationTags.encode_value({:enumerated, new_state_c}, opts),
{:ok, new_mode_c} <-
Constants.by_name_with_reason(
:life_safety_mode,
params.new_mode,
{:unknown_life_safety_mode, params.new_mode}
),
{:ok, new_mode, _header} <-
ApplicationTags.encode_value({:enumerated, new_mode_c}, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts),
{:ok, lsop_c} <-
Constants.by_name_with_reason(
:life_safety_operation,
params.operation_expected,
{:unknown_life_safety_operation, params.operation_expected}
),
{:ok, lsop, _header} <-
ApplicationTags.encode_value({:enumerated, lsop_c}, opts) do
{:ok,
{:constructed,
{8,
[
tagged: {0, new_state, byte_size(new_state)},
tagged: {1, new_mode, byte_size(new_mode)},
tagged: {2, flags, byte_size(flags)},
tagged: {3, lsop, byte_size(lsop)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%Extended{} = params, opts) do
with true <-
is_integer(params.vendor_id) and params.vendor_id >= 0 and params.vendor_id <= 65_535,
true <-
is_integer(params.extended_notification_type) and
params.extended_notification_type >= 0,
true <-
is_list(params.parameters) and
Enum.all?(params.parameters, &is_struct(&1, ApplicationTags.Encoding)),
{:ok, vendor_id, _header} <-
ApplicationTags.encode_value({:unsigned_integer, params.vendor_id}, opts),
{:ok, extended_notification_type, _header} <-
ApplicationTags.encode_value(
{:unsigned_integer, params.extended_notification_type},
opts
),
{:ok, parameters} <-
Enum.reduce_while(params.parameters, {:ok, []}, fn param, {:ok, acc} ->
case ApplicationTags.Encoding.to_encoding(param) do
{:ok, parameters} -> {:cont, {:ok, [parameters | acc]}}
term -> {:halt, term}
end
end) do
{:ok,
{:constructed,
{9,
[
tagged: {0, vendor_id, byte_size(vendor_id)},
tagged: {1, extended_notification_type, byte_size(extended_notification_type)},
constructed: {2, Enum.reverse(parameters), 0}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%BufferReady{} = params, opts) do
with true <- is_struct(params.buffer_property, DeviceObjectPropertyRef),
true <-
is_integer(params.previous_notification) and params.previous_notification >= 0 and
ApplicationTags.valid_int?(params.previous_notification, 32),
true <-
is_integer(params.current_notification) and params.current_notification >= 0 and
ApplicationTags.valid_int?(params.current_notification, 32),
{:ok, bufferobj} <- DeviceObjectPropertyRef.encode(params.buffer_property, opts),
{:ok, previous_notification, _header} <-
ApplicationTags.encode_value({:unsigned_integer, params.previous_notification}, opts),
{:ok, current_notification, _header} <-
ApplicationTags.encode_value({:unsigned_integer, params.current_notification}, opts) do
{:ok,
{:constructed,
{10,
[
constructed: {0, bufferobj, 0},
tagged: {1, previous_notification, byte_size(previous_notification)},
tagged: {2, current_notification, byte_size(current_notification)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%UnsignedRange{} = params, opts) do
with true <- is_integer(params.exceeding_value) and params.exceeding_value >= 0,
true <- is_struct(params.status_flags, StatusFlags),
true <- is_integer(params.exceeded_limit) and params.exceeded_limit >= 0,
{:ok, excvalue, _header} <-
ApplicationTags.encode_value({:unsigned_integer, params.exceeding_value}, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts),
{:ok, exclimit, _header} <-
ApplicationTags.encode_value({:unsigned_integer, params.exceeded_limit}, opts) do
{:ok,
{:constructed,
{11,
[
tagged: {0, excvalue, byte_size(excvalue)},
tagged: {1, flags, byte_size(flags)},
tagged: {2, exclimit, byte_size(exclimit)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%DoubleOutOfRange{} = params, opts) do
with true <- is_float(params.exceeding_value),
true <- is_struct(params.status_flags, StatusFlags),
true <- is_float(params.deadband),
true <- is_float(params.exceeded_limit),
{:ok, excvalue, _header} <-
ApplicationTags.encode_value({:double, params.exceeding_value}, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts),
{:ok, deadband, _header} <-
ApplicationTags.encode_value({:double, params.deadband}, opts),
{:ok, exclimit, _header} <-
ApplicationTags.encode_value({:double, params.exceeded_limit}, opts) do
{:ok,
{:constructed,
{14,
[
tagged: {0, excvalue, byte_size(excvalue)},
tagged: {1, flags, byte_size(flags)},
tagged: {2, deadband, byte_size(deadband)},
tagged: {3, exclimit, byte_size(exclimit)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%SignedOutOfRange{} = params, opts) do
with true <- is_integer(params.exceeding_value),
true <- is_struct(params.status_flags, StatusFlags),
true <- is_integer(params.deadband) and params.deadband >= 0,
true <- is_integer(params.exceeded_limit),
{:ok, excvalue, _header} <-
ApplicationTags.encode_value({:signed_integer, params.exceeding_value}, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts),
{:ok, deadband, _header} <-
ApplicationTags.encode_value({:unsigned_integer, params.deadband}, opts),
{:ok, exclimit, _header} <-
ApplicationTags.encode_value({:signed_integer, params.exceeded_limit}, opts) do
{:ok,
{:constructed,
{15,
[
tagged: {0, excvalue, byte_size(excvalue)},
tagged: {1, flags, byte_size(flags)},
tagged: {2, deadband, byte_size(deadband)},
tagged: {3, exclimit, byte_size(exclimit)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%UnsignedOutOfRange{} = params, opts) do
with true <- is_integer(params.exceeding_value) and params.exceeding_value >= 0,
true <- is_struct(params.status_flags, StatusFlags),
true <- is_integer(params.deadband) and params.deadband >= 0,
true <- is_integer(params.exceeded_limit) and params.exceeded_limit >= 0,
{:ok, excvalue, _header} <-
ApplicationTags.encode_value({:unsigned_integer, params.exceeding_value}, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts),
{:ok, deadband, _header} <-
ApplicationTags.encode_value({:unsigned_integer, params.deadband}, opts),
{:ok, exclimit, _header} <-
ApplicationTags.encode_value({:unsigned_integer, params.exceeded_limit}, opts) do
{:ok,
{:constructed,
{16,
[
tagged: {0, excvalue, byte_size(excvalue)},
tagged: {1, flags, byte_size(flags)},
tagged: {2, deadband, byte_size(deadband)},
tagged: {3, exclimit, byte_size(exclimit)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%ChangeOfCharacterString{} = params, opts) do
with true <- is_binary(params.changed_value) and String.valid?(params.changed_value),
true <- is_struct(params.status_flags, StatusFlags),
true <- is_binary(params.alarm_value) and String.valid?(params.alarm_value),
{:ok, changedvalue, _header} <-
ApplicationTags.encode_value({:character_string, params.changed_value}, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts),
{:ok, alvalue, _header} <-
ApplicationTags.encode_value({:character_string, params.alarm_value}, opts) do
{:ok,
{:constructed,
{17,
[
tagged: {0, changedvalue, byte_size(changedvalue)},
tagged: {1, flags, byte_size(flags)},
tagged: {2, alvalue, byte_size(alvalue)}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%ChangeOfStatusFlags{} = params, opts) do
with true <- is_struct(params.referenced_flags, StatusFlags),
true <-
is_nil(params.present_value) or
is_struct(params.present_value, ApplicationTags.Encoding),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.referenced_flags), opts),
{:ok, present_value} <-
(if params.present_value do
with {:ok, pv} <-
ApplicationTags.Encoding.to_encoding(params.present_value) do
{:ok, {:constructed, {0, pv, 0}}}
end
else
{:ok, nil}
end) do
status_flags = {:tagged, {1, flags, byte_size(flags)}}
tags =
case params.present_value do
nil -> [status_flags]
_term -> [present_value, status_flags]
end
{:ok, {:constructed, {18, tags, 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
def encode(%ChangeOfReliability{} = params, opts) do
with true <- is_atom(params.reliability),
true <- is_struct(params.status_flags, StatusFlags),
{:ok, reliability_c} <-
Constants.by_name_with_reason(
:reliability,
params.reliability,
{:unknown_reliability, params.reliability}
),
{:ok, reliability, _header} <-
ApplicationTags.encode_value({:enumerated, reliability_c}, opts),
{:ok, flags, _header} <-
ApplicationTags.encode_value(StatusFlags.to_bitstring(params.status_flags), opts),
{:ok, propvalue} <-
BACnet.Protocol.PropertyValue.encode_all(params.property_values, opts) do
{:ok,
{:constructed,
{19,
[
tagged: {0, reliability, byte_size(reliability)},
tagged: {1, flags, byte_size(flags)},
constructed: {2, propvalue, 0}
], 0}}}
else
false -> {:error, :invalid_params}
{:error, _err} = err -> err
end
end
@doc """
Validates whether the given notification parameter is in form valid.
It only validates the struct is valid as per type specification.
"""
@spec valid?(notification_parameter()) :: boolean()
def valid?(t)
for module <- [
__MODULE__.ChangeOfBitstring,
__MODULE__.ChangeOfState,
__MODULE__.ChangeOfValue,
__MODULE__.CommandFailure,
__MODULE__.FloatingLimit,
__MODULE__.OutOfRange,
__MODULE__.ComplexEventType,
__MODULE__.ChangeOfLifeSafety,
__MODULE__.Extended,
__MODULE__.BufferReady,
__MODULE__.UnsignedRange,
__MODULE__.DoubleOutOfRange,
__MODULE__.SignedOutOfRange,
__MODULE__.UnsignedOutOfRange,
__MODULE__.ChangeOfCharacterString,
__MODULE__.ChangeOfStatusFlags,
__MODULE__.ChangeOfReliability
] do
var = Macro.var(:t, __MODULE__)
def valid?(%unquote(module){} = unquote(var)) do
unquote(BACnet.Internal.generate_valid_clause(module, __ENV__))
end
end
end