While working on the updated specification I encountered the challenge of updating the principles of data type compatibility. An update is needed because we must take into account the new concept of Port ID and the new CAN ID layout, which lacks space for the major data type version number.
With the new approach to communication, nodes are no longer equipped with reliable methods of detecting data type compatibility problems. The following diagram illustrates the problem both for services and messages.
+-------------+ +-------------+
| | Request vA.0 | |
| |---------------------->| |
| Service | | Service |
| version A.0 | | version B.0 |
| | Response vB.0 |Service ID X |
| |<----------------------| |
+-------------+ +-------------+
+-------------+ +-------------+
| | | |
| Publisher | Message vA.0 | Subscriber |
| version A.0 |---------------------->| version B.0 |
|Subject ID Y | |Subject ID Y |
+-------------+ +-------------+
Generally, messages and services with dynamically assigned ID are not affected by this problem, because it is assumed that since a pre-configuration step is required (either manual or, eventually, perhaps in v1.1, automatic), it is up to the configuring entity (a human or a high-level controller) to ensure that the computation graph is conflict-free. Shall there be conflicting data types, the configuring entity should be able to map conflicting data types to different subjects/services (the umbrella term is “port”, pending approval), and if a conflict-free configuration turns out to be unattainable, it can always be detected very early where the cost of error is negligibly low (e.g., while the aircraft is being serviced as opposed to while it’s in the air).
Data types with fixed IDs, however, lack the ability of being remapped, due to the very fact that their IDs are fixed by design. It used to be possible to work around that by choosing the right handler dynamically, since the version information was attached directly to the transfer. That is no longer possible.
A simple solution that came to mind is to add a new version management rule: when the major version number of a data type definition is incremented (i.e., when either bit compatibility or semantic compatibility or both are broken), the static ID of the data type must be changed. When an old major version is removed from the data type set (after a lengthy deprecation period, of course), its static ID can be re-used for a new purpose – either for a newer major version of the same data type, or for a completely different data type.
Consider sirius_cyber_corp.golgafrincham_b_ark.CryopodStatus
as an example:
- First we came up with
62000.CryopodStatus.1.0.uavcan
. The static ID was chosen and released. - Newer minor versions are released. As minor versions are by definition compatible with each other, nodes can communicate with each other successfully while using definitions with different minor versions, e.g.,
62000.CryopodStatus.1.6.uavcan
. - A breaking change is required. Some nodes now have to support both the new version and the latest version released under the previous major version number. The new version has to use a different static ID in order to ensure lack of runtime type compatibility conflicts. Both versions can coexist on the same bus conflict-free, at the same time nodes are not burdened with additional runtime checks and extra logic – lack of conflict is ensured statically.
62000.CryopodStatus.1.6.uavcan
62001.CryopodStatus.2.0.uavcan
To summarize:
- Presence of a static ID can’t be changed under the same major version number. Meaning that if a new major version was first released with a static ID, it can’t be removed until a new major version is released. Likewise, if a new major version was released without a static ID, it can’t be added until a new major version is released.
- All revisions of a data type that share the same major version number have to use the same static ID. The implication is that the same static ID can’t be used for incompatible definitions.