epics/Aruduino_UNO_Q: arduino_bridge.hpp

File arduino_bridge.hpp, 5.9 KB (added by Tetsuya Michikawa, 43 hours ago)
Line 
1#ifndef ARDUINO_BRIDGE_HPP
2#define ARDUINO_BRIDGE_HPP
3
4#include <msgpack.hpp>
5#include <sys/socket.h>
6#include <sys/un.h>
7#include <unistd.h>
8#include <vector>
9#include <string>
10#include <sstream>
11#include <iostream>
12#include <map>
13#include <thread>
14#include <mutex>
15#include <condition_variable>
16#include <cstring>
17#include <utility>
18
19class ArduinoBridge {
20public:
21 struct Response {
22 bool success;
23 msgpack::object_handle result;
24 std::string error;
25
26 Response() = default;
27 Response(const Response&) = delete;
28 Response& operator=(const Response&) = delete;
29 Response(Response&&) noexcept = default;
30 Response& operator=(Response&&) noexcept = default;
31 };
32
33private:
34 int sock_fd;
35 uint32_t msg_counter;
36 std::thread recv_thread;
37 bool running;
38 std::mutex response_mutex;
39 std::condition_variable response_cv;
40 std::map<uint32_t, Response> pending_responses;
41
42public:
43 ArduinoBridge() : sock_fd(-1), msg_counter(0), running(false) {}
44
45 bool connect() {
46 sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
47 if (sock_fd < 0) {
48 std::cerr << "Failed to create socket: " << strerror(errno) << std::endl;
49 return false;
50 }
51
52 struct sockaddr_un addr;
53 memset(&addr, 0, sizeof(addr));
54 addr.sun_family = AF_UNIX;
55 strncpy(addr.sun_path, "/var/run/arduino-router.sock",
56 sizeof(addr.sun_path) - 1);
57
58 if (::connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
59 std::cerr << "Failed to connect: " << strerror(errno) << std::endl;
60 close(sock_fd);
61 sock_fd = -1;
62 return false;
63 }
64
65 running = true;
66 recv_thread = std::thread(&ArduinoBridge::receive_loop, this);
67
68 return true;
69 }
70
71 template<typename... Args>
72 Response call(const std::string& method, Args... args) {
73 int type = 0;
74 uint32_t msgid = ++msg_counter;
75
76 std::vector<msgpack::type::variant> params;
77 pack_args(params, args...);
78
79 std::stringstream buffer;
80 msgpack::pack(buffer, std::make_tuple(type, msgid, method, params));
81 std::string data = buffer.str();
82
83 {
84 std::lock_guard<std::mutex> lock(response_mutex);
85 pending_responses[msgid] = Response{false, msgpack::object_handle(), ""};
86 }
87
88 ssize_t sent = send(sock_fd, data.c_str(), data.size(), 0);
89 if (sent != (ssize_t)data.size()) {
90 return Response{false, msgpack::object_handle(), "Send failed"};
91 }
92
93 std::unique_lock<std::mutex> lock(response_mutex);
94 bool received = response_cv.wait_for(
95 lock,
96 std::chrono::seconds(5),
97 [this, msgid]() {
98 return pending_responses[msgid].success ||
99 !pending_responses[msgid].error.empty();
100 }
101 );
102
103 if (!received) {
104 pending_responses.erase(msgid);
105 return Response{false, msgpack::object_handle(), "Timeout"};
106 }
107
108 Response response = std::move(pending_responses[msgid]);
109 pending_responses.erase(msgid);
110
111 return response;
112 }
113
114 template<typename... Args>
115 bool notify(const std::string& method, Args... args) {
116 int type = 2;
117
118 std::vector<msgpack::type::variant> params;
119 pack_args(params, args...);
120
121 std::stringstream buffer;
122 msgpack::pack(buffer, std::make_tuple(type, method, params));
123 std::string data = buffer.str();
124
125 ssize_t sent = send(sock_fd, data.c_str(), data.size(), 0);
126 return (sent == (ssize_t)data.size());
127 }
128
129 void disconnect() {
130 running = false;
131 if (recv_thread.joinable()) {
132 recv_thread.join();
133 }
134 if (sock_fd >= 0) {
135 close(sock_fd);
136 sock_fd = -1;
137 }
138 }
139
140 ~ArduinoBridge() {
141 disconnect();
142 }
143
144private:
145 void receive_loop() {
146 char buffer[4096];
147 msgpack::unpacker unpacker;
148
149 while (running) {
150 ssize_t received = recv(sock_fd, buffer, sizeof(buffer), 0);
151 if (received <= 0) break;
152
153 unpacker.reserve_buffer(received);
154 memcpy(unpacker.buffer(), buffer, received);
155 unpacker.buffer_consumed(received);
156
157 msgpack::object_handle oh;
158 while (unpacker.next(oh)) {
159 handle_response(oh.get());
160 }
161 }
162 }
163
164 void handle_response(const msgpack::object& obj) {
165 if (obj.type != msgpack::type::ARRAY) return;
166
167 auto arr = obj.via.array;
168 if (arr.size < 4) return;
169
170 int type = arr.ptr[0].as<int>();
171 if (type != 1) return;
172
173 uint32_t msgid = arr.ptr[1].as<uint32_t>();
174
175 std::lock_guard<std::mutex> lock(response_mutex);
176 auto it = pending_responses.find(msgid);
177 if (it != pending_responses.end()) {
178 if (!arr.ptr[2].is_nil()) {
179 it->second.error = arr.ptr[2].as<std::string>();
180 } else {
181 it->second.success = true;
182 it->second.result = msgpack::clone(arr.ptr[3]);
183 }
184 response_cv.notify_all();
185 }
186 }
187
188 template<typename T, typename... Rest>
189 void pack_args(std::vector<msgpack::type::variant>& params,
190 T first, Rest... rest) {
191 params.push_back(msgpack::type::variant(first));
192 pack_args(params, rest...);
193 }
194
195 void pack_args(std::vector<msgpack::type::variant>& params) {}
196};
197
198#endif