попытки сделать crsf, не взлетела часть с пультом
This commit is contained in:
138
lib/crsf.cpp
Normal file
138
lib/crsf.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "crsf.h"
|
||||
|
||||
// rawBuffer — полный CRSF-пакет:
|
||||
// [0] = preamble (0xC8)
|
||||
// [1] = size
|
||||
// [2] = address
|
||||
// [3] = type
|
||||
// [4..N-2] = payload
|
||||
// [N-1] = crc
|
||||
crsf::CrsfFrame::CrsfFrame(std::span<const uint8_t> rawBuffer) {
|
||||
if (rawBuffer.size() < 6) {
|
||||
// минимальный размер CRSF кадра — 6 байт
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t size = rawBuffer[1];
|
||||
if (rawBuffer.size() != size + 2) {
|
||||
// размер несоответствует фактической длине буфера
|
||||
return;
|
||||
}
|
||||
|
||||
address = rawBuffer[2];
|
||||
type = rawBuffer[3];
|
||||
|
||||
size_t payloadSize = size - 2; // address + type занимают первые 2
|
||||
payload.assign(rawBuffer.begin() + 4, rawBuffer.begin() + 4 + payloadSize);
|
||||
|
||||
crc = rawBuffer[rawBuffer.size() - 1];
|
||||
}
|
||||
|
||||
bool crsf::CrsfFrame::checkCrc() const {
|
||||
// CRC считается по address + type + payload
|
||||
uint8_t expected = crsfComputeCrc(std::span<const uint8_t>(payload.empty()
|
||||
? nullptr : &payload[0], payload.size() + 2));
|
||||
|
||||
// Нам нужно подготовить временный буфер, потому что адрес и тип не в payload
|
||||
std::vector<uint8_t> tmp;
|
||||
tmp.reserve(payload.size() + 2);
|
||||
tmp.push_back(address);
|
||||
tmp.push_back(type);
|
||||
tmp.insert(tmp.end(), payload.begin(), payload.end());
|
||||
|
||||
expected = crsfComputeCrc(tmp);
|
||||
|
||||
return expected == crc;
|
||||
}
|
||||
void crsf::CrsfFrame::setCrc() {
|
||||
// аналогично checkCrc, но записываем CRC в поле структуры
|
||||
std::vector<uint8_t> tmp;
|
||||
tmp.reserve(payload.size() + 2);
|
||||
tmp.push_back(address);
|
||||
tmp.push_back(type);
|
||||
tmp.insert(tmp.end(), payload.begin(), payload.end());
|
||||
|
||||
crc = crsfComputeCrc(tmp);
|
||||
}
|
||||
void crsf::CrsfFrame::writeToBuffer(std::vector<uint8_t>& dest) const {
|
||||
dest.clear();
|
||||
|
||||
const uint8_t preamble = 0xC8;
|
||||
const uint8_t size = static_cast<uint8_t>(2 + payload.size() + 1); // addr + type + payload + crc
|
||||
|
||||
dest.reserve(size + 2);
|
||||
|
||||
dest.push_back(preamble);
|
||||
dest.push_back(size);
|
||||
dest.push_back(address);
|
||||
dest.push_back(type);
|
||||
|
||||
dest.insert(dest.end(), payload.begin(), payload.end());
|
||||
|
||||
// финальный байт crc
|
||||
dest.push_back(crc);
|
||||
}
|
||||
|
||||
|
||||
uint8_t crsf::crsfComputeCrc(std::span<const uint8_t> data) {
|
||||
uint8_t crc = 0;
|
||||
for (const auto i: data) {
|
||||
crc += i;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void crsf::CrsfParser::reset() {
|
||||
bufferPos = 0;
|
||||
frameSize = 0;
|
||||
}
|
||||
|
||||
void crsf::CrsfParser::parseBytes(const std::vector<uint8_t>& data) {
|
||||
for (auto byte : data) {
|
||||
// преамбула
|
||||
if (bufferPos == 0) {
|
||||
if (byte == PREAMBLE) {
|
||||
buffer[0] = byte;
|
||||
bufferPos = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// чтение size
|
||||
if (bufferPos == 1) {
|
||||
buffer[1] = byte;
|
||||
frameSize = byte;
|
||||
|
||||
if (frameSize < 3 || frameSize > (CRSF_MAX_FRAME_SIZE - 2)) {
|
||||
reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
bufferPos = 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// обработка тела кадра
|
||||
buffer[bufferPos++] = byte;
|
||||
|
||||
const size_t fullFrameSize = frameSize + 2; // preamble + size + frameSize bytes
|
||||
|
||||
if (bufferPos == fullFrameSize) {
|
||||
auto frame = std::make_unique<CrsfFrame>(std::span(buffer, bufferPos));
|
||||
if (frame->checkCrc()) {
|
||||
frames.push_back(std::move(frame));
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<crsf::CrsfFrame> crsf::CrsfParser::pullPacket() {
|
||||
std::unique_ptr<CrsfFrame> out;
|
||||
if (!this->frames.empty()) {
|
||||
out = std::move(this->frames.front());
|
||||
frames.pop_front();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
61
lib/crsf.h
Normal file
61
lib/crsf.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef SDRPI_FPV_CONTROL_PLATFORM_PORT_CRSF_H
|
||||
#define SDRPI_FPV_CONTROL_PLATFORM_PORT_CRSF_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace crsf {
|
||||
struct CrsfFrame {
|
||||
uint8_t address; // адрес получателя
|
||||
uint8_t type; // тип пакета
|
||||
std::vector<uint8_t> payload; // полезная нагрузка
|
||||
uint8_t crc; // контрольная сумма
|
||||
|
||||
CrsfFrame();
|
||||
CrsfFrame(uint8_t addr, uint8_t t, std::vector<uint8_t> p);
|
||||
CrsfFrame(std::span<const uint8_t> rawBuffer);
|
||||
|
||||
bool checkCrc() const;
|
||||
void setCrc();
|
||||
|
||||
void writeToBuffer(std::vector<uint8_t>& dest) const;
|
||||
};
|
||||
|
||||
constexpr size_t CRSF_MAX_FRAME_SIZE = 260;
|
||||
|
||||
uint8_t crsfComputeCrc(std::span<const uint8_t> data);
|
||||
|
||||
class CrsfParser {
|
||||
public:
|
||||
CrsfParser() = default;
|
||||
|
||||
/**
|
||||
* Разбирает поток байт и формирует пакеты, добавляя их во внутреннюю очередь.
|
||||
* Метод может принимать чанки любых размеров (например, 512 байт с UART).
|
||||
*/
|
||||
void parseBytes(const std::vector<uint8_t>& data);
|
||||
|
||||
/**
|
||||
* Отдает все готовые пакеты и очищает очередь.
|
||||
*/
|
||||
std::unique_ptr<CrsfFrame> pullPacket();
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
static constexpr uint8_t PREAMBLE = 0xC8;
|
||||
|
||||
uint8_t buffer[CRSF_MAX_FRAME_SIZE]{};
|
||||
size_t bufferPos{0};
|
||||
size_t frameSize{0};
|
||||
|
||||
std::deque<std::unique_ptr<CrsfFrame>> frames;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //SDRPI_FPV_CONTROL_PLATFORM_PORT_CRSF_H
|
||||
2
lib/port/poller-general.cpp
Normal file
2
lib/port/poller-general.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
|
||||
54
lib/port/poller.h
Normal file
54
lib/port/poller.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef SDRPI_FPV_CONTROL_GROUND_PORT_POLLER_H
|
||||
#define SDRPI_FPV_CONTROL_GROUND_PORT_POLLER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace poller {
|
||||
/**
|
||||
* PollObject - базовый объект для мониторинга I/O.
|
||||
* Платформенно-специфичные дескрипторы хранятся в защищённых полях.
|
||||
*/
|
||||
class PollObject {
|
||||
public:
|
||||
bool isPollIn() const;
|
||||
bool isPollOut() const;
|
||||
bool isPollHup() const;
|
||||
|
||||
virtual ~PollObject();
|
||||
protected:
|
||||
#ifdef _WIN32
|
||||
// UART HANDLE или UDP событие
|
||||
HANDLE hCom{INVALID_HANDLE_VALUE};
|
||||
SOCKET sock{INVALID_SOCKET};
|
||||
WSAEVENT winHandle{nullptr};
|
||||
short revents{0}; // событие произошло
|
||||
#else
|
||||
int fd{0};
|
||||
short events{0};
|
||||
short revents{0};
|
||||
#endif
|
||||
friend class PollWrapper;
|
||||
};
|
||||
|
||||
/**
|
||||
* Класс-обертка для мониторинга файловых дискрипторов. Использует стек для хранения массива структур мониторинга.
|
||||
*/
|
||||
class PollWrapper {
|
||||
public:
|
||||
PollWrapper();
|
||||
|
||||
std::vector<std::shared_ptr<PollObject>> objects;
|
||||
|
||||
/**
|
||||
* Функция, которую нужно вызывать в бесконечном цикле. Вызывает `poll`, после чего выполняет обработчики событий, если нужно.
|
||||
* @param timeoutMs
|
||||
*/
|
||||
void loop(int timeoutMs = -1);
|
||||
|
||||
~PollWrapper();
|
||||
};
|
||||
|
||||
} // namespace poller
|
||||
|
||||
#endif //SDRPI_FPV_CONTROL_GROUND_PORT_POLLER_H
|
||||
25
lib/port/uart.h
Normal file
25
lib/port/uart.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef SDRPI_FPV_CONTROL_GROUND_PORT_UART_H
|
||||
#define SDRPI_FPV_CONTROL_GROUND_PORT_UART_H
|
||||
#include <cstdint>
|
||||
|
||||
#include "port/poller.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
|
||||
namespace drivers {
|
||||
class UartDriver : public poller::PollObject {
|
||||
public:
|
||||
UartDriver(const std::string& path, int baud);
|
||||
bool writeData(std::span<const uint8_t> data);
|
||||
size_t readChunk(std::vector<uint8_t>& out);
|
||||
~UartDriver() override;
|
||||
|
||||
#if _WIN32
|
||||
protected:
|
||||
OVERLAPPED overlapped{};
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SDRPI_FPV_CONTROL_GROUND_PORT_UART_H
|
||||
21
lib/port/udp.h
Normal file
21
lib/port/udp.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef SDRPI_FPV_CONTROL_GROUND_PORT_UDP_H
|
||||
#define SDRPI_FPV_CONTROL_GROUND_PORT_UDP_H
|
||||
|
||||
#include "port/poller.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
namespace drivers {
|
||||
class UdpDriver : public poller::PollObject {
|
||||
public:
|
||||
// port - локальный порт
|
||||
explicit UdpDriver(uint16_t port);
|
||||
bool sendTo(std::span<const uint8_t> data, const std::string& addr, uint16_t port);
|
||||
bool recvPacket(std::vector<uint8_t>& out);
|
||||
~UdpDriver() override;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SDRPI_FPV_CONTROL_GROUND_PORT_UDP_H
|
||||
51
lib/port/unix/poller.cpp
Normal file
51
lib/port/unix/poller.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "port/poller.h"
|
||||
|
||||
#include <sys/poll.h>
|
||||
#include <algorithm>
|
||||
|
||||
bool poller::PollObject::isPollIn() const { return revents & POLLIN; }
|
||||
bool poller::PollObject::isPollOut() const { return revents & POLLOUT; }
|
||||
bool poller::PollObject::isPollHup() const { return revents & POLLHUP; }
|
||||
poller::PollObject::~PollObject() = default;
|
||||
|
||||
poller::PollWrapper::PollWrapper() = default;
|
||||
|
||||
void poller::PollWrapper::loop(int timeoutMs) {
|
||||
if (this->objects.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// проверяем, что нет объектов с fd < 0, удаляем такие объекты если они есть
|
||||
for (size_t index = 0; index < this->objects.size();) {
|
||||
if (this->objects[index]->fd < 0) {
|
||||
this->objects.erase(this->objects.begin() + static_cast<ssize_t>(index));
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
const auto qsize = this->objects.size();
|
||||
|
||||
// массив для вызова poll
|
||||
pollfd pollFds[qsize];
|
||||
|
||||
// заполняем данные для poll
|
||||
for (size_t i = 0; i < qsize; i++) {
|
||||
pollFds[i].revents = 0;
|
||||
pollFds[i].fd = this->objects[i]->fd;
|
||||
pollFds[i].events = this->objects[i]->events;
|
||||
}
|
||||
|
||||
// выполняем poll
|
||||
poll(pollFds, qsize, timeoutMs);
|
||||
|
||||
// проверяем события
|
||||
for (size_t i = 0; i < qsize; i++) {
|
||||
objects[i]->revents = pollFds[i].revents;
|
||||
// if (pollFds[i].revents != 0) {
|
||||
// objects[i]->callback();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
poller::PollWrapper::~PollWrapper() = default;
|
||||
60
lib/port/unix/uart.cpp
Normal file
60
lib/port/unix/uart.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "port/uart.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <asm/termbits.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <atomic>
|
||||
#include <span>
|
||||
|
||||
|
||||
drivers::UartDriver::UartDriver(const std::string& path, int baud) {
|
||||
fd = open(path.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||
if (fd < 0) throw std::runtime_error(std::string("UartDriver open error ") + path + ": " + std::strerror(errno));
|
||||
|
||||
struct termios2 tio{};
|
||||
if (ioctl(fd, TCGETS2, &tio) != 0) {
|
||||
close(fd);
|
||||
throw std::runtime_error(std::string("UartDriver setup error: ") + std::strerror(errno));
|
||||
}
|
||||
// 8bit
|
||||
tio.c_cflag &= ~CSIZE;
|
||||
tio.c_cflag |= CS8;
|
||||
// baud rate
|
||||
tio.c_ispeed = baud;
|
||||
tio.c_ospeed = baud;
|
||||
// other
|
||||
tio.c_iflag |= (INPCK|IGNBRK|IGNCR|ISTRIP);
|
||||
tio.c_cflag &= ~CBAUD;
|
||||
tio.c_cflag |= (BOTHER|CREAD|CLOCAL);
|
||||
|
||||
if (ioctl(fd, TCSETS2, &tio) != 0) {
|
||||
close(fd);
|
||||
throw std::runtime_error(std::string("UartDriver setup error: ") + std::strerror(errno));
|
||||
}
|
||||
|
||||
events = POLLIN;
|
||||
}
|
||||
bool drivers::UartDriver::writeData(std::span<const uint8_t> data) {
|
||||
if (fd < 0) return false;
|
||||
auto w = write(fd, data.data(), data.size());
|
||||
return w == data.size();
|
||||
}
|
||||
size_t drivers::UartDriver::readChunk(std::vector<uint8_t>& out) {
|
||||
if (fd < 0) return 0;
|
||||
out.resize(1024);
|
||||
auto r = read(fd, out.data(), out.size());
|
||||
if (r <= 0) return 0;
|
||||
out.resize(r);
|
||||
return static_cast<size_t>(r);
|
||||
}
|
||||
drivers::UartDriver::~UartDriver() {
|
||||
close(fd);
|
||||
}
|
||||
55
lib/port/unix/udp.cpp
Normal file
55
lib/port/unix/udp.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "port/udp.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <poll.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "crsf.h"
|
||||
|
||||
|
||||
drivers::UdpDriver::UdpDriver(uint16_t port) {
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) {
|
||||
throw std::runtime_error(std::string("UdpDriver open error: ") + std::strerror(errno));
|
||||
}
|
||||
|
||||
sockaddr_in addr{};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(fd, (sockaddr*)&addr, sizeof(addr)) < 0) if (fd < 0) {
|
||||
close(fd);
|
||||
throw std::runtime_error(std::string("UdpDriver bind error: ") + std::strerror(errno));
|
||||
}
|
||||
|
||||
events = POLLIN;
|
||||
}
|
||||
bool drivers::UdpDriver::sendTo(std::span<const uint8_t> data, const std::string& addr, uint16_t port) {
|
||||
if (fd < 0) return false;
|
||||
|
||||
sockaddr_in dst{};
|
||||
dst.sin_family = AF_INET;
|
||||
dst.sin_port = htons(port);
|
||||
inet_aton(addr.c_str(), &dst.sin_addr);
|
||||
|
||||
auto w = sendto(fd, data.data(), data.size(), 0, (sockaddr*)&dst, sizeof(dst));
|
||||
return w == data.size();
|
||||
}
|
||||
bool drivers::UdpDriver::recvPacket(std::vector<uint8_t>& out) {
|
||||
out.resize(crsf::CRSF_MAX_FRAME_SIZE);
|
||||
ssize_t received = recvfrom(fd, out.data(), out.size(), MSG_DONTWAIT, nullptr, nullptr);
|
||||
|
||||
if (received > 0) {
|
||||
out.resize(received);
|
||||
return true;
|
||||
}
|
||||
out.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
drivers::UdpDriver::~UdpDriver() {
|
||||
close(fd);
|
||||
}
|
||||
64
lib/port/win/poller.cpp
Normal file
64
lib/port/win/poller.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "port/poller.h"
|
||||
#include <windows.h>
|
||||
#include <stdexcept>
|
||||
|
||||
bool poller::PollObject::isPollIn() const { return revents != 0; }
|
||||
bool poller::PollObject::isPollOut() const { return true; } // для Windows драйверов обычно всегда можно писать
|
||||
bool poller::PollObject::isPollHup() const {
|
||||
// UART: проверка ClearCommError
|
||||
if (hCom != INVALID_HANDLE_VALUE) {
|
||||
DWORD errors;
|
||||
if (!ClearCommError(hCom, &errors, nullptr)) {
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_INVALID_HANDLE || err == ERROR_FILE_NOT_FOUND)
|
||||
return true; // COM порт физически пропал
|
||||
}
|
||||
if (errors & CE_RXOVER) return true; // overflow тоже можно трактовать как проблему
|
||||
}
|
||||
|
||||
// UDP: проверка ошибки сокета через SO_ERROR
|
||||
if (sock != INVALID_SOCKET) {
|
||||
int err = 0;
|
||||
int len = sizeof(err);
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0) {
|
||||
if (err != 0) return true; // есть ошибка на сокете
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
poller::PollObject::~PollObject() = default;
|
||||
|
||||
poller::PollWrapper::PollWrapper() = default;
|
||||
|
||||
void poller::PollWrapper::loop(int timeoutMs) {
|
||||
// собираем все события
|
||||
std::vector<HANDLE> handles;
|
||||
for (auto& obj : objects) {
|
||||
#ifdef _WIN32
|
||||
if (!obj) continue;
|
||||
// UART HANDLE или UDP событие
|
||||
if (obj->winHandle)
|
||||
handles.push_back(obj->winHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (handles.empty()) return;
|
||||
|
||||
DWORD waitTime = (timeoutMs < 0) ? INFINITE : static_cast<DWORD>(timeoutMs);
|
||||
DWORD rc = WaitForMultipleObjects(static_cast<DWORD>(handles.size()),
|
||||
handles.data(),
|
||||
FALSE, // ждем любого события
|
||||
waitTime);
|
||||
if (rc == WAIT_FAILED)
|
||||
throw std::runtime_error("WaitForMultipleObjects failed");
|
||||
|
||||
// Определяем, какой объект сработал
|
||||
int idx = rc - WAIT_OBJECT_0;
|
||||
if (idx >= 0 && idx < static_cast<int>(handles.size())) {
|
||||
auto& obj = objects[idx];
|
||||
obj->revents = 1; // простая метка, что событие произошло
|
||||
}
|
||||
}
|
||||
|
||||
poller::PollWrapper::~PollWrapper() = default;
|
||||
71
lib/port/win/uart.cpp
Normal file
71
lib/port/win/uart.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "port/poller.h"
|
||||
#include "port/uart.h"
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
|
||||
drivers::UartDriver::UartDriver(const std::string& path, int baud) {
|
||||
hCom = CreateFileA(path.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0, nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED,
|
||||
nullptr);
|
||||
if (hCom == INVALID_HANDLE_VALUE)
|
||||
throw std::runtime_error("Cannot open UART port");
|
||||
|
||||
// Настройка DCB
|
||||
DCB dcb{};
|
||||
dcb.DCBlength = sizeof(dcb);
|
||||
if (!GetCommState(hCom, &dcb))
|
||||
throw std::runtime_error("GetCommState failed");
|
||||
|
||||
dcb.BaudRate = baud;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = EVENPARITY; // 8E2
|
||||
dcb.StopBits = TWOSTOPBITS;
|
||||
|
||||
if (!SetCommState(hCom, &dcb))
|
||||
throw std::runtime_error("SetCommState failed");
|
||||
|
||||
// Настройка событий
|
||||
winHandle = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
if (!winHandle) throw std::runtime_error("CreateEvent failed");
|
||||
|
||||
if (!SetCommMask(hCom, EV_RXCHAR))
|
||||
throw std::runtime_error("SetCommMask failed");
|
||||
|
||||
// Первый вызов WaitCommEvent в асинхронном режиме
|
||||
memset(&overlapped, 0, sizeof(overlapped));
|
||||
overlapped.hEvent = winHandle;
|
||||
DWORD dummy;
|
||||
WaitCommEvent(hCom, &dummy, &overlapped);
|
||||
}
|
||||
bool drivers::UartDriver::writeData(std::span<const uint8_t> data) {
|
||||
DWORD written;
|
||||
OVERLAPPED ov{};
|
||||
ov.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
|
||||
bool ok = WriteFile(hCom, data.data(), (DWORD)data.size(), &written, &ov) ||
|
||||
GetOverlappedResult(hCom, &ov, &written, TRUE);
|
||||
CloseHandle(ov.hEvent);
|
||||
return ok && written == data.size();
|
||||
}
|
||||
size_t drivers::UartDriver::readChunk(std::vector<uint8_t>& out) {
|
||||
out.resize(512);
|
||||
DWORD read = 0;
|
||||
OVERLAPPED ov{};
|
||||
ov.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
bool ok = ReadFile(hCom, out.data(), (DWORD)out.size(), &read, &ov) ||
|
||||
GetOverlappedResult(hCom, &ov, &read, TRUE);
|
||||
CloseHandle(ov.hEvent);
|
||||
out.resize(read);
|
||||
return read;
|
||||
}
|
||||
drivers::UartDriver::~UartDriver() {
|
||||
if (hCom != INVALID_HANDLE_VALUE) CloseHandle(hCom);
|
||||
if (winHandle) CloseHandle(winHandle);
|
||||
}
|
||||
|
||||
66
lib/port/win/udp.cpp
Normal file
66
lib/port/win/udp.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// File: src/port/win/udp_driver_win.cpp
|
||||
#include "pollobject.h"
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
|
||||
namespace drivers {
|
||||
|
||||
class UdpDriver : public poller::PollObject {
|
||||
public:
|
||||
explicit UdpDriver(uint16_t port) {
|
||||
WSADATA wsa;
|
||||
WSAStartup(MAKEWORD(2,2), &wsa);
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == INVALID_SOCKET) throw std::runtime_error("Cannot create socket");
|
||||
|
||||
sockaddr_in addr{};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(sock, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
|
||||
throw std::runtime_error("Bind failed");
|
||||
|
||||
winHandle = WSACreateEvent();
|
||||
if (!winHandle) throw std::runtime_error("WSACreateEvent failed");
|
||||
|
||||
WSAEventSelect(sock, winHandle, FD_READ);
|
||||
}
|
||||
|
||||
bool sendTo(std::span<const uint8_t> data, const std::string& addrStr, uint16_t port) {
|
||||
sockaddr_in dest{};
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_port = htons(port);
|
||||
inet_pton(AF_INET, addrStr.c_str(), &dest.sin_addr);
|
||||
|
||||
int ret = sendto(sock, (const char*)data.data(), (int)data.size(), 0,
|
||||
(sockaddr*)&dest, sizeof(dest));
|
||||
return ret == (int)data.size();
|
||||
}
|
||||
|
||||
bool recvPacket(std::vector<uint8_t>& out) {
|
||||
out.resize(512);
|
||||
sockaddr_in src{};
|
||||
int len = sizeof(src);
|
||||
int ret = recvfrom(sock, (char*)out.data(), (int)out.size(), MSG_PEEK,
|
||||
(sockaddr*)&src, &len);
|
||||
if (ret <= 0) return false; // ничего нет или ошибка
|
||||
out.resize(ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
~UdpDriver() override {
|
||||
if (sock != INVALID_SOCKET) closesocket(sock);
|
||||
if (winHandle) WSACloseEvent(winHandle);
|
||||
WSACleanup();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace drivers
|
||||
Reference in New Issue
Block a user