36#ifdef _USE_SYSTEM_JSONLIB
37#include <nlohmann/json.hpp>
48static const char* DISCOVERY_MESSAGE =
"alpacadiscovery";
51 : m_DiscoveryPort(discoveryPort)
52 , m_AlpacaPort(alpacaPort)
54 , m_StopRequested(false)
76 m_StopRequested =
false;
77 m_DiscoveryThread = std::thread(&AlpacaDiscovery::discoveryThreadFunc,
this);
80 std::this_thread::sleep_for(std::chrono::milliseconds(100));
101 m_StopRequested =
true;
104 if (m_DiscoveryThread.joinable())
105 m_DiscoveryThread.join();
108 for (
int socket : m_Sockets)
127 if (port > 0 && port < 65536)
129 m_DiscoveryPort = port;
140 return m_DiscoveryPort;
145 if (port > 0 && port < 65536)
161void AlpacaDiscovery::discoveryThreadFunc()
166 int ipv4Socket = createIPv4Socket();
169 m_Sockets.push_back(ipv4Socket);
178 int ipv6Socket = createIPv6Socket();
181 m_Sockets.push_back(ipv6Socket);
190 if (m_Sockets.empty())
197 std::vector<struct pollfd> fds;
198 for (
int socket : m_Sockets)
213 while (!m_StopRequested)
216 int result = poll(fds.data(), fds.size(), 1000);
224 else if (result == 0)
231 for (
size_t i = 0; i < fds.size(); i++)
233 if (fds[i].revents & POLLIN)
236 struct sockaddr_storage senderAddr;
237 socklen_t senderAddrLen =
sizeof(senderAddr);
240 ssize_t bytesRead = recvfrom(fds[i].
fd, buffer,
sizeof(buffer) - 1, 0,
241 (
struct sockaddr*)&senderAddr, &senderAddrLen);
249 processDiscoveryRequest(fds[i].
fd, buffer, bytesRead,
250 (
struct sockaddr*)&senderAddr, senderAddrLen);
257 for (
int socket : m_Sockets)
268int AlpacaDiscovery::createIPv4Socket()
271 int sock = socket(AF_INET, SOCK_DGRAM, 0);
280 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval)) < 0)
288 if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval,
sizeof(optval)) < 0)
295 struct sockaddr_in addr;
296 memset(&addr, 0,
sizeof(addr));
297 addr.sin_family = AF_INET;
298 addr.sin_addr.s_addr = htonl(INADDR_ANY);
299 addr.sin_port = htons(m_DiscoveryPort);
301 if (bind(sock, (
struct sockaddr*)&addr,
sizeof(addr)) < 0)
311int AlpacaDiscovery::createIPv6Socket()
314 int sock = socket(AF_INET6, SOCK_DGRAM, 0);
323 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval)) < 0)
331 if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval,
sizeof(optval)) < 0)
339 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &optval,
sizeof(optval)) < 0)
345 struct sockaddr_in6 addr;
346 memset(&addr, 0,
sizeof(addr));
347 addr.sin6_family = AF_INET6;
348 addr.sin6_addr = in6addr_any;
349 addr.sin6_port = htons(m_DiscoveryPort);
351 if (bind(sock, (
struct sockaddr*)&addr,
sizeof(addr)) < 0)
359 struct ipv6_mreq mreq;
360 memset(&mreq, 0,
sizeof(mreq));
363 if (inet_pton(AF_INET6,
"ff12::a1:9aca", &mreq.ipv6mr_multiaddr) <= 0)
371 mreq.ipv6mr_interface = 0;
373 if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
sizeof(mreq)) < 0)
383void AlpacaDiscovery::processDiscoveryRequest(
int socket,
const char* buffer,
size_t length,
384 const struct sockaddr* senderAddr, socklen_t senderAddrLen)
389 if (strncmp(buffer, DISCOVERY_MESSAGE, strlen(DISCOVERY_MESSAGE)) == 0)
392 char addrStr[INET6_ADDRSTRLEN];
395 if (senderAddr->sa_family == AF_INET)
397 struct sockaddr_in* addr = (
struct sockaddr_in*)senderAddr;
398 inet_ntop(AF_INET, &addr->sin_addr, addrStr,
sizeof(addrStr));
399 port = ntohs(addr->sin_port);
401 else if (senderAddr->sa_family == AF_INET6)
403 struct sockaddr_in6* addr = (
struct sockaddr_in6*)senderAddr;
404 inet_ntop(AF_INET6, &addr->sin6_addr, addrStr,
sizeof(addrStr));
405 port = ntohs(addr->sin6_port);
410 if (length > strlen(DISCOVERY_MESSAGE))
411 version =
buffer[strlen(DISCOVERY_MESSAGE)];
414 "Received discovery request from %s:%d (message: %s, version: %c)",
415 addrStr, port, buffer, version);
418 sendDiscoveryResponse(socket, senderAddr, senderAddrLen);
422void AlpacaDiscovery::sendDiscoveryResponse(
int socket,
const struct sockaddr* senderAddr, socklen_t senderAddrLen)
425 std::string response = generateResponseMessage();
428 ssize_t bytesSent = sendto(socket, response.c_str(), response.length(), 0, senderAddr, senderAddrLen);
433 "Failed to send discovery response: %s", strerror(
errno));
438 "Sent discovery response: %s", response.c_str());
442std::string AlpacaDiscovery::generateResponseMessage()
const
447 {
"AlpacaPort", m_AlpacaPort}
450 return response.
dump();
AlpacaDiscovery(int discoveryPort=32227, int alpacaPort=11111)
Constructor.
void setDiscoveryPort(int port)
Set the discovery port.
bool start()
Start the discovery server.
~AlpacaDiscovery()
Destructor.
bool stop()
Stop the discovery server.
bool isRunning() const
Check if the discovery server is running.
void setAlpacaPort(int port)
Set the Alpaca API port.
int getDiscoveryPort() const
Get the discovery port.
int getAlpacaPort() const
Get the Alpaca API port.
a class to store JSON values
string_t dump(const int indent=-1, const char indent_char=' ', const bool ensure_ascii=false, const error_handler_t error_handler=error_handler_t::strict) const
serialization
#define DEBUGDEVICE(device, priority, msg)
#define DEBUGFDEVICE(device, priority, msg,...)
std::vector< uint8_t > buffer
basic_json<> json
default specialization