Skip to content

Commit 11aaeaf

Browse files
authored
Merge pull request #229 from 107-systems/port-id-config-reg
[example/host] Port ID shall be configurable via register.
2 parents 09696be + 718dd4d commit 11aaeaf

File tree

5 files changed

+243
-18
lines changed

5 files changed

+243
-18
lines changed

examples/host-example-01-opencyphal-basic-node/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set(EXAMPLE_01_TARGET host-example-01-basic-cyphal-node)
66
add_executable(${EXAMPLE_01_TARGET}
77
host-example-01-opencyphal-basic-node.cpp
88
socketcan.c
9+
kv_host.cpp
910
)
1011
##########################################################################
1112
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)

examples/host-example-01-opencyphal-basic-node/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,31 @@ $ y r 42 cyphal.node.id
4343

4444
$ y r 42 cyphal.node.id 12
4545
12
46+
47+
$ y r 42 cyphal.pub.counter.id
48+
65535
49+
50+
$ y r 42 cyphal.pub.counter.id 1001
51+
1001
52+
```
53+
54+
Store changed configuration and restart node:
55+
```bash
56+
$ y cmd 42 store
57+
$ y cmd 42 restart
58+
```
59+
**Note**: `restart` is implemented in this example as `exit(0)`, hence you need to restart the application yourself.
60+
61+
Subscribe to counter subject:
62+
```bash
63+
y sub 1001:uavcan.primitive.scalar.Integer8.1.0 --with-metadata
64+
---
65+
1001:
66+
_meta_: {ts_system: 1691741133.026175, ts_monotonic: 25484.463219, source_node_id: 12, transfer_id: 1, priority: nominal, dtype: uavcan.primitive.scalar.Integer8.1.0}
67+
value: 1
68+
---
69+
1001:
70+
_meta_: {ts_system: 1691741138.027977, ts_monotonic: 25489.465034, source_node_id: 12, transfer_id: 2, priority: nominal, dtype: uavcan.primitive.scalar.Integer8.1.0}
71+
value: 2
72+
...
4673
```

examples/host-example-01-opencyphal-basic-node/host-example-01-opencyphal-basic-node.cpp

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@
2020

2121
#include <cyphal++/cyphal++.h>
2222

23+
#include "kv_host.h"
2324
#include "socketcan.h"
2425

2526
/**************************************************************************************
2627
* CONSTANT
2728
**************************************************************************************/
2829

29-
static CanardPortID const DEFAULT_COUNTER_PORT_ID = 1001U;
30-
static uint16_t const DEFAULT_COUNTER_UPDATE_PERIOD_ms = 5*1000UL;
30+
static uint16_t const COUNTER_UPDATE_PERIOD_ms = 5*1000UL;
31+
static uint16_t const HEARTBEAT_UPDATE_PERIOD_ms = 1000UL;
3132

3233
/**************************************************************************************
3334
* TYPEDEF
@@ -41,6 +42,14 @@ typedef uavcan::primitive::scalar::Integer8_1_0 CounterMsg;
4142

4243
extern "C" CanardMicrosecond micros();
4344
extern "C" unsigned long millis();
45+
uavcan::node::ExecuteCommand::Response_1_1 onExecuteCommand_1_1_Request_Received(uavcan::node::ExecuteCommand::Request_1_1 const &);
46+
47+
/**************************************************************************************
48+
* GLOBAL VARIABLES
49+
**************************************************************************************/
50+
51+
static cyphal::support::platform::storage::host::KeyValueStorage kv_storage;
52+
static std::shared_ptr<cyphal::registry::Registry> node_registry;
4453

4554
/**************************************************************************************
4655
* MAIN
@@ -58,25 +67,45 @@ int main(int argc, char ** argv)
5867
cyphal::Node node_hdl(node_heap.data(), node_heap.size(), micros, [socket_can_fd] (CanardFrame const & frame) { return (socketcanPush(socket_can_fd, &frame, 1000*1000UL) > 0); });
5968
std::mutex node_mtx;
6069

61-
cyphal::Publisher<uavcan::node::Heartbeat_1_0> heartbeat_pub = node_hdl.create_publisher<uavcan::node::Heartbeat_1_0>
62-
(1*1000*1000UL /* = 1 sec in usecs. */);
70+
cyphal::Publisher<uavcan::node::Heartbeat_1_0> heartbeat_pub = node_hdl.create_publisher<uavcan::node::Heartbeat_1_0>(1*1000*1000UL /* = 1 sec in usecs. */);
6371

64-
cyphal::Publisher<CounterMsg> counter_pub = node_hdl.create_publisher<CounterMsg>
65-
(DEFAULT_COUNTER_PORT_ID, 1*1000*1000UL /* = 1 sec in usecs. */);
72+
cyphal::Publisher<CounterMsg> counter_pub;
6673

6774
/* REGISTER ***************************************************************************/
6875

6976
CanardNodeID node_id = node_hdl.getNodeId();
70-
CanardPortID counter_port_id = DEFAULT_COUNTER_PORT_ID;
71-
uint16_t counter_update_period_ms = DEFAULT_COUNTER_UPDATE_PERIOD_ms;
77+
CanardPortID counter_port_id = std::numeric_limits<CanardPortID>::max();
7278

73-
const auto node_registry = node_hdl.create_registry();
79+
node_registry = node_hdl.create_registry();
7480

7581
const auto reg_ro_node_description = node_registry->route ("cyphal.node.description", {true}, []() { return "basic-cyphal-node"; });
7682
const auto reg_ro_pub_counter_type = node_registry->route ("cyphal.pub.counter.type", {true}, []() { return "uavcan.primitive.scalar.Integer8.1.0"; });
77-
const auto reg_rw_node_id = node_registry->expose("cyphal.node.id", {}, node_id);
78-
const auto reg_rw_pub_counter_id = node_registry->expose("cyphal.pub.counter.id", {}, counter_port_id);
79-
const auto reg_rw_pub_counter_update_period_ms = node_registry->expose("cyphal.pub.counter.update_period_ms", {}, counter_update_period_ms);
83+
const auto reg_rw_node_id = node_registry->expose("cyphal.node.id", {true}, node_id);
84+
const auto reg_rw_pub_counter_id = node_registry->expose("cyphal.pub.counter.id", {true}, counter_port_id);
85+
86+
/* Configure service server for storing persistent
87+
* states upon command request.
88+
*/
89+
auto const rc_load = cyphal::support::load(kv_storage, *node_registry);
90+
if (rc_load.has_value()) {
91+
std::cerr << "cyphal::support::load failed with " << static_cast<int>(rc_load.value()) << std::endl;
92+
return EXIT_FAILURE;
93+
}
94+
95+
cyphal::ServiceServer execute_command_srv = node_hdl.create_service_server<
96+
uavcan::node::ExecuteCommand::Request_1_1,
97+
uavcan::node::ExecuteCommand::Response_1_1>(
98+
2*1000*1000UL,
99+
onExecuteCommand_1_1_Request_Received);
100+
101+
/* Update node configuration from register value.
102+
*/
103+
node_hdl.setNodeId(node_id);
104+
if (counter_port_id != std::numeric_limits<CanardPortID>::max())
105+
counter_pub = node_hdl.create_publisher<CounterMsg>(counter_port_id, 1*1000*1000UL /* = 1 sec in usecs. */);
106+
107+
std::cout << "Node #" << static_cast<int>(node_id) << std::endl
108+
<< "\tCounter Port ID: " << counter_port_id << std::endl;
80109

81110
/* NODE INFO **************************************************************************/
82111
const auto node_info = node_hdl.create_node_info
@@ -131,17 +160,14 @@ int main(int argc, char ** argv)
131160

132161
for (;;)
133162
{
134-
/* Update node configuration from register values. */
135-
node_hdl.setNodeId(node_id);
136-
137163
{
138164
std::lock_guard<std::mutex> lock(node_mtx);
139165
node_hdl.spinSome();
140166
}
141167

142168
auto const now = millis();
143169

144-
if ((now - prev_heartbeat) > 1000UL)
170+
if ((now - prev_heartbeat) > HEARTBEAT_UPDATE_PERIOD_ms)
145171
{
146172
prev_heartbeat = now;
147173

@@ -155,10 +181,13 @@ int main(int argc, char ** argv)
155181
heartbeat_pub->publish(msg);
156182
}
157183

158-
if ((now - prev_counter) > counter_update_period_ms)
184+
if ((now - prev_counter) > COUNTER_UPDATE_PERIOD_ms)
159185
{
160186
prev_counter = now;
161-
counter_pub->publish(counter_msg);
187+
188+
if (counter_pub)
189+
counter_pub->publish(counter_msg);
190+
162191
counter_msg.value++;
163192
}
164193

@@ -193,3 +222,30 @@ unsigned long millis()
193222
{
194223
return micros() / 1000;
195224
}
225+
226+
uavcan::node::ExecuteCommand::Response_1_1 onExecuteCommand_1_1_Request_Received(uavcan::node::ExecuteCommand::Request_1_1 const & req)
227+
{
228+
uavcan::node::ExecuteCommand::Response_1_1 rsp;
229+
230+
if (req.command == uavcan::node::ExecuteCommand::Request_1_1::COMMAND_RESTART)
231+
{
232+
rsp.status = uavcan::node::ExecuteCommand::Response_1_1::STATUS_SUCCESS;
233+
exit(0);
234+
}
235+
else if (req.command == uavcan::node::ExecuteCommand::Request_1_1::COMMAND_STORE_PERSISTENT_STATES)
236+
{
237+
auto const rc_save = cyphal::support::save(kv_storage, *node_registry, []() { /* watchdog function */ });
238+
if (rc_save.has_value())
239+
{
240+
std::cerr << "cyphal::support::save failed with " << static_cast<int>(rc_save.value()) << std::endl;
241+
rsp.status = uavcan::node::ExecuteCommand::Response_1_1::STATUS_FAILURE;
242+
return rsp;
243+
}
244+
rsp.status = uavcan::node::ExecuteCommand::Response_1_1::STATUS_SUCCESS;
245+
}
246+
else {
247+
rsp.status = uavcan::node::ExecuteCommand::Response_1_1::STATUS_BAD_COMMAND;
248+
}
249+
250+
return rsp;
251+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* This software is distributed under the terms of the MIT License.
3+
* Copyright (c) 2023 LXRobotics.
4+
* Author: Alexander Entinger <[email protected]>
5+
* Contributors: https://github.com/107-systems/107-Arduino-Cyphal-Support/graphs/contributors.
6+
*/
7+
8+
/**************************************************************************************
9+
* INCLUDE
10+
**************************************************************************************/
11+
12+
#include "kv_host.h"
13+
14+
#if !defined(__GNUC__) || (__GNUC__ >= 11)
15+
16+
#include <cstdio>
17+
18+
#include <sstream>
19+
#include <fstream>
20+
21+
/**************************************************************************************
22+
* NAMESPACE
23+
**************************************************************************************/
24+
25+
namespace cyphal::support::platform::storage::host
26+
{
27+
28+
/**************************************************************************************
29+
* PRIVATE FREE FUNCTIONS
30+
**************************************************************************************/
31+
32+
[[nodiscard]] static inline std::string toFilename(std::string_view const key)
33+
{
34+
auto const key_hash = std::hash<std::string_view>{}(key);
35+
std::stringstream key_filename;
36+
key_filename << key_hash;
37+
return key_filename.str();
38+
}
39+
40+
/**************************************************************************************
41+
* PUBLIC MEMBER FUNCTIONS
42+
**************************************************************************************/
43+
44+
auto KeyValueStorage::get(const std::string_view key, const std::size_t size, void* const data) const
45+
-> std::variant<Error, std::size_t>
46+
{
47+
std::ifstream in(toFilename(key), std::ifstream::in | std::ifstream::binary);
48+
49+
if (!in.good())
50+
return Error::Existence;
51+
52+
in.read(static_cast<char *>(data), size);
53+
size_t const bytes_read = in ? size : in.gcount();
54+
in.close();
55+
56+
return bytes_read;
57+
}
58+
59+
auto KeyValueStorage::put(const std::string_view key, const std::size_t size, const void* const data)
60+
-> std::optional<Error>
61+
{
62+
std::ofstream out(toFilename(key), std::ofstream::in | std::ofstream::binary | std::ofstream::trunc);
63+
64+
if (!out.good())
65+
return Error::Existence;
66+
67+
out.write(static_cast<const char *>(data), size);
68+
out.close();
69+
70+
return std::nullopt;
71+
}
72+
73+
auto KeyValueStorage::drop(const std::string_view key) -> std::optional<Error>
74+
{
75+
if (std::remove(toFilename(key).c_str()))
76+
return Error::IO;
77+
78+
return std::nullopt;
79+
}
80+
81+
/**************************************************************************************
82+
* NAMESPACE
83+
**************************************************************************************/
84+
85+
} /* cyphal::support::platform::storage::host */
86+
87+
#endif /* !defined(__GNUC__) || (__GNUC__ >= 11) */
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* This software is distributed under the terms of the MIT License.
3+
* Copyright (c) 2023 LXRobotics.
4+
* Author: Alexander Entinger <[email protected]>
5+
* Contributors: https://github.com/107-systems/107-Arduino-Cyphal-Support/graphs/contributors.
6+
*/
7+
8+
#pragma once
9+
10+
/**************************************************************************************
11+
* INCLUDE
12+
**************************************************************************************/
13+
14+
#include <107-Arduino-Cyphal.h>
15+
16+
#if !defined(__GNUC__) || (__GNUC__ >= 11)
17+
18+
/**************************************************************************************
19+
* NAMESPACE
20+
**************************************************************************************/
21+
22+
namespace cyphal::support::platform::storage::host
23+
{
24+
25+
/**************************************************************************************
26+
* CLASS DECLARATION
27+
**************************************************************************************/
28+
29+
class KeyValueStorage final : public interface::KeyValueStorage
30+
{
31+
public:
32+
KeyValueStorage() { }
33+
virtual ~KeyValueStorage() { }
34+
35+
/// The return value is the number of bytes read into the buffer or the error.
36+
[[nodiscard]] virtual auto get(const std::string_view key, const std::size_t size, void* const data) const
37+
-> std::variant<Error, std::size_t> override;
38+
39+
/// Existing data, if any, is replaced entirely. New file and its parent directories created implicitly.
40+
/// Either all or none of the data bytes are written.
41+
[[nodiscard]] virtual auto put(const std::string_view key, const std::size_t size, const void* const data)
42+
-> std::optional<Error> override;
43+
44+
/// Remove key. If the key does not exist, the existence error is returned.
45+
[[nodiscard]] virtual auto drop(const std::string_view key) -> std::optional<Error> override;
46+
};
47+
48+
/**************************************************************************************
49+
* NAMESPACE
50+
**************************************************************************************/
51+
52+
} /* cyphal::support::platform::storage::host */
53+
54+
#endif /* !defined(__GNUC__) || (__GNUC__ >= 11) */

0 commit comments

Comments
 (0)