Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(simu): cross-platform connection of host serial ports to simu radio #5584

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmake/QtDefs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ find_package(Qt5LinguistTools)
find_package(Qt5PrintSupport)
find_package(Qt5Multimedia)
find_package(Qt5Svg)
find_package(Qt5SerialPort)

if(Qt5Core_FOUND)
message(STATUS "Qt Version: ${Qt5Core_VERSION}")
Expand Down
1 change: 1 addition & 0 deletions companion/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ target_link_libraries(common
Qt5::Core
Qt5::Xml
Qt5::Widgets
Qt5::SerialPort
${PTHREAD_LIBRARY}
${SDL2_LIBRARIES}
${WIN_LINK_LIBRARIES}
Expand Down
2 changes: 2 additions & 0 deletions companion/src/simulation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ set(${PROJECT_NAME}_NAMES
widgets/lcdwidget
widgets/radiowidget
widgets/virtualjoystickwidget
serialportsdialog
hostserialconnector
)

if(SDL2_FOUND)
Expand Down
186 changes: 186 additions & 0 deletions companion/src/simulation/hostserialconnector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include "hostserialconnector.h"

HostSerialConnector::HostSerialConnector(QObject *parent, SimulatorInterface *simulator)
: simulator(simulator)
{
for (int i = 0; i < MAX_HOST_SERIAL; i++) {
hostAuxPorts[i] = nullptr;
hostAuxPortsEncoding[i] = SERIAL_ENCODING_8N1;
hostAuxPortsBaudRate[i] = 9600;
hostAuxPortsOpen[i] = false;
}
}

HostSerialConnector::~HostSerialConnector()
{
for (int i = 0; i < MAX_HOST_SERIAL; i++) {
if (hostAuxPorts[i] != nullptr) {
hostAuxPorts[i]->close();
hostAuxPorts[i]->deleteLater();
}
}
}

QString HostSerialConnector::getConnectedSerialPortName(int index)
{
if (index >= MAX_HOST_SERIAL)
return QString("");

QMutexLocker locker(&hostAuxPortsMutex);

QSerialPort * port = hostAuxPorts[index];
if (port == nullptr)
return QString("");

return port->portName();
}

void HostSerialConnector::connectSerialPort(int index, QString portName)
{
if (index >= MAX_HOST_SERIAL)
return;

QMutexLocker locker(&hostAuxPortsMutex);

QSerialPort * port = hostAuxPorts[index];
if (port != nullptr) {
port->close();
port->deleteLater();
}

if (portName.isEmpty()) {
hostAuxPorts[index] = nullptr;
return;
}

port = new QSerialPort(portName, this);
hostAuxPorts[index] = port;

setSerialEncoding(index, hostAuxPortsEncoding[index]);
setSerialBaudRate(index, hostAuxPortsBaudRate[index]);

connect(port, &QSerialPort::readyRead, [this, index, port]() {
QByteArray data = port->readAll();
simulator->receiveAuxSerialData(index, data);
});

if (hostAuxPortsOpen[index])
serialStart(index);
}

void HostSerialConnector::sendSerialData(const quint8 index, const QByteArray & data)
{
if (index >= MAX_HOST_SERIAL)
return;

QMutexLocker locker(&hostAuxPortsMutex);

QSerialPort * port = hostAuxPorts[index];
if (port == nullptr)
return;

port->write(data);
}

void HostSerialConnector::setSerialEncoding(const quint8 index, const quint8 encoding)
{
if (index >= MAX_HOST_SERIAL)
return;

QMutexLocker locker(&hostAuxPortsMutex);

hostAuxPortsEncoding[index] = encoding;

QSerialPort * port = hostAuxPorts[index];
if (port == nullptr)
return;

switch(encoding) {
case SERIAL_ENCODING_8N1:
port->setDataBits(QSerialPort::Data8);
port->setParity(QSerialPort::NoParity);
port->setStopBits(QSerialPort::OneStop);
break;
case SERIAL_ENCODING_8E2:
port->setDataBits(QSerialPort::Data8);
port->setParity(QSerialPort::EvenParity);
port->setStopBits(QSerialPort::TwoStop);
break;
default:
// Do nothing, QSerialPort can't do SERIAL_ENCODING_PXX1_PWM
break;
}
}

void HostSerialConnector::setSerialBaudRate(const quint8 index, const quint32 baudrate)
{
if (index >= MAX_HOST_SERIAL)
return;

QMutexLocker locker(&hostAuxPortsMutex);

hostAuxPortsBaudRate[index] = baudrate;

QSerialPort * port = hostAuxPorts[index];
if (port == nullptr)
return;

if (!port->setBaudRate(baudrate))
qDebug() << "Failed to set baudrate";
}

void HostSerialConnector::serialStart(const quint8 index)
{
if (index >= MAX_HOST_SERIAL)
return;

QMutexLocker locker(&hostAuxPortsMutex);

hostAuxPortsOpen[index] = true;

QSerialPort * port = hostAuxPorts[index];
if (port == nullptr)
return;

if (port->open(QIODevice::ReadWrite))
qDebug() << "Opened host serial " << index;
else
qDebug() << "Failed to open host serial " << index << ": " << port->errorString();
}

void HostSerialConnector::serialStop(const quint8 index)
{
if (index >= MAX_HOST_SERIAL)
return;

QMutexLocker locker(&hostAuxPortsMutex);

hostAuxPortsOpen[index] = false;

QSerialPort * port = hostAuxPorts[index];
if (port == nullptr)
return;

port->close();
}
57 changes: 57 additions & 0 deletions companion/src/simulation/hostserialconnector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#pragma once

#include "simulatorinterface.h"

#include <QSerialPort>
#include <QMutex>

#define MAX_HOST_SERIAL 2

class HostSerialConnector : public QObject
{
Q_OBJECT

public:
explicit HostSerialConnector(QObject * parent, SimulatorInterface * simulator);
~HostSerialConnector();

QString getConnectedSerialPortName(int index);

public slots:
void connectSerialPort(int index, QString portName);
void sendSerialData(const quint8 index, const QByteArray & data);
void setSerialEncoding(const quint8 index, const quint8 encoding);
void setSerialBaudRate(const quint8 index, const quint32 baudrate);
void serialStart(const quint8 index);
void serialStop(const quint8 index);

private:
SimulatorInterface * simulator;

QRecursiveMutex hostAuxPortsMutex;
QSerialPort * hostAuxPorts[MAX_HOST_SERIAL];
quint8 hostAuxPortsEncoding[MAX_HOST_SERIAL];
quint32 hostAuxPortsBaudRate[MAX_HOST_SERIAL];
bool hostAuxPortsOpen[MAX_HOST_SERIAL];
};
89 changes: 89 additions & 0 deletions companion/src/simulation/serialportsdialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include "serialportsdialog.h"
#include "ui_serialportsdialog.h"

SerialPortsDialog::SerialPortsDialog(QWidget *parent, SimulatorInterface *simulator, HostSerialConnector *connector) :
QDialog(parent),
simulator(simulator),
connector(connector),
ui(new Ui::SerialPortsDialog)
{
ui->setupUi(this);

aux1 = connector->getConnectedSerialPortName(0);
aux2 = connector->getConnectedSerialPortName(1);

populateSerialPortCombo(ui->aux1Combo, aux1);
populateSerialPortCombo(ui->aux2Combo, aux2);

ui->aux1Combo->setEnabled(simulator->getCapability(SimulatorInterface::Capability::CAP_SERIAL_AUX1));
ui->aux2Combo->setEnabled(simulator->getCapability(SimulatorInterface::Capability::CAP_SERIAL_AUX2));
}

SerialPortsDialog::~SerialPortsDialog()
{
delete ui;
}

void SerialPortsDialog::populateSerialPortCombo(QComboBox * cb, QString currentPortName)
{
cb->clear();
cb->addItem(tr("Not Assigned"), "");
if (currentPortName == "") {
cb->setCurrentIndex(0);
}

const auto serialPortInfos = QSerialPortInfo::availablePorts();
for (int i = 0; i < serialPortInfos.size(); i++) {
const auto portInfo = serialPortInfos[i];
cb->addItem(portInfo.portName(), portInfo.portName());
if (portInfo.portName() == currentPortName)
cb->setCurrentIndex(i + 1);
}
}

void SerialPortsDialog::on_cancelButton_clicked()
{
this->reject();
}

void SerialPortsDialog::on_okButton_clicked()
{
this->accept();
}

void SerialPortsDialog::on_refreshButton_clicked()
{
populateSerialPortCombo(ui->aux1Combo, aux1);
populateSerialPortCombo(ui->aux2Combo, aux2);
}

void SerialPortsDialog::on_aux1Combo_currentIndexChanged(int index)
{
aux1 = ui->aux1Combo->itemData(index).toString();
}

void SerialPortsDialog::on_aux2Combo_currentIndexChanged(int index)
{
aux2 = ui->aux2Combo->itemData(index).toString();
}
Loading
Loading