Skip to content
Snippets Groups Projects
QGCCameraControl.cc 57.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*!
     * @file
     *   @brief Camera Controller
     *   @author Gus Grubba <mavlink@grubba.com>
     *
     */
    
    #include "QGCCameraControl.h"
    #include "QGCCameraIO.h"
    
    Gus Grubba's avatar
    Gus Grubba committed
    #include "SettingsManager.h"
    #include "VideoManager.h"
    #include "QGCMapEngine.h"
    
    
    Gus Grubba's avatar
    Gus Grubba committed
    #include <QDir>
    
    Gus Grubba's avatar
    Gus Grubba committed
    #include <QStandardPaths>
    
    #include <QDomDocument>
    #include <QDomNodeList>
    
    QGC_LOGGING_CATEGORY(CameraControlLog, "CameraControlLog")
    QGC_LOGGING_CATEGORY(CameraControlLogVerbose, "CameraControlLogVerbose")
    
    
    static const char* kCondition       = "condition";
    
    static const char* kControl         = "control";
    static const char* kDefault         = "default";
    
    static const char* kDefnition       = "definition";
    
    static const char* kDescription     = "description";
    static const char* kExclusion       = "exclude";
    
    static const char* kExclusions      = "exclusions";
    static const char* kLocale          = "locale";
    static const char* kLocalization    = "localization";
    
    static const char* kMax             = "max";
    static const char* kMin             = "min";
    
    static const char* kModel           = "model";
    static const char* kName            = "name";
    static const char* kOption          = "option";
    static const char* kOptions         = "options";
    
    static const char* kOriginal        = "original";
    
    static const char* kParameter       = "parameter";
    static const char* kParameterrange  = "parameterrange";
    static const char* kParameterranges = "parameterranges";
    static const char* kParameters      = "parameters";
    static const char* kReadOnly        = "readonly";
    
    static const char* kWriteOnly       = "writeonly";
    
    static const char* kRoption         = "roption";
    
    static const char* kStep            = "step";
    
    static const char* kDecimalPlaces   = "decimalPlaces";
    
    static const char* kStrings         = "strings";
    
    static const char* kTranslated      = "translated";
    
    static const char* kUnit            = "unit";
    
    static const char* kUpdate          = "update";
    static const char* kUpdates         = "updates";
    static const char* kValue           = "value";
    static const char* kVendor          = "vendor";
    static const char* kVersion         = "version";
    
    static const char* kPhotoMode       = "PhotoMode";
    static const char* kPhotoLapse      = "PhotoLapse";
    static const char* kPhotoLapseCount = "PhotoLapseCount";
    
    
    //-----------------------------------------------------------------------------
    static bool
    read_attribute(QDomNode& node, const char* tagName, bool& target)
    {
        QDomNamedNodeMap attrs = node.attributes();
        if(!attrs.count()) {
            return false;
        }
        QDomNode subNode = attrs.namedItem(tagName);
        if(subNode.isNull()) {
            return false;
        }
        target = subNode.nodeValue() != "0";
        return true;
    }
    
    //-----------------------------------------------------------------------------
    static bool
    read_attribute(QDomNode& node, const char* tagName, int& target)
    {
        QDomNamedNodeMap attrs = node.attributes();
        if(!attrs.count()) {
            return false;
        }
        QDomNode subNode = attrs.namedItem(tagName);
        if(subNode.isNull()) {
            return false;
        }
        target = subNode.nodeValue().toInt();
        return true;
    }
    
    //-----------------------------------------------------------------------------
    static bool
    read_attribute(QDomNode& node, const char* tagName, QString& target)
    {
        QDomNamedNodeMap attrs = node.attributes();
        if(!attrs.count()) {
            return false;
        }
        QDomNode subNode = attrs.namedItem(tagName);
        if(subNode.isNull()) {
            return false;
        }
        target = subNode.nodeValue();
        return true;
    }
    
    //-----------------------------------------------------------------------------
    static bool
    read_value(QDomNode& element, const char* tagName, QString& target)
    {
        QDomElement de = element.firstChildElement(tagName);
        if(de.isNull()) {
            return false;
        }
        target = de.text();
        return true;
    }
    
    //-----------------------------------------------------------------------------
    QGCCameraControl::QGCCameraControl(const mavlink_camera_information_t *info, Vehicle* vehicle, int compID, QObject* parent)
        : FactGroup(0, parent)
        , _vehicle(vehicle)
        , _compID(compID)
        , _version(0)
    
    Gus Grubba's avatar
    Gus Grubba committed
        , _cached(false)
    
    Gus Grubba's avatar
    Gus Grubba committed
        , _storageFree(0)
        , _storageTotal(0)
        , _netManager(NULL)
    
        , _cameraMode(CAM_MODE_UNDEFINED)
    
        , _photoMode(PHOTO_CAPTURE_SINGLE)
        , _photoLapse(1.0)
        , _photoLapseCount(0)
    
    Gus Grubba's avatar
    Gus Grubba committed
        , _video_status(VIDEO_CAPTURE_STATUS_UNDEFINED)
    
        , _photo_status(PHOTO_CAPTURE_STATUS_UNDEFINED)
    
        , _storageInfoRetries(0)
        , _captureInfoRetries(0)
    
        QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
    
        memcpy(&_info, info, sizeof(mavlink_camera_information_t));
    
    Gus Grubba's avatar
    Gus Grubba committed
        connect(this, &QGCCameraControl::dataReady, this, &QGCCameraControl::_dataReady);
    
        _vendor = QString((const char*)(void*)&info->vendor_name[0]);
        _modelName = QString((const char*)(void*)&info->model_name[0]);
        int ver = (int)_info.cam_definition_version;
        _cacheFile.sprintf("%s/%s_%s_%03d.xml",
            qgcApp()->toolbox()->settingsManager()->appSettings()->parameterSavePath().toStdString().c_str(),
            _vendor.toStdString().c_str(),
            _modelName.toStdString().c_str(),
            ver);
    
    Gus Grubba's avatar
    Gus Grubba committed
        if(info->cam_definition_uri[0] != 0) {
    
    Gus Grubba's avatar
    Gus Grubba committed
            //-- Process camera definition file
    
    Gus Grubba's avatar
    Gus Grubba committed
            _handleDefinitionFile(info->cam_definition_uri);
    
    Gus Grubba's avatar
    Gus Grubba committed
        } else {
            _initWhenReady();
        }
    
        QSettings settings;
        _photoMode = (PhotoMode)settings.value(kPhotoMode, (int)PHOTO_CAPTURE_SINGLE).toInt();
        _photoLapse = settings.value(kPhotoLapse, 1.0).toDouble();
        _photoLapseCount = settings.value(kPhotoLapseCount, 0).toInt();
    
    }
    
    //-----------------------------------------------------------------------------
    QGCCameraControl::~QGCCameraControl()
    {
    
    Gus Grubba's avatar
    Gus Grubba committed
        if(_netManager) {
            delete _netManager;
        }
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::_initWhenReady()
    {
        qCDebug(CameraControlLog) << "_initWhenReady()";
        if(isBasic()) {
            qCDebug(CameraControlLog) << "Basic, MAVLink only messages.";
            _requestCameraSettings();
        } else {
            _requestAllParameters();
            //-- Give some time to load the parameters before going after the camera settings
            QTimer::singleShot(2000, this, &QGCCameraControl::_requestCameraSettings);
        }
        connect(_vehicle, &Vehicle::mavCommandResult, this, &QGCCameraControl::_mavCommandResult);
        connect(&_captureStatusTimer, &QTimer::timeout, this, &QGCCameraControl::_requestCaptureStatus);
        _captureStatusTimer.setSingleShot(true);
        QTimer::singleShot(2500, this, &QGCCameraControl::_requestStorageInfo);
        _captureStatusTimer.start(2750);
        emit infoChanged();
        if(_netManager) {
            delete _netManager;
            _netManager = NULL;
        }
    
    }
    
    //-----------------------------------------------------------------------------
    QString
    QGCCameraControl::firmwareVersion()
    {
        int major = (_info.firmware_version >> 24) & 0xFF;
        int minor = (_info.firmware_version >> 16) & 0xFF;
        int build = _info.firmware_version & 0xFFFF;
        QString ver;
        ver.sprintf("%d.%d.%d", major, minor, build);
        return ver;
    }
    
    
    Gus Grubba's avatar
    Gus Grubba committed
    //-----------------------------------------------------------------------------
    QGCCameraControl::VideoStatus
    QGCCameraControl::videoStatus()
    {
        return _video_status;
    }
    
    
    //-----------------------------------------------------------------------------
    QGCCameraControl::PhotoStatus
    QGCCameraControl::photoStatus()
    {
        return _photo_status;
    }
    
    
    Gus Grubba's avatar
    Gus Grubba committed
    //-----------------------------------------------------------------------------
    QString
    QGCCameraControl::storageFreeStr()
    {
    
        return QGCMapEngine::bigSizeToString((quint64)_storageFree * 1024 * 1024);
    
    Gus Grubba's avatar
    Gus Grubba committed
    }
    
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::setCameraMode(CameraMode mode)
    {
        qCDebug(CameraControlLog) << "setCameraMode(" << mode << ")";
    
        if(mode == CAM_MODE_VIDEO) {
    
            setVideoMode();
    
        } else if(mode == CAM_MODE_PHOTO) {
    
            setPhotoMode();
        } else {
            qCDebug(CameraControlLog) << "setCameraMode() Invalid mode:" << mode;
            return;
        }
    
    Gus Grubba's avatar
    Gus Grubba committed
    }
    
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::setPhotoMode(PhotoMode mode)
    {
        _photoMode = mode;
        QSettings settings;
        settings.setValue(kPhotoMode, (int)mode);
        emit photoModeChanged();
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::setPhotoLapse(qreal interval)
    {
        _photoLapse = interval;
        QSettings settings;
        settings.setValue(kPhotoLapse, interval);
        emit photoLapseChanged();
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::setPhotoLapseCount(int count)
    {
        _photoLapseCount = count;
        QSettings settings;
        settings.setValue(kPhotoLapseCount, count);
        emit photoLapseCountChanged();
    }
    
    
    Gus Grubba's avatar
    Gus Grubba committed
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::_setCameraMode(CameraMode mode)
    {
    
        _cameraMode = mode;
        emit cameraModeChanged();
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::toggleMode()
    {
    
        if(cameraMode() == CAM_MODE_PHOTO || cameraMode() == CAM_MODE_SURVEY) {
    
            setVideoMode();
    
        } else if(cameraMode() == CAM_MODE_VIDEO) {
    
            setPhotoMode();
        }
    }
    
    //-----------------------------------------------------------------------------
    
    Gus Grubba's avatar
    Gus Grubba committed
    bool
    QGCCameraControl::toggleVideo()
    
    Gus Grubba's avatar
    Gus Grubba committed
        if(videoStatus() == VIDEO_CAPTURE_STATUS_RUNNING) {
            return stopVideo();
        } else {
            return startVideo();
        }
    }
    
    Gus Grubba's avatar
    Gus Grubba committed
    //-----------------------------------------------------------------------------
    bool
    QGCCameraControl::takePhoto()
    {
        qCDebug(CameraControlLog) << "takePhoto()";
        //-- Check if camera can capture photos or if it can capture it while in Video Mode
    
        if(!capturesPhotos() || (cameraMode() == CAM_MODE_VIDEO && !photosInVideoMode()) || photoStatus() != PHOTO_CAPTURE_IDLE) {
    
    Gus Grubba's avatar
    Gus Grubba committed
            return false;
        }
        if(capturesPhotos()) {
            _vehicle->sendMavCommand(
    
                MAV_CMD_IMAGE_START_CAPTURE,                                // Command id
                false,                                                      // ShowError
                0,                                                          // Reserved (Set to 0)
                _photoMode == PHOTO_CAPTURE_SINGLE ? 0 : _photoLapse,       // Duration between two consecutive pictures (in seconds--ignored if single image)
                _photoMode == PHOTO_CAPTURE_SINGLE ? 1 : _photoLapseCount); // Number of images to capture total - 0 for unlimited capture
    
            _setPhotoStatus(PHOTO_CAPTURE_IN_PROGRESS);
    
            _captureInfoRetries = 0;
    
    Gus Grubba's avatar
    Gus Grubba committed
            //-- Capture local image as well
            QString photoPath = qgcApp()->toolbox()->settingsManager()->appSettings()->savePath()->rawValue().toString() + QStringLiteral("/Photo");
            QDir().mkpath(photoPath);
            photoPath += + "/" + QDateTime::currentDateTime().toString("yyyy-MM-dd_hh.mm.ss.zzz") + ".jpg";
            qgcApp()->toolbox()->videoManager()->videoReceiver()->grabImage(photoPath);
            return true;
        }
        return false;
    
    //-----------------------------------------------------------------------------
    bool
    QGCCameraControl::stopTakePhoto()
    {
        qCDebug(CameraControlLog) << "stopTakePhoto()";
        if(photoStatus() == PHOTO_CAPTURE_IDLE || (photoStatus() != PHOTO_CAPTURE_INTERVAL_IDLE && photoStatus() != PHOTO_CAPTURE_INTERVAL_IN_PROGRESS)) {
            return false;
        }
        if(capturesPhotos()) {
            _vehicle->sendMavCommand(
    
                MAV_CMD_IMAGE_STOP_CAPTURE,                                 // Command id
                false,                                                      // ShowError
                0);                                                         // Reserved (Set to 0)
            _setPhotoStatus(PHOTO_CAPTURE_IDLE);
            _captureInfoRetries = 0;
            return true;
        }
        return false;
    }
    
    
    //-----------------------------------------------------------------------------
    
    Gus Grubba's avatar
    Gus Grubba committed
    bool
    
    QGCCameraControl::startVideo()
    {
    
    Gus Grubba's avatar
    Gus Grubba committed
        qCDebug(CameraControlLog) << "startVideo()";
        //-- Check if camera can capture videos or if it can capture it while in Photo Mode
    
        if(!capturesVideo() || (cameraMode() == CAM_MODE_PHOTO && !videoInPhotoMode())) {
    
    Gus Grubba's avatar
    Gus Grubba committed
            return false;
        }
        if(videoStatus() != VIDEO_CAPTURE_STATUS_RUNNING) {
            _vehicle->sendMavCommand(
    
    Gus Grubba's avatar
    Gus Grubba committed
                MAV_CMD_VIDEO_START_CAPTURE,                // Command id
                true,                                       // ShowError
    
    Gus Grubba's avatar
    Gus Grubba committed
                0,                                          // Reserved (Set to 0)
    
    Gus Grubba's avatar
    Gus Grubba committed
                0);                                         // CAMERA_CAPTURE_STATUS Frequency
            return true;
        }
        return false;
    
    }
    
    //-----------------------------------------------------------------------------
    
    Gus Grubba's avatar
    Gus Grubba committed
    bool
    
    QGCCameraControl::stopVideo()
    {
    
    Gus Grubba's avatar
    Gus Grubba committed
        qCDebug(CameraControlLog) << "stopVideo()";
        if(videoStatus() == VIDEO_CAPTURE_STATUS_RUNNING) {
            _vehicle->sendMavCommand(
    
    Gus Grubba's avatar
    Gus Grubba committed
                MAV_CMD_VIDEO_STOP_CAPTURE,                 // Command id
                true,                                       // ShowError
    
    Gus Grubba's avatar
    Gus Grubba committed
                0);                                         // Reserved (Set to 0)
    
    Gus Grubba's avatar
    Gus Grubba committed
            return true;
        }
        return false;
    
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::setVideoMode()
    {
    
        if(hasModes() && _cameraMode != CAM_MODE_VIDEO) {
    
            qCDebug(CameraControlLog) << "setVideoMode()";
            //-- Use basic MAVLink message
            _vehicle->sendMavCommand(
                _compID,                                // Target component
                MAV_CMD_SET_CAMERA_MODE,                // Command id
                true,                                   // ShowError
    
    Gus Grubba's avatar
    Gus Grubba committed
                0,                                      // Reserved (Set to 0)
    
                CAM_MODE_VIDEO);                        // Camera mode (0: photo, 1: video)
    
            _setCameraMode(CAM_MODE_VIDEO);
    
        }
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::setPhotoMode()
    {
    
        if(hasModes() && _cameraMode != CAM_MODE_PHOTO) {
    
            qCDebug(CameraControlLog) << "setPhotoMode()";
            //-- Use basic MAVLink message
            _vehicle->sendMavCommand(
                _compID,                                // Target component
                MAV_CMD_SET_CAMERA_MODE,                // Command id
                true,                                   // ShowError
    
    Gus Grubba's avatar
    Gus Grubba committed
                0,                                      // Reserved (Set to 0)
    
                CAM_MODE_PHOTO);                        // Camera mode (0: photo, 1: video)
    
            _setCameraMode(CAM_MODE_PHOTO);
    
        }
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::resetSettings()
    {
        qCDebug(CameraControlLog) << "resetSettings()";
    
    Gus Grubba's avatar
    Gus Grubba committed
        _vehicle->sendMavCommand(
            _compID,                                // Target component
            MAV_CMD_RESET_CAMERA_SETTINGS,          // Command id
            true,                                   // ShowError
            1);                                     // Do Reset
    
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::formatCard(int id)
    {
        qCDebug(CameraControlLog) << "formatCard()";
        if(_vehicle) {
            _vehicle->sendMavCommand(
                _compID,                                // Target component
                MAV_CMD_STORAGE_FORMAT,                 // Command id
                true,                                   // ShowError
                id,                                     // Storage ID (1 for first, 2 for second, etc.)
                1);                                     // Do Format
        }
    }
    
    
    Gus Grubba's avatar
    Gus Grubba committed
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::_requestCaptureStatus()
    {
    
        qCDebug(CameraControlLog) << "_requestCaptureStatus()";
    
    Gus Grubba's avatar
    Gus Grubba committed
        _vehicle->sendMavCommand(
            _compID,                                // target component
            MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS,  // command id
            false,                                  // showError
            1);                                     // Do Request
    }
    
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::factChanged(Fact* pFact)
    {
        _updateActiveList();
        _updateRanges(pFact);
    }
    
    //-----------------------------------------------------------------------------
    
    Gus Grubba's avatar
    Gus Grubba committed
    void
    QGCCameraControl::_mavCommandResult(int vehicleId, int component, int command, int result, bool noReponseFromVehicle)
    
    Gus Grubba's avatar
    Gus Grubba committed
        //-- Is this ours?
        if(_vehicle->id() != vehicleId || compID() != component) {
            return;
        }
    
        if(!noReponseFromVehicle && result == MAV_RESULT_IN_PROGRESS) {
            //-- Do Nothing
            qCDebug(CameraControlLog) << "In progress response for" << command;
        }else if(!noReponseFromVehicle && result == MAV_RESULT_ACCEPTED) {
    
    Gus Grubba's avatar
    Gus Grubba committed
            switch(command) {
                case MAV_CMD_RESET_CAMERA_SETTINGS:
                    if(isBasic()) {
                        _requestCameraSettings();
                    } else {
    
                        QTimer::singleShot(500, this, &QGCCameraControl::_requestAllParameters);
                        QTimer::singleShot(2500, this, &QGCCameraControl::_requestCameraSettings);
    
    Gus Grubba's avatar
    Gus Grubba committed
                    }
                    break;
                case MAV_CMD_VIDEO_START_CAPTURE:
                    _setVideoStatus(VIDEO_CAPTURE_STATUS_RUNNING);
                    _captureStatusTimer.start(1000);
                    break;
                case MAV_CMD_VIDEO_STOP_CAPTURE:
                    _setVideoStatus(VIDEO_CAPTURE_STATUS_STOPPED);
    
                    _captureStatusTimer.start(1000);
    
    Gus Grubba's avatar
    Gus Grubba committed
                    break;
    
                case MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS:
                    _captureInfoRetries = 0;
                    break;
                case MAV_CMD_REQUEST_STORAGE_INFORMATION:
                    _storageInfoRetries = 0;
                    break;
    
    Gus Grubba's avatar
    Gus Grubba committed
            }
    
    Gus Grubba's avatar
    Gus Grubba committed
        } else {
    
            if(noReponseFromVehicle || result == MAV_RESULT_TEMPORARILY_REJECTED || result == MAV_RESULT_FAILED) {
                if(noReponseFromVehicle) {
                    qCDebug(CameraControlLog) << "No response for" << command;
                } else if (result == MAV_RESULT_TEMPORARILY_REJECTED) {
                    qCDebug(CameraControlLog) << "Command temporarily rejected for" << command;
                } else {
                    qCDebug(CameraControlLog) << "Command failed for" << command;
                }
                switch(command) {
    
                    case MAV_CMD_IMAGE_START_CAPTURE:
    
                    case MAV_CMD_IMAGE_STOP_CAPTURE:
    
                        if(++_captureInfoRetries < 5) {
                            _captureStatusTimer.start(1000);
                        } else {
                            qCDebug(CameraControlLog) << "Giving up requesting capture status";
                        }
                        break;
    
                    case MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS:
                        if(++_captureInfoRetries < 3) {
    
                            _captureStatusTimer.start(500);
    
                        } else {
                            qCDebug(CameraControlLog) << "Giving up requesting capture status";
                        }
                        break;
                    case MAV_CMD_REQUEST_STORAGE_INFORMATION:
                        if(++_storageInfoRetries < 3) {
    
                            QTimer::singleShot(500, this, &QGCCameraControl::_requestStorageInfo);
    
                        } else {
                            qCDebug(CameraControlLog) << "Giving up requesting storage status";
                        }
                        break;
                }
    
    Gus Grubba's avatar
    Gus Grubba committed
            } else {
    
                qCDebug(CameraControlLog) << "Bad response for" << command << result;
    
    Gus Grubba's avatar
    Gus Grubba committed
            }
    
    Gus Grubba's avatar
    Gus Grubba committed
        }
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::_setVideoStatus(VideoStatus status)
    {
        if(_video_status != status) {
            _video_status = status;
            emit videoStatusChanged();
    
    Gus Grubba's avatar
    Gus Grubba committed
    }
    
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::_setPhotoStatus(PhotoStatus status)
    {
        if(_photo_status != status) {
            _photo_status = status;
            emit photoStatusChanged();
        }
    }
    
    
    Gus Grubba's avatar
    Gus Grubba committed
    //-----------------------------------------------------------------------------
    bool
    QGCCameraControl::_loadCameraDefinitionFile(QByteArray& bytes)
    {
    
    Gus Grubba's avatar
    Gus Grubba committed
        QByteArray originalData(bytes);
    
        //-- Handle localization
        if(!_handleLocalization(bytes)) {
            return false;
        }
        int errorLine;
        QString errorMsg;
        QDomDocument doc;
        if(!doc.setContent(bytes, false, &errorMsg, &errorLine)) {
            qCritical() << "Unable to parse camera definition file on line:" << errorLine;
            qCritical() << errorMsg;
            return false;
        }
        //-- Load camera constants
        QDomNodeList defElements = doc.elementsByTagName(kDefnition);
        if(!defElements.size() || !_loadConstants(defElements)) {
    
    Gus Grubba's avatar
    Gus Grubba committed
            qWarning() <<  "Unable to load camera constants from camera definition";
    
            return false;
        }
        //-- Load camera parameters
        QDomNodeList paramElements = doc.elementsByTagName(kParameters);
        if(!paramElements.size() || !_loadSettings(paramElements)) {
    
    Gus Grubba's avatar
    Gus Grubba committed
            qWarning() <<  "Unable to load camera parameters from camera definition";
    
            return false;
        }
    
    Gus Grubba's avatar
    Gus Grubba committed
        //-- If this is new, cache it
        if(!_cached) {
    
            qCDebug(CameraControlLog) << "Saving camera definition file" << _cacheFile;
            QFile file(_cacheFile);
    
    Gus Grubba's avatar
    Gus Grubba committed
            if (!file.open(QIODevice::WriteOnly)) {
    
                qWarning() << QString("Could not save cache file %1. Error: %2").arg(_cacheFile).arg(file.errorString());
    
    Gus Grubba's avatar
    Gus Grubba committed
            } else {
                file.write(originalData);
            }
        }
    
        return true;
    }
    
    //-----------------------------------------------------------------------------
    bool
    QGCCameraControl::_loadConstants(const QDomNodeList nodeList)
    {
        QDomNode node = nodeList.item(0);
        if(!read_attribute(node, kVersion, _version)) {
            return false;
        }
        if(!read_value(node, kModel, _modelName)) {
            return false;
        }
        if(!read_value(node, kVendor, _vendor)) {
            return false;
        }
        return true;
    }
    
    //-----------------------------------------------------------------------------
    bool
    QGCCameraControl::_loadSettings(const QDomNodeList nodeList)
    {
        QDomNode node = nodeList.item(0);
        QDomElement elem = node.toElement();
        QDomNodeList parameters = elem.elementsByTagName(kParameter);
        //-- Pre-process settings (maintain order and skip non-controls)
        for(int i = 0; i < parameters.size(); i++) {
            QDomNode parameterNode = parameters.item(i);
            QString name;
            if(read_attribute(parameterNode, kName, name)) {
                bool control = true;
                read_attribute(parameterNode, kControl, control);
                if(control) {
                    _settings << name;
                }
            } else {
                qCritical() << "Parameter entry missing parameter name";
                return false;
            }
        }
        //-- Load parameters
        for(int i = 0; i < parameters.size(); i++) {
            QDomNode parameterNode = parameters.item(i);
            QString factName;
            read_attribute(parameterNode, kName, factName);
            QString type;
            if(!read_attribute(parameterNode, kType, type)) {
                qCritical() << QString("Parameter %1 missing parameter type").arg(factName);
                return false;
            }
    
            //-- Does it have a control?
            bool control = true;
            read_attribute(parameterNode, kControl, control);
            //-- Is it read only?
            bool readOnly = false;
            read_attribute(parameterNode, kReadOnly, readOnly);
    
            //-- Is it write only?
            bool writeOnly = false;
            read_attribute(parameterNode, kWriteOnly, writeOnly);
            //-- It can't be both
            if(readOnly && writeOnly) {
                qCritical() << QString("Parameter %1 cannot be both read only and write only").arg(factName);
            }
    
            //-- Param type
    
            bool unknownType;
            FactMetaData::ValueType_t factType = FactMetaData::stringToType(type, unknownType);
            if (unknownType) {
                qCritical() << QString("Unknown type for parameter %1").arg(factName);
                return false;
            }
    
            //-- By definition, custom types do not have control
            if(factType == FactMetaData::valueTypeCustom) {
                control = false;
            }
    
            //-- Description
    
            QString description;
            if(!read_value(parameterNode, kDescription, description)) {
                qCritical() << QString("Parameter %1 missing parameter description").arg(factName);
                return false;
            }
    
            //-- Check for updates
            QStringList updates = _loadUpdates(parameterNode);
            if(updates.size()) {
                qCDebug(CameraControlLogVerbose) << "Parameter" << factName << "requires updates for:" << updates;
                _requestUpdates[factName] = updates;
    
            //-- Build metadata
            FactMetaData* metaData = new FactMetaData(factType, factName, this);
    
            QQmlEngine::setObjectOwnership(metaData, QQmlEngine::CppOwnership);
    
            metaData->setShortDescription(description);
            metaData->setLongDescription(description);
    
            metaData->setHasControl(control);
            metaData->setReadOnly(readOnly);
    
            metaData->setWriteOnly(writeOnly);
    
            //-- Options (enums)
            QDomElement optionElem = parameterNode.toElement();
            QDomNodeList optionsRoot = optionElem.elementsByTagName(kOptions);
            if(optionsRoot.size()) {
                //-- Iterate options
                QDomNode node = optionsRoot.item(0);
                QDomElement elem = node.toElement();
                QDomNodeList options = elem.elementsByTagName(kOption);
                for(int i = 0; i < options.size(); i++) {
                    QDomNode option = options.item(i);
                    QString optName;
                    QString optValue;
                    QVariant optVariant;
                    if(!_loadNameValue(option, factName, metaData, optName, optValue, optVariant)) {
                        delete metaData;
                        return false;
                    }
                    metaData->addEnumInfo(optName, optVariant);
                    _originalOptNames[factName]  << optName;
                    _originalOptValues[factName] << optVariant;
                    //-- Check for exclusions
                    QStringList exclusions = _loadExclusions(option);
                    if(exclusions.size()) {
                        qCDebug(CameraControlLogVerbose) << "New exclusions:" << factName << optValue << exclusions;
                        QGCCameraOptionExclusion* pExc = new QGCCameraOptionExclusion(this, factName, optValue, exclusions);
    
                        QQmlEngine::setObjectOwnership(pExc, QQmlEngine::CppOwnership);
    
                        _valueExclusions.append(pExc);
                    }
                    //-- Check for range rules
                    if(!_loadRanges(option, factName, optValue)) {
                        delete metaData;
                        return false;
                    }
                }
            }
            QString defaultValue;
            if(read_attribute(parameterNode, kDefault, defaultValue)) {
                QVariant defaultVariant;
                QString  errorString;
                if (metaData->convertAndValidateRaw(defaultValue, false, defaultVariant, errorString)) {
                    metaData->setRawDefaultValue(defaultVariant);
                } else {
                    qWarning() << "Invalid default value for" << factName
                               << " type:"  << metaData->type()
                               << " value:" << defaultValue
                               << " error:" << errorString;
                }
            }
            //-- Set metadata and Fact
            if (_nameToFactMetaDataMap.contains(factName)) {
                qWarning() << QStringLiteral("Duplicate fact name:") << factName;
                delete metaData;
            } else {
    
                {
                    //-- Check for Min Value
                    QString attr;
                    if(read_attribute(parameterNode, kMin, attr)) {
                        QVariant typedValue;
                        QString  errorString;
                        if (metaData->convertAndValidateRaw(attr, true /* convertOnly */, typedValue, errorString)) {
                            metaData->setRawMin(typedValue);
                        } else {
                            qWarning() << "Invalid min value for" << factName
                                       << " type:"  << metaData->type()
                                       << " value:" << attr
                                       << " error:" << errorString;
                        }
                    }
                }
                {
                    //-- Check for Max Value
                    QString attr;
                    if(read_attribute(parameterNode, kMax, attr)) {
                        QVariant typedValue;
                        QString  errorString;
                        if (metaData->convertAndValidateRaw(attr, true /* convertOnly */, typedValue, errorString)) {
                            metaData->setRawMax(typedValue);
                        } else {
                            qWarning() << "Invalid max value for" << factName
                                       << " type:"  << metaData->type()
                                       << " value:" << attr
                                       << " error:" << errorString;
                        }
                    }
                }
                {
                    //-- Check for Step Value
                    QString attr;
                    if(read_attribute(parameterNode, kStep, attr)) {
                        QVariant typedValue;
                        QString  errorString;
                        if (metaData->convertAndValidateRaw(attr, true /* convertOnly */, typedValue, errorString)) {
    
                            metaData->setRawIncrement(typedValue.toDouble());
    
                        } else {
                            qWarning() << "Invalid step value for" << factName
                                       << " type:"  << metaData->type()
                                       << " value:" << attr
                                       << " error:" << errorString;
                        }
                    }
                }
    
                {
                    //-- Check for Decimal Places
                    QString attr;
                    if(read_attribute(parameterNode, kDecimalPlaces, attr)) {
                        QVariant typedValue;
                        QString  errorString;
                        if (metaData->convertAndValidateRaw(attr, true /* convertOnly */, typedValue, errorString)) {
                            metaData->setDecimalPlaces(typedValue.toInt());
                        } else {
                            qWarning() << "Invalid decimal places value for" << factName
                                       << " type:"  << metaData->type()
                                       << " value:" << attr
                                       << " error:" << errorString;
                        }
                    }
                }
    
                {
                    //-- Check for Units
                    QString attr;
                    if(read_attribute(parameterNode, kUnit, attr)) {
                        metaData->setRawUnits(attr);
                    }
                }
    
                qCDebug(CameraControlLog) << "New parameter:" << factName << (readOnly ? "ReadOnly" : "Writable") << (writeOnly ? "WriteOnly" : "Readable");
    
                _nameToFactMetaDataMap[factName] = metaData;
                Fact* pFact = new Fact(_compID, factName, factType, this);
    
                QQmlEngine::setObjectOwnership(pFact, QQmlEngine::CppOwnership);
    
                pFact->setMetaData(metaData);
    
    Gus Grubba's avatar
    Gus Grubba committed
                pFact->_containerSetRawValue(metaData->rawDefaultValue());
    
                QGCCameraParamIO* pIO = new QGCCameraParamIO(this, pFact, _vehicle);
    
                QQmlEngine::setObjectOwnership(pIO, QQmlEngine::CppOwnership);
    
                _paramIO[factName] = pIO;
                _addFact(pFact, factName);
            }
        }
        if(_nameToFactMetaDataMap.size() > 0) {
            _addFactGroup(this, "camera");
            _processRanges();
            _activeSettings = _settings;
            emit activeSettingsChanged();
            return true;
        }
        return false;
    }
    
    //-----------------------------------------------------------------------------
    bool
    QGCCameraControl::_handleLocalization(QByteArray& bytes)
    {
        QString errorMsg;
        int errorLine;
        QDomDocument doc;
        if(!doc.setContent(bytes, false, &errorMsg, &errorLine)) {
            qCritical() << "Unable to parse camera definition file on line:" << errorLine;
            qCritical() << errorMsg;
            return false;
        }
        //-- Find out where we are
        QLocale locale = QLocale::system();
    
    #if defined (Q_OS_MAC)
    
        locale = QLocale(locale.name());
    #endif
        QString localeName = locale.name().toLower().replace("-", "_");
        qCDebug(CameraControlLog) << "Current locale:" << localeName;
        if(localeName == "en_us") {
            // Nothing to do
            return true;
        }
        QDomNodeList locRoot = doc.elementsByTagName(kLocalization);
        if(!locRoot.size()) {
            // Nothing to do
            return true;
        }
        //-- Iterate locales
        QDomNode node = locRoot.item(0);
        QDomElement elem = node.toElement();
        QDomNodeList locales = elem.elementsByTagName(kLocale);
        for(int i = 0; i < locales.size(); i++) {
            QDomNode locale = locales.item(i);
            QString name;
            if(!read_attribute(locale, kName, name)) {
                qWarning() << "Localization entry is missing its name attribute";
                continue;
            }
            // If we found a direct match, deal with it now
            if(localeName == name.toLower().replace("-", "_")) {
                return _replaceLocaleStrings(locale, bytes);
            }
        }
        //-- No direct match. Pick first matching language (if any)
        localeName = localeName.left(3);
        for(int i = 0; i < locales.size(); i++) {
            QDomNode locale = locales.item(i);
            QString name;
            read_attribute(locale, kName, name);
            if(name.toLower().startsWith(localeName)) {
                return _replaceLocaleStrings(locale, bytes);
            }
        }
        //-- Could not find a language to use
        qWarning() <<  "No match for" << QLocale::system().name() << "in camera definition file";
        //-- Just use default, en_US
        return true;
    }
    
    //-----------------------------------------------------------------------------
    bool
    QGCCameraControl::_replaceLocaleStrings(const QDomNode node, QByteArray& bytes)
    {
        QDomElement stringElem = node.toElement();
        QDomNodeList strings = stringElem.elementsByTagName(kStrings);
        for(int i = 0; i < strings.size(); i++) {
            QDomNode stringNode = strings.item(i);
            QString original;
            QString translated;
            if(read_attribute(stringNode, kOriginal, original)) {
                if(read_attribute(stringNode, kTranslated, translated)) {
                    QString o; o = "\"" + original + "\"";
                    QString t; t = "\"" + translated + "\"";
                    bytes.replace(o.toUtf8(), t.toUtf8());
                    o = ">" + original + "<";
                    t = ">" + translated + "<";
                    bytes.replace(o.toUtf8(), t.toUtf8());
                }
            }
        }
        return true;
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::_requestAllParameters()
    {
        //-- Reset receive list
    
        for(QString paramName: _paramIO.keys()) {
    
            if(_paramIO[paramName]) {
                _paramIO[paramName]->setParamRequest();
            } else {
                qCritical() << "QGCParamIO is NULL" << paramName;
            }
    
        }
        MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol();
        mavlink_message_t msg;
        mavlink_msg_param_ext_request_list_pack_chan(
            mavlink->getSystemId(),
            mavlink->getComponentId(),
            _vehicle->priorityLink()->mavlinkChannel(),
            &msg,
            _vehicle->id(),
            compID());
        _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg);
        qCDebug(CameraControlLogVerbose) << "Request all parameters";
    }
    
    //-----------------------------------------------------------------------------
    QString
    QGCCameraControl::_getParamName(const char* param_id)
    {
        QByteArray bytes(param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
        QString parameterName(bytes);
        return parameterName;
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::handleParamAck(const mavlink_param_ext_ack_t& ack)
    {
        QString paramName = _getParamName(ack.param_id);
        if(!_paramIO.contains(paramName)) {
            qCWarning(CameraControlLog) << "Received PARAM_EXT_ACK for unknown param:" << paramName;
            return;
        }
    
        if(_paramIO[paramName]) {
            _paramIO[paramName]->handleParamAck(ack);
        } else {
            qCritical() << "QGCParamIO is NULL" << paramName;
        }
    
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::handleParamValue(const mavlink_param_ext_value_t& value)
    {
        QString paramName = _getParamName(value.param_id);
        if(!_paramIO.contains(paramName)) {
            qCWarning(CameraControlLog) << "Received PARAM_EXT_VALUE for unknown param:" << paramName;
            return;
        }
    
        if(_paramIO[paramName]) {
            _paramIO[paramName]->handleParamValue(value);
        } else {
            qCritical() << "QGCParamIO is NULL" << paramName;
        }
    
    }
    
    //-----------------------------------------------------------------------------
    void
    QGCCameraControl::_updateActiveList()