-
Notifications
You must be signed in to change notification settings - Fork 130
Release Notes
The ObjectIdentifier
can now be given a string in the form type:instance
where type is a name like analogInput or an unsigned integer, and
instance is an unsigned integer. This is a quite simple change except
all the sample applications have been updated to use the new syntax.
For example, the ReadProperty.py
sample application took commands like this:
> read 123:45 analogInput 67 presentValue
It now takes commands like this:
> read 123:45 analogInput:67 presentValue
The network layer nows supports these two relatively new NPDUs for discovering
the local topology. When a Network Service Access Point is bound to a network
stack (like a BACnet/IP BVLL stack or a VLAN node) and the network number is
None
it can be learned. The adapater object has an additional attribute
that matches the 'flag' in the Network-Number-Is, zero (0) is learned and one
(1) is configured.
Note that there is a chance that the local adapter learns the network number
to which it is connected and the adapter.adapterNet
will seem to mysteriously
change from None
to a network number. Applications should not rely on
nsap.adapters[None]
always referencing the local adapter,
use nsap.local_adapter
instead. The WhoIsRouter.py
sample application
has been updated to reflect this change.
As it stands now, you cannot build a router between two completely unknown networks, you have to have at least one of them known. This limitation might be lifted in the future.
You can use the new what_is_network_number()
function to ask about all the
networks you don't know about, or pass it an adapter to learn about a
specific downstream connection. You can pass it an additional optional
destination for a unicast request.
Similarly, the new network_number_is()
function sends out local broadcast
messages about configured networks (and routers should call this function
at startup). If a specific adapter parameter is given, it will send out a
local broadcast about a "learned" network.
There is a new i_am_router_to_network()
function (and routers should call
this function at startup) which announces its network topology information.
The covPeriod
property is now supported for the PulseConverterObject
.
There is now support for fixed length arrays. The ArrayOf()
function,
which returns a class that is a subclass of Array
, now has two additional
parameters, a fixed_length
with constrains the array to a specific number
of elements and a prototype
for initializing array elements.
The simplest example is an ArrayOf(Integer, fixed_length=3)
which is just
what it looks like. The PriorityArray
is a little more interesting because
each element of the array is a PriorityValue
which is a Choice
. So in that
case, since the array elements cannot be None
, the prototype is cloned into
new array elements.
The prototype
parameter is also used for variable length arrays, so when the
array is extended (by increasing the length, writing to array index zero which
are BACnet array semantics) the additional elements are deep copies of the
prototype.
This section unintentionally left blank.
This release cleans up lots of loose ends, most of the effort has been in the COV subscription/notification code.
When a COV initiates a subscription request which is acknowlegded by the server the server is suppoed to immediately send out a notification to the client with the current values of the monitored properties. This was inadvertantly omitted from the service processing.
The real story is in the changes to the state_machine and time_machine to make it easier to build tests for this issue and to start the process of testing the rest of the algorithms for the different point types. There are a lot of point types and it's going to take a whlie to write tests for all of them, but at least there is some existing code to copy/paste the test steps.
And it's easier to start with running code!
There is a new class in the bacpypes.comm
module for "switching" components
in and out of the stack, both at the top, in the middle and at the bottom.
This is in response to being able to start the application in one mode (like
a simple BACnet/IP node) and be able to switch to being a foreign device or
a BBMD.
The problem I thought I fixed in 0.17.2 persisted, there is a spurious
IAmRouterToNetwork
message going out every once in a while that I finally
hunted down.
There is a bug in the NetworkServiceElement
when it is responding to a
WhoIsRouterToNetwork
the router will respond requests from a directly
connected, even when those requests were on the same adapter. For example,
a router between network 1 and 2 would respond as a router to network 1 for
requests like "who is router to network 1" from devices on network 1, which
is an error.
The number and types of optional parameters to the DeviceObject that could be defined in keyword arguments or in an INI file was driving me crazy. There is a new simple way that is reflected in the sample applications where you can go from this:
# make a device object
this_device = LocalDeviceObject(
objectName=args.ini.objectname,
objectIdentifier=int(args.ini.objectidentifier),
maxApduLengthAccepted=int(args.ini.maxapdulengthaccepted),
segmentationSupported=args.ini.segmentationsupported,
vendorIdentifier=int(args.ini.vendoridentifier),
)
to this:
this_device = LocalDeviceObject(ini=args.ini)
if _debug: _log.debug(" - this_device: %r", this_device)
The expectations of the DeviceInfoCache have changed to make it closer to what would be expected if it had a database backend.
-
the
get_device_info()
function no longer returns a mocked up DeviceInfo record if one doesn't exist -
there are new
acquire()
andrelease()
functions that signal when a DeviceInfo record is being used by a state machine. -
a new
device_info_class
parameter is used when constructing new DeviceInfo objects so both the DeviceInfoCache and DeviceInfo can be subclassed.
The segmentation state machine classes have been significantly worked over to respect the APCI header fields and is no longer as tightly dependant on the device information cache.
The retry count is now correct, it was a vicious off-by-one error.
The restrictions on what can be sent and received by an unregistered foreign device have been relaxed.
The recept of a request for an unrecognized service is now rejected.
The functions for encoding and decoding the maximum segments accepted and
the maximum APDU length accepted have changed to more accurately reflect
unknown or unspecified values. To carry the correct sematics into the
client and server segmentation state machines the apduMaxSegs
and apduMaxResp
fields in the APCI are now the values in the PDU rather than the encoded/decoded
versions.
The task manager now executes tasks that were scheduled for the same time in the same order they were scheduled. When A and B are both scheduled at time t, it was the case that B could be executed before A and it made unit tests harder than necessary.
In the bacpypes.vlan
module, IPNetworks
can be given a name which can
help with debugging test traffic through IPRouterNode
objects.
This release contains a number of new features that will break existing code.
Change
SequenceOf
toListOf
for property definitions and values
The object class definitions started out being generated from the ASN.1 descriptions of objects in Annex C which has since been deleted. That notation used forms like this:
active-vt-sessions [5] SEQUENCE OF BACnetVTSession OPTIONAL,
which were translated into BACpypes definitions like this:
OptionalProperty('activeVtSessions', SequenceOf(VTSession))
But the standard changed to definitions like this (and no longer in an Annex):
Active_VT_Sessions BACnetLIST of BACnetVTSession
So BACpypes now uses definitions like this:
OptionalProperty('activeVtSessions', ListOf(VTSession))
No change to BACpypes applications unless
NetworkServiceAccessPoint
has been subclassed
The network service layer consisting of NetworkAdapter
, NetworkServiceAccessPoint
,
and the NetworkServiceElement
had no mechanism for a BACpypes application to
provide the address of a router, all of the path information it gathered by
inspecting upstream packets. It also did not suspend application layer requests
to try and find a path, it simply broadcast the request, which is incorrect.
There is a new RouterInfoCache
class that can be subclassed and provided to
the network layer and it is similar to the DeviceInfoCache
at the application
layer. A well-known set of functions are called for finding out routing paths
for downstream packets and updating paths for upstream packets.
There is a queue of APDU's waiting path discovery in pending_nets
. There is
(currently) no signal back to the application when the discovery process times
out.
The LocalDeviceObject is used to create a DeviceObject
in an application that
automatically returns the system date and time when the localDate
and localTime
properties are read. This class was in the bacpypes.service.device
module
and it has been moved to the new bacpypes.local.device
module.
BACpypes applications must change from this:
from bacpypes.service.device import LocalDeviceObject
to this:
from bacpypes.local.device import LocalDeviceObject
Similarly, the CurrentPropertyList
class that was in the bacpypes.service.object
module is now in the bacpypes.local.object
module because it belongs with
local implementations of objects.
The LocalRecordAccessFileObject
and LocalStreamAccessFileObject
classes
have moved to the bacpypes.local.file
module.
There is an implementation of a LocalScheduleObject
in the bacpypes.local.sched
module. It includes a companion class, an "interpreter" that is a task that
keeps the present value current. It does not support writing to or overriding
objects outside of the local device context.
All of the sample applications that have a full stack had code that computed the protocol services supported and set that in the device object.
# get the services supported
services_supported = this_application.get_services_supported()
if _debug: _log.debug(" - services_supported: %r", services_supported)
# let the device object know
this_device.protocolServicesSupported = services_supported.value
This functionality has been rolled into the LocalDeviceObject
so this code
must be removed.
There is a new traffic_log
attribute in the VLAN networking that can reference
a function to call to log each of the PDUs that the network will process. This
was added to make debugging tests easier.
All of the sample applications have been updated with the changes listed above.
There is a new LocalScheduleObject.py
sample application that tests for
specific dates and times in a variety of schedule objects. The sample schedules
could be used for additional, more detailed, and better coverage tests.
The ReadWriteProperty.py
sample application has a funky interpreter for making
sure values being written can be coerced into appropriate values.