145 lines
4.4 KiB
C++
145 lines
4.4 KiB
C++
#include <dmf-engine/node_control_client.hpp>
|
|
|
|
#include <spdlog/spdlog.h>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <cstring>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sstream>
|
|
|
|
namespace dmf_engine {
|
|
|
|
void NodeControlClient::register_node(const std::string& node_id, uint16_t port) {
|
|
node_ports_[node_id] = port;
|
|
spdlog::info("NodeControlClient: registered node '{}' on port {}", node_id, port);
|
|
}
|
|
|
|
void NodeControlClient::unregister_node(const std::string& node_id) {
|
|
node_ports_.erase(node_id);
|
|
}
|
|
|
|
uint16_t NodeControlClient::get_port(const std::string& node_id) const {
|
|
auto it = node_ports_.find(node_id);
|
|
return it != node_ports_.end() ? it->second : 0;
|
|
}
|
|
|
|
bool NodeControlClient::send_command(uint16_t port, const std::string& json_cmd) {
|
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (fd < 0) {
|
|
spdlog::error("NodeControlClient: socket() failed: {}", strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
struct timeval tv;
|
|
tv.tv_sec = 2;
|
|
tv.tv_usec = 0;
|
|
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
|
|
|
struct sockaddr_in addr;
|
|
std::memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
|
|
|
|
if (connect(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
|
|
spdlog::error("NodeControlClient: connect to port {} failed: {}", port, strerror(errno));
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
std::ostringstream req;
|
|
req << "POST /cmd HTTP/1.1\r\n"
|
|
<< "Host: 127.0.0.1:" << port << "\r\n"
|
|
<< "Content-Type: application/json\r\n"
|
|
<< "Content-Length: " << json_cmd.size() << "\r\n"
|
|
<< "Connection: close\r\n"
|
|
<< "\r\n"
|
|
<< json_cmd;
|
|
|
|
auto request = req.str();
|
|
auto sent = write(fd, request.data(), request.size());
|
|
if (sent != static_cast<ssize_t>(request.size())) {
|
|
spdlog::error("NodeControlClient: write failed on port {}", port);
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
char resp_buf[4096] = {};
|
|
auto n = read(fd, resp_buf, sizeof(resp_buf) - 1);
|
|
close(fd);
|
|
|
|
if (n <= 0) {
|
|
spdlog::error("NodeControlClient: read failed on port {}", port);
|
|
return false;
|
|
}
|
|
|
|
std::string resp(resp_buf, n);
|
|
bool ok = resp.find("200 OK") != std::string::npos || resp.find("201 Created") != std::string::npos;
|
|
if (!ok) {
|
|
spdlog::warn("NodeControlClient: command failed on port {}: {}", port, resp.substr(0, 100));
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
std::string NodeControlClient::send_command_with_response(uint16_t port, const std::string& json_cmd) {
|
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (fd < 0) {
|
|
spdlog::error("NodeControlClient: socket() failed: {}", strerror(errno));
|
|
return "";
|
|
}
|
|
|
|
struct timeval tv;
|
|
tv.tv_sec = 2;
|
|
tv.tv_usec = 0;
|
|
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
|
|
|
struct sockaddr_in addr;
|
|
std::memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
|
|
|
|
if (connect(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
|
|
spdlog::error("NodeControlClient: connect to port {} failed: {}", port, strerror(errno));
|
|
close(fd);
|
|
return "";
|
|
}
|
|
|
|
std::ostringstream req;
|
|
req << "POST /cmd HTTP/1.1\r\n"
|
|
<< "Host: 127.0.0.1:" << port << "\r\n"
|
|
<< "Content-Type: application/json\r\n"
|
|
<< "Content-Length: " << json_cmd.size() << "\r\n"
|
|
<< "Connection: close\r\n"
|
|
<< "\r\n"
|
|
<< json_cmd;
|
|
|
|
auto request = req.str();
|
|
auto sent = write(fd, request.data(), request.size());
|
|
if (sent != static_cast<ssize_t>(request.size())) {
|
|
spdlog::error("NodeControlClient: write failed on port {}", port);
|
|
close(fd);
|
|
return "";
|
|
}
|
|
|
|
std::string full_resp;
|
|
char resp_buf[4096];
|
|
while (true) {
|
|
auto n = read(fd, resp_buf, sizeof(resp_buf));
|
|
if (n <= 0) break;
|
|
full_resp.append(resp_buf, n);
|
|
}
|
|
close(fd);
|
|
|
|
auto body_start = full_resp.find("\r\n\r\n");
|
|
if (body_start == std::string::npos) return "";
|
|
return full_resp.substr(body_start + 4);
|
|
}
|
|
|
|
} // namespace dmf_engine
|