2 ******************************************************************************
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * @addtogroup GCSPlugins GCS Plugins
8 * @addtogroup UAVTalkPlugin UAVTalk Plugin
10 * @brief The UAVTalk protocol plugin
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include <extensionsystem/pluginmanager.h>
29 #include <coreplugin/generalsettings.h>
30 #include <utils/crc.h>
36 #ifdef VERBOSE_UAVTALK
37 // uncomment and adapt the following lines to filter verbose logging to include specific object(s) only
38 // #include "flighttelemetrystats.h"
39 // #define VERBOSE_FILTER(objId) if (objId == FlightTelemetryStats::OBJID)
41 #ifndef VERBOSE_FILTER
42 #define VERBOSE_FILTER(objId)
47 using namespace Utils
;
52 UAVTalk::UAVTalk(QIODevice
*iodev
, UAVObjectManager
*objMngr
) : io(iodev
), objMngr(objMngr
), mutex(QMutex::Recursive
)
57 memset(&stats
, 0, sizeof(ComStats
));
59 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
60 Core::Internal::GeneralSettings
*settings
= pm
->getObject
<Core::Internal::GeneralSettings
>();
61 useUDPMirror
= settings
->useUDPMirror();
63 qDebug() << "UAVTalk::UAVTalk -*** UDP mirror is enabled ***";
66 udpSocketTx
= new QUdpSocket(this);
67 udpSocketRx
= new QUdpSocket(this);
68 udpSocketTx
->bind(9000);
69 udpSocketRx
->connectToHost(QHostAddress::LocalHost
, 9000);
70 connect(udpSocketTx
, SIGNAL(readyRead()), this, SLOT(dummyUDPRead()));
71 connect(udpSocketRx
, SIGNAL(readyRead()), this, SLOT(dummyUDPRead()));
77 // According to Qt, it is not necessary to disconnect upon object deletion.
78 // disconnect(io, SIGNAL(readyRead()), worker, SLOT(processInputStream()));
80 closeAllTransactions();
84 * Reset the statistics counters
86 void UAVTalk::resetStats()
88 QMutexLocker
locker(&mutex
);
90 memset(&stats
, 0, sizeof(ComStats
));
94 * Get the statistics counters
96 UAVTalk::ComStats
UAVTalk::getStats()
98 QMutexLocker
locker(&mutex
);
103 void UAVTalk::dummyUDPRead()
105 QUdpSocket
*socket
= qobject_cast
<QUdpSocket
*>(sender());
108 while (socket
->hasPendingDatagrams()) {
109 junk
.resize(socket
->pendingDatagramSize());
110 socket
->readDatagram(junk
.data(), junk
.size());
115 * Send the specified object through the telemetry link.
116 * \param[in] obj Object to send
117 * \param[in] acked Selects if an ack is required
118 * \param[in] allInstances If set true then all instances will be updated
119 * \return Success (true), Failure (false)
121 bool UAVTalk::sendObject(UAVObject
*obj
, bool acked
, bool allInstances
)
123 QMutexLocker
locker(&mutex
);
128 instId
= ALL_INSTANCES
;
130 instId
= obj
->getInstID();
132 bool success
= false;
134 success
= objectTransaction(TYPE_OBJ_ACK
, obj
->getObjID(), instId
, obj
);
136 success
= objectTransaction(TYPE_OBJ
, obj
->getObjID(), instId
, obj
);
143 * Request an update for the specified object, on success the object data would have been
144 * updated by the GCS.
145 * \param[in] obj Object to update
146 * \param[in] allInstances If set true then all instances will be updated
147 * \return Success (true), Failure (false)
149 bool UAVTalk::sendObjectRequest(UAVObject
*obj
, bool allInstances
)
151 QMutexLocker
locker(&mutex
);
156 instId
= ALL_INSTANCES
;
158 instId
= obj
->getInstID();
160 return objectTransaction(TYPE_OBJ_REQ
, obj
->getObjID(), instId
, obj
);
164 * Cancel a pending transaction
166 void UAVTalk::cancelTransaction(UAVObject
*obj
)
168 QMutexLocker
locker(&mutex
);
173 Transaction
*trans
= findTransaction(obj
->getObjID(), obj
->getInstID());
175 closeTransaction(trans
);
180 * Execute the requested transaction on an object.
181 * \param[in] obj Object
182 * \param[in] type Transaction type
183 * TYPE_OBJ: send object,
184 * TYPE_OBJ_REQ: request object update
185 * TYPE_OBJ_ACK: send object with an ack
186 * \param[in] allInstances If set true then all instances will be updated
187 * \return Success (true), Failure (false)
189 bool UAVTalk::objectTransaction(quint8 type
, quint32 objId
, quint16 instId
, UAVObject
*obj
)
192 // Send object depending on if a response is needed
193 // transactions of TYPE_OBJ_REQ are acked by the response
194 if (type
== TYPE_OBJ_ACK
|| type
== TYPE_OBJ_REQ
) {
195 if (transmitObject(type
, objId
, instId
, obj
)) {
196 openTransaction(type
, objId
, instId
);
201 } else if (type
== TYPE_OBJ
) {
202 return transmitObject(type
, objId
, instId
, obj
);
209 * Called each time there are data in the input buffer
211 void UAVTalk::processInputStream()
215 if (io
&& io
->isReadable()) {
216 while (io
->bytesAvailable() > 0) {
217 int ret
= io
->read((char *)&tmp
, 1);
219 processInputByte(tmp
);
223 if (rxState
== STATE_COMPLETE
) {
225 if (receiveObject(rxType
, rxObjId
, rxInstId
, rxBuffer
, rxLength
)) {
226 stats
.rxObjectBytes
+= rxLength
;
234 // it is safe to do this outside of the above critical section as the rxDataArray is
235 // accessed from this thread only
236 udpSocketTx
->writeDatagram(rxDataArray
, QHostAddress::LocalHost
, udpSocketRx
->localPort());
244 * Process an byte from the telemetry stream.
245 * \param[in] rxbyte Received byte
246 * \return Success (true), Failure (false)
248 bool UAVTalk::processInputByte(quint8 rxbyte
)
250 if (rxState
== STATE_COMPLETE
|| rxState
== STATE_ERROR
) {
251 rxState
= STATE_SYNC
;
261 // update packet byte count
265 rxDataArray
.append(rxbyte
);
268 // Receive state machine
272 if (rxbyte
!= SYNC_VAL
) {
273 // continue until sync byte is matched
274 stats
.rxSyncErrors
++;
278 // Initialize and update CRC
279 rxCS
= Crc::updateCRC(0, rxbyte
);
283 // case local byte counter, don't forget to zero it after use.
286 rxState
= STATE_TYPE
;
292 rxCS
= Crc::updateCRC(rxCS
, rxbyte
);
294 if ((rxbyte
& TYPE_MASK
) != TYPE_VER
) {
295 qWarning() << "UAVTalk - error : bad type";
297 rxState
= STATE_ERROR
;
305 rxState
= STATE_SIZE
;
311 rxCS
= Crc::updateCRC(rxCS
, rxbyte
);
314 packetSize
+= rxbyte
;
318 packetSize
+= (quint32
)rxbyte
<< 8;
322 if (packetSize
< HEADER_LENGTH
|| packetSize
> HEADER_LENGTH
+ MAX_PAYLOAD_LENGTH
) {
323 // incorrect packet size
324 qWarning() << "UAVTalk - error : incorrect packet size";
326 rxState
= STATE_ERROR
;
330 rxState
= STATE_OBJID
;
336 rxCS
= Crc::updateCRC(rxCS
, rxbyte
);
338 rxTmpBuffer
[rxCount
++] = rxbyte
;
344 rxObjId
= (qint32
)qFromLittleEndian
<quint32
>(rxTmpBuffer
);
346 // Message always contain an instance ID
348 rxState
= STATE_INSTID
;
354 rxCS
= Crc::updateCRC(rxCS
, rxbyte
);
356 rxTmpBuffer
[rxCount
++] = rxbyte
;
362 rxInstId
= (qint16
)qFromLittleEndian
<quint16
>(rxTmpBuffer
);
364 // Search for object, if not found reset state machine
366 UAVObject
*rxObj
= objMngr
->getObject(rxObjId
);
367 if (rxObj
== NULL
&& rxType
!= TYPE_OBJ_REQ
) {
368 qWarning().noquote() << "UAVTalk - error : unknown object" << QString::number(rxObjId
, 16).toUpper();
370 rxState
= STATE_ERROR
;
374 // Determine data length
375 if (rxType
== TYPE_OBJ_REQ
|| rxType
== TYPE_ACK
|| rxType
== TYPE_NACK
) {
379 rxLength
= rxObj
->getNumBytes();
381 rxLength
= packetSize
- rxPacketLength
;
385 // Check length and determine next state
386 if (rxLength
>= MAX_PAYLOAD_LENGTH
) {
387 // packet error - exceeded payload max length
388 qWarning().noquote() << "UAVTalk - error : exceeded payload max length" << QString::number(rxObjId
, 16).toUpper();
390 rxState
= STATE_ERROR
;
394 // Check the lengths match
395 if ((rxPacketLength
+ rxLength
) != packetSize
) {
396 // packet error - mismatched packet size
397 qWarning().noquote() << "UAVTalk - error : mismatched packet size" << QString::number(rxObjId
, 16).toUpper();
399 rxState
= STATE_ERROR
;
404 // If there is a payload get it, otherwise receive checksum
406 rxState
= STATE_DATA
;
415 rxCS
= Crc::updateCRC(rxCS
, rxbyte
);
417 rxBuffer
[rxCount
++] = rxbyte
;
418 if (rxCount
< rxLength
) {
431 if (rxCS
!= rxCSPacket
) {
432 // packet error - faulty CRC
433 qWarning().noquote() << "UAVTalk - error : failed CRC check" << QString::number(rxObjId
, 16).toUpper();
435 rxState
= STATE_ERROR
;
439 if (rxPacketLength
!= packetSize
+ CHECKSUM_LENGTH
) {
440 // packet error - mismatched packet size
441 qWarning().noquote() << "UAVTalk - error : mismatched packet size" << QString::number(rxObjId
, 16).toUpper();
443 rxState
= STATE_ERROR
;
447 rxState
= STATE_COMPLETE
;
452 qWarning() << "UAVTalk - error : bad state";
453 rxState
= STATE_ERROR
;
462 * Receive an object. This function process objects received through the telemetry stream.
464 * Parser errors are considered as transmission errors and are not NACKed.
465 * Some senders (GCS) can timeout and retry if the message is not answered by an ack or nack.
467 * Object handling errors are considered as application errors and are NACked.
468 * In that case we want to nack as there is no point in the sender retrying to send invalid objects.
470 * \param[in] type Type of received message (TYPE_OBJ, TYPE_OBJ_REQ, TYPE_OBJ_ACK, TYPE_ACK, TYPE_NACK)
471 * \param[in] obj Handle of the received object
472 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
473 * \param[in] data Data buffer
474 * \param[in] length Buffer length
475 * \return Success (true), Failure (false)
477 bool UAVTalk::receiveObject(quint8 type
, quint32 objId
, quint16 instId
, quint8
*data
, qint32 length
)
481 UAVObject
*obj
= NULL
;
483 bool allInstances
= (instId
== ALL_INSTANCES
);
485 // Process message type
488 // All instances, not allowed for OBJ messages
490 // Get object and update its data
491 obj
= updateObject(objId
, instId
, data
);
492 #ifdef VERBOSE_UAVTALK
493 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - received object" << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
496 // Check if this object acks a pending OBJ_REQ message
497 // any OBJ message can ack a pending OBJ_REQ message
498 // even one that was not sent in response to the OBJ_REQ message
499 updateAck(type
, objId
, instId
, obj
);
509 // All instances, not allowed for OBJ_ACK messages
511 // Get object and update its data
512 obj
= updateObject(objId
, instId
, data
);
513 #ifdef VERBOSE_UAVTALK
514 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - received object (acked)" << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
517 // Object updated or created, transmit ACK
518 error
= !transmitObject(TYPE_ACK
, objId
, instId
, obj
);
526 // failed to update object, transmit NACK
527 transmitObject(TYPE_NACK
, objId
, instId
, NULL
);
532 // Check if requested object exists
534 // All instances, so get instance zero
535 obj
= objMngr
->getObject(objId
);
537 obj
= objMngr
->getObject(objId
, instId
);
539 #ifdef VERBOSE_UAVTALK
540 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - received object request" << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
543 // Object found, transmit it
544 // The sent object will ack the object request on the receiver side
545 error
= !transmitObject(TYPE_OBJ
, objId
, instId
, obj
);
550 // failed to send object, transmit NACK
551 transmitObject(TYPE_NACK
, objId
, instId
, NULL
);
556 // All instances, not allowed for ACK messages
559 obj
= objMngr
->getObject(objId
, instId
);
560 #ifdef VERBOSE_UAVTALK
561 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - received ack" << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
564 // Check if an ACK is pending
565 updateAck(type
, objId
, instId
, obj
);
573 // All instances, not allowed for NACK messages
576 obj
= objMngr
->getObject(objId
, instId
);
577 #ifdef VERBOSE_UAVTALK
578 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - received nack" << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
581 // Check if a NACK is pending
582 updateNack(objId
, instId
, obj
);
593 qWarning() << "UAVTalk - !!! error receiving" << typeToString(type
) << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
600 * Update the data of an object from a byte array (unpack).
601 * If the object instance could not be found in the list, then a
602 * new one is created.
604 UAVObject
*UAVTalk::updateObject(quint32 objId
, quint16 instId
, quint8
*data
)
607 UAVObject
*obj
= objMngr
->getObject(objId
, instId
);
609 // If the instance does not exist create it
611 // Get the object type
612 UAVObject
*typeObj
= objMngr
->getObject(objId
);
613 if (typeObj
== NULL
) {
614 qWarning() << "UAVTalk - failed to get object, object ID :" << objId
;
617 // Make sure this is a data object
618 UAVDataObject
*dataObj
= dynamic_cast<UAVDataObject
*>(typeObj
);
619 if (dataObj
== NULL
) {
622 // Create a new instance, unpack and register
623 UAVDataObject
*instObj
= dataObj
->clone(instId
);
624 if (!objMngr
->registerObject(instObj
)) {
625 qWarning() << "UAVTalk - failed to register object " << instObj
->toStringBrief();
628 instObj
->unpack(data
);
631 // Unpack data into object instance
638 * Check if a transaction is pending and if yes complete it.
640 void UAVTalk::updateAck(quint8 type
, quint32 objId
, quint16 instId
, UAVObject
*obj
)
646 Transaction
*trans
= findTransaction(objId
, instId
);
647 if (trans
&& trans
->respType
== type
) {
648 if (trans
->respInstId
== ALL_INSTANCES
) {
650 // last instance received, complete transaction
651 closeTransaction(trans
);
652 emit
transactionCompleted(obj
, true);
654 // TODO extend timeout?
657 closeTransaction(trans
);
658 emit
transactionCompleted(obj
, true);
664 * Check if a transaction is pending and if yes complete it.
666 void UAVTalk::updateNack(quint32 objId
, quint16 instId
, UAVObject
*obj
)
672 Transaction
*trans
= findTransaction(objId
, instId
);
674 closeTransaction(trans
);
675 emit
transactionCompleted(obj
, false);
680 * Send an object through the telemetry link.
681 * \param[in] type Transaction type
682 * \param[in] objId Object ID to send
683 * \param[in] instId Instance ID to send
684 * \param[in] obj Object to send (null when type is NACK)
685 * \return Success (true), Failure (false)
687 bool UAVTalk::transmitObject(quint8 type
, quint32 objId
, quint16 instId
, UAVObject
*obj
)
689 // Important note : obj can be null (when type is NACK for example) so protect all obj dereferences.
691 // If all instances are requested on a single instance object it is an error
692 if ((obj
!= NULL
) && (instId
== ALL_INSTANCES
) && obj
->isSingleInstance()) {
695 bool allInstances
= (instId
== ALL_INSTANCES
);
697 #ifdef VERBOSE_UAVTALK
698 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - transmitting" << typeToString(type
) << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
701 // Process message type
703 if (type
== TYPE_OBJ
|| type
== TYPE_OBJ_ACK
) {
705 // Send all instances in reverse order
706 // This allows the receiver to detect when the last object has been received (i.e. when instance 0 is received)
708 quint32 numInst
= objMngr
->getNumInstances(objId
);
709 for (quint32 n
= 0; n
< numInst
; ++n
) {
710 quint32 i
= numInst
- n
- 1;
711 UAVObject
*o
= objMngr
->getObject(objId
, i
);
712 if (!transmitSingleObject(type
, objId
, i
, o
)) {
718 ret
= transmitSingleObject(type
, objId
, instId
, obj
);
720 } else if (type
== TYPE_OBJ_REQ
) {
721 ret
= transmitSingleObject(TYPE_OBJ_REQ
, objId
, instId
, NULL
);
722 } else if (type
== TYPE_ACK
|| type
== TYPE_NACK
) {
724 ret
= transmitSingleObject(type
, objId
, instId
, NULL
);
728 #ifdef VERBOSE_UAVTALK
730 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - failed to transmit" << typeToString(type
) << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
738 * Send an object through the telemetry link.
739 * \param[in] type Transaction type
740 * \param[in] objId Object ID to send
741 * \param[in] instId Instance ID to send
742 * \param[in] obj Object to send (null when type is NACK)
743 * \return Success (true), Failure (false)
745 bool UAVTalk::transmitSingleObject(quint8 type
, quint32 objId
, quint16 instId
, UAVObject
*obj
)
749 // IMPORTANT : obj can be null (when type is NACK for example)
752 txBuffer
[0] = SYNC_VAL
;
755 // next 2 bytes are reserved for data length (inserted here later)
757 qToLittleEndian
<quint32
>(objId
, &txBuffer
[4]);
759 qToLittleEndian
<quint16
>(instId
, &txBuffer
[8]);
761 // Determine data length
762 if (type
== TYPE_OBJ_REQ
|| type
== TYPE_ACK
|| type
== TYPE_NACK
) {
765 length
= obj
->getNumBytes();
769 if (length
>= MAX_PAYLOAD_LENGTH
) {
770 qWarning() << "UAVTalk - error transmitting : object exceeds max payload length" << obj
->toStringBrief();
775 // Copy data (if any)
777 if (!obj
->pack(&txBuffer
[HEADER_LENGTH
])) {
778 qWarning() << "UAVTalk - error transmitting : failed to pack object" << obj
->toStringBrief();
784 // Store the packet length
785 qToLittleEndian
<quint16
>(HEADER_LENGTH
+ length
, &txBuffer
[2]);
787 // Calculate checksum
788 txBuffer
[HEADER_LENGTH
+ length
] = Crc::updateCRC(0, txBuffer
, HEADER_LENGTH
+ length
);
790 // Send buffer, check that the transmit backlog does not grow above limit
791 if (!io
.isNull() && io
->isWritable()) {
792 if (io
->bytesToWrite() < TX_BUFFER_SIZE
) {
793 io
->write((const char *)txBuffer
, HEADER_LENGTH
+ length
+ CHECKSUM_LENGTH
);
795 udpSocketRx
->writeDatagram((const char *)txBuffer
, HEADER_LENGTH
+ length
+ CHECKSUM_LENGTH
, QHostAddress::LocalHost
, udpSocketTx
->localPort());
798 qWarning() << "UAVTalk - error transmitting : io device full";
803 qWarning() << "UAVTalk - error transmitting : io device not writable";
810 stats
.txObjectBytes
+= length
;
811 stats
.txBytes
+= HEADER_LENGTH
+ length
+ CHECKSUM_LENGTH
;
817 UAVTalk::Transaction
*UAVTalk::findTransaction(quint32 objId
, quint16 instId
)
819 // Lookup the transaction in the transaction map
820 QMap
<quint32
, Transaction
*> *objTransactions
= transMap
.value(objId
, NULL
);
821 if (objTransactions
!= NULL
) {
822 Transaction
*trans
= objTransactions
->value(instId
, NULL
);
824 // see if there is an ALL_INSTANCES transaction
825 trans
= objTransactions
->value(ALL_INSTANCES
, NULL
);
832 void UAVTalk::openTransaction(quint8 type
, quint32 objId
, quint16 instId
)
834 Transaction
*trans
= new Transaction();
836 trans
->respType
= (type
== TYPE_OBJ_REQ
) ? TYPE_OBJ
: TYPE_ACK
;
837 trans
->respObjId
= objId
;
838 trans
->respInstId
= instId
;
840 QMap
<quint32
, Transaction
*> *objTransactions
= transMap
.value(trans
->respObjId
, NULL
);
841 if (objTransactions
== NULL
) {
842 objTransactions
= new QMap
<quint32
, Transaction
*>();
843 transMap
.insert(trans
->respObjId
, objTransactions
);
845 objTransactions
->insert(trans
->respInstId
, trans
);
848 void UAVTalk::closeTransaction(Transaction
*trans
)
850 QMap
<quint32
, Transaction
*> *objTransactions
= transMap
.value(trans
->respObjId
, NULL
);
851 if (objTransactions
!= NULL
) {
852 objTransactions
->remove(trans
->respInstId
);
853 // Keep the map even if it is empty
854 // There are at most 100 different object IDs...
859 void UAVTalk::closeAllTransactions()
861 foreach(quint32 objId
, transMap
.keys()) {
862 QMap
<quint32
, Transaction
*> *objTransactions
= transMap
.value(objId
, NULL
);
863 foreach(quint32 instId
, objTransactions
->keys()) {
864 Transaction
*trans
= objTransactions
->value(instId
, NULL
);
866 qWarning() << "UAVTalk - closing active transaction for object" << trans
->respObjId
;
867 objTransactions
->remove(instId
);
870 transMap
.remove(objId
);
871 delete objTransactions
;
875 const char *UAVTalk::typeToString(quint8 type
)
884 return "object (acked)";
889 return "object request";