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

lib/bacnet/protocol/calendar_entry.ex

defmodule BACnet.Protocol.CalendarEntry do
# TODO: Docs
# TODO: Throw argument error in encode if not valid
alias BACnet.Protocol.ApplicationTags
alias BACnet.Protocol.BACnetDate
alias BACnet.Protocol.Constants
alias BACnet.Protocol.DateRange
alias BACnet.Protocol.WeekNDay
require Constants
@type t :: %__MODULE__{
type: :date | :date_range | :week_n_day,
date: BACnetDate.t() | nil,
date_range: DateRange.t() | nil,
week_n_day: WeekNDay.t() | nil
}
@fields [:type, :date, :date_range, :week_n_day]
@enforce_keys @fields
defstruct @fields
@doc """
Encode a calendar entry into application tag-encoded.
"""
@spec encode(t(), Keyword.t()) ::
{:ok, ApplicationTags.encoding_list()} | {:error, term()}
def encode(entry, opts \\ [])
def encode(%__MODULE__{type: :date, date: %BACnetDate{} = date} = _entry, opts) do
with {:ok, date, _header} <- ApplicationTags.encode_value({:date, date}, opts) do
{:ok, [{:tagged, {0, date, byte_size(date)}}]}
end
end
def encode(
%__MODULE__{
type: :date_range,
date_range: %DateRange{
start_date: %BACnetDate{} = start_date,
end_date: %BACnetDate{} = end_date
}
} = _entry,
_opts
) do
{:ok,
[
{:constructed,
{1,
[
date: start_date,
date: end_date
], 0}}
]}
end
def encode(%__MODULE__{type: :week_n_day, week_n_day: %WeekNDay{} = week_n_day} = _entry, _opts) do
with {:ok, [{:octet_string, str}]} <- WeekNDay.encode(week_n_day) do
{:ok, [{:tagged, {2, str, 3}}]}
end
end
@doc """
Parse application tag-encoded calendar entry into a struct.
"""
@spec parse(ApplicationTags.encoding_list()) ::
{:ok, {t(), rest :: ApplicationTags.encoding_list()}}
| {:error, term()}
def parse(tags) when is_list(tags) do
case tags do
[{:tagged, {0, tags, _len}} | rest] ->
with {:ok, {:date, date}} <- ApplicationTags.unfold_to_type(:date, tags) do
cal = %__MODULE__{
type: :date,
date: date,
date_range: nil,
week_n_day: nil
}
{:ok, {cal, rest}}
end
[
{:constructed,
{1,
[
{:date, start_date},
{:date, end_date}
], _len}}
| rest
] ->
cal = %__MODULE__{
type: :date_range,
date: nil,
date_range: %DateRange{
start_date: start_date,
end_date: end_date
},
week_n_day: nil
}
{:ok, {cal, rest}}
[{:tagged, {2, str, _len}} | rest] ->
with {:ok, {week, _rest}} <- WeekNDay.parse([{:octet_string, str}]) do
cal = %__MODULE__{
type: :week_n_day,
date: nil,
date_range: nil,
week_n_day: week
}
{:ok, {cal, rest}}
end
_term ->
{:error, :invalid_tags}
end
end
@doc """
Validates whether the given BACnet calendar entry is in form valid.
It only validates the struct is valid as per type specification.
"""
@spec valid?(t()) :: boolean()
def valid?(t)
def valid?(
%__MODULE__{
type: :date,
date: %BACnetDate{} = date
} = _t
) do
BACnetDate.valid?(date)
end
def valid?(
%__MODULE__{
type: :date_range,
date_range: %DateRange{} = range
} = _t
) do
DateRange.valid?(range)
end
def valid?(
%__MODULE__{
type: :week_n_day,
week_n_day: %WeekNDay{} = week
} = _t
) do
WeekNDay.valid?(week)
end
def valid?(%__MODULE__{} = _t), do: false
end