/** * @RDE_HEADER@ * TTCI-Server Library * $Id$ * $HeadURL$ * $Author$ * Created by Patric Karwat, NLS, 2019-01-17 */ #include "ttci_device.h" #include "settings.h" #include #include #include namespace TrainingAreaBaseOC { Device::Device(QObject *parent, bool tcpMode) : QObject(parent) , m_spQuerry(new LtsSerialPortQuerry::LtsSerialPortQuerry(this)) , m_socket(new QTcpSocket(this)) , m_restartTimer(new QTimer(this)) , m_receiveTimeoutTimer(new QTimer(this)) , m_tcpMode(tcpMode) , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("TTCI.Device")) { connect(m_spQuerry, &LtsSerialPortQuerry::LtsSerialPortQuerry::connectionEstablished, this, &Device::open); m_spQuerry->restartQuerry(); m_receiveTimeoutTimer->setInterval(15000); connect(m_receiveTimeoutTimer, &QTimer::timeout, m_spQuerry, &LtsSerialPortQuerry::LtsSerialPortQuerry::continueQuerry); connect(m_receiveTimeoutTimer, &QTimer::timeout, this, &Device::disconnectedFromEq); m_receiveTimeoutTimer->start(); } Device::~Device() { close(); } void Device::reOpen() { DEBUG_LOG(m_logger) << "ConnectionHandlerSerial::reOpen"; close(); QTimer::singleShot(2000, this, &Device::open); } bool Device::open() { if (m_tcpMode) { m_restartTimer->stop(); close(); m_socket = new QTcpSocket(this); m_socket->connectToHost(Settings::hostName(), Settings::portTtci()); if (!m_socket->waitForConnected()) { ERROR_LOG(m_logger) << "Could not (re)open tcp destination " << m_socket->peerName() << ":" << m_socket->peerPort() << ", error: " << m_socket->errorString(); return false; } connect(m_socket, &QTcpSocket::readyRead, this, &Device::onDataAvailable); connect(m_socket, QOverload::of(&QAbstractSocket::error), this, &Device::handleError); connect(m_socket, &QTcpSocket::disconnected, this, &Device::open); DEBUG_LOG(m_logger) << "(Re)openend tcp destination " << m_socket->peerName() << ":" << Settings::portTtci(); } else { //use portscanner m_serialPort = m_spQuerry->currentSerialPort(); DEBUG_LOG(m_logger) << "(Re)openend serial destination " << m_serialPort->portName() << ":"; connect(m_serialPort, &QSerialPort::readyRead, this, &Device::onDataAvailable); } m_receiveTimeoutTimer->start(); return true; } bool Device::close() { if (m_socket) { if (m_socket->isOpen()) m_socket->close(); DEBUG_LOG(m_logger) << "Closing TCP Port"; delete m_socket; m_socket = nullptr; m_receiveTimeoutTimer->stop(); } return true; } bool Device::isOpen() const { if (m_tcpMode) return m_socket->isOpen(); else return m_serialPort->isOpen(); } void Device::onDataAvailable() { if (m_tcpMode) m_buffer += m_socket->readAll(); else m_buffer += m_serialPort->readAll(); parseBuffer(); } void Device::parseBuffer() { DEBUG_LOG(m_logger) << "Buffer - size: " << m_buffer.size() << ", data: " << m_buffer.toHex() ; // TTCI-Frame: 02 03 // data: // nicht gefunden oder letztes Byte -> ignore int idxStart = -1; while ((idxStart = m_buffer.indexOf(0x02)) >= 0) { TRACE_LOG(m_logger) << "idxStart: " << idxStart ; // Laengenbyte im Buffer? int idxLen = idxStart + 1; if (idxLen >= m_buffer.size()) return; unsigned char len = static_cast(m_buffer.at(idxLen)); TRACE_LOG(m_logger) << "idxLen: " << idxLen << ", len: " << len << " = 0x" << QByteArray::number(len, 16); int idxCrc = idxLen + len + 1; int idxEnd = idxCrc + 1; if (idxEnd >= m_buffer.size()) return; TRACE_LOG(m_logger) << "idxCrc: " << idxCrc << ", idxEnd: " << idxEnd ; if (idxEnd >= m_buffer.size()) return; // korrekten Ende-Marker gefunden? if (m_buffer.at(idxEnd) != 0x03) { // nein -> neu aufsetzen WARNING_LOG(m_logger) << "Could not find valid end marker - searching new start marker" ; m_buffer = m_buffer.mid(1); continue; } unsigned char crc = static_cast(m_buffer.at(idxCrc)); unsigned char crcCalc = calcChecksum(m_buffer.mid(idxStart, idxCrc - idxStart)); TRACE_LOG(m_logger) << "crc: " << crc << ", calculated: " << crcCalc ; if (crc != crcCalc) { WARNING_LOG(m_logger) << "Invalid checksum - is: " << crcCalc << " - should be: " << crc ; } unsigned char destination = static_cast(m_buffer.at(idxLen + 1)); QByteArray payload = m_buffer.mid(idxLen + 2, len - 1); if (len == 255 || !m_payload.isEmpty()) { DEBUG_LOG(m_logger) << "telegram with more than 254 bytes ..." ; // Erstes Telegram -> destination merken if (len == 255 && m_payload.isEmpty()) DEBUG_LOG(m_logger) << "Remember the payload destination: " << destination ; m_payload += payload; if (len != 255) { // Letztes Telegramm -> ausgeben DEBUG_LOG(m_logger) << "destination: " << destination << ", data: " << m_payload.toHex() ; emit dataAvailable(m_payload); m_payload.clear(); } } else { // normales Telegramm -> ausgeben DEBUG_LOG(m_logger) << "destination: " << destination << ", data: " << payload.toHex() ; emit dataAvailable(payload); } m_buffer = m_buffer.mid(idxEnd + 1); m_receiveTimeoutTimer->start(); } } bool Device::writeData(const QChar destination, const QByteArray &payload) { if ((m_tcpMode && !m_socket->isOpen()) || (!m_tcpMode && !m_serialPort->isOpen())) return false; DEBUG_LOG(m_logger) << "Writing " << payload.size() << " Bytes to channel " << destination ; // in TTCI-Frames verpacken, nicht schnell aber fuer die paar Bytes sollte es ok sein QByteArray data = payload; do { QByteArray payloadToWrite = data.left(254); QByteArray buf; buf.reserve(payloadToWrite.size() + 5); buf.append(0x02); buf.append(static_cast(payloadToWrite.size() + 1)); buf.append(destination.toLatin1()); buf.append(payloadToWrite); buf.append(calcChecksum(buf)); buf.append(0x03); DEBUG_LOG(m_logger) << "Writing: " << buf.toHex() ; if (m_tcpMode) m_socket->write(buf); else m_serialPort->write(buf); data = data.mid(254); } while (!data.isEmpty()); if(m_tcpMode) m_socket->flush(); else m_serialPort->flush(); return true; } unsigned char Device::calcChecksum(const QByteArray &checksumData) { unsigned char sum = 0; for (const char c : checksumData) sum ^= static_cast(c); return sum; } void Device::handleError(QAbstractSocket::SocketError error) { DEBUG_LOG(m_logger) << "Spontaneous error " << error << " doesn't have to be an error ..."; const QString errStr(QStringLiteral("QTcpSocket::SocketError ") + QString::number(error) + QStringLiteral(" from tcp port ") + QString::number(m_socket->peerPort()) + QStringLiteral(" error: ") + m_socket->errorString()); switch (error) { case QTcpSocket::SocketError::ConnectionRefusedError: case QTcpSocket::SocketError::SocketAccessError: case QTcpSocket::SocketError::SocketResourceError: { ERROR_LOG(m_logger) << "Closing port trying to reopen it."; close(); m_restartTimer->start(); Q_FALLTHROUGH(); } case QTcpSocket::SocketError::RemoteHostClosedError: { ERROR_LOG(m_logger) << "Host closed trying to reopen."; m_restartTimer->start(); break; } default: { ERROR_LOG(m_logger) << errStr; close(); m_restartTimer->start(); break; } } } void Device::handleSerialError(QSerialPort::SerialPortError error) { DEBUG_LOG(m_logger) << "Spontaneous error " << error << " doesn't have to be an error ..."; const QString errStr(QStringLiteral("QSerialPort::SerialPortError ") + QString::number(error) + QStringLiteral(" error: ") + m_serialPort->errorString()); switch (error) { case QSerialPort::SerialPortError::OpenError: case QSerialPort::SerialPortError::NotOpenError: case QSerialPort::SerialPortError::DeviceNotFoundError: case QSerialPort::SerialPortError::PermissionError: case QSerialPort::SerialPortError::WriteError: { WARNING_LOG(m_logger) << "Closing port trying to reopen it."; m_restartTimer->start(); Q_FALLTHROUGH(); } default: { WARNING_LOG(m_logger) << errStr; m_restartTimer->start(); break; } } } } //namespace TrainingAreaBaseOC