Skip to content

Commit

Permalink
Cyphalize and release v1.0 (#15)
Browse files Browse the repository at this point in the history
* Cyphalize
* Update catch, clang-format, Yakut
* Release v1.0
  • Loading branch information
pavel-kirienko authored May 9, 2022
1 parent 39496f3 commit 2b9c7f6
Show file tree
Hide file tree
Showing 18 changed files with 10,939 additions and 4,982 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
tests
- working-directory: ${{github.workspace}}/build
run: |
yakut compile https://github.com/UAVCAN/public_regulated_data_types/archive/refs/heads/master.zip
yakut compile https://github.com/OpenCyphal/public_regulated_data_types/archive/refs/heads/master.zip
make VERBOSE=1 -j2
make test
- uses: actions/upload-artifact@v2
Expand Down Expand Up @@ -76,7 +76,7 @@ jobs:
- working-directory: ${{github.workspace}}/build
run: |
make VERBOSE=1 -j2
yakut compile https://github.com/UAVCAN/public_regulated_data_types/archive/refs/heads/master.zip
yakut compile https://github.com/OpenCyphal/public_regulated_data_types/archive/refs/heads/master.zip
make test
- uses: actions/upload-artifact@v2
if: always()
Expand All @@ -101,12 +101,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: DoozyX/clang-format-lint-action@v0.12
- uses: DoozyX/clang-format-lint-action@v0.13
with:
source: ./kocherga ./tests
exclude: ./tests/3rd_party
extensions: cpp,hpp
clangFormatVersion: 12
clangFormatVersion: 13
- run: |
pip install black
black --check ./tools/
Expand Down Expand Up @@ -167,7 +167,7 @@ jobs:
run: |
cmake tests -DCMAKE_BUILD_TYPE=Debug -DNO_STATIC_ANALYSIS=1 -DCMAKE_CXX_FLAGS='-DNDEBUG=1'
build-wrapper-linux-x86-64 --out-dir . make all VERBOSE=1
yakut compile https://github.com/UAVCAN/public_regulated_data_types/archive/refs/heads/master.zip
yakut compile https://github.com/OpenCyphal/public_regulated_data_types/archive/refs/heads/master.zip
make test
gcov --preserve-paths --long-file-names $(find integration/CMakeFiles/bootloader.dir -name '*.gcno')
gcov --preserve-paths --long-file-names $(find unit/CMakeFiles/test_cov.dir -name '*.gcno')
Expand Down
40 changes: 20 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,20 @@ boot. Only if a valid `AppInfo` structure is found the application will be launc
structure closer to the beginning of the image in order to speed up its verification. The structure is defined as
follows:

Offset | Type | Description
-------|----------|-----------------------------------------------------------------------------------------------------
-16 |`uint64` | Constant value 0x5E4415146FC0C4C7 used for locating the descriptor and detecting the byte order.
-8 |`uint8[8]`| Set to `APDesc00`; used for compatibility with legacy deployments.
0 |`uint64` | CRC-64-WE of the entire application image when this field itself is set to zero.
8 |`uint32` | Size of the application image, in bytes. Note that the image must be padded to eight bytes.
12 |`void32` | Reserved. Used to contain the 32-bit version control system revision ID; see replacement below.
16 |`uint8[2]`| Major and minor semantic version numbers.
18 |`uint8` | Flags: 1 - this is a release build; 2 - this is a dirty build (uncommitted changes present).
19 |`void8` | Reserved; set to 0.
20 |`uint32` | UNIX UTC build timestamp; i.e., the number of seconds since 1970-01-01T00:00:00Z.
24 |`uint64` | Version control system (VCS) revision ID (e.g., the git commit hash).
32 |`void64` | Reserved.
40 |`void64` | Reserved.
| Offset | Type | Description |
|--------|------------|--------------------------------------------------------------------------------------------------|
| -16 | `uint64` | Constant value 0x5E4415146FC0C4C7 used for locating the descriptor and detecting the byte order. |
| -8 | `uint8[8]` | Set to `APDesc00`; used for compatibility with legacy deployments. |
| 0 | `uint64` | CRC-64-WE of the entire application image when this field itself is set to zero. |
| 8 | `uint32` | Size of the application image, in bytes. Note that the image must be padded to eight bytes. |
| 12 | `void32` | Reserved. Used to contain the 32-bit version control system revision ID; see replacement below. |
| 16 | `uint8[2]` | Major and minor semantic version numbers. |
| 18 | `uint8` | Flags: 1 - this is a release build; 2 - this is a dirty build (uncommitted changes present). |
| 19 | `void8` | Reserved; set to 0. |
| 20 | `uint32` | UNIX UTC build timestamp; i.e., the number of seconds since 1970-01-01T00:00:00Z. |
| 24 | `uint64` | Version control system (VCS) revision ID (e.g., the git commit hash). |
| 32 | `void64` | Reserved. |
| 40 | `void64` | Reserved. |

When computing the application image CRC, the process will eventually encounter the location where the CRC itself is
stored. In order to avoid recursive dependency, the CRC storage location must be replaced with zero bytes when
Expand Down Expand Up @@ -94,12 +94,12 @@ The following diagram documents the state machine of the bootloader:

The bootloader states are mapped onto Cyphal node states as follows:

Bootloader state | Node mode | Node health| Vendor-specific status code
---------------------|-----------------|------------|-------------------------------
NoAppToBoot |`SOFTWARE_UPDATE`| `WARNING` | 0
BootDelay |`SOFTWARE_UPDATE`| `NOMINAL` | 0
BootCancelled |`SOFTWARE_UPDATE`| `ADVISORY` | 0
AppUpdateInProgress |`SOFTWARE_UPDATE`| `NOMINAL` | number of read requests, always >0
| Bootloader state | Node mode | Node health | Vendor-specific status code |
|---------------------|-------------------|-------------|------------------------------------|
| NoAppToBoot | `SOFTWARE_UPDATE` | `WARNING` | 0 |
| BootDelay | `SOFTWARE_UPDATE` | `NOMINAL` | 0 |
| BootCancelled | `SOFTWARE_UPDATE` | `ADVISORY` | 0 |
| AppUpdateInProgress | `SOFTWARE_UPDATE` | `NOMINAL` | number of read requests, always >0 |

### API usage

Expand Down
16 changes: 8 additions & 8 deletions kocherga/kocherga.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
#include <optional>
#include <type_traits>

#define KOCHERGA_VERSION_MAJOR 0 // NOLINT NOSONAR
#define KOCHERGA_VERSION_MINOR 2 // NOLINT NOSONAR
#define KOCHERGA_VERSION_MAJOR 1 // NOLINT NOSONAR
#define KOCHERGA_VERSION_MINOR 0 // NOLINT NOSONAR

#ifndef KOCHERGA_ASSERT
# include <cassert>
Expand All @@ -31,7 +31,7 @@ using TransferID = std::uint64_t;
using NodeID = std::uint16_t;
using PortID = std::uint16_t;

/// UAVCAN subjects used by Kocherga.
/// Cyphal subjects used by Kocherga.
enum class SubjectID : PortID
{
NodeHeartbeat = 7509,
Expand All @@ -40,16 +40,16 @@ enum class SubjectID : PortID
DiagnosticRecord = 8184,
};

/// UAVCAN services used by Kocherga.
/// Cyphal services used by Kocherga.
enum class ServiceID : PortID
{
FileRead = 408,
NodeGetInfo = 430,
NodeExecuteCommand = 435,
};

/// Version of the UAVCAN specification implemented by this library, major and minor.
static constexpr SemanticVersion UAVCANSpecificationVersion{{1, 0}};
/// Version of the Cyphal specification implemented by this library, major and minor.
static constexpr SemanticVersion CyphalSpecificationVersion{{1, 0}};

/// The service response timeout used by the bootloader.
/// This value applies when the bootloader invokes uavcan.file.Read during the update.
Expand Down Expand Up @@ -770,8 +770,8 @@ class Presenter final : public IReactor
const auto app_info = controller_.getAppInfo();
auto* const base_ptr = out_response;
auto* ptr = base_ptr;
*ptr++ = UAVCANSpecificationVersion.at(0);
*ptr++ = UAVCANSpecificationVersion.at(1);
*ptr++ = CyphalSpecificationVersion.at(0);
*ptr++ = CyphalSpecificationVersion.at(1);
*ptr++ = system_info_.hardware_version.at(0);
*ptr++ = system_info_.hardware_version.at(1);
if (app_info)
Expand Down
48 changes: 24 additions & 24 deletions kocherga/kocherga_can.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ inline auto makePseudoUniqueID(const SystemInfo::UniqueID& uid) -> std::uint64_t

/// If the local-ID is provided, the filter will match only on service transfers addressed to the local node.
/// If the local-ID is not provided, the filter will match on PnP allocation response messages only.
template <std::uint8_t UAVCANVersion>
template <std::uint8_t Version>
[[nodiscard]] auto makeAcceptanceFilter(const std::optional<std::uint8_t> local_node_id) -> CANAcceptanceFilterConfig;
template <>
[[nodiscard]] inline auto makeAcceptanceFilter<0>(const std::optional<std::uint8_t> local_node_id)
Expand Down Expand Up @@ -383,7 +383,7 @@ struct ServiceFrameModel : FrameModel
const std::uint8_t transfer_id = (tail & MaxTransferID);
if (start_of_transfer && (!toggle))
{
return {}; // UAVCAN v0
return {}; // DroneCAN
}
if (((!start_of_transfer) || (!end_of_transfer)) && (out_payload_size == 0))
{
Expand Down Expand Up @@ -450,7 +450,7 @@ struct ServiceFrameModel : FrameModel
-> std::optional<std::variant<MessageFrameModel, ServiceFrameModel>>
{
KOCHERGA_ASSERT(payload != nullptr);
if ((payload_size < 1) || (payload_size > 8)) // Legacy UAVCAN v0 is compatible only with Classic CAN.
if ((payload_size < 1) || (payload_size > 8)) // Legacy is compatible only with Classic CAN.
{ // This is because the low granularity of DLC in CAN FD breaks TAO.
return {};
}
Expand All @@ -462,7 +462,7 @@ struct ServiceFrameModel : FrameModel
const std::uint8_t transfer_id = (tail & MaxTransferID);
if (start_of_transfer && toggle)
{
return {}; // UAVCAN v1
return {}; // Cyphal
}
const auto priority = static_cast<std::uint8_t>((extended_can_id >> 24U) & 31U);
const auto source_node_id = static_cast<std::uint8_t>(extended_can_id & 0x7FU);
Expand Down Expand Up @@ -671,7 +671,7 @@ class BasicTransferReasmV0
}
if (frame.payload_size > (buffer_.size() - state_->payload_size))
{
state_.reset(); // Too much payload -- UAVCAN v0 does not define payload truncation.
state_.reset(); // Too much payload -- DroneCAN does not define payload truncation.
return {};
}
std::copy_n(frame.payload, frame.payload_size, buffer_.begin() + state_->payload_size);
Expand Down Expand Up @@ -778,7 +778,7 @@ class BasicServiceTransferReasmV0 : public BasicTransferReasmV0<MaxPayloadSize>
const std::uint8_t local_node_id_;
};

/// Send one UAVCAN/CAN v1 transfer. The push_frame callback is invoked per each transmitted frame; its type should be:
/// Send one Cyphal/CAN transfer. The push_frame callback is invoked per each transmitted frame; its type should be:
/// (std::size_t, const std::uint8_t*) -> bool
/// The return value is true on success, false otherwise.
/// The callback shall not be an std::function<> or std::bind<> to avoid heap allocation.
Expand Down Expand Up @@ -1070,7 +1070,7 @@ class IActivity
auto operator=(IActivity&&) -> IActivity& = delete;
};

/// The v0 main activity is a simplified translation layer between UAVCAN/CAN v1 and UAVCAN v0.
/// The v0 main activity is a simplified translation layer between Cyphal/CAN and DroneCAN.
/// The following ports are supported:
///
/// Service RX TX
Expand Down Expand Up @@ -1437,7 +1437,7 @@ class V0MainActivity : public IActivity
BasicServiceTransferReasmV0<300> rx_res_file_read_{FileReadSignature, local_node_id_};
};

/// The following example shows the CAN exchange dump collected from a real network using the old UAVCAN v0 GUI Tool.
/// The following example shows the CAN exchange dump collected from a real network using the old GUI Tool.
///
/// Unique-ID of the allocatee: 35 FF D5 05 50 59 31 34 61 41 23 43 00 00 00 00
/// Preferred node-ID: 0 (any, no preference)
Expand Down Expand Up @@ -1619,7 +1619,6 @@ class V0NodeIDAllocationActivity : public IActivity
KOCHERGA_ASSERT((0 <= randomized) && (randomized <= delta));
const auto delay = std::max(std::chrono::microseconds(1), range.first) + std::chrono::microseconds(randomized);
KOCHERGA_ASSERT(range.first <= delay);
KOCHERGA_ASSERT(range.second >= delay);
deadline_ = now + delay;
}

Expand Down Expand Up @@ -2090,17 +2089,17 @@ class VersionDetectionActivity : public IActivity
while (const auto frame = driver_.pop(buf))
{
const auto [can_id, payload_size] = *frame;
if (payload_size > 0) // UAVCAN frames are guaranteed to contain the tail byte always.
if (payload_size > 0) // Cyphal frames are guaranteed to contain the tail byte always.
{
if (const auto uavcan_version = tryDetectVersionFromFrame(can_id, buf.at(payload_size - 1U)))
if (const auto version = tryDetectVersionFromFrame(can_id, buf.at(payload_size - 1U)))
{
if (!highest_version_seen_)
{
deadline_ = uptime + ListeningPeriod;
}
if ((!highest_version_seen_) || (*highest_version_seen_ < *uavcan_version))
if ((!highest_version_seen_) || (*highest_version_seen_ < *version))
{
highest_version_seen_ = uavcan_version;
highest_version_seen_ = version;
KOCHERGA_ASSERT(highest_version_seen_);
}
}
Expand All @@ -2114,7 +2113,7 @@ class VersionDetectionActivity : public IActivity
-> std::optional<std::uint8_t>
{
// CAN ID is not validated at the moment. This may be improved in the future to avoid misdetection if there
// are other protocols besides UAVCAN on the same network.
// are other protocols besides Cyphal on the same network.
(void) can_id;
if ((tail_byte & TailByteStartOfTransfer) != 0)
{
Expand Down Expand Up @@ -2200,22 +2199,23 @@ class BitrateDetectionActivity : public IActivity

} // namespace detail

/// Kocherga node implementing the UAVCAN/CAN v1 transport along with UAVCAN v0 with automatic version detection.
/// Kocherga node implementing the Cyphal/CAN transport along with DroneCAN with automatic version detection.
class CANNode : public kocherga::INode
{
public:
/// The local UID shall be the same that is passed to the bootloader. It is used for PnP node-ID allocation.
/// The protocol version is 0 for DroneCAN (derived from legacy UAVCAN v0) and 1 for Cyphal/CAN.
/// By default, this implementation will auto-detect the parameters of the network and do a PnP node-ID allocation.
/// The application can opt-out of autoconfiguration by providing the required data to the constructor.
/// Unknown parameters shall be set to empty options.
CANNode(ICANDriver& driver,
const SystemInfo::UniqueID& local_unique_id,
const std::optional<ICANDriver::Bitrate>& can_bitrate = {},
const std::optional<std::uint8_t> uavcan_version = {},
const std::optional<NodeID> local_node_id = {})
const std::optional<ICANDriver::Bitrate>& can_bitrate = {},
const std::optional<std::uint8_t> protocol_version = {},
const std::optional<NodeID> local_node_id = {})
{
if ((activity_ == nullptr) && can_bitrate && //
uavcan_version && (*uavcan_version == 0) && //
if ((activity_ == nullptr) && can_bitrate && //
protocol_version && (*protocol_version == 0) && //
local_node_id && (*local_node_id > 0) && (*local_node_id <= MaxNodeID))
{
if (const auto bus_mode =
Expand All @@ -2229,8 +2229,8 @@ class CANNode : public kocherga::INode
static_cast<std::uint8_t>(*local_node_id));
}
}
if ((activity_ == nullptr) && can_bitrate && //
uavcan_version && (*uavcan_version == 1) && //
if ((activity_ == nullptr) && can_bitrate && //
protocol_version && (*protocol_version == 1) && //
local_node_id && (*local_node_id <= MaxNodeID))
{
if (const auto bus_mode =
Expand All @@ -2244,7 +2244,7 @@ class CANNode : public kocherga::INode
static_cast<std::uint8_t>(*local_node_id));
}
}
if ((activity_ == nullptr) && can_bitrate && uavcan_version && (*uavcan_version == 0))
if ((activity_ == nullptr) && can_bitrate && protocol_version && (*protocol_version == 0))
{
if (const auto bus_mode = driver.configure(*can_bitrate, false, detail::makeAcceptanceFilter<0>({})))
{
Expand All @@ -2255,7 +2255,7 @@ class CANNode : public kocherga::INode
*can_bitrate);
}
}
if ((activity_ == nullptr) && can_bitrate && uavcan_version && (*uavcan_version == 1))
if ((activity_ == nullptr) && can_bitrate && protocol_version && (*protocol_version == 1))
{
if (const auto bus_mode = driver.configure(*can_bitrate, false, detail::makeAcceptanceFilter<1>({})))
{
Expand Down
4 changes: 2 additions & 2 deletions kocherga/kocherga_serial.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ struct Transfer
const std::uint8_t* payload = nullptr;
};

/// UAVCAN/serial stream parser. Extracts UAVCAN/serial frames from raw stream of bytes in constant time.
/// Cyphal/serial stream parser. Extracts Cyphal/serial frames from raw stream of bytes in constant time.
/// Frames that contain more than MaxPayloadSize bytes of payload are rejected as invalid.
template <std::size_t MaxPayloadSize>
class StreamParser
Expand Down Expand Up @@ -465,7 +465,7 @@ class ISerialPort
auto operator=(ISerialPort&&) -> ISerialPort& = delete;
};

/// Kocherga node implementing the UAVCAN/serial transport.
/// Kocherga node implementing the Cyphal/serial transport.
class SerialNode : public kocherga::INode
{
public:
Expand Down
Loading

0 comments on commit 2b9c7f6

Please sign in to comment.