Skip to content

Commit d10c702

Browse files
author
CI
committed
Sync to GitHub
1 parent 5e28dba commit d10c702

4 files changed

Lines changed: 106 additions & 5 deletions

File tree

lib/bacnet/protocol/object_types/device.ex

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,28 @@ defmodule BACnet.Protocol.ObjectTypes.Device do
88
but is also unique throughout the BACnet internetwork.
99
1010
(ASHRAE 135 - Clause 12.11)
11+
12+
For the following properties the BACnet device server needs to
13+
take special care of when other BACnet user read or write to them:
14+
- `active_cov_subscriptions`
15+
- `apdu_timeout` (propagation on write)
16+
- `auto_slave_discovery`
17+
- `device_address_binding`
18+
- `manual_slave_address_binding`
19+
- `max_info_frames` (marked as "readonly" and "default" value should be `1`)
20+
Propagation to the MS/TP Transport must be done by the BACnet device server
21+
or user of the library, if not implemented in the device server.
22+
- `max_master` (marked as "readonly" and "default" value should be `127`)
23+
Propagation to the MS/TP Transport must be done by the BACnet device server
24+
or user of the library, if not implemented in the device server.
25+
- `number_of_apdu_retries` (propagation on write)
26+
- `object_list`
27+
- `slave_address_binding`
28+
- `slave_proxy_enable`
29+
- `structured_object_list`
30+
- `utc_offset` (when handling UTC Time Synchronization)
31+
- All properties related to Backup/Restore
32+
- All properties related to (UTC) Time Synchronization
1133
"""
1234

1335
# TODO: Docs
@@ -136,13 +158,27 @@ defmodule BACnet.Protocol.ObjectTypes.Device do
136158
)
137159

138160
# Properties for when the device is capable of tracking date and time
139-
field(:local_date, BACnetDate.t(), readonly: true, init_fun: &BACnetDate.utc_today/0)
140-
field(:local_time, BACnetTime.t(), readonly: true, init_fun: &BACnetTime.utc_now/0)
161+
field(:local_date, BACnetDate.t(),
162+
readonly: true,
163+
init_fun: &BACnetDate.utc_today/0,
164+
annotation: [on_read_function: &update_property(&1, :local_date, BACnetDate.utc_today())]
165+
)
166+
167+
field(:local_time, BACnetTime.t(),
168+
readonly: true,
169+
init_fun: &BACnetTime.utc_now/0,
170+
annotation: [on_read_function: &update_property(&1, :local_time, BACnetTime.utc_now())]
171+
)
141172

142173
# Properties for COV reporting service
143174
field(:active_cov_subscriptions, [CovSubscription.t()], readonly: true, default: [])
144175

145176
# Properties for (Utc)TimeSynchronization service
177+
field(:time_synchronization_interval, non_neg_integer(),
178+
default: 0,
179+
implicit_relationship: :interval_offset
180+
)
181+
146182
field(:time_synchronization_recipients, [Recipient.t()],
147183
default: [],
148184
implicit_relationship: :interval_offset
@@ -242,7 +278,10 @@ defmodule BACnet.Protocol.ObjectTypes.Device do
242278
bac_type: {:with_validator, :unsigned_integer, &(&1 >= 1 and &1 <= 127)}
243279
)
244280

245-
field(:max_info_frames, non_neg_integer(), readonly: true)
281+
field(:max_info_frames, pos_integer(),
282+
readonly: true,
283+
bac_type: {:with_validator, :unsigned_integer, &(&1 >= 1)}
284+
)
246285

247286
# Virtual Terminal properties (not supported by this bacstack)
248287
# :vt_classes_supported, [BACnetVtClass.t()]

lib/bacnet/protocol/objects_macro.ex

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,19 @@ defmodule BACnet.Protocol.ObjectsMacro do
1010
Mix project, you will need to recompile bacstack with `mix deps.compile bacstack --force`.
1111
1212
The following has to be taken care of when trying decode/encode properties:
13-
- Check the annotations for decoder/encoder functions (single argument - the plain value (no tag encoding))
13+
- Check the annotations for decoder/encoder functions (single argument - decoding: `Encoding` struct)
1414
- Check the annotations for `encode_as` primitive type declaration (i.e. used to declare enumerated booleans)
1515
- Check the properties types map - 99% should be covered by this (1% is covered by annotations)
1616
- Custom decoding/encoding by hand for special properties (not yet supported properties/objects)
17+
18+
BACnet device server implementations need to make sure to support `on_read_function` annotation,
19+
which should always be called (single arity - the object itself and returns an ok-tuple object),
20+
when another BACnet user is reading the property with such an annotation before returning
21+
property value. Use cases are for example the BACnet device local time,
22+
which should always be returned up to date and not a stale value.
23+
24+
Some properties need to be handled in a special way in the BACnet device server
25+
for both reading and writing the property, no such annotation exists though.
1726
"""
1827

1928
alias BACnet.BeamTypes

lib/bacnet/stack/client.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ defmodule BACnet.Stack.Client do
1010
a Reject APDU with reason `:other` to the remote BACnet device,
1111
the application won't be informed in any way.
1212
13-
BACnet BVLL and BACnet NPDU are directly forwarded to the application, without any processing.
13+
BACnet BVLL, BACnet NPDU and proprietary are directly forwarded to the application, without any processing.
1414
1515
For BACnet BVLL, the following message is sent:
1616
```elixir
@@ -74,6 +74,9 @@ defmodule BACnet.Stack.Client do
7474
Outgoing service request APDUs validation can be explicitely disabled by configuring
7575
application environment `:bacstack` key `:client_prod_compilation` to `true`.
7676
If this is a dependency of your project, don't forget to `mix deps.compile --force bacstack`.
77+
78+
If this library is a dependency in a project, Mix always compiles dependencies in `:prod`.
79+
To override this behaviour, see `mix help deps`.
7780
"""
7881

7982
alias BACnet.Protocol
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
defmodule BACnet.Test.Protocol.ObjectTypes.DeviceTest do
2+
alias BACnet.Protocol.BACnetDate
3+
alias BACnet.Protocol.BACnetTime
4+
alias BACnet.Protocol.ObjectTypes.Device
5+
6+
use ExUnit.Case, async: true
7+
8+
@moduletag :object_test
9+
@moduletag :bacnet_object
10+
@moduletag :bacnet_object_device
11+
12+
# This test suite only extends the basic and utility test suite to
13+
# cover additional implemented functionality
14+
15+
test "Property local_date has on_read_function and works" do
16+
annotations = Device.get_annotation(:local_date)
17+
assert Keyword.keyword?(annotations)
18+
19+
fun = Keyword.fetch!(annotations, :on_read_function)
20+
assert is_function(fun, 1)
21+
22+
assert {:ok, %Device{} = dev} =
23+
Device.create(1, "Dev", %{
24+
local_date: BACnetDate.from_date(Date.add(Date.utc_today(), -1))
25+
})
26+
27+
assert {:ok, %Device{} = dev2} = fun.(dev)
28+
29+
# Dev2 date must be newer
30+
assert BACnetDate.compare(dev.local_date, dev2.local_date) == :lt
31+
end
32+
33+
test "Property local_time has on_read_function and works" do
34+
annotations = Device.get_annotation(:local_time)
35+
assert Keyword.keyword?(annotations)
36+
37+
fun = Keyword.fetch!(annotations, :on_read_function)
38+
assert is_function(fun, 1)
39+
40+
assert {:ok, %Device{} = dev} =
41+
Device.create(1, "Dev", %{
42+
local_time: BACnetTime.from_time(Time.add(Time.utc_now(), -1))
43+
})
44+
45+
assert {:ok, %Device{} = dev2} = fun.(dev)
46+
47+
# Dev2 time must be newer
48+
assert BACnetTime.compare(dev.local_time, dev2.local_time) == :lt
49+
end
50+
end

0 commit comments

Comments
 (0)