Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / ground / gcs / src / plugins / uavobjects / uavobject.cpp
blob8a95d873b08165024d8d7b47df98e560d21e9c62
1 /**
2 ******************************************************************************
4 * @file uavobject.cpp
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
8 * @{
9 * @addtogroup UAVObjectsPlugin UAVObjects Plugin
10 * @{
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
22 * for more details.
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>
32 #include <QtEndian>
33 #include <QDebug>
34 #include <QXmlStreamWriter>
35 #include <QXmlStreamReader>
36 #include <QJsonObject>
37 #include <QJsonArray>
39 using namespace Utils;
41 // Constants
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
51 // Macros
52 #define SET_BITS(var, shift, value, mask) var = (var & ~(mask << shift)) | (value << shift);
54 /**
55 * Constructor
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)
62 this->objID = objID;
63 this->instID = 0;
64 this->isSingleInst = isSingleInst;
65 this->name = name;
66 this->data = 0;
67 this->numBytes = 0;
68 this->mutex = new QMutex(QMutex::Recursive);
69 m_isKnown = false;
72 /**
73 * Initialize object with its instance ID
75 void UAVObject::initialize(quint32 instID)
77 QMutexLocker locker(mutex);
79 this->instID = instID;
82 /**
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;
93 this->data = data;
94 this->fields = fields;
95 // Initialize fields
96 quint32 offset = 0;
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)
109 Q_UNUSED(field);
110 // emit objectUpdatedAuto(this); // trigger object updated event
111 // emit objectUpdated(this);
115 * Get the object ID
117 quint32 UAVObject::getObjID()
119 return objID;
123 * Get the instance ID
125 quint32 UAVObject::getInstID()
127 return instID;
131 * Returns true if this is a single instance object
133 bool UAVObject::isSingleInstance()
135 return isSingleInst;
139 * Get the name of the object
141 QString UAVObject::getName()
143 return name;
147 * Get the description of the object
149 QString UAVObject::getDescription()
151 return description;
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()
167 return category;
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()
183 return numBytes;
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()
200 if (instID == 0) {
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()
220 if (instID == 0) {
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()
232 mutex->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()
248 mutex->unlock();
252 * Get object's mutex
254 QMutex *UAVObject::getMutex()
256 return mutex;
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);
276 return fields;
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);
287 // Look for field
288 for (int n = 0; n < fields.length(); ++n) {
289 if (name.compare(fields[n]->getName()) == 0) {
290 return fields[n];
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.";
296 return NULL;
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);
306 qint32 offset = 0;
308 for (int n = 0; n < fields.length(); ++n) {
309 fields[n]->pack(&dataOut[offset]);
310 offset += fields[n]->getNumBytes();
312 return numBytes;
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);
322 qint32 offset = 0;
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);
331 return numBytes;
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);
346 return crc;
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);
360 // Open file
361 QFile file(name + ".uavobj");
363 if (!file.open(QFile::WriteOnly)) {
364 return false;
367 // Write object
368 if (!save(file)) {
369 return false;
372 // Close file
373 file.close();
374 return true;
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];
387 quint8 tmpId[4];
389 // Write the object ID
390 qToLittleEndian<quint32>(objID, tmpId);
391 if (file.write((const char *)tmpId, 4) == -1) {
392 return false;
395 // Write the instance ID
396 if (!isSingleInst) {
397 qToLittleEndian<quint16>(instID, tmpId);
398 if (file.write((const char *)tmpId, 2) == -1) {
399 return false;
403 // Write the data
404 pack(buffer);
405 if (file.write((const char *)buffer, numBytes) == -1) {
406 return false;
409 // Done
410 return true;
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);
424 // Open file
425 QFile file(name + ".uavobj");
427 if (!file.open(QFile::ReadOnly)) {
428 return false;
431 // Load object
432 if (!load(file)) {
433 return false;
436 // Close file
437 file.close();
438 return true;
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];
451 quint8 tmpId[4];
453 // Read the object ID
454 if (file.read((char *)tmpId, 4) != 4) {
455 return false;
458 // Check that the IDs match
459 if (qFromLittleEndian<quint32>(tmpId) != objID) {
460 return false;
463 // Read the instance ID
464 if (file.read((char *)tmpId, 2) != 2) {
465 return false;
468 // Check that the IDs match
469 if (qFromLittleEndian<quint16>(tmpId) != instID) {
470 return false;
473 // Read and unpack the data
474 if (file.read((char *)buffer, numBytes) != numBytes) {
475 return false;
477 unpack(buffer);
479 // Done
480 return true;
484 * Return a string with the object information
486 QString UAVObject::toString()
488 QString sout;
490 sout.append(toStringBrief());
491 sout.append('\n');
492 sout.append(toStringData());
493 return sout;
497 * Return a string with the object information (only the header)
499 QString UAVObject::toStringBrief()
501 QString sout;
503 // object Id is converted to uppercase hexadecimal
504 sout.append(QString("%1 (ID: %2-%3, %4 bytes, %5)")
505 .arg(getName())
506 .arg(getObjID(), 1, 16).toUpper()
507 .arg(getInstID())
508 .arg(getNumBytes())
509 .arg(isSingleInstance() ? "single" : "multiple"));
510 return sout;
514 * Return a string with the object information (only the data)
516 QString UAVObject::toStringData()
518 QString sout;
520 sout.append("Data:\n");
521 for (int n = 0; n < fields.length(); ++n) {
522 sout.append(QString("\t%1").arg(fields[n]->toString()));
524 return sout;
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());
586 if (field != NULL) {
587 field->fromJson(jsonField);
590 updated();
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);
614 return m_isKnown;
617 void UAVObject::setIsKnown(bool isKnown)
619 lock();
620 bool changed = m_isKnown != isKnown;
621 m_isKnown = isKnown;
622 unlock();
624 if (changed) {
625 emit isKnownChanged(this);
629 bool UAVObject::isSettingsObject()
631 return false;
634 bool UAVObject::isDataObject()
636 return false;
639 bool UAVObject::isMetaDataObject()
641 return false;
645 * Initialize a UAVObjMetadata object.
646 * \param[in] metadata The metadata object
648 void UAVObject::MetadataInitialize(UAVObject::Metadata & metadata)
650 metadata.flags =
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);