Initial commit: add Location Plotter
Ubuntu build / build-and-test (ubuntu-22.04) (push) Has been cancelled
Ubuntu build / build-and-test (ubuntu-24.04) (push) Has been cancelled

This commit is contained in:
2026-07-16 13:15:54 +03:00
commit 2856c5a560
25 changed files with 4630 additions and 0 deletions
+266
View File
@@ -0,0 +1,266 @@
#include "SerialReader.h"
#include <QSocketNotifier>
#include <QTimer>
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
namespace {
constexpr qsizetype MaxBytesPerReadCycle = 64 * 1024;
speed_t baudToSpeed(int baudRate)
{
switch (baudRate) {
case 1200:
return B1200;
case 2400:
return B2400;
case 4800:
return B4800;
case 9600:
return B9600;
case 19200:
return B19200;
case 38400:
return B38400;
case 57600:
return B57600;
case 115200:
return B115200;
case 230400:
return B230400;
case 460800:
return B460800;
case 921600:
return B921600;
default:
return 0;
}
}
} // namespace
SerialReader::SerialReader(QObject *parent)
: QObject(parent)
{
m_pollTimer = new QTimer(this);
m_pollTimer->setInterval(20);
connect(m_pollTimer, &QTimer::timeout, this, &SerialReader::handleReadyRead);
m_partialLineTimer = new QTimer(this);
m_partialLineTimer->setSingleShot(true);
m_partialLineTimer->setInterval(250);
connect(m_partialLineTimer, &QTimer::timeout, this, &SerialReader::flushPartialLine);
}
SerialReader::~SerialReader()
{
closePort();
}
bool SerialReader::isOpen() const
{
return m_fd >= 0;
}
QString SerialReader::portName() const
{
return m_portName;
}
bool SerialReader::openPort(const QString &path, int baudRate)
{
closePort();
const QByteArray localPath = path.toLocal8Bit();
m_fd = ::open(localPath.constData(), O_RDWR | O_NOCTTY | O_NONBLOCK);
if (m_fd < 0) {
emitSystemError(QStringLiteral("Port acilamadi"));
return false;
}
m_portName = path;
if (!configurePort(baudRate)) {
closePort();
return false;
}
setModemControlLines();
m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
connect(m_notifier, &QSocketNotifier::activated, this, &SerialReader::handleReadyRead);
m_pollTimer->start();
emit statusChanged(QStringLiteral("%1 dinleniyor (%2 baud)").arg(path).arg(baudRate));
return true;
}
void SerialReader::closePort()
{
if (m_notifier != nullptr) {
m_notifier->setEnabled(false);
m_notifier->deleteLater();
m_notifier = nullptr;
}
m_pollTimer->stop();
m_partialLineTimer->stop();
if (m_fd >= 0) {
::close(m_fd);
m_fd = -1;
}
m_buffer.clear();
if (!m_portName.isEmpty()) {
emit statusChanged(QStringLiteral("%1 kapatildi").arg(m_portName));
}
m_portName.clear();
}
void SerialReader::handleReadyRead()
{
if (m_fd < 0) {
return;
}
char data[4096];
qsizetype bytesProcessed = 0;
while (true) {
const ssize_t bytesRead = ::read(m_fd, data, sizeof(data));
if (bytesRead > 0) {
bytesProcessed += static_cast<qsizetype>(bytesRead);
m_buffer.append(data, static_cast<qsizetype>(bytesRead));
processBuffer();
if (m_buffer.size() > 64 * 1024) {
m_buffer.clear();
emit errorOccurred(QStringLiteral("Satir sonu gelmeyen seri veri tamponu temizlendi"));
}
if (bytesProcessed >= MaxBytesPerReadCycle) {
QTimer::singleShot(0, this, &SerialReader::handleReadyRead);
return;
}
continue;
}
if (bytesRead == 0 || errno == EAGAIN || errno == EWOULDBLOCK) {
return;
}
emitSystemError(QStringLiteral("Seri port okuma hatasi"));
closePort();
return;
}
}
void SerialReader::flushPartialLine()
{
if (m_buffer.isEmpty()) {
return;
}
QByteArray line = m_buffer;
m_buffer.clear();
while (line.endsWith('\r') || line.endsWith('\n')) {
line.chop(1);
}
if (!line.isEmpty()) {
emit lineReceived(QString::fromUtf8(line));
}
}
bool SerialReader::configurePort(int baudRate)
{
const speed_t speed = baudToSpeed(baudRate);
if (speed == 0) {
emit errorOccurred(QStringLiteral("Desteklenmeyen baud rate: %1").arg(baudRate));
return false;
}
termios options {};
if (::tcgetattr(m_fd, &options) != 0) {
emitSystemError(QStringLiteral("Port ayarlari okunamadi"));
return false;
}
::cfmakeraw(&options);
::cfsetispeed(&options, speed);
::cfsetospeed(&options, speed);
options.c_cflag |= CLOCAL | CREAD;
options.c_cflag &= ~HUPCL;
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 0;
if (::tcsetattr(m_fd, TCSANOW, &options) != 0) {
emitSystemError(QStringLiteral("Port ayarlari uygulanamadi"));
return false;
}
::tcflush(m_fd, TCIFLUSH);
return true;
}
void SerialReader::processBuffer()
{
bool emittedLine = false;
while (true) {
qsizetype delimiterIndex = -1;
for (qsizetype i = 0; i < m_buffer.size(); ++i) {
if (m_buffer.at(i) == '\n' || m_buffer.at(i) == '\r') {
delimiterIndex = i;
break;
}
}
if (delimiterIndex < 0) {
break;
}
QByteArray line = m_buffer.left(delimiterIndex);
qsizetype removeCount = delimiterIndex + 1;
while (removeCount < m_buffer.size()
&& (m_buffer.at(removeCount) == '\n' || m_buffer.at(removeCount) == '\r')) {
++removeCount;
}
m_buffer.remove(0, removeCount);
if (!line.isEmpty()) {
emittedLine = true;
emit lineReceived(QString::fromUtf8(line));
}
}
if (!m_buffer.isEmpty()) {
m_partialLineTimer->start();
} else if (emittedLine) {
m_partialLineTimer->stop();
}
}
void SerialReader::setModemControlLines()
{
int status = 0;
if (::ioctl(m_fd, TIOCMGET, &status) != 0) {
return;
}
status |= TIOCM_DTR;
status |= TIOCM_RTS;
::ioctl(m_fd, TIOCMSET, &status);
}
void SerialReader::emitSystemError(const QString &prefix)
{
emit errorOccurred(QStringLiteral("%1: %2").arg(prefix, QString::fromLocal8Bit(std::strerror(errno))));
}