Skip to content
Joel Bender edited this page Jan 29, 2020 · 20 revisions

Release Notes

0.18.0

Python 3.8

Python3.8 is now listed as a supported version because there has been some success with it. Work continues on a bacpypes3 replacement that is based on async/await but it has not been released yet.

Module Updates

  • app - add additional "guard" so that applications have to provide an IOCB when making confirmed service requests.
  • appservice - fixe a segmentation bug with the initial sequence number.
  • basetypes - floating and integer scales.
  • core - stop supressing output.
  • object - fixed length arrays for event message properties
  • service/cov - fix an array encoding bug

HTTP Server Sample

This sample application has been updated to work on Python3+ that moved the locations of various libraries.

Read Property Redis Sample

In the sandbox there is a sample application that reads a list of points and saves them in a Redis key-value store and also publishes the value to a Redis Stream. This sample application has a JSON file for settings.

Redis streams are an excellent alternative to Apache Kafka.

0.17.7

There is a new way of handling strange topology problems, a settings module for gathering information from the environment in a more generic way, the network layer and router information cache has been overhauled, and lots of other smaller improvements along with additional sample applications.

Route Aware Addresses

Using remote station address like "10:20" implies that the router to network 10 can be found by doing a local broadcast Who-Is-Router-To-Network. Occassionally there are times when the client is not really on the BACnet network, just lurking nearby. Unicast messages work fine because servers respond to the address that sent the request.

This route aware address format is extends the usual format with an additional suffix, net:addr@route where route is the address of the router to network net. For example, sending a request from 192.168.0.12 to "10:[email protected]" bypasses the request to look for the router to network 10 and simply sends it on as a unicast message to 10.168.0.15. When the reply from the device is received by the router, the DNET/DLEN/DADR will have the source network in it and the router will assume that 192.168.0.12 is an acceptable address.

At the application layer of the client, the address will be presented with the address of router, which is normally abstracted away in the network layer. In this case the network topology information in the network layer is not updated.

To use this format the route_aware setting must be set, from the environmental variable BACPYPES_ROUTE_AWARE or from some other configuration information.

Settings

There is a new settings module that contains the debug and route_aware settings for applications. When applications start up they can provide their own initialization information from the built-in os module, or can come from other sources like an INI or JSON file.

To make the settings names and the environmental variable names more consistent with each other the BACPYPES_DEBUG_FILE, BACPYPES_MAX_BYTES, and BACPYPES_BACKUP_COUNT have been renamed with underscores.

To take advantage of this new set of configuration options, there is a new JSONArgumentParser argument parser that mirrors the existing ConfigArgumentParser but uses a --json parameter rather than an --ini parameter. JSON files can contain much richer content than INI files.

Commandable (#224)

In the bacpypes.local.object module there are now "commandable" classes that implement the side effects of writing to a priority array. Some of the possible classes like BinaryLightingOutputCmdObject and ChannelCmdObject still need implementations.

Router Info Cache (#213)

The routing information cache which contains the relationship between local station addresses and the reachable networks through a router has been significantly changed. It's a small data structure change from a dictionary of path information to a cross reference of routers and networks.

The NetworkServiceElement now has a startup function that builds a list of reachable networks for each adapter and calls i_am_router_to_network() to send them out.

The function add_router_references() changed to update_router_references()

For some reason lots in the sands of time, the BIPNetworkApplication passed a true for noBroacast which means that applications that were for browsing around a network couldn't receive I-Am-Router-To-Network broadcasts. This has been changed.

Other Issues

Issue (#286) supports disabling COV for specific objects.

Issue (#283) fixes a bug handles socket error bindings.

Issue (#273) skips the propertyList property returning all properties for a ReadPropertyMultiple request.

Support Python3.7 using the same Python3.4 code base until "bacpypes3" is released. The new library will be based on async/await for Python3.6+ and will require Python3.7+ and websockets for BACnet/SC which requires TLS 1.3.

Sample Applications

  • mini_device.py is an application that supports many core services that applications need to present data on a BACnet network. It supports Who-Is and I-Am for device binding, Read and Write Property, Read and Write Property Multiple, and COV subscriptions.

  • OpenWeatherServer.py is an application that uses the https://openweathermap.org/ service to get weather data and make it available over BACnet.

  • WhoIsIAmVLAN.py is a router from BACnet/IP to a VLAN with devices that support Who-Is and I-Am.

  • RecurringWriteProperty.py application demonstrates writing a series of values at a regular interval.

0.17.6

Placeholder.

0.17.5

Object Identifier Strings

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

What-Is-Network-Number/Network-Number-Is

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.

I-Am-Router-To-Network Helper

There is a new i_am_router_to_network() function (and routers should call this function at startup) which announces its network topology information.

COV Period

The covPeriod property is now supported for the PulseConverterObject.

Fixed Size Arrays

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.

0.17.4

This section unintentionally left blank.

0.17.3

This release cleans up lots of loose ends, most of the effort has been in the COV subscription/notification code.

COV Server Notifications Fail

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!

Communications Switch

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.

Routing Loopback Reappears

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.

0.17.2

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.

0.17.1

Simplified Configuration of Local Device Object

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)

Device Information Cache

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() and release() 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.

Segmented APDUs

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.

APDU Retry Count Fixed

The retry count is now correct, it was a vicious off-by-one error.

Foreign Device Communications

The restrictions on what can be sent and received by an unregistered foreign device have been relaxed.

Unrecognized Services

The recept of a request for an unrecognized service is now rejected.

Other Minor Changes

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.

0.17.0

This release contains a number of new features that will break existing code.

New ListOf Classes

Change SequenceOf to ListOf 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))

New RouterInfoCache

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.

LocalDeviceObject moved

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.

Local File Objects Moved

The LocalRecordAccessFileObject and LocalStreamAccessFileObject classes have moved to the bacpypes.local.file module.

New LocalScheduleObject Class

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.

Automatic Protocol Services Supported

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.

Traffic Log

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.

Sample Applications

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.

Sidebar items:

Clone this wiki locally