Skip to content

Commit 1c8b97b

Browse files
authored
⚙ Added TOML config and CAN transport support. (#6)
Next PR will contain ready to use Node Exec Cmd functionality.
1 parent d7e318d commit 1c8b97b

File tree

10 files changed

+211
-19
lines changed

10 files changed

+211
-19
lines changed

.clang-tidy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Checks: >-
2727
-readability-avoid-const-params-in-decls,
2828
-readability-identifier-length,
2929
-*-use-designated-initializers,
30+
-*-use-ranges,
3031
-*-use-trailing-return-type,
3132
-*-named-parameter,
3233
-misc-include-cleaner,

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@
2020
path = submodules/spdlog
2121
url = https://github.com/gabime/spdlog.git
2222
branch = v1.x
23+
[submodule "submodules/toml11"]
24+
path = submodules/toml11
25+
url = https://github.com/ToruNiina/toml11.git

init.d/ocvsmd.toml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Cyphal application layer settings.
2+
[cyphal.application]
3+
# The ID assigned to OCVSMD Cyphal node.
4+
# Must be unique in the Cyphal network.
5+
# Supported values: 0-65534 (for UDP)
6+
node_id = 0
7+
# The Unique-ID (16 bytes) of the Cyphal node. Automatically generated on the first run.
8+
unique_id = []
9+
10+
# Cyphal transport layer settings.
11+
[cyphal.transport]
12+
# List of interfaces for the Cyphal network.
13+
# Up to three redundant homogeneous interfaces are supported.
14+
# UDP has priorioty over CAN if both types are present.
15+
# Supported formats:
16+
# - 'udp://<ip4>'
17+
# - 'socketcan:<can_device>'
18+
interfaces = [
19+
'udp://127.0.0.1',
20+
]
21+
22+
# File Server settings.
23+
[file_server]
24+
# List of file server roots.
25+
# The daemon will canonicalize paths and resolve symlinks.
26+
roots = [
27+
'.',
28+
]
29+
30+
# IPC server settings.
31+
[ipc]
32+
# Connection strings for the IPC server.
33+
# Currently, only one (the first) connection is supported.
34+
# Supported formats:
35+
# - 'tcp://*:<port>'
36+
# - 'tcp://<ip4>:<port>'
37+
# - 'tcp://[<ip6>]:<port>'
38+
# - 'unix:<file_path>'
39+
# - 'unix-abstract:<reverse-dns>' (linux only)
40+
connections = [
41+
'unix-abstract:org.opencyphal.ocvsmd.ipc',
42+
]
43+
44+
# Logging related settings.
45+
# See also README documentation for more details.
46+
[logging]
47+
# The path to the log file.
48+
file = '/var/log/ocvsmd.log'
49+
# Supported log levels: 'trace', 'debug', 'info', 'warning', 'error', 'critical', 'off'.
50+
level = 'info'
51+
# By default, the log file is not immediately flushed to disk (at `off` level).
52+
flush_level = 'off'
53+
54+
# Metadata of the configuration file.
55+
[__meta__]
56+
# The version of the configuration file.
57+
version = 1
58+
# The last modified date of the configuration file.
59+
last_modified = 1970-01-01T00:00:00Z
60+

src/common/ipc/server_router.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class ServerRouterImpl final : public ServerRouter
7070
void registerChannelFactory(const detail::ServiceDesc service_desc, //
7171
TypeErasedChannelFactory channel_factory) override
7272
{
73-
logger_->debug("Registering '{}' service (id=0x{:X}).", service_desc.name, service_desc.id);
73+
logger_->trace("Registering '{}' service (id=0x{:X}).", service_desc.name, service_desc.id);
7474
service_id_to_channel_factory_[service_desc.id] = std::move(channel_factory);
7575
}
7676

src/daemon/engine/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ target_include_directories(ocvsmd_engine
6363
target_include_directories(ocvsmd_engine SYSTEM
6464
PUBLIC ${submodules_dir}/cetl/include
6565
PUBLIC ${submodules_dir}/libcyphal/include
66+
PRIVATE ${submodules_dir}/toml11/include
6667
)
6768
add_dependencies(ocvsmd_engine
6869
${engine_transpiled}

src/daemon/engine/config.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55

66
#include "config.hpp"
7-
/*
7+
88
#include <cetl/pf17/cetlpf.hpp>
99

1010
#include <spdlog/spdlog.h>
@@ -18,7 +18,7 @@
1818
#include <string>
1919
#include <utility>
2020
#include <vector>
21-
*/
21+
2222
namespace ocvsmd
2323
{
2424
namespace daemon
@@ -27,7 +27,7 @@ namespace engine
2727
{
2828
namespace
2929
{
30-
/*
30+
3131
class ConfigImpl final : public Config
3232
{
3333
public:
@@ -143,15 +143,13 @@ class ConfigImpl final : public Config
143143
bool is_dirty_;
144144

145145
}; // ConfigImpl
146-
*/
146+
147147
} // namespace
148148

149-
Config::Ptr Config::make(const std::string& file_path) //
149+
Config::Ptr Config::make(std::string file_path)
150150
{
151-
(void) file_path; //
152-
return nullptr; //
153-
// ➕ auto root = toml::parse<ConfigImpl::TomlConf>(file_path);
154-
// ➕ return std::make_shared<ConfigImpl>(std::move(file_path), std::move(root));
151+
auto root = toml::parse<ConfigImpl::TomlConf>(file_path);
152+
return std::make_shared<ConfigImpl>(std::move(file_path), std::move(root));
155153
}
156154

157155
} // namespace engine

src/daemon/engine/config.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class Config
3333
using UniqueId = std::array<std::uint8_t, 16>; // NOLINT(*-magic-numbers)
3434
};
3535

36-
CETL_NODISCARD static Ptr make(const std::string& file_path); //
36+
CETL_NODISCARD static Ptr make(std::string file_path);
3737

3838
Config(const Config&) = delete;
3939
Config(Config&&) noexcept = delete;
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//
2+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
// SPDX-License-Identifier: MIT
4+
//
5+
6+
#ifndef OCVSMD_DAEMON_ENGINE_CYPHAL_CAN_TRANSPORT_BAG_HPP_INCLUDED
7+
#define OCVSMD_DAEMON_ENGINE_CYPHAL_CAN_TRANSPORT_BAG_HPP_INCLUDED
8+
9+
#include "any_transport_bag.hpp"
10+
#include "config.hpp"
11+
#include "platform/can/can_media.hpp"
12+
#include "transport_helpers.hpp"
13+
14+
#include <cetl/pf17/cetlpf.hpp>
15+
#include <libcyphal/executor.hpp>
16+
#include <libcyphal/transport/can/can_transport.hpp>
17+
#include <libcyphal/transport/can/can_transport_impl.hpp>
18+
#include <libcyphal/transport/errors.hpp>
19+
#include <libcyphal/types.hpp>
20+
21+
#include <cstddef>
22+
#include <memory>
23+
#include <utility>
24+
25+
namespace ocvsmd
26+
{
27+
namespace daemon
28+
{
29+
namespace engine
30+
{
31+
namespace cyphal
32+
{
33+
34+
/// Holds (internally) instance of the CAN transport and its media (if any).
35+
///
36+
class CanTransportBag final : public AnyTransportBag
37+
{
38+
/// Defines private specification for making interface unique ptr.
39+
///
40+
struct Spec
41+
{
42+
explicit Spec() = default;
43+
};
44+
45+
public:
46+
Transport& getTransport() const override
47+
{
48+
CETL_DEBUG_ASSERT(transport_, "");
49+
return *transport_;
50+
}
51+
52+
static Ptr make(cetl::pmr::memory_resource& memory, libcyphal::IExecutor& executor, const Config::Ptr& config)
53+
{
54+
CETL_DEBUG_ASSERT(config, "");
55+
56+
static const std::string can_prefix{"socketcan:"};
57+
58+
std::string can_ifaces;
59+
for (const auto& iface : config->getCyphalTransportInterfaces())
60+
{
61+
if (0 == iface.compare(0, can_prefix.size(), can_prefix))
62+
{
63+
can_ifaces += iface.substr(can_prefix.size());
64+
can_ifaces += ",";
65+
}
66+
}
67+
common::getLogger("io")->trace("Attempting to create CAN transport (ifaces='{}')…", can_ifaces);
68+
69+
auto transport_bag = std::make_unique<CanTransportBag>(Spec{}, memory, executor);
70+
71+
auto& media_collection = transport_bag->media_collection_;
72+
media_collection.parse(can_ifaces);
73+
if (media_collection.count() == 0)
74+
{
75+
return nullptr;
76+
}
77+
78+
auto maybe_transport = makeTransport({memory}, executor, media_collection.span(), TxQueueCapacity);
79+
if (const auto* failure = cetl::get_if<libcyphal::transport::FactoryFailure>(&maybe_transport))
80+
{
81+
(void) failure;
82+
common::getLogger("io")->warn("Failed to create CAN transport.");
83+
return nullptr;
84+
}
85+
transport_bag->transport_ = cetl::get<TransportPtr>(std::move(maybe_transport));
86+
87+
// To support redundancy (multiple homogeneous interfaces), it's important to have a non-default
88+
// handler which "swallows" expected transient failures (by returning `nullopt` result).
89+
// Otherwise, the default Cyphal behavior will fail/interrupt current and future transfers
90+
// if some of its media encounter transient failures - thus breaking the whole redundancy goal,
91+
// namely, maintain communication if at least one of the interfaces is still up and running.
92+
//
93+
transport_bag->transport_->setTransientErrorHandler([](auto&) { return cetl::nullopt; });
94+
// transport_bag->transport_->setTransientErrorHandler(TransportHelpers::CanTransientErrorReporter{});
95+
96+
common::getLogger("io")->debug("Created CAN transport (ifaces={}).", media_collection.count());
97+
return transport_bag;
98+
}
99+
100+
CanTransportBag(Spec, cetl::pmr::memory_resource& memory, libcyphal::IExecutor& executor)
101+
: memory_{memory}
102+
, executor_{executor}
103+
, media_collection_{memory, executor, memory}
104+
{
105+
}
106+
107+
private:
108+
using TransportPtr = libcyphal::UniquePtr<libcyphal::transport::can::ICanTransport>;
109+
110+
// Our current max `SerializationBufferSizeBytes` is 313 bytes (for `uavcan.node.GetInfo.Response.1.0`)
111+
// Assuming CAN classic presentation MTU of 7 bytes (plus a bit of overhead like CRC and stuff),
112+
// let's calculate the required TX queue capacity, and make it twice to accommodate 2 such messages.
113+
static constexpr std::size_t TxQueueCapacity = 2 * (313U + 8U) / 7U;
114+
115+
cetl::pmr::memory_resource& memory_;
116+
libcyphal::IExecutor& executor_;
117+
platform::can::CanMediaCollection media_collection_;
118+
TransportPtr transport_;
119+
120+
}; // CanTransportBag
121+
122+
} // namespace cyphal
123+
} // namespace engine
124+
} // namespace daemon
125+
} // namespace ocvsmd
126+
127+
#endif // OCVSMD_DAEMON_ENGINE_CYPHAL_CAN_TRANSPORT_BAG_HPP_INCLUDED

src/daemon/engine/engine.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
#include "engine.hpp"
77

88
#include "config.hpp"
9-
//#include "cyphal/can_transport_bag.hpp"
9+
#include "cyphal/can_transport_bag.hpp"
1010
// ➕ #include "cyphal/file_provider.hpp"
1111
#include "cyphal/udp_transport_bag.hpp"
12+
#include "engine_helpers.hpp"
1213
#include "io/socket_address.hpp"
1314
#include "ipc/pipe/server_pipe.hpp"
1415
#include "ipc/pipe/socket_server.hpp"
@@ -57,11 +58,11 @@ cetl::optional<std::string> Engine::init()
5758
}
5859
else
5960
{
60-
//if (auto maybe_can_transport_bag = cyphal::CanTransportBag::make(memory_, executor_, config_))
61-
//{
62-
// any_transport_bag_ = std::move(maybe_can_transport_bag);
63-
//}
64-
//else
61+
if (auto maybe_can_transport_bag = cyphal::CanTransportBag::make(memory_, executor_, config_))
62+
{
63+
any_transport_bag_ = std::move(maybe_can_transport_bag);
64+
}
65+
else
6566
{
6667
std::string msg = "Failed to create Cyphal transport.";
6768
logger_->error(msg);
@@ -169,9 +170,9 @@ void Engine::runWhile(const std::function<bool()>& loop_predicate)
169170
timeout = std::min(timeout, spin_result.next_exec_time.value() - executor_.now());
170171
}
171172

172-
if (const auto maybe_poll_failure = executor_.pollAwaitableResourcesFor(cetl::make_optional(timeout)))
173+
if (const auto poll_failure = executor_.pollAwaitableResourcesFor(cetl::make_optional(timeout)))
173174
{
174-
spdlog::warn("Failed to poll awaitable resources.");
175+
spdlog::warn("Failed to poll awaitable resources (err={}).", failureToErrorCode(*poll_failure));
175176
}
176177
}
177178
spdlog::debug("Run loop predicate is fulfilled (worst_lateness={}us).",

submodules/toml11

Submodule toml11 added at 499be3c

0 commit comments

Comments
 (0)