Changelog#
Version 2.0#
Added controller observe_connection and connector_factory#
aes70::controller::observe_connection can be used to manage a connection with automatic reconnection and is compatible with an inner subscription. libuv::tcp::connector_factory works with this API and applies backoff when a connection fails. lwip::tcp provides equivalent connect functionality and backoff for embedded use.
lwIP controller support#
The lwIP TCP stack now supports connect functionality (lwip::tcp), a timer API, and lwip::call_immediate, enabling controller applications on embedded targets (e.g. ESP32). Examples and tests for ESP32 were added.
aes70::boost::tcp connect and timers#
aes70::boost::tcp gained connect infrastructure and timer abstractions, allowing controller applications without libuv.
Connection ownership and io_connection_interface#
Connections must now extend aes70::io_connection_interface. Connection policy implementations and a generic connection state were introduced. TCP ports gained close_connections(). Corking is implemented in the connection base. Connection ownership was refactored for libuv, lwip, and boost backends.
Added aes70::device::result#
aes70::device::result is a type that holds an optional value and an OcaStatus, for methods that either return a value or fail with an error. It is roughly equivalent to std::optional with an error code.
aes70::common::static_vector improvements#
static_vector can store unique_ptr, uses storage_for for construction, and was updated for C++23 deprecations and correct element destruction.
Support for AES70-2024#
Support for the AES70-2024 standard was added, including OCC (OCA Control Classes) updates and related protocol changes.
aes70_proxy EV2 support#
The aes70_proxy tool supports AES70-2024 (EV2) and handles EV2 exceptions (e.g. subscription removal on exception).
Optional simpleoca library#
The optional simpleoca library (CMake option AES70_BUILD_SIMPLEOCA) provides implementations of many standard AES70 device classes for use with the dynamic device APIs. An example (e.g. mic-preamp 4ch) demonstrates its use.
static_http::port#
static_http::port was added for hosting HTTP/WebSocket servers in a consistent way with other port types.
Breaking change
The introduction of static_http::port is a breaking change for code that previously used the static HTTP server without a port type.
Keepalive interval limits#
Limits were introduced on the keepalive interval to avoid misconfiguration.
Version 1.3#
Added aes70::device::error_status#
aes70::device::error_status is an std::exception that can be thrown by OCA methods in devices. It is translated to the corresponding response and sent back to the controller.
Custom object number allocation#
Static devices and dynamic devices can now allocate object numbers manually, allowing applications to control the numbering of objects in the tree.
Custom class events in tools/generate#
The class generator (tools/generate) supports custom class events for both C++ and Javascript outputs.
aes70::OcaBoolean typedef to bool#
aes70::OcaBoolean is now a typedef to bool for simpler use in method signatures and conditionals.
Added tools/mkfs.mjs#
This tool intends to implement the same functionality as tools/mkfs.pike but is written for NodeJS, instead.
On windows the library should now compile without defining NOMINMAX#
As a side effect of that the min, max and value functions on property_change_events have been deprecated. The min and max methods are only available when min and max are not defined. Users are encouraged to use valueChanged, minChanged and maxChanged instead.
Reduced compiler warnings on windows when compiling with MSVC#
Added a top-level CMakeLists.txt#
This simplifies integration of libaes70 into cmake projects by making it available as an interface library.
Added support for picohttpparser library#
The static_http module can now be used also with picohttpparser using
the preprocessor define STATIC_HTTP_USE_PICOHTTPPARSER.
Added support for llhttp library#
The static_http module was based on the http_parser library. As of 2023 this library has become unmaintained and llhttp is recommended to be used as a replacement by the authors.
llhttp support can be enabled by setting the STATIC_HTTP_USE_LLHTTP
preprocessor define.
Refactored testsuite to use cmake and catch2#
This refactoring replaced the custom test runner by using a standard cmake setup using catch2.
The new testsuite also builds with more compiler warning levels active, which means that as part of this refactoring many compiler warnings were fixed both in the library itself and the testsuite code.
Optimize the storage of subscriptions in linear_device#
For property change subscriptions using the same subscriber context we now store the object a controller is subscribed to using a bitset.
Make the ownership model of dynamic devices configurable#
Before this change, objects inside of dynamic device blocks were allocated using std::make_shared and stored as shared pointers. This is convenient in practice, however it comes with a certain overhead both in memory usage and execution time.
This change makes dynamic devices and their blocks configurable with respect to the way objects are being stored. This is done by selecting an ownership model. The ownership model simply defines type aliases for owning and observing pointers. The shared pointer ownership model (which is equivalent to the previous behavior) uses shared pointers for both. The unique pointer ownership model uses unique pointers for the owner and raw pointers for observing pointers.
The default ownership model remains the one using shared pointers, however
by setting the preprocessor define AES70_OPTIMIZE_DYNAMIC_DEVICE_UNIQUE_PTR
the default ownership model can be switched to use unique pointers.
Added dynamic_device::continuous_block#
This block template is used to represent blocks which contain a list of (possibly identical) members. It can be used to efficiently represent - for instance - a series of identical input channels.
Added dynamic_device::static_block#
This block is used to represent recurring blocks with identical member types efficiently. It can be used to - for example - build blocks which contain their members as class members.
Added example/secretstream#
This example demonstrates an integration with libsodium secretstream. libsodium secretstream is an API for encrypting a stream of messages using a shared secret key.
The underlying libsodium API is documented here.
Changed websocket::port API#
The API for using websocket::port has changed. It is now declared inside of the tcp port as
libuv::tcp::port<websocket::port<aes70::port<...>>> ws_port;
instead of being outside of the tcp port. This change was required to allow for further layering of protocols around websockets. It also makes more sense conceptually, as the WebSocket protocol conceptually sits between tcp and aes70.
Drop support for AES70_LWIP_USE_MEM_MALLOC#
Previously, defining this macro would switch some part of the lwip module to use mem_malloc/mem_free instead of std::allocator. This behavior was rarely useful, because in most situations there was no difference between the two APIs. However, activating this in applications made some APIs difficult to use correctly.
Version 1.2#
Added controller observer APIs#
aes70::controller::member_observer can be used to continuously observe members of a block.
aes70::controller::member_path_observer can be used to observe members of a block with specific class and path name.
aes70::controller::path_observer can be used to observe objects in a subtree with a specific path name and class.
websocket#
Attempt to grow frames from the 1 byte to 2 byte length representation. This means that fewer but larger websocket frames are generated by repeatedly appending e.g. aes70 messages to a websocket connection.
Cork connections in aes70_proxy#
This optimization improves the aes70_proxy tool to cork connections while receiving data. This reduces the number of write calls the proxy makes and guarantees that batched messages result in batched messages being sent by the proxy both to devices and controllers.
Added dynamic_device::linear_device#
This new device type is API compatible with dynamic_device::device and uses different internal data structure to store the object tree. It is called linear because it will store objects in a linear array. If objects are removed from the device their slot remains empty. The linear device storage is more efficient in situations where objects are never or rarely removed.
Reduce memory usage of dynamic_device::device#
The memory usage of all dynamic devices was reduced by:
- Merge two hashtables (storing objects and parent object numbers) which reduces the memory usage overhead.
- Store raw pointers to control objects instead of shared pointers. This is safe due to the ownership model of blocks. Control objects are owned by the containing block which removes them from the device when they are removed from the block. This reduces the memory usage overhead per control object by sizeof(void*).
dynamic_device::block#
- Added reserve() method.
Introduce optional single device optimization#
The global define AES70_OPTIMIZE_SINGLE_DEVICE can be used to activate an
optimizations which assumes that the application defines only a single device.
Objects will then not contain an explicit pointer to the device structure,
instead the device pointer will be stored in static storage. This optimization
reduces the memory requirements by each object by sizeof(void*).
New APIs for event generation in device objects#
The legacy API (aes70::property_change_event) for defining property change events inside of device objects relied upon indirect calls to implement the generation of notification packets using the right encoder class. Unfortunately, this leads to indirect calls which requires additional memory for storing a function pointer, as well as preventing possible inlining.
The new API relies on a new template class aes70::device::property_change_event which is instantiated for each property of each control class. The resulting types follow the naming convention aes70::device::OcaClass_PropertyName. Those classes are otherwise API compatible with the legacy API. For the moment both APIs are supported. The use of the new API is encouraged.
A similar API was introduced as a replacement for aes70::event. The corresponding types are named aes70::device::OcaControlClass_EventName. They are API compatible to aes70::event.
Version 1.1#
Namespace and directory structure reorganization#
In order to add the controller code, it was necessary to move some classes into the aes70::device. This is to prevent them from colliding with symbols which are to be added to the aes70::controller namespace.
The two namespaces aes70::static_device and aes70::dynamic_device now both rely on shared code inside of aes70::device.
The renamed classes are:
- aes70::object → aes70::device::object
- aes70::connection → aes70::device::connection
- aes70::static_device::Oca → aes70::device::Oca
- aes70::object_base → aes70::device::object_base
- aes70::debug_ → aes70::debug_device::
Added controller support#
libuv::tcp#
- added connect()
- connections are now shared pointers
- added port::listen()
- added port::get_port_number();
Added aes70::dynamic_classid#
Added aes70_proxy tool#
This proxy can aggregate any number of remote AES70 devices into a single object tree. It otherwise has no features currently, it aims to serve as an example and to demonstrate the principle of an AES70 proxy.
aes70::dynamic_device::block#
- added clear() method
aes70::device::object#
- the signature of call() was changed to include the parameterCount
aes70::OCP1#
- added comparison operators to all PDU types
- Command and CommandResponseRequired were merged into one type
Added websocket::parser#
This parser optionally supports streaming, i.e. parsing partial frames while they are being received. When used in a connection this reduces the amount of bytes which need to be buffered on the receiving side. Specifically, when receiving a large binary frame, the containing data can be passed on into the application while the frame is being received.
WebSocket token support#
This feature adds the ability to use a secure token to restrict WebSocket connections to the same limitations as standard XHR requests.
Combine outgoing WebSocket frames#
When a WebSocket connection is using streaming mode new outgoing chunks of data are added to the previous frame if possible. This reduces both memory and network usage for outgoing data on a connection.
Added aes70::parser#
This parser optionally supports streaming, i.e. parsing PDUs from messages while they are being received. On TCP connections this allows us to handle PDUs before a complete message has been received, instead of having to allocate a large buffer to hold the complete message. This can reduce the buffer required on the receiving side considerably.
Combine PDUs into larger messages#
If possible, a PDU is written into the outgoing buffer it is now appended to the previous message. This reduces both the memory consumption and network usage for outgoing data.
AFL fuzzer tests#
Added test harnesses to fuzz parser implementations.
Integrated AES70-2018(a)#
This amended version of AES70-2018 contains several fixes and corrections to the 2018 version of the standard.
Added an example device with (almost) all control classes#
This example contains mockup implementations of almost all control classes. It can serve as a starting point when implementing certain objects.
Better handling of out-of-memory situations#
Now, if memory allocation fails while handling a command or during response construction, the corresponding connection is closed. Previously, no response would have been sent.
Support AES70.js version 1.3 in tools/generate#
The custom class generator now support the new AES70.js version.