Skip to content

Commit 30948c7

Browse files
authored
📂 Added 'File Server' feature. (#10)
1 parent 00e5943 commit 30948c7

File tree

71 files changed

+3765
-198
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+3765
-198
lines changed

.github/workflows/tests.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ jobs:
7474
retention-days: 2
7575

7676
style_check:
77-
if: false #➕ Disable for now
7877
runs-on: ubuntu-latest
7978
steps:
8079
- uses: actions/checkout@v4

CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# SPDX-License-Identifier: MIT
44
#
55

6-
cmake_minimum_required(VERSION 3.27)
6+
cmake_minimum_required(VERSION 3.25)
77

88
project(ocvsmd
99
LANGUAGES C CXX
@@ -26,6 +26,13 @@ set(test_dir "${CMAKE_SOURCE_DIR}/test")
2626
set(include_dir "${CMAKE_SOURCE_DIR}/include")
2727
set(submodules_dir "${CMAKE_SOURCE_DIR}/submodules")
2828

29+
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
30+
# Disable PSABI warnings in GCC (on RPi).
31+
list(APPEND CXX_FLAG_SET "-Wno-psabi")
32+
endif()
33+
34+
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:${CXX_FLAG_SET}>")
35+
2936
# clang-format
3037
find_program(clang_format NAMES clang-format)
3138
if (NOT clang_format)

include/ocvsmd/sdk/daemon.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#ifndef OCVSMD_SDK_DAEMON_HPP_INCLUDED
77
#define OCVSMD_SDK_DAEMON_HPP_INCLUDED
88

9+
#include "file_server.hpp"
910
#include "node_command_client.hpp"
1011

1112
#include <cetl/cetl.hpp>
@@ -52,6 +53,13 @@ class Daemon
5253

5354
virtual ~Daemon() = default;
5455

56+
/// Gets a pointer to the shared entity which represents the File Server component of the OCVSMD engine.
57+
///
58+
/// @return Shared pointer to the client side of the File Server component.
59+
/// The component is always present in the OCVSMD engine, so the result is never `nullptr`.
60+
///
61+
virtual FileServer::Ptr getFileServer() const = 0;
62+
5563
/// Gets a pointer to the shared entity which represents the Node Exec Command component of the OCVSMD engine.
5664
///
5765
/// @return Shared pointer to the client side of the Node Exec Command component.

include/ocvsmd/sdk/execution.hpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,40 @@ class SenderOf
128128

129129
}; // SenderOf
130130

131+
/// A sender factory that returns a sender which completes immediately
132+
/// by calling the receiver `operator()` with a `Result` type instance built using the given pack of arguments.
133+
///
134+
/// @param args The arguments to construct the result of the sender.
135+
/// The result is constructed immediately (during this `just` call),
136+
/// and later (on `SenderOf::submit`) is `move`-d to the receiver.
137+
///
138+
template <typename Result, typename... Args>
139+
typename SenderOf<Result>::Ptr just(Args&&... args)
140+
{
141+
// Private implementation of the sender.
142+
class JustSender final : public SenderOf<Result>
143+
{
144+
public:
145+
explicit JustSender(Args&&... args)
146+
: result_{std::forward<Args>(args)...}
147+
{
148+
}
149+
150+
private:
151+
// SenderOf
152+
153+
void submitImpl(std::function<void(Result&&)>&& receiver) override
154+
{
155+
std::move(receiver)(std::move(result_));
156+
}
157+
158+
Result result_;
159+
160+
}; // JustSender
161+
162+
return std::make_unique<JustSender>(std::forward<Args>(args)...);
163+
}
164+
131165
/// Initiates an operation execution by submitting a given receiver to the sender.
132166
///
133167
/// Submit "consumes" the receiver (no longer usable after this call).

include/ocvsmd/sdk/file_server.hpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//
2+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
// SPDX-License-Identifier: MIT
4+
//
5+
6+
#ifndef OCVSMD_SDK_FILE_SERVER_HPP_INCLUDED
7+
#define OCVSMD_SDK_FILE_SERVER_HPP_INCLUDED
8+
9+
#include "execution.hpp"
10+
11+
#include <cetl/pf17/cetlpf.hpp>
12+
13+
#include <chrono>
14+
#include <memory>
15+
#include <string>
16+
#include <vector>
17+
18+
namespace ocvsmd
19+
{
20+
namespace sdk
21+
{
22+
23+
/// Defines client side interface of the OCVSMD File Server component.
24+
///
25+
class FileServer
26+
{
27+
public:
28+
/// Defines the shared pointer type for the interface.
29+
///
30+
using Ptr = std::shared_ptr<FileServer>;
31+
32+
// No copy/move semantics.
33+
FileServer(FileServer&&) = delete;
34+
FileServer(const FileServer&) = delete;
35+
FileServer& operator=(FileServer&&) = delete;
36+
FileServer& operator=(const FileServer&) = delete;
37+
38+
virtual ~FileServer() = default;
39+
40+
/// Makes async sender which emits a current list of the File Server root paths.
41+
///
42+
/// @return An execution sender which emits the async result of the operation.
43+
/// The returned paths are the same values as they were added by `pushRoot`.
44+
/// The entries are not unique, and the order is preserved.
45+
///
46+
struct ListRoots final
47+
{
48+
using Success = std::vector<std::string>;
49+
using Failure = int; // `errno`-like error code
50+
using Result = cetl::variant<Success, Failure>;
51+
};
52+
virtual SenderOf<ListRoots::Result>::Ptr listRoots() = 0;
53+
54+
/// Does nothing if such root path does not exist (no error reported).
55+
/// If such a path is listed more than once, only one copy is removed.
56+
/// The `back` flag determines whether the path is searched from the front or the back of the list.
57+
/// The flag has no effect if there are no duplicates.
58+
/// The changed list of paths is persisted by the daemon (in its configuration; on its exit),
59+
/// so the list will be automatically restored on the next daemon start.
60+
///
61+
struct PopRoot final
62+
{
63+
using Success = cetl::monostate; // like `void`
64+
using Failure = int; // `errno`-like error code
65+
using Result = cetl::variant<Success, Failure>;
66+
};
67+
/// Removes a root directory from the list of directories that the file server will serve.
68+
///
69+
/// @return An execution sender which emits the async result of the operation.
70+
///
71+
virtual SenderOf<PopRoot::Result>::Ptr popRoot(const std::string& path, const bool back) = 0;
72+
73+
/// When the file server handles a request, it will attempt to locate the path relative to each of its root
74+
/// directories. See Yakut for a hands-on example.
75+
/// The `path` could be a relative or an absolute path. In case of a relative path
76+
/// (without leading `/`), the daemon will resolve it against its current working directory.
77+
/// The daemon will internally canonicalize the path and resolve symlinks,
78+
/// and use real the file system path when matching and serving Cyphal network 'File' requests.
79+
/// The same path may be added multiple times to avoid interference across different clients.
80+
/// Currently, the path should be a directory (later we might support a direct file as well).
81+
/// The `back` flag determines whether the path is added to the front or the back of the list.
82+
/// The changed list of paths is persisted by the daemon (in its configuration; on its exit),
83+
/// so the list will be automatically restored on the next daemon start.
84+
///
85+
struct PushRoot final
86+
{
87+
using Success = cetl::monostate; // like `void`
88+
using Failure = int; // `errno`-like error code
89+
using Result = cetl::variant<Success, Failure>;
90+
};
91+
/// Adds a new root directory to the list of directories that the file server will serve.
92+
///
93+
/// @return An execution sender which emits the async result of the operation.
94+
///
95+
virtual SenderOf<PushRoot::Result>::Ptr pushRoot(const std::string& path, const bool back) = 0;
96+
97+
protected:
98+
FileServer() = default;
99+
100+
}; // FileServer
101+
102+
} // namespace sdk
103+
} // namespace ocvsmd
104+
105+
#endif // OCVSMD_SDK_FILE_SERVER_HPP_INCLUDED

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# SPDX-License-Identifier: MIT
44
#
55

6-
cmake_minimum_required(VERSION 3.27)
6+
cmake_minimum_required(VERSION 3.25)
77

88
add_subdirectory(common)
99
add_subdirectory(daemon)

src/cli/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# SPDX-License-Identifier: MIT
44
#
55

6-
cmake_minimum_required(VERSION 3.27)
6+
cmake_minimum_required(VERSION 3.25)
77

88
add_executable(ocvsmd-cli
99
main.cpp

src/cli/main.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,73 @@ int main(const int argc, const char** const argv)
111111
}
112112
}
113113
#endif
114+
#if 0 // NOLINT
115+
116+
// Demo of daemon's file server - push root.
117+
{
118+
using PushRoot = ocvsmd::sdk::FileServer::PushRoot;
119+
120+
auto file_server = daemon->getFileServer();
121+
122+
const std::string path{"key"};
123+
auto sender = file_server->pushRoot(path, true);
124+
auto cmd_result = ocvsmd::sdk::sync_wait<PushRoot::Result>(executor, std::move(sender));
125+
if (const auto* const err = cetl::get_if<PushRoot::Failure>(&cmd_result))
126+
{
127+
spdlog::error("Failed to push FS root (path='{}'): {}.", path, std::strerror(*err));
128+
}
129+
else
130+
{
131+
spdlog::info("File Server responded ok on 'PushRoot'.");
132+
}
133+
}
134+
#endif
135+
#if 0 // NOLINT
136+
137+
// Demo of daemon's file server - pop root.
138+
{
139+
using PopRoot = ocvsmd::sdk::FileServer::PopRoot;
140+
141+
auto file_server = daemon->getFileServer();
142+
143+
const std::string path{"key"};
144+
auto sender = file_server->popRoot(path, true);
145+
auto cmd_result = ocvsmd::sdk::sync_wait<PopRoot::Result>(executor, std::move(sender));
146+
if (const auto* const err = cetl::get_if<PopRoot::Failure>(&cmd_result))
147+
{
148+
spdlog::error("Failed to pop FS root (path='{}'): {}.", path, std::strerror(*err));
149+
}
150+
else
151+
{
152+
spdlog::info("File Server responded ok on 'PopRoot'.");
153+
}
154+
}
155+
#endif
156+
#if 1 // NOLINT
157+
158+
// Demo of daemon's file server - getting the list of roots.
159+
{
160+
using ListRoots = ocvsmd::sdk::FileServer::ListRoots;
161+
162+
auto file_server = daemon->getFileServer();
163+
164+
auto sender = file_server->listRoots();
165+
auto cmd_result = ocvsmd::sdk::sync_wait<ListRoots::Result>(executor, std::move(sender));
166+
if (const auto* const err = cetl::get_if<ListRoots::Failure>(&cmd_result))
167+
{
168+
spdlog::error("Failed to list FS roots: {}", std::strerror(*err));
169+
}
170+
else
171+
{
172+
const auto roots = cetl::get<ListRoots::Success>(std::move(cmd_result));
173+
spdlog::info("File Server responded with list of roots (cnt={}):", roots.size());
174+
for (std::size_t i = 0; i < roots.size(); ++i)
175+
{
176+
spdlog::info("{:4} → '{}'", i, roots[i]);
177+
}
178+
}
179+
}
180+
#endif
114181

115182
if (g_running == 0)
116183
{

src/common/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# SPDX-License-Identifier: MIT
44
#
55

6-
cmake_minimum_required(VERSION 3.27)
6+
cmake_minimum_required(VERSION 3.25)
77

88
set(dsdl_ocvsmd_dir ${CMAKE_CURRENT_SOURCE_DIR}/dsdl/ocvsmd)
99
file(GLOB_RECURSE dsdl_ocvsmd_files CONFIGURE_DEPENDS ${dsdl_ocvsmd_dir}/*.dsdl)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
@extent 512 * 8
3+
4+
---
5+
6+
uavcan.file.Path.2.0 item
7+
8+
@extent 512 * 8

0 commit comments

Comments
 (0)