2 ******************************************************************************
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * @see The GNU Public License (GPL) Version 3
7 * @addtogroup GCSPlugins GCS Plugins
9 * @addtogroup UAVObjectsPlugin UAVObjects Plugin
11 * @brief The UAVUObjects GCS plugin
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "uavobject.h"
30 #include <utils/crc.h>
34 #include <QXmlStreamWriter>
35 #include <QXmlStreamReader>
36 #include <QJsonObject>
39 using namespace Utils
;
42 #define UAVOBJ_ACCESS_SHIFT 0
43 #define UAVOBJ_GCS_ACCESS_SHIFT 1
44 #define UAVOBJ_TELEMETRY_ACKED_SHIFT 2
45 #define UAVOBJ_GCS_TELEMETRY_ACKED_SHIFT 3
46 #define UAVOBJ_TELEMETRY_UPDATE_MODE_SHIFT 4
47 #define UAVOBJ_GCS_TELEMETRY_UPDATE_MODE_SHIFT 6
48 #define UAVOBJ_LOGGING_UPDATE_MODE_SHIFT 8
49 #define UAVOBJ_UPDATE_MODE_MASK 0x3
52 #define SET_BITS(var, shift, value, mask) var = (var & ~(mask << shift)) | (value << shift);
56 * @param objID The object ID
57 * @param isSingleInst True if this object can only have a single instance
58 * @param name Object name
60 UAVObject::UAVObject(quint32 objID
, bool isSingleInst
, const QString
& name
)
64 this->isSingleInst
= isSingleInst
;
68 this->mutex
= new QMutex(QMutex::Recursive
);
73 * Initialize object with its instance ID
75 void UAVObject::initialize(quint32 instID
)
77 QMutexLocker
locker(mutex
);
79 this->instID
= instID
;
83 * Initialize objects' data fields
84 * @param fields List of fields held by the object
85 * @param data Pointer to that actual object data, this is needed by the fields to access the data
86 * @param numBytes Number of bytes in the object (total, including all fields)
88 void UAVObject::initializeFields(QList
<UAVObjectField
*> & fields
, quint8
*data
, quint32 numBytes
)
90 QMutexLocker
locker(mutex
);
92 this->numBytes
= numBytes
;
94 this->fields
= fields
;
97 for (int n
= 0; n
< fields
.length(); ++n
) {
98 fields
[n
]->initialize(data
, offset
, this);
99 offset
+= fields
[n
]->getNumBytes();
100 connect(fields
[n
], SIGNAL(fieldUpdated(UAVObjectField
*)), this, SLOT(fieldUpdated(UAVObjectField
*)));
105 * Called from the fields each time they are updated
107 void UAVObject::fieldUpdated(UAVObjectField
*field
)
110 // emit objectUpdatedAuto(this); // trigger object updated event
111 // emit objectUpdated(this);
117 quint32
UAVObject::getObjID()
123 * Get the instance ID
125 quint32
UAVObject::getInstID()
131 * Returns true if this is a single instance object
133 bool UAVObject::isSingleInstance()
139 * Get the name of the object
141 QString
UAVObject::getName()
147 * Get the description of the object
149 QString
UAVObject::getDescription()
155 * Set the description of the object
157 void UAVObject::setDescription(const QString
& description
)
159 this->description
= description
;
163 * Get the category of the object
165 QString
UAVObject::getCategory()
171 * Set the category of the object
173 void UAVObject::setCategory(const QString
& category
)
175 this->category
= category
;
179 * Get the total number of bytes of the object's data
181 quint32
UAVObject::getNumBytes()
187 * Request that this object is updated with the latest values from the autopilot
189 void UAVObject::requestUpdate()
191 emit
updateRequested(this);
195 * Request that all instances of this object are updated with the latest values from the autopilot
196 * Must be called on instance zero
198 void UAVObject::requestUpdateAll()
201 emit
updateRequested(this, true);
206 * Signal that the object has been updated
208 void UAVObject::updated()
210 emit
objectUpdatedManual(this);
211 emit
objectUpdated(this);
215 * Signal that all instance of the object have been updated
216 * Must be called on instance zero
218 void UAVObject::updatedAll()
221 emit
objectUpdatedManual(this, true);
222 // TODO call objectUpdated() for all instances?
223 // emit objectUpdated(this);
228 * Lock mutex of this object
230 void UAVObject::lock()
236 * Lock mutex of this object
238 void UAVObject::lock(int timeoutMs
)
240 mutex
->tryLock(timeoutMs
);
244 * Unlock mutex of this object
246 void UAVObject::unlock()
254 QMutex
*UAVObject::getMutex()
260 * Get the number of fields held by this object
262 qint32
UAVObject::getNumFields()
264 QMutexLocker
locker(mutex
);
266 return fields
.count();
270 * Get the object's fields
272 QList
<UAVObjectField
*> UAVObject::getFields()
274 QMutexLocker
locker(mutex
);
280 * Get a specific field
281 * @returns The field or NULL if not found
283 UAVObjectField
*UAVObject::getField(const QString
& name
)
285 QMutexLocker
locker(mutex
);
288 for (int n
= 0; n
< fields
.length(); ++n
) {
289 if (name
.compare(fields
[n
]->getName()) == 0) {
293 // If this point is reached then the field was not found
294 qWarning() << "UAVObject::getField Non existant field" << name
<< "requested."
295 << "This indicates a bug. Make sure you also have null checking for non-debug code.";
300 * Pack the object data into a byte array
301 * @returns The number of bytes copied
303 qint32
UAVObject::pack(quint8
*dataOut
)
305 QMutexLocker
locker(mutex
);
308 for (int n
= 0; n
< fields
.length(); ++n
) {
309 fields
[n
]->pack(&dataOut
[offset
]);
310 offset
+= fields
[n
]->getNumBytes();
316 * Unpack the object data from a byte array
317 * @returns The number of bytes copied
319 qint32
UAVObject::unpack(const quint8
*dataIn
)
321 QMutexLocker
locker(mutex
);
324 for (int n
= 0; n
< fields
.length(); ++n
) {
325 fields
[n
]->unpack(&dataIn
[offset
]);
326 offset
+= fields
[n
]->getNumBytes();
328 emit
objectUnpacked(this); // trigger object updated event
329 emit
objectUpdated(this);
335 * Update a CRC with the object data
336 * @returns The updated CRC
338 quint8
UAVObject::updateCRC(quint8 crc
)
340 QMutexLocker
locker(mutex
);
342 // crc = Crc::updateCRC(crc, (quint8 *) &objID, sizeof(objID));
343 // crc = Crc::updateCRC(crc, (quint8 *) &instID, sizeof(instID));
344 crc
= Crc::updateCRC(crc
, data
, numBytes
);
350 * Save the object data to the file.
351 * The file will be created in the current directory
352 * and its name will be the same as the object with
353 * the .uavobj extension.
354 * @returns True on success, false on failure
356 bool UAVObject::save()
358 QMutexLocker
locker(mutex
);
361 QFile
file(name
+ ".uavobj");
363 if (!file
.open(QFile::WriteOnly
)) {
378 * Save the object data to the file.
379 * The file is expected to be already open for writting.
380 * The data will be appended and the file will not be closed.
381 * @returns True on success, false on failure
383 bool UAVObject::save(QFile
& file
)
385 QMutexLocker
locker(mutex
);
386 quint8 buffer
[numBytes
];
389 // Write the object ID
390 qToLittleEndian
<quint32
>(objID
, tmpId
);
391 if (file
.write((const char *)tmpId
, 4) == -1) {
395 // Write the instance ID
397 qToLittleEndian
<quint16
>(instID
, tmpId
);
398 if (file
.write((const char *)tmpId
, 2) == -1) {
405 if (file
.write((const char *)buffer
, numBytes
) == -1) {
414 * Load the object data from a file.
415 * The file will be openned in the current directory
416 * and its name will be the same as the object with
417 * the .uavobj extension.
418 * @returns True on success, false on failure
420 bool UAVObject::load()
422 QMutexLocker
locker(mutex
);
425 QFile
file(name
+ ".uavobj");
427 if (!file
.open(QFile::ReadOnly
)) {
442 * Load the object data from file.
443 * The file is expected to be already open for reading.
444 * The data will be read and the file will not be closed.
445 * @returns True on success, false on failure
447 bool UAVObject::load(QFile
& file
)
449 QMutexLocker
locker(mutex
);
450 quint8 buffer
[numBytes
];
453 // Read the object ID
454 if (file
.read((char *)tmpId
, 4) != 4) {
458 // Check that the IDs match
459 if (qFromLittleEndian
<quint32
>(tmpId
) != objID
) {
463 // Read the instance ID
464 if (file
.read((char *)tmpId
, 2) != 2) {
468 // Check that the IDs match
469 if (qFromLittleEndian
<quint16
>(tmpId
) != instID
) {
473 // Read and unpack the data
474 if (file
.read((char *)buffer
, numBytes
) != numBytes
) {
484 * Return a string with the object information
486 QString
UAVObject::toString()
490 sout
.append(toStringBrief());
492 sout
.append(toStringData());
497 * Return a string with the object information (only the header)
499 QString
UAVObject::toStringBrief()
503 // object Id is converted to uppercase hexadecimal
504 sout
.append(QString("%1 (ID: %2-%3, %4 bytes, %5)")
506 .arg(getObjID(), 1, 16).toUpper()
509 .arg(isSingleInstance() ? "single" : "multiple"));
514 * Return a string with the object information (only the data)
516 QString
UAVObject::toStringData()
520 sout
.append("Data:\n");
521 for (int n
= 0; n
< fields
.length(); ++n
) {
522 sout
.append(QString("\t%1").arg(fields
[n
]->toString()));
527 void UAVObject::toXML(QXmlStreamWriter
*xmlWriter
)
529 xmlWriter
->writeStartElement("object");
530 xmlWriter
->writeAttribute("name", getName());
531 xmlWriter
->writeAttribute("id", QString("%1").arg(getObjID(), 1, 16).toUpper());
532 xmlWriter
->writeAttribute("instance", QString("%1").arg(getInstID()));
533 xmlWriter
->writeStartElement("fields");
534 foreach(UAVObjectField
* field
, fields
) {
535 field
->toXML(xmlWriter
);
537 xmlWriter
->writeEndElement(); // fields
538 xmlWriter
->writeEndElement(); // object
541 void UAVObject::fromXML(QXmlStreamReader
*xmlReader
)
543 // Check that we are the correct object by name, and that we are the same instance id
544 // but dont check id since we want to be able to be forward compatible.
545 if (xmlReader
->name() == "object" &&
546 xmlReader
->attributes().value("name") == getName() &&
547 xmlReader
->attributes().value("instance") == QString("%1").arg(getInstID())) {
548 QXmlStreamReader::TokenType token
= xmlReader
->readNext();
549 if (token
== QXmlStreamReader::StartElement
&& xmlReader
->name() == "fields") {
550 while (xmlReader
->readNextStartElement()) {
551 if (xmlReader
->name() == "field") {
552 QStringRef fieldName
= xmlReader
->attributes().value("name");
553 if (fieldName
!= "") {
554 getField(*fieldName
.string())->fromXML(xmlReader
);
562 void UAVObject::toJson(QJsonObject
&jsonObject
)
564 jsonObject
["name"] = getName();
565 jsonObject
["setting"] = isSettingsObject();
566 jsonObject
["id"] = QString("%1").arg(getObjID(), 1, 16).toUpper();
567 jsonObject
["instance"] = (int)getInstID();
568 QJsonArray jSonFields
;
569 foreach(UAVObjectField
* field
, fields
) {
570 QJsonObject jSonField
;
572 field
->toJson(jSonField
);
573 jSonFields
.append(jSonField
);
575 jsonObject
["fields"] = jSonFields
;
578 void UAVObject::fromJson(const QJsonObject
&jsonObject
)
580 if (jsonObject
["name"].toString() == getName() &&
581 jsonObject
["instance"].toInt() == (int)getInstID()) {
582 QJsonArray jsonFields
= jsonObject
["fields"].toArray();
583 for (int i
= 0; i
< jsonFields
.size(); i
++) {
584 QJsonObject jsonField
= jsonFields
.at(i
).toObject();
585 UAVObjectField
*field
= getField(jsonField
["name"].toString());
587 field
->fromJson(jsonField
);
595 * Emit the transactionCompleted event (used by the UAVTalk plugin)
597 void UAVObject::emitTransactionCompleted(bool success
)
599 emit
transactionCompleted(this, success
);
603 * Emit the newInstance event
605 void UAVObject::emitNewInstance(UAVObject
*obj
)
607 emit
newInstance(obj
);
610 bool UAVObject::isKnown() const
612 QMutexLocker
locker(mutex
);
617 void UAVObject::setIsKnown(bool isKnown
)
620 bool changed
= m_isKnown
!= isKnown
;
625 emit
isKnownChanged(this);
629 bool UAVObject::isSettingsObject()
634 bool UAVObject::isDataObject()
639 bool UAVObject::isMetaDataObject()
645 * Initialize a UAVObjMetadata object.
646 * \param[in] metadata The metadata object
648 void UAVObject::MetadataInitialize(UAVObject::Metadata
& metadata
)
651 ACCESS_READWRITE
<< UAVOBJ_ACCESS_SHIFT
|
652 ACCESS_READWRITE
<< UAVOBJ_GCS_ACCESS_SHIFT
|
653 1 << UAVOBJ_TELEMETRY_ACKED_SHIFT
|
654 1 << UAVOBJ_GCS_TELEMETRY_ACKED_SHIFT
|
655 UPDATEMODE_ONCHANGE
<< UAVOBJ_TELEMETRY_UPDATE_MODE_SHIFT
|
656 UPDATEMODE_ONCHANGE
<< UAVOBJ_GCS_TELEMETRY_UPDATE_MODE_SHIFT
|
657 UPDATEMODE_ONCHANGE
<< UAVOBJ_LOGGING_UPDATE_MODE_SHIFT
;
658 metadata
.flightTelemetryUpdatePeriod
= 0;
659 metadata
.gcsTelemetryUpdatePeriod
= 0;
660 metadata
.loggingUpdatePeriod
= 0;
664 * Get the UAVObject metadata access member
665 * \param[in] metadata The metadata object
666 * \return the access type
668 UAVObject::AccessMode
UAVObject::GetFlightAccess(const UAVObject::Metadata
& metadata
)
670 return UAVObject::AccessMode((metadata
.flags
>> UAVOBJ_ACCESS_SHIFT
) & 1);
674 * Set the UAVObject metadata access member
675 * \param[in] metadata The metadata object
676 * \param[in] mode The access mode
678 void UAVObject::SetFlightAccess(UAVObject::Metadata
& metadata
, UAVObject::AccessMode mode
)
680 SET_BITS(metadata
.flags
, UAVOBJ_ACCESS_SHIFT
, mode
, 1);
684 * Get the UAVObject metadata GCS access member
685 * \param[in] metadata The metadata object
686 * \return the GCS access type
688 UAVObject::AccessMode
UAVObject::GetGcsAccess(const UAVObject::Metadata
& metadata
)
690 return UAVObject::AccessMode((metadata
.flags
>> UAVOBJ_GCS_ACCESS_SHIFT
) & 1);
694 * Set the UAVObject metadata GCS access member
695 * \param[in] metadata The metadata object
696 * \param[in] mode The access mode
698 void UAVObject::SetGcsAccess(UAVObject::Metadata
& metadata
, UAVObject::AccessMode mode
)
700 SET_BITS(metadata
.flags
, UAVOBJ_GCS_ACCESS_SHIFT
, mode
, 1);
704 * Get the UAVObject metadata telemetry acked member
705 * \param[in] metadata The metadata object
706 * \return the telemetry acked boolean
708 quint8
UAVObject::GetFlightTelemetryAcked(const UAVObject::Metadata
& metadata
)
710 return (metadata
.flags
>> UAVOBJ_TELEMETRY_ACKED_SHIFT
) & 1;
714 * Set the UAVObject metadata telemetry acked member
715 * \param[in] metadata The metadata object
716 * \param[in] val The telemetry acked boolean
718 void UAVObject::SetFlightTelemetryAcked(UAVObject::Metadata
& metadata
, quint8 val
)
720 SET_BITS(metadata
.flags
, UAVOBJ_TELEMETRY_ACKED_SHIFT
, val
, 1);
724 * Get the UAVObject metadata GCS telemetry acked member
725 * \param[in] metadata The metadata object
726 * \return the telemetry acked boolean
728 quint8
UAVObject::GetGcsTelemetryAcked(const UAVObject::Metadata
& metadata
)
730 return (metadata
.flags
>> UAVOBJ_GCS_TELEMETRY_ACKED_SHIFT
) & 1;
734 * Set the UAVObject metadata GCS telemetry acked member
735 * \param[in] metadata The metadata object
736 * \param[in] val The GCS telemetry acked boolean
738 void UAVObject::SetGcsTelemetryAcked(UAVObject::Metadata
& metadata
, quint8 val
)
740 SET_BITS(metadata
.flags
, UAVOBJ_GCS_TELEMETRY_ACKED_SHIFT
, val
, 1);
744 * Get the UAVObject metadata telemetry update mode
745 * \param[in] metadata The metadata object
746 * \return the telemetry update mode
748 UAVObject::UpdateMode
UAVObject::GetFlightTelemetryUpdateMode(const UAVObject::Metadata
& metadata
)
750 return UAVObject::UpdateMode((metadata
.flags
>> UAVOBJ_TELEMETRY_UPDATE_MODE_SHIFT
) & UAVOBJ_UPDATE_MODE_MASK
);
754 * Set the UAVObject metadata telemetry update mode member
755 * \param[in] metadata The metadata object
756 * \param[in] val The telemetry update mode
758 void UAVObject::SetFlightTelemetryUpdateMode(UAVObject::Metadata
& metadata
, UAVObject::UpdateMode val
)
760 SET_BITS(metadata
.flags
, UAVOBJ_TELEMETRY_UPDATE_MODE_SHIFT
, val
, UAVOBJ_UPDATE_MODE_MASK
);
764 * Get the UAVObject metadata GCS telemetry update mode
765 * \param[in] metadata The metadata object
766 * \return the GCS telemetry update mode
768 UAVObject::UpdateMode
UAVObject::GetGcsTelemetryUpdateMode(const UAVObject::Metadata
& metadata
)
770 return UAVObject::UpdateMode((metadata
.flags
>> UAVOBJ_GCS_TELEMETRY_UPDATE_MODE_SHIFT
) & UAVOBJ_UPDATE_MODE_MASK
);
774 * Set the UAVObject metadata GCS telemetry update mode member
775 * \param[in] metadata The metadata object
776 * \param[in] val The GCS telemetry update mode
778 void UAVObject::SetGcsTelemetryUpdateMode(UAVObject::Metadata
& metadata
, UAVObject::UpdateMode val
)
780 SET_BITS(metadata
.flags
, UAVOBJ_GCS_TELEMETRY_UPDATE_MODE_SHIFT
, val
, UAVOBJ_UPDATE_MODE_MASK
);
784 * Get the UAVObject metadata logging update mode
785 * \param[in] metadata The metadata object
786 * \return the logging update mode
788 UAVObject::UpdateMode
UAVObject::GetLoggingUpdateMode(const UAVObject::Metadata
& metadata
)
790 return UAVObject::UpdateMode((metadata
.flags
>> UAVOBJ_LOGGING_UPDATE_MODE_SHIFT
) & UAVOBJ_UPDATE_MODE_MASK
);
794 * Set the UAVObject metadata logging update mode member
795 * \param[in] metadata The metadata object
796 * \param[in] val The logging update mode
798 void UAVObject::SetLoggingUpdateMode(UAVObject::Metadata
& metadata
, UAVObject::UpdateMode val
)
800 SET_BITS(metadata
.flags
, UAVOBJ_LOGGING_UPDATE_MODE_SHIFT
, val
, UAVOBJ_UPDATE_MODE_MASK
);