Independent CANopen implementations in containers, with one command-line contract, for interoperability testing of CANopen software such as go-canopen. Nothing in this repository depends on go-canopen.
Every image runs one upstream CANopen stack behind the same commands, flags,
JSON documents and exit codes (container contract,
JSON Schemas): as a device (SDO server, NMT slave, heartbeat,
EMCY and PDO producer with the object dictionary of a shared EDS) and as a
master (SDO client with block transfer, NMT master, heartbeat and EMCY
consumers, PDO and SYNC), plus an EDS/DCF checker. Contract 1.1 adds LSS
(CiA 305) masters and unconfigured LSS slaves, an RPDO sender with the SYNC
window, multiplexed PDOs (DAM/SAM), the CiA 302 boot-up of a network with
concise DCFs, and concise DCF generators. A self-test runs the stacks
against each other on Linux virtual CAN (vcan), so a test suite can trust
a reference before it trusts a result against it.
| Adapter | Upstream | Version | Language | Licence | Operations |
|---|---|---|---|---|---|
canopennode |
CANopenNode with CANopenLinux (canopend) |
v4.1 | C | Apache-2.0 | device (also LSS slave), sdo-upload, sdo-download, nmt, listen, lss |
lely |
Lely CANopen (lely-core) | v2.3.5 | C/C++ | Apache-2.0 | device (also LSS slave), sdo-upload, sdo-download, nmt, listen, pdo-receive, eds-check, lss, mpdo-send, mpdo-receive, boot, cdcf |
pythoncanopen |
canopen on python-can 4.6.1 | 2.4.1 | Python | MIT (python-can: LGPL-3.0) | device, sdo-upload, sdo-download, nmt, listen, pdo-configure, pdo-receive, sync, eds-check, cia402, lss, rpdo-send |
- canopennode: the device is
canopendwith an object dictionary generated from the shared EDS by CANopenEditor v4.2.3 at build time (with-i 0xFFan unconfigured LSS slave); the master iscanopendwith CANopenNode's example object dictionary, driven through its CiA 309-3 ASCII gateway (SDO, NMT and the LSS commands). - lely: a small C++ program on liblely-coapp (
BasicSlave,AsyncMaster,Sdo,LssMaster) and liblely-co (NMT command, RPDO service, MPDO, concise DCF writer), and Lely's Python dcf-tools (dcfchkforeds-check,dcfgenforbootandcdcf). - pythoncanopen:
LocalNode,RemoteNode(also its RPDOs),BaseNode402, the SYNC producer, the LSS master and the EDS importer of python canopen.
No pinned stack implements CANopen FD (CiA 1301); see LIMITATIONS.md.
Capabilities per image: docs/CAPABILITIES.md and
print-capabilities. What each stack cannot do: LIMITATIONS.md.
fixtures/eds/interop-device.eds (CiA 306
EDS, in every image as /fixtures/eds/interop-device.eds): the
communication objects of CiA 301 (0x1000–0x1A03, four RPDOs and TPDOs, TPDO1
mapping the counter 0x2002 event-driven) and a test area 0x2000–0x200A
(every basic type, a 64-character string, a DOMAIN for segmented and block
transfers, read-only, write-only and range-limited objects).
docs/FIXTURES.md lists every object.
On Linux with the CAN modules (Ubuntu: linux-modules-extra-$(uname -r)):
make vcan # vcan0 (sudo)
make images # every image for this machine's architecture
make smoke selftest # contract smoke test, reference-to-reference self-testOn macOS or with Docker Desktop (no CAN in its kernel): make images smoke selftest-eds runs everything that does not need a CAN interface.
Use a published image directly:
docker run --rm --network host ghcr.io/otfabric/canopen-interop-lely:v0.2.0 \
device --interface vcan0 --node-id 5 --eds /fixtures/eds/interop-device.eds --timeout-ms 60000 &
docker run --rm --network host ghcr.io/otfabric/canopen-interop-canopennode:v0.2.0 \
sdo-upload --interface vcan0 --node-id 5 --index 0x1008CAN interfaces belong to a network namespace and ordinary Docker networks do
not carry CAN, so containers run with --network host against vcan
interfaces of the host namespace.
scripts/selftest.py (TESTING.md) runs, capability driven,
every master against every device: the object dictionary of the shared EDS
read on every device; SDO expedited, segmented and block upload and download
with read-back and the device's own view of the written values; SDO aborts;
NMT commands and the heartbeat states that follow, through each master's
heartbeat consumer; EMCY; event-driven, configured and SYNC-driven TPDOs;
and the EDS/DCF checkers on every fixture; since 1.1 also RPDOs with the
SYNC window, every LSS master against every LSS slave (inquire, configure
node-ID, store, bit timing, fastscan, assign-all), DAM/SAM-MPDOs, the CiA 302
boot-up of a network of all three devices with concise DCFs, and the
concise DCFs byte for byte. Accepted differences are listed in
tests/known-differences.tsv and reported as
known, never as pass; a scenario a stack cannot do is skip with the
reason. Results: docs/COMPATIBILITY.md.
ghcr.io/otfabric/canopen-interop-otfabric is go-canopen behind the same
contract, so you can test your stack against ours the way we test ours against
yours. Despite the name it shares with the reference images, it is not an
independent implementation (its capabilities say upstream.independentEngine: false) and not part of a release of this repository, so its tags are
go-canopen versions: see own/go-canopen/README.md.
| Document | Content |
|---|---|
| docs/CONTAINER_CONTRACT.md | Commands, flags, documents, exit codes |
| docs/CAPABILITIES.md | Capability matrix |
| REQUIREMENTS.md | What this repository must and must not do |
| docs/COMPATIBILITY.md | Self-test results |
| docs/FIXTURES.md | The shared test device and the EDS/DCF fixtures |
| INTEROPERABILITY.md | How to test an implementation against these images |
| TESTING.md | Running the tests |
| LIMITATIONS.md | What is not covered, stack by stack |
| SECURITY.md | Reporting, image hardening, safety of CAN traffic |
| CONTRIBUTING.md | Adding an adapter, changing the contract |
| RELEASE.md / CHANGELOG.md | Releases |
| THIRD_PARTY_NOTICES.md | Upstream licences |
The adapters, scripts, schemas and fixtures of this repository are MIT licensed (LICENSE). Each image also contains its upstream stack under that stack's licence (THIRD_PARTY_NOTICES.md).