New release: v2.3.0
v2.3.0 has just been released. This is a major release containing several new features and a few minor API changes (see below).
ECSS compliance
This release contains several changes necessary to make the Lely CANopen stack
suitable for use in space, in particular to
make it compliant with the
ECSS-E-ST-50-15C - CANbus extension protocol.
Full ECSS compliance requires breaking changes and will not be achieved until
the v3 release (under deleveopment in the ecss
branch at
https://gitlab.com/n7space/canopen/lely-core.). This release contains the
necessary non-breaking changes (with the exception of a few
minor changes that should not affect normal users).
Configuration options
An ECSS-compliant build can be generated by specifying the
--enable-ecss-compliance
option to the configure
script. This option
disables all functionality not covered by the ECSS-E-ST-50-15C - CANbus
extension protocol, which includes the I/O libraries and all C++ APIs.
Additionally, it configures the project for a freestanding environment with
(almost) no support from the standard C library.
Standard C library support can also be selectively disabled with the following
new configure
options (or preprocessor macros)
--disable-errno
(LELY_NO_ERRNO
): disable support forerrno
and<errno.h>
.--disable-malloc
(LELY_NO_MALLOC
) disable support for dynamic memory allocation.--disable-stdio
(LELY_NO_STDIO
) disable standard I/O support.
Note: LELY_NO_MALLOC
support is preliminary and will not yet disable all
dynamic memory allocation.
Additionally, --disable-diag
(LELY_NO_DIAG
) disables the diagnostic
functions from liblely-util.
The NMT boot slave and configuration request services are not covered by ECSS.
They can be disabled with the new --disable-nmt-boot
(LELY_NO_CO_NMT_BOOT
)
and --disable-nmt-cfg
(LELY_NO_CO_NMT_CFG
) options, respectively. Node
guarding is also out of scope. It can be disabled with the new --disable-ng
(LELY_NO_CO_NG
) option.
For more information, see the build configuration documentation.
Static object dictionary generator
Disabling dynamic memory allocation prevents the generation of the object
dictionary at runtime. It also precludes the use of the static object dictionary
generated by dcf2c
, since that object
dictionary still needs to be converted to the real object dictionary at runtime.
The new dcf2dev
tool (part of the
EDS/DCF tools) can generate a
compile-time object dictionary that is directly usable by the CANopen stack.
Note: Because of the way array values are handled, the generated object
dictionary can only be used when LELY_NO_MALLOC
is defined.
Unit tests
Part of the ECSS-compliance effort is the development of a test suite that
covers 100% of the code that is in scope. This test suite is based on the
CppUTest and can be found in the unit-tests
directory. It can be disabled with the --disable-unit-tests
option.
New features
Stop token
A stop token implementation is now provided by <lely/util/stop.h>
and
<lely/util/stop.hpp>
. The API is based on
<stop_token>
in C++20.
Stop tokens provide a generic mechanism for users to cancel (sequences of)
tasks, using (global) timeouts or any other condition.
This blog post
provides a good overview of why this is useful.
Support for stop tokens will be added to the asynchronous I/O library in a future release.
Transmit-PDO sampling support
CiA 301 allows for a SYNC-driven Transmit-PDO to start sampling after a SYNC
message has been received (and before the synchronous window expires). It is now
possible to register a callback function with a TPDO (with
co_tpdo_set_sample_ind()
) that implements the sampling process. Once the
process is complete, the user calls co_tpdo_sample_res()
to report the result,
at which point the PDO is sent.
If the user does not register a sampling callback function, the current value of the mapped object is sent the moment the SYNC message is received.
Multiplex PDO support
Support for Multiplex PDOs (MPDOs) has been added to the Receive-PDO and Transmit-PDO services. Both destination address mode (DAM) and source address mode (SAM) MPDOs are supported.
The object dictionary supports event tracking for SAM-MPDOs. The user can call
co_dev_sam_mpdo_event()
to indicate that an event occurred for a specific
sub-object. If that sub-object is mapped to a SAM-MPDO, the PDO will
automatically be sent. The SetEvent()
method in the C++ CANopen application
library will invoke both co_dev_tpdo_event()
and co_dev_sam_mpdo_event()
, so
the user does not need to know to which type of PDO a sub-object is mapped.
DAM-MPDO events can be triggered with co_dam_mpdo_event()
and the
DamMpdoEvent()
method.
The RpdoRead()
/RpdoGet()
and OnRpdoWrite()
methods treat incoming
SAM-MPDOs the same as regular PDOs. For DAM-MPDOs the originating node-ID and
object (sub-)index are unknown. To respond to an incoming DAM-MPDO, use the
OnWrite()
callback for the local object dictionary. However, it is impossible
to distinguish between an incoming DAM-MPDO and an SDO download request.
dcfgen
does not yet create the object scanner and object dispatching lists
used by SAM-MPDOs. Support for SAM-MPDOs will be added in a future release.
MPDO support can be disabled by specifying the --disable-mpdo
to configure
or by defining the LELY_NO_CO_MPDO
preprocessor macro.
SDO download DCF requests
It is now possible for the user to issue a series of SDO download requests in
the form of a concise DCF, either from a file or a memory region. Concise DCF
SDO requests can be generated with dcfgen
. They were already supported as part
of the NMT configuration request sequence during the boot process, but now it is
possible to use them manually from an application.
The co_csdo_dn_dcf_req()
and co_dev_dn_dcf_req()
function can be used to
issue such requests to remote and local object dictionaries, respectively. In
the C++ CANopen application library, use the WriteDcf()
, SubmitWriteDcf()
and AsyncWriteDcf()
methods.
As part of the implementation, the co_val_read_file()
/co_val_read_frbuf()
and co_val_write_file()
/co_val_write_fwbuf()
helper functions were added to
read/write a CANopen value directly to/from a file (buffer).
Other
Utilities library:
- Re-entrant versions of the error string functions (
errc2str()
,errno2str()
anderrnum2str()
) are now available. Following the glibc convention, the names of the re-entrant versions end in_r
. - The fiber implementations in the utilities library have been unified. Fibers can now also be migrated between threads on platforms that support it.
- A
contains()
method has been added to all containers in the utilities library. This method allows the user to check if a node is present in a container. - The
pheap_find()
andpheap_remove()
methods if the pairing heap now have a non-recursive implementation. This prevents potential stack overflows when using those methods with large heaps. - Several 16-bit Unicode string functions (
str16len()
,str16ncmp()
andstr16ncpy()
) are now available in<lely/util/ustring.h>
. These functions are equivalent to the corresponding standard library functions, except that they operate on strings of typechar16_t
instead ofchar
.
Asynchronous I/O library:
- Synchronous CAN channel operations no longer require a valid executor in the constructor of the CAN channel. This allows synchronous operations to be used in the absence of an event loop.
- The
ENOTSUP
error from RTNETLINK is now ignored when creating a CAN controller on Linux. This enables support for SLCAN network interfaces, which do not provide CAN bus attributes.
CANopen and C++ CANopen application libraries:
- All CANopen services now have
start()
,stop()
andis_stopped()
methods. This allows the user to explicitly disable or enable a service after it is created. To not break backward compatibility, services are automatically started after they are created. - SDO block transfers can now be explicitly requested in the C++ CANopen
application library. The
co_csdo_blk_dn_val_req()
has been added to the C API to make block downloads more convenient. - The new
co_nmt_chk_bootup()
can be used to check if boot-up messages have been received for a specific node or for all mandatory nodes. This function is available even ifLELY_NO_CO_NMT_BOOT
is defined.
EDS/DCF tools:
- Bare
$NODEID
values are now allowed in EDS/DCF files. - Keys with empty values are now ignored in EDS/DCF files.
- Custom data types are now supported when using the EDS/DCF tools as a Python module. The command-line tools ignore unknown data types.
dcfgen
no longer configures the master to read sub-objects from object 1018h on the slave that do not exist. Only sub-index 1 (Vendor-ID) is mandatory. Sub-indices 2 (Product code), 3 (Revision number) and 4 (Serial number) are all optional.
CI/CD:
- The Docker images used in the CI/CD pipeline are now documented in
docker/README.md
. - The C and C++ code style is now checked with clang-format in the CI/CD pipeline.
- Python code style checks have been added using Black and Flake8.
API changes
The C++ interface of liblely-co is deprecated and will be removed in a future version. liblely-coapp now uses the C interface of liblely-co.
membuf_init()
now takes two extra parameters that can be used to initialize the buffer with a pre-existing memory region. This change is necessary to support static memory buffers whenLELY_NO_MALLOC
is defined.membuf_init(buf, NULL, 0)
is equivalent to the originalmembuf_init(buf)
.can_buf_init()
now also takes an extra parameter that can be used to initialize a CAN frame buffer with a pre-existing memory region. Thesize
parameter is now required to be a power of 2, instead of automatically being rounded up. Additionally, the unusedcan_buf_create()
andcan_buf_destroy()
functions have been removed.- Due to the new
start()
andstop()
methods of CANopen services, the existingco_time_start()
andco_time_stop()
functions have been renamed toco_time_start_prod()
andco_time_stop_prod()
, respectively.
Download
You can download the source from GitLab or the Ubuntu packages from our PPA.