Compare commits

...

2 Commits

Author SHA1 Message Date
Jonathan Schwartz 8cb62c2cae Device: Carry bootloader version for potential compatibility checks 2026-01-12 17:45:10 -05:00
Kyle Schwarz d74051f57e Driver: Servd: Refactor to TCP 2026-01-12 13:03:55 -05:00
7 changed files with 314 additions and 349 deletions

View File

@ -111,7 +111,7 @@ void init_chipid(pybind11::module_& m) {
.value("RAD_GALAXY_2_ZMPCHIP_ID", ChipID::RAD_GALAXY_2_ZMPCHIP_ID) .value("RAD_GALAXY_2_ZMPCHIP_ID", ChipID::RAD_GALAXY_2_ZMPCHIP_ID)
.value("NewDevice59_MCHIP", ChipID::NewDevice59_MCHIP) .value("NewDevice59_MCHIP", ChipID::NewDevice59_MCHIP)
.value("RADMoon2_Z7010_ZYNQ", ChipID::RADMoon2_Z7010_ZYNQ) .value("RADMoon2_Z7010_ZYNQ", ChipID::RADMoon2_Z7010_ZYNQ)
.value("neoVIFIRE2_CORE_SG4", ChipID::neoVIFIRE2_CORE_SG4) .value("neoVIFIRE2_Core_SG4", ChipID::neoVIFIRE2_Core_SG4)
.value("RADBMS_MCHIP", ChipID::RADBMS_MCHIP) .value("RADBMS_MCHIP", ChipID::RADBMS_MCHIP)
.value("RADMoon2_ZL_MCHIP", ChipID::RADMoon2_ZL_MCHIP) .value("RADMoon2_ZL_MCHIP", ChipID::RADMoon2_ZL_MCHIP)
.value("RADGigastar_USBZ_Z7010_ZYNQ", ChipID::RADGigastar_USBZ_Z7010_ZYNQ) .value("RADGigastar_USBZ_Z7010_ZYNQ", ChipID::RADGigastar_USBZ_Z7010_ZYNQ)

View File

@ -110,7 +110,7 @@ enum class ChipID : uint8_t {
RAD_GALAXY_2_ZMPCHIP_ID = 102, RAD_GALAXY_2_ZMPCHIP_ID = 102,
NewDevice59_MCHIP = 103, NewDevice59_MCHIP = 103,
RADMoon2_Z7010_ZYNQ = 104, RADMoon2_Z7010_ZYNQ = 104,
neoVIFIRE2_CORE_SG4 = 105, neoVIFIRE2_Core_SG4 = 105,
RADBMS_MCHIP = 106, RADBMS_MCHIP = 106,
RADMoon2_ZL_MCHIP = 107, RADMoon2_ZL_MCHIP = 107,
RADGigastar_USBZ_Z7010_ZYNQ = 108, RADGigastar_USBZ_Z7010_ZYNQ = 108,

View File

@ -162,6 +162,14 @@ public:
bool hasBootloader() { return !!getBootloader(); } bool hasBootloader() { return !!getBootloader(); }
virtual bool supportsSwVersionValidate() const {
return true;
}
void setBootloaderVersion(const HardwareInfo::Version& version) {
bootloaderVersion = version;
}
static std::string SerialNumToString(uint32_t serial); static std::string SerialNumToString(uint32_t serial);
static uint32_t SerialStringToNum(const std::string& serial); static uint32_t SerialStringToNum(const std::string& serial);
static bool SerialStringIsNumeric(const std::string& serial); static bool SerialStringIsNumeric(const std::string& serial);
@ -982,6 +990,7 @@ protected:
LEDState ledState; LEDState ledState;
void updateLEDState(); void updateLEDState();
std::optional<HardwareInfo::Version> bootloaderVersion = std::nullopt;
private: private:
neodevice_t data; neodevice_t data;

View File

@ -98,13 +98,15 @@ public:
} }
CoreChipVariant getCoreChipVariant() { CoreChipVariant getCoreChipVariant() {
const auto& hardwareInfo = getHardwareInfo(std::chrono::milliseconds(1000)); if(!bootloaderVersion.has_value()) {
if(!hardwareInfo) { const auto& hardwareInfo = getHardwareInfo(std::chrono::milliseconds(1000));
chipVariant = CoreChipVariant::Invalid; if(!hardwareInfo) {
return chipVariant; chipVariant = CoreChipVariant::Invalid;
return chipVariant;
}
setBootloaderVersion(hardwareInfo->bootloaderVersion);
} }
const auto& bootloaderVersion = hardwareInfo->bootloaderVersion; if(bootloaderVersion->major >= CORE_SG4_BL_MAJOR_VERSION_CUTOFF) {
if(bootloaderVersion.major >= CORE_SG4_BL_MAJOR_VERSION_CUTOFF) {
chipVariant = CoreChipVariant::Core_SG4; chipVariant = CoreChipVariant::Core_SG4;
} else { } else {
chipVariant = CoreChipVariant::Core; chipVariant = CoreChipVariant::Core;
@ -122,7 +124,7 @@ public:
static std::vector<ChipInfo> chipsSG4 = { static std::vector<ChipInfo> chipsSG4 = {
{ChipID::neoVIFIRE2_MCHIP, true, "MCHIP", "fire2_mchip_ief", 0, FirmwareType::IEF}, {ChipID::neoVIFIRE2_MCHIP, true, "MCHIP", "fire2_mchip_ief", 0, FirmwareType::IEF},
{ChipID::neoVIFIRE2_ZYNQ, true, "ZCHIP", "fire2_zchip_ief", 1, FirmwareType::IEF}, {ChipID::neoVIFIRE2_ZYNQ, true, "ZCHIP", "fire2_zchip_ief", 1, FirmwareType::IEF},
{ChipID::neoVIFIRE2_CORE_SG4, true, "Core", "fire2_core_sg4", 2, FirmwareType::IEF} {ChipID::neoVIFIRE2_Core_SG4, true, "Core", "fire2_core_sg4", 2, FirmwareType::IEF}
}; };
if(chipVariant == CoreChipVariant::Core_SG4) { if(chipVariant == CoreChipVariant::Core_SG4) {
@ -137,7 +139,7 @@ public:
pipeline.add<EnterBootloaderPhase>() pipeline.add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::neoVIFIRE2_MCHIP, BootloaderCommunication::RED); .add<FlashPhase>(ChipID::neoVIFIRE2_MCHIP, BootloaderCommunication::RED);
if(chipVariant == CoreChipVariant::Core_SG4) { if(chipVariant == CoreChipVariant::Core_SG4) {
pipeline.add<FlashPhase>(ChipID::neoVIFIRE2_CORE_SG4, BootloaderCommunication::REDCore, false, false); pipeline.add<FlashPhase>(ChipID::neoVIFIRE2_Core_SG4, BootloaderCommunication::REDCore, false, false);
} else { } else {
pipeline.add<FlashPhase>(ChipID::neoVIFIRE2_Core, BootloaderCommunication::REDCore, false, false); pipeline.add<FlashPhase>(ChipID::neoVIFIRE2_Core, BootloaderCommunication::REDCore, false, false);
} }
@ -147,6 +149,10 @@ public:
return pipeline; return pipeline;
} }
bool supportsSwVersionValidate() const override {
return bootloaderVersion.has_value() && (bootloaderVersion->major > 4 || (bootloaderVersion->major == 4 && bootloaderVersion->minor >= 3));
}
std::vector<VersionReport> getChipVersions(bool refreshComponents = true) override { std::vector<VersionReport> getChipVersions(bool refreshComponents = true) override {
if(chipVariant == CoreChipVariant::Invalid) { if(chipVariant == CoreChipVariant::Invalid) {
getCoreChipVariant(); getCoreChipVariant();

View File

@ -18,27 +18,24 @@ class Servd : public Driver {
public: public:
static void Find(std::vector<FoundDevice>& foundDevices); static void Find(std::vector<FoundDevice>& foundDevices);
static bool Enabled(); static bool Enabled();
Servd(const device_eventhandler_t& err, neodevice_t& forDevice, const std::unordered_set<std::string>& availableDrivers); Servd(const device_eventhandler_t& err, neodevice_t& forDevice, const Address& address);
~Servd() override; ~Servd() override;
bool open() override; bool open() override;
bool isOpen() override; bool isOpen() override;
bool close() override; bool close() override;
bool faa(const std::string& key, int32_t inc, int32_t& orig);
bool enableCommunication(bool enable, bool& sendMsg) override; bool enableCommunication(bool enable, bool& sendMsg) override;
driver_finder_t getFinder() override { return Servd::Find; } driver_finder_t getFinder() override { return Servd::Find; }
private: private:
void alive(); void read();
void read(Address&& address); void write();
void write(Address&& address);
neodevice_t& device; neodevice_t& device;
std::thread aliveThread; // makes sure the client and server are healthy
std::thread writeThread; std::thread writeThread;
std::thread readThread; std::thread readThread;
Socket messageSocket; Socket messageSocket;
bool opened = false; bool opened = false;
bool comEnabled = false; bool comEnabled = false;
std::string driver; std::unique_ptr<Socket> dataSocket;
}; };
} }

View File

@ -1,210 +1,223 @@
#ifndef __SOCKET_H_ #ifndef __SOCKET_H_
#define __SOCKET_H_ #define __SOCKET_H_
#ifdef __cplusplus #ifdef __cplusplus
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define NOMINMAX #define NOMINMAX
#include <windows.h> #include <windows.h>
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#else #else
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <poll.h> #include <poll.h>
#endif #endif
#include <string> #include <string>
#include <chrono> #include <chrono>
namespace icsneo { namespace icsneo {
#ifdef _WIN32 #ifdef _WIN32
class WSA { class WSA {
public: public:
WSA() { WSA() {
// TODO: add error checking // TODO: add error checking
WSAStartup(MAKEWORD(2, 2), &wsaData); WSAStartup(MAKEWORD(2, 2), &wsaData);
} }
~WSA() { ~WSA() {
WSACleanup(); WSACleanup();
} }
private: private:
WSADATA wsaData; WSADATA wsaData;
}; };
#endif #endif
class Address { class Address {
public: public:
Address() = default; Address() = default;
Address(const char* ip, uint16_t port) Address(const char* ip, uint16_t port)
: _ip(ip), _port(port) : _ip(ip), _port(port)
{ {
_sockaddr.sin_family = AF_INET; _sockaddr.sin_family = AF_INET;
inet_pton(AF_INET, ip, &_sockaddr.sin_addr); inet_pton(AF_INET, ip, &_sockaddr.sin_addr);
_sockaddr.sin_port = htons(port); _sockaddr.sin_port = htons(port);
} }
Address(sockaddr_in& sockaddr) Address(sockaddr_in& sockaddr)
: _sockaddr(sockaddr) : _sockaddr(sockaddr)
{ {
char cip[INET_ADDRSTRLEN]; char cip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sockaddr.sin_addr, cip, sizeof(cip)); inet_ntop(AF_INET, &sockaddr.sin_addr, cip, sizeof(cip));
_ip = cip; _ip = cip;
_port = ntohs(sockaddr.sin_port); _port = ntohs(sockaddr.sin_port);
} }
const std::string& ip() const { return _ip; } const std::string& ip() const { return _ip; }
const uint16_t& port() const { return _port; } const uint16_t& port() const { return _port; }
const sockaddr_in& sockaddr() const { return _sockaddr; } const sockaddr_in& sockaddr() const { return _sockaddr; }
private: private:
std::string _ip; std::string _ip;
uint16_t _port; uint16_t _port;
sockaddr_in _sockaddr; sockaddr_in _sockaddr;
}; };
class Socket { class Socket {
public: public:
#ifdef _WIN32 #ifdef _WIN32
using SocketHandleType = SOCKET; using SocketHandleType = SOCKET;
#else #else
using SocketHandleType = int; using SocketHandleType = int;
#endif #endif
Socket() { template<class... Args>
#ifdef _WIN32 Socket(Args&&... args) {
static WSA wsa; #ifdef _WIN32
#endif static WSA wsa;
mFD = socket(AF_INET, SOCK_DGRAM, 0); #endif
} mFD = socket(std::forward<Args>(args)...);
}
~Socket() {
#ifdef _WIN32 ~Socket() {
closesocket(mFD); #ifdef _WIN32
#else closesocket(mFD);
close(mFD); #else
#endif close(mFD);
} #endif
}
bool set_reuse(bool value) {
int ival = value; bool set_reuse(bool value) {
return ::setsockopt(mFD, SOL_SOCKET, SO_REUSEADDR, (const char*)&ival, sizeof(ival)) != -1; int ival = value;
} return ::setsockopt(mFD, SOL_SOCKET, SO_REUSEADDR, (const char*)&ival, sizeof(ival)) != -1;
}
bool set_nonblocking() {
#ifdef _WIN32 bool set_nonblocking() {
u_long nonblock = 1; #ifdef _WIN32
return ioctlsocket(mFD, FIONBIO, &nonblock) != SOCKET_ERROR; u_long nonblock = 1;
#else return ioctlsocket(mFD, FIONBIO, &nonblock) != SOCKET_ERROR;
return fcntl(mFD, F_SETFL, fcntl(mFD, F_GETFL, 0) | O_NONBLOCK) != -1; #else
#endif return fcntl(mFD, F_SETFL, fcntl(mFD, F_GETFL, 0) | O_NONBLOCK) != -1;
} #endif
}
bool bind(const Address& at) {
return ::bind(mFD, (sockaddr*)&at.sockaddr(), sizeof(sockaddr_in)) != -1; bool connect(const Address& to) {
} return ::connect(mFD, (sockaddr*)&to.sockaddr(), sizeof(sockaddr_in)) != -1;
}
bool poll(const std::chrono::milliseconds& timeout, bool& in) {
#ifdef _WIN32 bool bind(const Address& at) {
WSAPOLLFD pfd; return ::bind(mFD, (sockaddr*)&at.sockaddr(), sizeof(sockaddr_in)) != -1;
pfd.fd = mFD; }
pfd.events = POLLIN;
if (::WSAPoll(&pfd, 1, static_cast<int>(timeout.count())) == SOCKET_ERROR) { bool poll(const std::chrono::milliseconds& timeout, bool& in) {
return false; #ifdef _WIN32
} WSAPOLLFD pfd;
in = pfd.revents & POLLIN; pfd.fd = mFD;
return true; pfd.events = POLLIN;
#else if (::WSAPoll(&pfd, 1, static_cast<int>(timeout.count())) == SOCKET_ERROR) {
struct pollfd pfd; return false;
pfd.fd = mFD; }
pfd.events = POLLIN; in = pfd.revents & POLLIN;
pfd.revents = 0; return true;
if (::poll(&pfd, 1, static_cast<int>(timeout.count())) == -1) { #else
return false; struct pollfd pfd;
} pfd.fd = mFD;
in = pfd.revents & POLLIN; pfd.events = POLLIN;
return true; pfd.revents = 0;
#endif if (::poll(&pfd, 1, static_cast<int>(timeout.count())) == -1) {
} return false;
}
bool sendto(const void* buffer, size_t size, const Address& to) { in = pfd.revents & POLLIN;
size_t totalSent = 0; return true;
do { #endif
const auto sent = ::sendto(mFD, (const char*)buffer, (int)size, 0, (sockaddr*)&to.sockaddr(), sizeof(sockaddr_in)); }
if (sent == -1) {
return false; bool sendto(const void* buffer, size_t size, const Address& to) {
} size_t totalSent = 0;
totalSent += sent; do {
} while (totalSent < size); const auto sent = ::sendto(mFD, (const char*)buffer, (int)size, 0, (sockaddr*)&to.sockaddr(), sizeof(sockaddr_in));
return true; if (sent == -1) {
} return false;
}
bool recvfrom(void* buffer, size_t& size, Address& from) { totalSent += sent;
sockaddr_in addr; } while (totalSent < size);
socklen_t addLen = sizeof(addr); return true;
const auto read = ::recvfrom(mFD, (char*)buffer, (int)size, 0, (sockaddr*)&addr, &addLen); }
if (read == -1) {
return false; bool send(const void* buffer, size_t size) {
} auto sent = ::send(mFD, (const char*)buffer, (int)size, 0);
size = read; if(sent == -1) {
from = Address(addr); return false;
return true; }
} return (size_t)sent == size;
}
bool recv(void* buffer, size_t& size) {
const auto read = ::recv(mFD, (char*)buffer, (int)size, 0); bool recvfrom(void* buffer, size_t& size, Address& from) {
if (read == -1) { sockaddr_in addr;
return false; socklen_t addLen = sizeof(addr);
} const auto read = ::recvfrom(mFD, (char*)buffer, (int)size, 0, (sockaddr*)&addr, &addLen);
size = read; if (read == -1) {
return true; return false;
} }
size = read;
template<typename REQ, typename RES> from = Address(addr);
bool transceive(const Address& to, REQ&& request, RES&& response, const std::chrono::milliseconds& timeout) { return true;
if(!sendto(request.data(), request.size(), to)) { }
return false;
} bool recv(void* buffer, size_t& size) {
bool hasData; const auto read = ::recv(mFD, (char*)buffer, (int)size, 0);
if(!poll(timeout, hasData)) { if (read == -1) {
return false; return false;
} }
if(!hasData) { size = read;
return false; return true;
} }
size_t responseSize = response.size();
if(!recv(response.data(), responseSize)) { template<typename REQ, typename RES>
return false; bool transceive(REQ&& request, RES&& response, const std::chrono::milliseconds& timeout) {
} if(!send(request.data(), request.size())) {
response.resize(responseSize); return false;
return true; }
} bool hasData;
if(!poll(timeout, hasData)) {
bool address(Address& address) const { return false;
sockaddr_in sin; }
socklen_t len = sizeof(sin); if(!hasData) {
getsockname(mFD, (sockaddr*)&sin, &len); return false;
address = Address(sin); }
return true; size_t responseSize = response.size();
} if(!recv(response.data(), responseSize)) {
return false;
bool join_multicast(const std::string& interfaceIP, const std::string& multicastIP) { }
ip_mreq mreq; response.resize(responseSize);
inet_pton(AF_INET, interfaceIP.c_str(), &mreq.imr_interface); return true;
inet_pton(AF_INET, multicastIP.c_str(), &mreq.imr_multiaddr); }
return setsockopt(mFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) == 0;
} bool address(Address& address) const {
sockaddr_in sin;
operator bool() const { return mFD != -1; } socklen_t len = sizeof(sin);
operator SocketHandleType() const { return mFD; } getsockname(mFD, (sockaddr*)&sin, &len);
private: address = Address(sin);
SocketHandleType mFD; return true;
}; }
} // namespace icsneo bool join_multicast(const std::string& interfaceIP, const std::string& multicastIP) {
ip_mreq mreq;
#endif // __cplusplus inet_pton(AF_INET, interfaceIP.c_str(), &mreq.imr_interface);
inet_pton(AF_INET, multicastIP.c_str(), &mreq.imr_multiaddr);
#endif // __SOCKET_H_ return setsockopt(mFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) == 0;
}
operator bool() const { return mFD != -1; }
operator SocketHandleType() const { return mFD; }
private:
SocketHandleType mFD;
};
} // namespace icsneo
#endif // __cplusplus
#endif // __SOCKET_H_

View File

@ -6,7 +6,7 @@
using namespace icsneo; using namespace icsneo;
#define SERVD_VERSION 1 #define SERVD_VERSION 2
static const Address SERVD_ADDRESS = Address("127.0.0.1", 26741); static const Address SERVD_ADDRESS = Address("127.0.0.1", 26741);
static const std::string SERVD_VERSION_STR = std::to_string(SERVD_VERSION); static const std::string SERVD_VERSION_STR = std::to_string(SERVD_VERSION);
@ -41,20 +41,17 @@ std::vector<std::string> split(const std::string_view& str, char delim = ' ') {
} }
void Servd::Find(std::vector<FoundDevice>& found) { void Servd::Find(std::vector<FoundDevice>& found) {
Socket socket; Socket socket(AF_INET, SOCK_DGRAM, 0);
socket.connect(SERVD_ADDRESS);
if(!socket.set_nonblocking()) { if(!socket.set_nonblocking()) {
EventManager::GetInstance().add(APIEvent::Type::ServdNonblockError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdNonblockError, APIEvent::Severity::Error);
return; return;
} }
if(!socket.bind(Address("127.0.0.1", 0))) {
EventManager::GetInstance().add(APIEvent::Type::ServdBindError, APIEvent::Severity::Error);
return;
}
std::string response; std::string response;
response.resize(512); response.resize(512);
const std::string version_request = SERVD_VERSION_STR + " version"; const std::string version_request = SERVD_VERSION_STR + " version";
if(!socket.transceive(SERVD_ADDRESS, version_request, response, std::chrono::milliseconds(5000))) { if(!socket.transceive(version_request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error);
return; return;
} }
@ -66,46 +63,39 @@ void Servd::Find(std::vector<FoundDevice>& found) {
response.resize(512); response.resize(512);
const std::string find_request = SERVD_VERSION_STR + " find"; const std::string find_request = SERVD_VERSION_STR + " find";
if(!socket.transceive(SERVD_ADDRESS, find_request, response, std::chrono::milliseconds(5000))) { if(!socket.transceive(find_request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error);
return; return;
} }
const auto lines = split(response, '\n'); const auto lines = split(response, '\n');
for(auto&& line : lines) { for(auto&& line : lines) {
const auto cols = split(line, ' '); const auto cols = split(line, ' ');
if(cols.size() < 2) { if(cols.size() < 3) {
EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error);
continue; continue;
} }
const auto& serial = cols[0]; const auto& serial = cols[0];
std::unordered_set<std::string> drivers; const auto& ip = cols[1];
for (size_t i = 1; i < cols.size(); ++i) { uint16_t port = 0;
drivers.emplace(cols[i]); try {
port = static_cast<uint16_t>(std::stoi(cols[2]));
} catch (const std::exception&) {
EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error);
continue;
} }
Address address(ip.c_str(), port);
auto& newFound = found.emplace_back(); auto& newFound = found.emplace_back();
std::copy(serial.begin(), serial.end(), newFound.serial); std::copy(serial.begin(), serial.end(), newFound.serial);
newFound.makeDriver = [=](device_eventhandler_t err, neodevice_t& forDevice) { newFound.makeDriver = [=](device_eventhandler_t err, neodevice_t& forDevice) {
return std::make_unique<Servd>(err, forDevice, drivers); return std::make_unique<Servd>(err, forDevice, address);
}; };
} }
} }
Servd::Servd(const device_eventhandler_t& err, neodevice_t& forDevice, const std::unordered_set<std::string>& availableDrivers) : Servd::Servd(const device_eventhandler_t& err, neodevice_t& forDevice, const Address& address) :
Driver(err), device(forDevice) { Driver(err), device(forDevice), messageSocket(AF_INET, SOCK_DGRAM, 0) {
messageSocket.connect(address);
messageSocket.set_nonblocking(); messageSocket.set_nonblocking();
messageSocket.bind(Address("127.0.0.1", 0));
if(availableDrivers.count("dxx")) {
driver = "dxx"; // prefer USB over Ethernet
} else if(availableDrivers.count("cab")) {
driver = "cab"; // prefer CAB over TCP
} else if(availableDrivers.count("tcp")) {
driver = "tcp";
} else if(availableDrivers.count("vcp")) {
driver = "vcp";
} else {
// just take the first driver
driver = *availableDrivers.begin();
}
} }
Servd::~Servd() { Servd::~Servd() {
@ -113,21 +103,31 @@ Servd::~Servd() {
} }
bool Servd::open() { bool Servd::open() {
const std::string request = SERVD_VERSION_STR + " open " + std::string(device.serial) + " " + driver; const std::string request = SERVD_VERSION_STR + " open";
std::string response; std::string response;
response.resize(512); response.resize(512);
if(!messageSocket.transceive(SERVD_ADDRESS, request, response, std::chrono::milliseconds(5000))) { if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error);
return false; return false;
} }
const auto tokens = split(response); const auto tokens = split(response);
if(tokens.size() != 4) { if(tokens.size() != 2) {
EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error);
return false; return false;
} }
aliveThread = std::thread(&Servd::alive, this); dataSocket = std::make_unique<Socket>(AF_INET, SOCK_STREAM, 0);
readThread = std::thread(&Servd::read, this, Address{tokens[2].c_str(), (uint16_t)std::stol(tokens[3].c_str())}); const auto& ip = tokens[0];
writeThread = std::thread(&Servd::write, this, Address{tokens[0].c_str(), (uint16_t)std::stol(tokens[1].c_str())}); uint16_t port = 0;
try {
port = static_cast<uint16_t>(std::stoi(tokens[1]));
} catch (const std::exception&) {
EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error);
return false;
}
Address address(ip.c_str(), port);
dataSocket->connect(address);
readThread = std::thread(&Servd::read, this);
writeThread = std::thread(&Servd::write, this);
opened = true; opened = true;
return true; return true;
} }
@ -138,9 +138,6 @@ bool Servd::isOpen() {
bool Servd::close() { bool Servd::close() {
setIsClosing(true); setIsClosing(true);
if(aliveThread.joinable()) {
aliveThread.join();
}
if(readThread.joinable()) { if(readThread.joinable()) {
readThread.join(); readThread.join();
} }
@ -148,8 +145,16 @@ bool Servd::close() {
writeThread.join(); writeThread.join();
} }
if(isOpen()) { if(isOpen()) {
const std::string request = SERVD_VERSION_STR + " close " + std::string(device.serial); Address localAddress;
messageSocket.sendto(request.data(), request.size(), SERVD_ADDRESS); dataSocket->address(localAddress);
const std::string request = SERVD_VERSION_STR + " close " + localAddress.ip() + " " + std::to_string(localAddress.port());
std::string response;
response.resize(1);
if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error);
return false;
}
dataSocket.reset();
} }
opened = false; opened = false;
setIsClosing(false); setIsClosing(false);
@ -159,13 +164,13 @@ bool Servd::close() {
bool Servd::enableCommunication(bool enable, bool& sendMsg) { bool Servd::enableCommunication(bool enable, bool& sendMsg) {
const std::string serialString(device.serial); const std::string serialString(device.serial);
{ {
const std::string request = SERVD_VERSION_STR + " lock " + serialString + " com 1000"; const std::string request = SERVD_VERSION_STR + " lock com 1000";
std::string response; std::string response;
response.resize(1); response.resize(1);
bool locked = false; bool locked = false;
const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(1); const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(1);
do { do {
if(!messageSocket.transceive(SERVD_ADDRESS, request, response, std::chrono::milliseconds(5000))) { if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
return false; return false;
} }
locked = response == "1" ? true : false; locked = response == "1" ? true : false;
@ -181,10 +186,10 @@ bool Servd::enableCommunication(bool enable, bool& sendMsg) {
} }
uint64_t com = 0; uint64_t com = 0;
{ {
const std::string request = SERVD_VERSION_STR + " load " + serialString + " com"; const std::string request = SERVD_VERSION_STR + " load com";
std::string response; std::string response;
response.resize(20); response.resize(20);
if(!messageSocket.transceive(SERVD_ADDRESS, request, response, std::chrono::milliseconds(5000))) { if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdTransceiveError, APIEvent::Severity::Error);
return false; return false;
} }
@ -202,16 +207,20 @@ bool Servd::enableCommunication(bool enable, bool& sendMsg) {
} }
if(comEnabled != enable) { if(comEnabled != enable) {
com += enable ? 1 : -1; com += enable ? 1 : -1;
const std::string request = SERVD_VERSION_STR + " store " + serialString + " com " + std::to_string(com); const std::string request = SERVD_VERSION_STR + " store com " + std::to_string(com);
if(!messageSocket.sendto(request.data(), request.size(), SERVD_ADDRESS)) { std::string response;
response.resize(1);
if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error);
return false; return false;
} }
} }
comEnabled = enable; comEnabled = enable;
{ {
const std::string request = SERVD_VERSION_STR + " unlock " + serialString + " com"; const std::string request = SERVD_VERSION_STR + " unlock com";
if(!messageSocket.sendto(request.data(), request.size(), SERVD_ADDRESS)) { std::string response;
response.resize(1);
if(!messageSocket.transceive(request, response, std::chrono::milliseconds(5000))) {
EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error);
return false; return false;
} }
@ -219,78 +228,11 @@ bool Servd::enableCommunication(bool enable, bool& sendMsg) {
return true; return true;
} }
void Servd::alive() { void Servd::read() {
Socket socket; std::vector<uint8_t> buf(2 * 1024 * 1024);
socket.set_nonblocking();
socket.bind(Address("127.0.0.1", 0));
const std::string statusRequest = SERVD_VERSION_STR + " status " + std::string(device.serial);
std::string statusResponse;
statusResponse.resize(8);
while(!isDisconnected() && !isClosing()) {
if(!socket.sendto(statusRequest.data(), statusRequest.size(), {"127.0.0.1", 26741})) {
EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
bool hasData;
if(!socket.poll(std::chrono::milliseconds(2000), hasData)) {
EventManager::GetInstance().add(APIEvent::Type::ServdPollError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
if(!hasData) {
EventManager::GetInstance().add(APIEvent::Type::ServdNoDataError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
size_t statusResponseSize = statusResponse.size();
if(!socket.recv(statusResponse.data(), statusResponseSize)) {
EventManager::GetInstance().add(APIEvent::Type::ServdRecvError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
statusResponse.resize(statusResponseSize);
if(statusRequest == "closed") {
EventManager::GetInstance().add(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
if(statusResponse != "open") {
EventManager::GetInstance().add(APIEvent::Type::ServdInvalidResponseError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
void Servd::read(Address&& address) {
Socket socket;
socket.set_nonblocking();
socket.set_reuse(true);
#ifdef _WIN32
if(!socket.bind(Address("127.0.0.1", address.port()))) {
EventManager::GetInstance().add(APIEvent::Type::ServdBindError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
#else
if(!socket.bind(Address(address.ip().c_str(), address.port()))) {
EventManager::GetInstance().add(APIEvent::Type::ServdBindError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
#endif
if(!socket.join_multicast("127.0.0.1", address.ip())) {
EventManager::GetInstance().add(APIEvent::Type::ServdJoinMulticastError, APIEvent::Severity::Error);
setIsDisconnected(true);
return;
}
std::vector<uint8_t> buf(65535);
while(!isDisconnected() && !isClosing()) { while(!isDisconnected() && !isClosing()) {
bool hasData; bool hasData;
if(!socket.poll(std::chrono::milliseconds(100), hasData)) { if(!dataSocket->poll(std::chrono::milliseconds(100), hasData)) {
EventManager::GetInstance().add(APIEvent::Type::ServdPollError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdPollError, APIEvent::Severity::Error);
setIsDisconnected(true); setIsDisconnected(true);
return; return;
@ -299,7 +241,7 @@ void Servd::read(Address&& address) {
continue; continue;
} }
size_t bufSize = buf.size(); size_t bufSize = buf.size();
if(!socket.recv(buf.data(), bufSize)) { if(!dataSocket->recv(buf.data(), bufSize)) {
EventManager::GetInstance().add(APIEvent::Type::ServdRecvError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdRecvError, APIEvent::Severity::Error);
setIsDisconnected(true); setIsDisconnected(true);
return; return;
@ -308,16 +250,14 @@ void Servd::read(Address&& address) {
} }
} }
void Servd::write(Address&& address) { void Servd::write() {
Socket socket;
socket.bind(Address("127.0.0.1", 0));
WriteOperation writeOp; WriteOperation writeOp;
while(!isDisconnected() && !isClosing()) { while(!isDisconnected() && !isClosing()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100))) { if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100))) {
continue; continue;
} }
if(!isClosing()) { if(!isClosing()) {
if(!socket.sendto(writeOp.bytes.data(), writeOp.bytes.size(), address)) { if(!dataSocket->send(writeOp.bytes.data(), writeOp.bytes.size())) {
EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::ServdSendError, APIEvent::Severity::Error);
setIsDisconnected(true); setIsDisconnected(true);
return; return;