File indexing completed on 2024-12-22 05:29:18
0001 /* 0002 * Copyright 2019 Eike Hein <hein@kde.org> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Lesser General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2.1 of the License, or (at your option) version 3, or any 0008 * later version accepted by the membership of KDE e.V. (or its 0009 * successor approved by the membership of KDE e.V.), which shall 0010 * act as a proxy defined in Section 6 of version 3 of the license. 0011 * 0012 * This library is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 * Lesser General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU Lesser General Public 0018 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 0019 */ 0020 0021 #include "ryzetelloconnection.h" 0022 #include "debug.h" 0023 0024 #include <QNetworkDatagram> 0025 0026 RyzeTelloConnection::RyzeTelloConnection(const QString &vehicleName, QObject *parent) 0027 : QObject(parent) 0028 , m_vehicleName(vehicleName) 0029 , m_address(QHostAddress("192.168.10.1")) 0030 , m_roll(0) 0031 , m_pitch(0) 0032 , m_yaw(0) 0033 , m_gaz(0) 0034 { 0035 } 0036 0037 RyzeTelloConnection::~RyzeTelloConnection() 0038 { 0039 } 0040 0041 void RyzeTelloConnection::handshake() 0042 { 0043 initSockets(); 0044 } 0045 0046 void RyzeTelloConnection::reset() 0047 { 0048 m_commandQueueTimer.reset(); 0049 0050 m_commandQueue.clear(); 0051 0052 m_controlSocket->abort(); 0053 delete m_controlSocket; 0054 0055 m_stateSocket->abort(); 0056 delete m_stateSocket; 0057 0058 emit stateChanged(Kirogi::AbstractVehicle::Disconnected); 0059 } 0060 0061 void RyzeTelloConnection::sendCommand(const QString &_command, bool retryForever) 0062 { 0063 RyzeTelloCommand command; 0064 command.text = _command; 0065 0066 if (retryForever) { 0067 command.retry = -1; 0068 } 0069 0070 m_commandQueue.enqueue(command); 0071 0072 if (!m_commandQueueTimer || !m_commandQueueTimer->isActive()) { 0073 pumpCommandQueue(); 0074 } 0075 } 0076 0077 void RyzeTelloConnection::pilot(qint8 roll, qint8 pitch, qint8 yaw, qint8 gaz) 0078 { 0079 m_roll = roll; 0080 m_pitch = pitch; 0081 m_yaw = yaw; 0082 m_gaz = gaz; 0083 0084 if (roll != 0 || pitch != 0 || yaw != 0 || gaz != 0) { 0085 if (!m_pilotingTimer) { 0086 m_pilotingTimer = std::make_unique<QTimer>(this); 0087 m_pilotingTimer->setInterval(40); 0088 QObject::connect(m_pilotingTimer.get(), &QTimer::timeout, this, &RyzeTelloConnection::sendPilotingCommand); 0089 } 0090 0091 if (!m_pilotingTimer->isActive()) { 0092 sendPilotingCommand(); 0093 } 0094 0095 m_pilotingTimer->start(); 0096 } else if (m_pilotingTimer) { 0097 m_pilotingTimer->stop(); 0098 sendPilotingCommand(); 0099 } 0100 } 0101 0102 void RyzeTelloConnection::receiveData() 0103 { 0104 while (m_controlSocket->hasPendingDatagrams()) { 0105 const QNetworkDatagram &datagram = m_controlSocket->receiveDatagram(); 0106 0107 if (datagram.isValid()) { 0108 const QByteArray &data = datagram.data(); 0109 const QString &encoded = QString::fromUtf8(data); 0110 0111 if (encoded != QStringLiteral("error")) { 0112 emit responseReceived(encoded); 0113 0114 if (m_commandQueue.size() > 1) { 0115 m_commandQueue.dequeue(); 0116 pumpCommandQueue(); 0117 } else { 0118 m_commandQueue.clear(); 0119 } 0120 } 0121 } 0122 } 0123 } 0124 0125 void RyzeTelloConnection::receiveState() 0126 { 0127 while (m_stateSocket->hasPendingDatagrams()) { 0128 const QNetworkDatagram &datagram = m_stateSocket->receiveDatagram(); 0129 0130 if (datagram.isValid()) { 0131 emit stateReceived(datagram.data()); 0132 } 0133 } 0134 } 0135 0136 void RyzeTelloConnection::pumpCommandQueue() 0137 { 0138 // We do this here instead of in the constructor so the timer is created on the 0139 // current thread. It's not allowed to start/stop timers across thread boundaries. 0140 if (!m_commandQueueTimer) { 0141 m_commandQueueTimer = std::make_unique<QTimer>(this); 0142 QObject::connect(m_commandQueueTimer.get(), &QTimer::timeout, this, &RyzeTelloConnection::pumpCommandQueue, Qt::QueuedConnection); 0143 } 0144 0145 m_commandQueueTimer->stop(); 0146 0147 if (m_commandQueue.isEmpty()) { 0148 return; 0149 } 0150 0151 RyzeTelloCommand &command = m_commandQueue.head(); 0152 0153 if (command.retry > -1) { 0154 if (command.retry >= 5) { 0155 m_commandQueue.dequeue(); 0156 0157 if (m_commandQueue.isEmpty()) { 0158 return; 0159 } 0160 } else { 0161 ++command.retry; 0162 } 0163 } 0164 0165 m_controlSocket->writeDatagram(m_commandQueue.head().text.toUtf8(), m_address, 8889); 0166 0167 // 300ms inspired by defaults in https://github.com/dji-sdk/Tello-Python 0168 m_commandQueueTimer->start(300); 0169 } 0170 0171 void RyzeTelloConnection::sendPilotingCommand() 0172 { 0173 const QByteArray &decoded = 0174 QString("rc %1 %2 %3 %4").arg(QString::number(m_roll), QString::number(m_pitch), QString::number(m_gaz), QString::number(m_yaw)).toUtf8(); 0175 m_controlSocket->writeDatagram(decoded, m_address, 8889); 0176 } 0177 0178 void RyzeTelloConnection::initSockets() 0179 { 0180 m_controlSocket = new QUdpSocket(this); 0181 QObject::connect(m_controlSocket, &QUdpSocket::readyRead, this, &RyzeTelloConnection::receiveData); 0182 m_controlSocket->bind(QHostAddress::AnyIPv4, 8889); 0183 0184 m_stateSocket = new QUdpSocket(this); 0185 QObject::connect(m_stateSocket, &QUdpSocket::readyRead, this, &RyzeTelloConnection::receiveState); 0186 m_stateSocket->bind(QHostAddress::AnyIPv4, 8890); 0187 0188 emit stateChanged(Kirogi::AbstractVehicle::Connecting); 0189 }