Initial commit: add Location Plotter
This commit is contained in:
@@ -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))));
|
||||
}
|
||||
Reference in New Issue
Block a user