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();
62 qDebug() << "USE UDP:::::::::::." << useUDPMirror
;
64 udpSocketTx
= new QUdpSocket(this);
65 udpSocketRx
= new QUdpSocket(this);
66 udpSocketTx
->bind(9000);
67 udpSocketRx
->connectToHost(QHostAddress::LocalHost
, 9000);
68 connect(udpSocketTx
, SIGNAL(readyRead()), this, SLOT(dummyUDPRead()));
69 connect(udpSocketRx
, SIGNAL(readyRead()), this, SLOT(dummyUDPRead()));
75 // According to Qt, it is not necessary to disconnect upon object deletion.
76 // disconnect(io, SIGNAL(readyRead()), worker, SLOT(processInputStream()));
78 closeAllTransactions();
82 * Reset the statistics counters
84 void UAVTalk::resetStats()
86 QMutexLocker
locker(&mutex
);
88 memset(&stats
, 0, sizeof(ComStats
));
92 * Get the statistics counters
94 UAVTalk::ComStats
UAVTalk::getStats()
96 QMutexLocker
locker(&mutex
);
101 void UAVTalk::dummyUDPRead()
103 QUdpSocket
*socket
= qobject_cast
<QUdpSocket
*>(sender());
106 while (socket
->hasPendingDatagrams()) {
107 junk
.resize(socket
->pendingDatagramSize());
108 socket
->readDatagram(junk
.data(), junk
.size());
113 * Send the specified object through the telemetry link.
114 * \param[in] obj Object to send
115 * \param[in] acked Selects if an ack is required
116 * \param[in] allInstances If set true then all instances will be updated
117 * \return Success (true), Failure (false)
119 bool UAVTalk::sendObject(UAVObject
*obj
, bool acked
, bool allInstances
)
121 QMutexLocker
locker(&mutex
);
126 instId
= ALL_INSTANCES
;
128 instId
= obj
->getInstID();
130 bool success
= false;
132 success
= objectTransaction(TYPE_OBJ_ACK
, obj
->getObjID(), instId
, obj
);
134 success
= objectTransaction(TYPE_OBJ
, obj
->getObjID(), instId
, obj
);
141 * Request an update for the specified object, on success the object data would have been
142 * updated by the GCS.
143 * \param[in] obj Object to update
144 * \param[in] allInstances If set true then all instances will be updated
145 * \return Success (true), Failure (false)
147 bool UAVTalk::sendObjectRequest(UAVObject
*obj
, bool allInstances
)
149 QMutexLocker
locker(&mutex
);
154 instId
= ALL_INSTANCES
;
156 instId
= obj
->getInstID();
158 return objectTransaction(TYPE_OBJ_REQ
, obj
->getObjID(), instId
, obj
);
162 * Cancel a pending transaction
164 void UAVTalk::cancelTransaction(UAVObject
*obj
)
166 QMutexLocker
locker(&mutex
);
171 Transaction
*trans
= findTransaction(obj
->getObjID(), obj
->getInstID());
173 closeTransaction(trans
);
178 * Execute the requested transaction on an object.
179 * \param[in] obj Object
180 * \param[in] type Transaction type
181 * TYPE_OBJ: send object,
182 * TYPE_OBJ_REQ: request object update
183 * TYPE_OBJ_ACK: send object with an ack
184 * \param[in] allInstances If set true then all instances will be updated
185 * \return Success (true), Failure (false)
187 bool UAVTalk::objectTransaction(quint8 type
, quint32 objId
, quint16 instId
, UAVObject
*obj
)
190 // Send object depending on if a response is needed
191 // transactions of TYPE_OBJ_REQ are acked by the response
192 if (type
== TYPE_OBJ_ACK
|| type
== TYPE_OBJ_REQ
) {
193 if (transmitObject(type
, objId
, instId
, obj
)) {
194 openTransaction(type
, objId
, instId
);
199 } else if (type
== TYPE_OBJ
) {
200 return transmitObject(type
, objId
, instId
, obj
);
207 * Called each time there are data in the input buffer
209 void UAVTalk::processInputStream()
213 if (io
&& io
->isReadable()) {
214 while (io
->bytesAvailable() > 0) {
215 int ret
= io
->read((char *)&tmp
, 1);
217 processInputByte(tmp
);
221 if (rxState
== STATE_COMPLETE
) {
223 if (receiveObject(rxType
, rxObjId
, rxInstId
, rxBuffer
, rxLength
)) {
224 stats
.rxObjectBytes
+= rxLength
;
232 // it is safe to do this outside of the above critical section as the rxDataArray is
233 // accessed from this thread only
234 udpSocketTx
->writeDatagram(rxDataArray
, QHostAddress::LocalHost
, udpSocketRx
->localPort());
242 * Process an byte from the telemetry stream.
243 * \param[in] rxbyte Received byte
244 * \return Success (true), Failure (false)
246 bool UAVTalk::processInputByte(quint8 rxbyte
)
248 if (rxState
== STATE_COMPLETE
|| rxState
== STATE_ERROR
) {
249 rxState
= STATE_SYNC
;
259 // update packet byte count
263 rxDataArray
.append(rxbyte
);
266 // Receive state machine
270 if (rxbyte
!= SYNC_VAL
) {
271 // continue until sync byte is matched
272 stats
.rxSyncErrors
++;
276 // Initialize and update CRC
277 rxCS
= Crc::updateCRC(0, rxbyte
);
281 // case local byte counter, don't forget to zero it after use.
284 rxState
= STATE_TYPE
;
290 rxCS
= Crc::updateCRC(rxCS
, rxbyte
);
292 if ((rxbyte
& TYPE_MASK
) != TYPE_VER
) {
293 qWarning() << "UAVTalk - error : bad type";
295 rxState
= STATE_ERROR
;
303 rxState
= STATE_SIZE
;
309 rxCS
= Crc::updateCRC(rxCS
, rxbyte
);
312 packetSize
+= rxbyte
;
316 packetSize
+= (quint32
)rxbyte
<< 8;
320 if (packetSize
< HEADER_LENGTH
|| packetSize
> HEADER_LENGTH
+ MAX_PAYLOAD_LENGTH
) {
321 // incorrect packet size
322 qWarning() << "UAVTalk - error : incorrect packet size";
324 rxState
= STATE_ERROR
;
328 rxState
= STATE_OBJID
;
334 rxCS
= Crc::updateCRC(rxCS
, rxbyte
);
336 rxTmpBuffer
[rxCount
++] = rxbyte
;
342 rxObjId
= (qint32
)qFromLittleEndian
<quint32
>(rxTmpBuffer
);
344 // Message always contain an instance ID
346 rxState
= STATE_INSTID
;
352 rxCS
= Crc::updateCRC(rxCS
, rxbyte
);
354 rxTmpBuffer
[rxCount
++] = rxbyte
;
360 rxInstId
= (qint16
)qFromLittleEndian
<quint16
>(rxTmpBuffer
);
362 // Search for object, if not found reset state machine
364 UAVObject
*rxObj
= objMngr
->getObject(rxObjId
);
365 if (rxObj
== NULL
&& rxType
!= TYPE_OBJ_REQ
) {
366 qWarning() << "UAVTalk - error : unknown object" << rxObjId
;
368 rxState
= STATE_ERROR
;
372 // Determine data length
373 if (rxType
== TYPE_OBJ_REQ
|| rxType
== TYPE_ACK
|| rxType
== TYPE_NACK
) {
377 rxLength
= rxObj
->getNumBytes();
379 rxLength
= packetSize
- rxPacketLength
;
383 // Check length and determine next state
384 if (rxLength
>= MAX_PAYLOAD_LENGTH
) {
385 // packet error - exceeded payload max length
386 qWarning() << "UAVTalk - error : exceeded payload max length" << rxObjId
;
388 rxState
= STATE_ERROR
;
392 // Check the lengths match
393 if ((rxPacketLength
+ rxLength
) != packetSize
) {
394 // packet error - mismatched packet size
395 qWarning() << "UAVTalk - error : mismatched packet size" << rxObjId
;
397 rxState
= STATE_ERROR
;
402 // If there is a payload get it, otherwise receive checksum
404 rxState
= STATE_DATA
;
413 rxCS
= Crc::updateCRC(rxCS
, rxbyte
);
415 rxBuffer
[rxCount
++] = rxbyte
;
416 if (rxCount
< rxLength
) {
429 if (rxCS
!= rxCSPacket
) {
430 // packet error - faulty CRC
431 qWarning() << "UAVTalk - error : failed CRC check" << rxObjId
;
433 rxState
= STATE_ERROR
;
437 if (rxPacketLength
!= packetSize
+ CHECKSUM_LENGTH
) {
438 // packet error - mismatched packet size
439 qWarning() << "UAVTalk - error : mismatched packet size" << rxObjId
;
441 rxState
= STATE_ERROR
;
445 rxState
= STATE_COMPLETE
;
450 qWarning() << "UAVTalk - error : bad state";
451 rxState
= STATE_ERROR
;
460 * Receive an object. This function process objects received through the telemetry stream.
462 * Parser errors are considered as transmission errors and are not NACKed.
463 * Some senders (GCS) can timeout and retry if the message is not answered by an ack or nack.
465 * Object handling errors are considered as application errors and are NACked.
466 * In that case we want to nack as there is no point in the sender retrying to send invalid objects.
468 * \param[in] type Type of received message (TYPE_OBJ, TYPE_OBJ_REQ, TYPE_OBJ_ACK, TYPE_ACK, TYPE_NACK)
469 * \param[in] obj Handle of the received object
470 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
471 * \param[in] data Data buffer
472 * \param[in] length Buffer length
473 * \return Success (true), Failure (false)
475 bool UAVTalk::receiveObject(quint8 type
, quint32 objId
, quint16 instId
, quint8
*data
, qint32 length
)
479 UAVObject
*obj
= NULL
;
481 bool allInstances
= (instId
== ALL_INSTANCES
);
483 // Process message type
486 // All instances, not allowed for OBJ messages
488 // Get object and update its data
489 obj
= updateObject(objId
, instId
, data
);
490 #ifdef VERBOSE_UAVTALK
491 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - received object" << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
494 // Check if this object acks a pending OBJ_REQ message
495 // any OBJ message can ack a pending OBJ_REQ message
496 // even one that was not sent in response to the OBJ_REQ message
497 updateAck(type
, objId
, instId
, obj
);
507 // All instances, not allowed for OBJ_ACK messages
509 // Get object and update its data
510 obj
= updateObject(objId
, instId
, data
);
511 #ifdef VERBOSE_UAVTALK
512 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - received object (acked)" << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
515 // Object updated or created, transmit ACK
516 error
= !transmitObject(TYPE_ACK
, objId
, instId
, obj
);
524 // failed to update object, transmit NACK
525 transmitObject(TYPE_NACK
, objId
, instId
, NULL
);
530 // Check if requested object exists
532 // All instances, so get instance zero
533 obj
= objMngr
->getObject(objId
);
535 obj
= objMngr
->getObject(objId
, instId
);
537 #ifdef VERBOSE_UAVTALK
538 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - received object request" << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
541 // Object found, transmit it
542 // The sent object will ack the object request on the receiver side
543 error
= !transmitObject(TYPE_OBJ
, objId
, instId
, obj
);
548 // failed to send object, transmit NACK
549 transmitObject(TYPE_NACK
, objId
, instId
, NULL
);
554 // All instances, not allowed for ACK messages
557 obj
= objMngr
->getObject(objId
, instId
);
558 #ifdef VERBOSE_UAVTALK
559 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - received ack" << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
562 // Check if an ACK is pending
563 updateAck(type
, objId
, instId
, obj
);
571 // All instances, not allowed for NACK messages
574 obj
= objMngr
->getObject(objId
, instId
);
575 #ifdef VERBOSE_UAVTALK
576 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - received nack" << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
579 // Check if a NACK is pending
580 updateNack(objId
, instId
, obj
);
591 qWarning() << "UAVTalk - !!! error receiving" << typeToString(type
) << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
598 * Update the data of an object from a byte array (unpack).
599 * If the object instance could not be found in the list, then a
600 * new one is created.
602 UAVObject
*UAVTalk::updateObject(quint32 objId
, quint16 instId
, quint8
*data
)
605 UAVObject
*obj
= objMngr
->getObject(objId
, instId
);
607 // If the instance does not exist create it
609 // Get the object type
610 UAVObject
*typeObj
= objMngr
->getObject(objId
);
611 if (typeObj
== NULL
) {
612 qWarning() << "UAVTalk - failed to get object, object ID :" << objId
;
615 // Make sure this is a data object
616 UAVDataObject
*dataObj
= dynamic_cast<UAVDataObject
*>(typeObj
);
617 if (dataObj
== NULL
) {
620 // Create a new instance, unpack and register
621 UAVDataObject
*instObj
= dataObj
->clone(instId
);
622 if (!objMngr
->registerObject(instObj
)) {
623 qWarning() << "UAVTalk - failed to register object " << instObj
->toStringBrief();
626 instObj
->unpack(data
);
629 // Unpack data into object instance
636 * Check if a transaction is pending and if yes complete it.
638 void UAVTalk::updateAck(quint8 type
, quint32 objId
, quint16 instId
, UAVObject
*obj
)
644 Transaction
*trans
= findTransaction(objId
, instId
);
645 if (trans
&& trans
->respType
== type
) {
646 if (trans
->respInstId
== ALL_INSTANCES
) {
648 // last instance received, complete transaction
649 closeTransaction(trans
);
650 emit
transactionCompleted(obj
, true);
652 // TODO extend timeout?
655 closeTransaction(trans
);
656 emit
transactionCompleted(obj
, true);
662 * Check if a transaction is pending and if yes complete it.
664 void UAVTalk::updateNack(quint32 objId
, quint16 instId
, UAVObject
*obj
)
670 Transaction
*trans
= findTransaction(objId
, instId
);
672 closeTransaction(trans
);
673 emit
transactionCompleted(obj
, false);
678 * Send an object through the telemetry link.
679 * \param[in] type Transaction type
680 * \param[in] objId Object ID to send
681 * \param[in] instId Instance ID to send
682 * \param[in] obj Object to send (null when type is NACK)
683 * \return Success (true), Failure (false)
685 bool UAVTalk::transmitObject(quint8 type
, quint32 objId
, quint16 instId
, UAVObject
*obj
)
687 // Important note : obj can be null (when type is NACK for example) so protect all obj dereferences.
689 // If all instances are requested on a single instance object it is an error
690 if ((obj
!= NULL
) && (instId
== ALL_INSTANCES
) && obj
->isSingleInstance()) {
693 bool allInstances
= (instId
== ALL_INSTANCES
);
695 #ifdef VERBOSE_UAVTALK
696 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - transmitting" << typeToString(type
) << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
699 // Process message type
701 if (type
== TYPE_OBJ
|| type
== TYPE_OBJ_ACK
) {
703 // Send all instances in reverse order
704 // This allows the receiver to detect when the last object has been received (i.e. when instance 0 is received)
706 quint32 numInst
= objMngr
->getNumInstances(objId
);
707 for (quint32 n
= 0; n
< numInst
; ++n
) {
708 quint32 i
= numInst
- n
- 1;
709 UAVObject
*o
= objMngr
->getObject(objId
, i
);
710 if (!transmitSingleObject(type
, objId
, i
, o
)) {
716 ret
= transmitSingleObject(type
, objId
, instId
, obj
);
718 } else if (type
== TYPE_OBJ_REQ
) {
719 ret
= transmitSingleObject(TYPE_OBJ_REQ
, objId
, instId
, NULL
);
720 } else if (type
== TYPE_ACK
|| type
== TYPE_NACK
) {
722 ret
= transmitSingleObject(type
, objId
, instId
, NULL
);
726 #ifdef VERBOSE_UAVTALK
728 VERBOSE_FILTER(objId
) qDebug() << "UAVTalk - failed to transmit" << typeToString(type
) << objId
<< instId
<< (obj
!= NULL
? obj
->toStringBrief() : "<null object>");
736 * Send an object through the telemetry link.
737 * \param[in] type Transaction type
738 * \param[in] objId Object ID to send
739 * \param[in] instId Instance ID to send
740 * \param[in] obj Object to send (null when type is NACK)
741 * \return Success (true), Failure (false)
743 bool UAVTalk::transmitSingleObject(quint8 type
, quint32 objId
, quint16 instId
, UAVObject
*obj
)
747 // IMPORTANT : obj can be null (when type is NACK for example)
750 txBuffer
[0] = SYNC_VAL
;
753 // next 2 bytes are reserved for data length (inserted here later)
755 qToLittleEndian
<quint32
>(objId
, &txBuffer
[4]);
757 qToLittleEndian
<quint16
>(instId
, &txBuffer
[8]);
759 // Determine data length
760 if (type
== TYPE_OBJ_REQ
|| type
== TYPE_ACK
|| type
== TYPE_NACK
) {
763 length
= obj
->getNumBytes();
767 if (length
>= MAX_PAYLOAD_LENGTH
) {
768 qWarning() << "UAVTalk - error transmitting : object exceeds max payload length" << obj
->toStringBrief();
773 // Copy data (if any)
775 if (!obj
->pack(&txBuffer
[HEADER_LENGTH
])) {
776 qWarning() << "UAVTalk - error transmitting : failed to pack object" << obj
->toStringBrief();
782 // Store the packet length
783 qToLittleEndian
<quint16
>(HEADER_LENGTH
+ length
, &txBuffer
[2]);
785 // Calculate checksum
786 txBuffer
[HEADER_LENGTH
+ length
] = Crc::updateCRC(0, txBuffer
, HEADER_LENGTH
+ length
);
788 // Send buffer, check that the transmit backlog does not grow above limit
789 if (!io
.isNull() && io
->isWritable()) {
790 if (io
->bytesToWrite() < TX_BUFFER_SIZE
) {
791 io
->write((const char *)txBuffer
, HEADER_LENGTH
+ length
+ CHECKSUM_LENGTH
);
793 udpSocketRx
->writeDatagram((const char *)txBuffer
, HEADER_LENGTH
+ length
+ CHECKSUM_LENGTH
, QHostAddress::LocalHost
, udpSocketTx
->localPort());
796 qWarning() << "UAVTalk - error transmitting : io device full";
801 qWarning() << "UAVTalk - error transmitting : io device not writable";
808 stats
.txObjectBytes
+= length
;
809 stats
.txBytes
+= HEADER_LENGTH
+ length
+ CHECKSUM_LENGTH
;
815 UAVTalk::Transaction
*UAVTalk::findTransaction(quint32 objId
, quint16 instId
)
817 // Lookup the transaction in the transaction map
818 QMap
<quint32
, Transaction
*> *objTransactions
= transMap
.value(objId
);
819 if (objTransactions
!= NULL
) {
820 Transaction
*trans
= objTransactions
->value(instId
);
822 // see if there is an ALL_INSTANCES transaction
823 trans
= objTransactions
->value(ALL_INSTANCES
);
830 void UAVTalk::openTransaction(quint8 type
, quint32 objId
, quint16 instId
)
832 Transaction
*trans
= new Transaction();
834 trans
->respType
= (type
== TYPE_OBJ_REQ
) ? TYPE_OBJ
: TYPE_ACK
;
835 trans
->respObjId
= objId
;
836 trans
->respInstId
= instId
;
838 QMap
<quint32
, Transaction
*> *objTransactions
= transMap
.value(trans
->respObjId
);
839 if (objTransactions
== NULL
) {
840 objTransactions
= new QMap
<quint32
, Transaction
*>();
841 transMap
.insert(trans
->respObjId
, objTransactions
);
843 objTransactions
->insert(trans
->respInstId
, trans
);
846 void UAVTalk::closeTransaction(Transaction
*trans
)
848 QMap
<quint32
, Transaction
*> *objTransactions
= transMap
.value(trans
->respObjId
);
849 if (objTransactions
!= NULL
) {
850 objTransactions
->remove(trans
->respInstId
);
851 // Keep the map even if it is empty
852 // There are at most 100 different object IDs...
857 void UAVTalk::closeAllTransactions()
859 foreach(quint32 objId
, transMap
.keys()) {
860 QMap
<quint32
, Transaction
*> *objTransactions
= transMap
.value(objId
);
861 foreach(quint32 instId
, objTransactions
->keys()) {
862 Transaction
*trans
= objTransactions
->value(instId
);
864 qWarning() << "UAVTalk - closing active transaction for object" << trans
->respObjId
;
865 objTransactions
->remove(instId
);
868 transMap
.remove(objId
);
869 delete objTransactions
;
873 const char *UAVTalk::typeToString(quint8 type
)
882 return "object (acked)";
887 return "object request";