<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

#include "RequestManager.h"
#include "NebulaLog.h"

#include "AuthManager.h"

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void RequestManager::ClusterAdd::execute(
    xmlrpc_c::paramList const& paramList,
    xmlrpc_c::value *   const  retval)
{
    string  session;

    int     hid;
    int     clid;
    int     rc;

    const string        method_name = "ClusterAdd";

    Host *      host;
    Cluster *   cluster;

    ostringstream oss;

    /*   -- RPC specific vars --  */
    vector<xmlrpc_c::value> arrayData;
    xmlrpc_c::value_array * arrayresult;

    NebulaLog::log("ReM",Log::DEBUG,"ClusterAdd method invoked");

    // Get the parameters
    session      = xmlrpc_c::value_string(paramList.getString(0));
    hid          = xmlrpc_c::value_int   (paramList.getInt(1));
    clid         = xmlrpc_c::value_int   (paramList.getInt(2));

    //Authenticate the user
    rc = ClusterAdd::upool->authenticate(session);

    if ( rc == -1 )
    {
        goto error_authenticate;
    }

     //Authorize the operation
    if ( rc != 0 ) // rc == 0 means oneadmin
    {
        AuthRequest ar(rc);

        ar.add_auth(AuthRequest::HOST,hid,AuthRequest::MANAGE,0,false);
        ar.add_auth(AuthRequest::CLUSTER,clid,AuthRequest::USE,0,false);

        if (UserPool::authorize(ar) == -1)
        {
            goto error_authorize;
        }
    }

    // Check if host exists
    host = ClusterAdd::hpool->get(hid,true);

    if ( host == 0 )
    {
        goto error_host_get;
    }

    // Check if cluster exists
    cluster = ClusterAdd::cpool->get(clid,true);

    if ( cluster == 0 )
    {
        goto error_cluster_get;
    }

    // Set cluster
    rc = host->set_cluster( cluster->get_name() );

    if ( rc != 0 )
    {
        goto error_cluster_add;
    }

    // Update the DB
    ClusterAdd::hpool->update(host);

    host->unlock();
    cluster->unlock();

    // All nice, return success to the client
    arrayData.push_back(xmlrpc_c::value_boolean(true)); // SUCCESS

    // Copy arrayresult into retval mem space
    arrayresult = new xmlrpc_c::value_array(arrayData);
    *retval = *arrayresult;

    delete arrayresult; // and get rid of the original

    return;

error_authenticate:
    oss.str(authenticate_error(method_name));
    goto error_common;

error_authorize:
    oss.str(authorization_error(method_name, "USE", "CLUSTER", rc, clid));
    goto error_common;

error_host_get:
    oss.str(get_error(method_name, "HOST", hid));
    goto error_common;

error_cluster_get:
    oss.str(get_error(method_name, "CLUSTER", clid));
    goto error_common;

error_cluster_add:
    host->unlock();
    oss.str(action_error(method_name, "USE", "CLUSTER", clid, rc));
    goto error_common;

error_common:

    arrayData.push_back(xmlrpc_c::value_boolean(false)); // FAILURE
    arrayData.push_back(xmlrpc_c::value_string(oss.str()));

    NebulaLog::log("ReM",Log::ERROR,oss);

    xmlrpc_c::value_array arrayresult_error(arrayData);

    *retval = arrayresult_error;

    return;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
<commit_msg>Feature #407: Fix deadlock when adding a Host to a non existing Cluster<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

#include "RequestManager.h"
#include "NebulaLog.h"

#include "AuthManager.h"

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void RequestManager::ClusterAdd::execute(
    xmlrpc_c::paramList const& paramList,
    xmlrpc_c::value *   const  retval)
{
    string  session;

    int     hid;
    int     clid;
    int     rc;

    const string        method_name = "ClusterAdd";

    Host *      host;
    Cluster *   cluster;

    ostringstream oss;

    /*   -- RPC specific vars --  */
    vector<xmlrpc_c::value> arrayData;
    xmlrpc_c::value_array * arrayresult;

    NebulaLog::log("ReM",Log::DEBUG,"ClusterAdd method invoked");

    // Get the parameters
    session      = xmlrpc_c::value_string(paramList.getString(0));
    hid          = xmlrpc_c::value_int   (paramList.getInt(1));
    clid         = xmlrpc_c::value_int   (paramList.getInt(2));

    //Authenticate the user
    rc = ClusterAdd::upool->authenticate(session);

    if ( rc == -1 )
    {
        goto error_authenticate;
    }

     //Authorize the operation
    if ( rc != 0 ) // rc == 0 means oneadmin
    {
        AuthRequest ar(rc);

        ar.add_auth(AuthRequest::HOST,hid,AuthRequest::MANAGE,0,false);
        ar.add_auth(AuthRequest::CLUSTER,clid,AuthRequest::USE,0,false);

        if (UserPool::authorize(ar) == -1)
        {
            goto error_authorize;
        }
    }

    // Check if host exists
    host = ClusterAdd::hpool->get(hid,true);

    if ( host == 0 )
    {
        goto error_host_get;
    }

    // Check if cluster exists
    cluster = ClusterAdd::cpool->get(clid,true);

    if ( cluster == 0 )
    {
        goto error_cluster_get;
    }

    // Set cluster
    rc = host->set_cluster( cluster->get_name() );

    if ( rc != 0 )
    {
        goto error_cluster_add;
    }

    // Update the DB
    ClusterAdd::hpool->update(host);

    host->unlock();
    cluster->unlock();

    // All nice, return success to the client
    arrayData.push_back(xmlrpc_c::value_boolean(true)); // SUCCESS

    // Copy arrayresult into retval mem space
    arrayresult = new xmlrpc_c::value_array(arrayData);
    *retval = *arrayresult;

    delete arrayresult; // and get rid of the original

    return;

error_authenticate:
    oss.str(authenticate_error(method_name));
    goto error_common;

error_authorize:
    oss.str(authorization_error(method_name, "USE", "CLUSTER", rc, clid));
    goto error_common;

error_host_get:
    oss.str(get_error(method_name, "HOST", hid));
    goto error_common;

error_cluster_get:
    host->unlock();
    oss.str(get_error(method_name, "CLUSTER", clid));
    goto error_common;

error_cluster_add:
    host->unlock();
    oss.str(action_error(method_name, "USE", "CLUSTER", clid, rc));
    goto error_common;

error_common:

    arrayData.push_back(xmlrpc_c::value_boolean(false)); // FAILURE
    arrayData.push_back(xmlrpc_c::value_string(oss.str()));

    NebulaLog::log("ReM",Log::ERROR,oss);

    xmlrpc_c::value_array arrayresult_error(arrayData);

    *retval = arrayresult_error;

    return;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
<|endoftext|>


<commit_before>/*
 * Copyright 2006-2008 The FLWOR Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "stdafx.h"

#include "diagnostics/assert.h"
#include "diagnostics/xquery_diagnostics.h"

#include "system/globalenv.h"

#include "context/namespace_context.h"
#include "context/static_context.h"
#include "context/dynamic_context.h"

#include "runtime/access/access.h"

#include "runtime/visitors/planiter_visitor.h"

#include <util/fs_util.h>
#include <util/uri_util.h>
#include <zorbatypes/URI.h>
#include <string>

#include "store/api/item.h"
#include "store/api/iterator.h"
#include "store/api/item_factory.h"
#include "store/api/shared_types.h"
#include "store/api/pul.h"
#include "store/api/index.h"
#include "store/api/annotation.h"
#include "store/api/ic.h"
#include "api/dynamiccontextimpl.h"
#include "api/unmarshaller.h"
#include "api/xqueryimpl.h"

namespace zorba {

  static zstring normalizeInput(zstring const& aUri, static_context* aSctx,
                              QueryLoc const& loc)
  {
    zstring const aBaseUri = aSctx->get_base_uri();
    zstring lResolvedURI;

    try
    {
      // To support the very common (if technically incorrect) use
      // case of users passing local filesystem paths to fn:doc(),
      // we use the following heuristic: IF the base URI has a file:
      // scheme AND the incoming URI has no scheme, we will assume
      // the incoming URI is actually a filesystem path.  QQQ For
      // the moment, we assume any "unknown" schemes are probably
      // Windows drive letters.
      if ((uri::get_scheme(aUri) == uri::none ||
           uri::get_scheme(aUri) == uri::unknown) &&
          uri::get_scheme(aBaseUri) == uri::file)
      {
        // Ok, we assume it's a filesystem path. First normalize it.
        zstring lNormalizedPath =
          fs::get_normalized_path(aUri, zstring(""));
        // QQQ For now, get_normalized_path() doesn't do what we
        // want when base URI represents a file. So, when the
        // normalized path is relative, we pretend it's a relative
        // URI and resolve it as such.
        if (fs::is_absolute(lNormalizedPath))
        {
          URI::encode_file_URI(lNormalizedPath, lResolvedURI);
        }
        else
        {
  #ifdef WIN32
          ascii::replace_all(lNormalizedPath, '\\', '/');
  #endif
          lResolvedURI = aSctx->resolve_relative_uri(lNormalizedPath, true);
        }
      }
      else
      {
        // We do NOT assume it's a filesystem path; just resolve it.
        lResolvedURI = aSctx->resolve_relative_uri(aUri, true);
      }
    }
    catch (ZorbaException& e)
    {
      if (e.diagnostic() == err::XQST0046)
        // the value of a URILiteral is of nonzero length and is not in the
        // lexical space of xs:anyURI.
        e.set_diagnostic(err::FODC0005);
      else
        e.set_diagnostic(err::FODC0002);

      set_source(e, loc);
      throw;
    }

    return lResolvedURI;
  }

  static void streamReleaser(std::istream* aStream)
  {
  }

  static void readDocument(
    zstring const& aUri,
    static_context* aSctx,
    PlanState& aPlanState,
    QueryLoc const& loc,
    store::Item_t& oResult)
  {
    //Normilize input to handle filesystem paths, etc.
    zstring const lNormUri(normalizeInput(aUri, aSctx, loc));

    store::LoadProperties lLoadProperties;

    zstring lErrorMessage;
    std::auto_ptr<internal::Resource> lResource = aSctx->resolve_uri
      (lNormUri, internal::EntityData::SOME_CONTENT, lErrorMessage);

    internal::StreamResource* lStreamResource =
      dynamic_cast<internal::StreamResource*>(lResource.get());

    if(lStreamResource == NULL)
    {
      throw XQUERY_EXCEPTION(err::FOUT1170, ERROR_PARAMS(aUri, lErrorMessage), ERROR_LOC(loc));
    }

    GENV_ITEMFACTORY->createStreamableString(
      oResult,
      *lStreamResource->getStream(),
      *lStreamResource->getStreamReleaser()
      );
    lStreamResource->setStreamReleaser(nullptr);

    if(oResult.isNull())
    {
      throw XQUERY_EXCEPTION(err::FOUT1170, ERROR_PARAMS(aUri), ERROR_LOC(loc));
    }
  }

/*******************************************************************************
  14.8.5 fn:unparsed-text
********************************************************************************/
  bool FnUnparsedTextIterator::nextImpl(store::Item_t& result, PlanState& planState) const
  {
    store::Item_t unparsedText;
    store::Item_t uriItem;
    store::Item_t encodingItem;
    zstring uriString;

    PlanIteratorState* state;
    DEFAULT_STACK_INIT(PlanIteratorState, state, planState);

    if(consumeNext(uriItem, theChildren[0].getp(), planState))
    {
      uriItem->getStringValue2(uriString);
      readDocument(uriString, theSctx, planState, loc, result);
      STACK_PUSH(true, state);
    }
    else
      STACK_PUSH(false, state);

    STACK_END(state);
  }

/*******************************************************************************
  14.8.7 fn:unparsed-text-available
********************************************************************************/

  bool FnUnparsedTextAvailableIterator::nextImpl(store::Item_t& result, PlanState& planState) const
  {
    store::Item_t unparsedText;
    store::Item_t uriItem;
    store::Item_t encodingItem;
    zstring uriString;

    PlanIteratorState* state;
    DEFAULT_STACK_INIT(PlanIteratorState, state, planState);

    if(consumeNext(uriItem, theChildren[0].getp(), planState))
    {
      uriItem->getStringValue2(uriString);
      readDocument(uriString, theSctx, planState, loc, unparsedText);
      STACK_PUSH(GENV_ITEMFACTORY->createBoolean(result, !(unparsedText.isNull()) ), state);
    }
    else
      STACK_PUSH(GENV_ITEMFACTORY->createBoolean(result, false), state);

    STACK_END(state);
  }

}//namespace zorba
<commit_msg>added include file to access_impl.cpp<commit_after>/*
 * Copyright 2006-2008 The FLWOR Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "stdafx.h"

#include "diagnostics/assert.h"
#include "diagnostics/xquery_diagnostics.h"

#include "system/globalenv.h"

#include "context/namespace_context.h"
#include "context/static_context.h"
#include "context/dynamic_context.h"

#include "runtime/access/access.h"

#include "runtime/visitors/planiter_visitor.h"

#include <util/fs_util.h>
#include <util/uri_util.h>
#include <zorbatypes/URI.h>
#include <string>

#include "store/api/item.h"
#include "store/api/iterator.h"
#include "store/api/item_factory.h"
#include "store/api/shared_types.h"
#include "store/api/pul.h"
#include "store/api/index.h"
#include "store/api/annotation.h"
#include "store/api/ic.h"
#include "store/api/load_properties.h"
#include "api/dynamiccontextimpl.h"
#include "api/unmarshaller.h"
#include "api/xqueryimpl.h"

namespace zorba {

  static zstring normalizeInput(zstring const& aUri, static_context* aSctx,
                              QueryLoc const& loc)
  {
    zstring const aBaseUri = aSctx->get_base_uri();
    zstring lResolvedURI;

    try
    {
      // To support the very common (if technically incorrect) use
      // case of users passing local filesystem paths to fn:doc(),
      // we use the following heuristic: IF the base URI has a file:
      // scheme AND the incoming URI has no scheme, we will assume
      // the incoming URI is actually a filesystem path.  QQQ For
      // the moment, we assume any "unknown" schemes are probably
      // Windows drive letters.
      if ((uri::get_scheme(aUri) == uri::none ||
           uri::get_scheme(aUri) == uri::unknown) &&
          uri::get_scheme(aBaseUri) == uri::file)
      {
        // Ok, we assume it's a filesystem path. First normalize it.
        zstring lNormalizedPath =
          fs::get_normalized_path(aUri, zstring(""));
        // QQQ For now, get_normalized_path() doesn't do what we
        // want when base URI represents a file. So, when the
        // normalized path is relative, we pretend it's a relative
        // URI and resolve it as such.
        if (fs::is_absolute(lNormalizedPath))
        {
          URI::encode_file_URI(lNormalizedPath, lResolvedURI);
        }
        else
        {
  #ifdef WIN32
          ascii::replace_all(lNormalizedPath, '\\', '/');
  #endif
          lResolvedURI = aSctx->resolve_relative_uri(lNormalizedPath, true);
        }
      }
      else
      {
        // We do NOT assume it's a filesystem path; just resolve it.
        lResolvedURI = aSctx->resolve_relative_uri(aUri, true);
      }
    }
    catch (ZorbaException& e)
    {
      if (e.diagnostic() == err::XQST0046)
        // the value of a URILiteral is of nonzero length and is not in the
        // lexical space of xs:anyURI.
        e.set_diagnostic(err::FODC0005);
      else
        e.set_diagnostic(err::FODC0002);

      set_source(e, loc);
      throw;
    }

    return lResolvedURI;
  }

  static void streamReleaser(std::istream* aStream)
  {
  }

  static void readDocument(
    zstring const& aUri,
    static_context* aSctx,
    PlanState& aPlanState,
    QueryLoc const& loc,
    store::Item_t& oResult)
  {
    //Normilize input to handle filesystem paths, etc.
    zstring const lNormUri(normalizeInput(aUri, aSctx, loc));

    store::LoadProperties lLoadProperties;

    zstring lErrorMessage;
    std::auto_ptr<internal::Resource> lResource = aSctx->resolve_uri
      (lNormUri, internal::EntityData::SOME_CONTENT, lErrorMessage);

    internal::StreamResource* lStreamResource =
      dynamic_cast<internal::StreamResource*>(lResource.get());

    if(lStreamResource == NULL)
    {
      throw XQUERY_EXCEPTION(err::FOUT1170, ERROR_PARAMS(aUri, lErrorMessage), ERROR_LOC(loc));
    }

    GENV_ITEMFACTORY->createStreamableString(
      oResult,
      *lStreamResource->getStream(),
      *lStreamResource->getStreamReleaser()
      );
    lStreamResource->setStreamReleaser(nullptr);

    if(oResult.isNull())
    {
      throw XQUERY_EXCEPTION(err::FOUT1170, ERROR_PARAMS(aUri), ERROR_LOC(loc));
    }
  }

/*******************************************************************************
  14.8.5 fn:unparsed-text
********************************************************************************/
  bool FnUnparsedTextIterator::nextImpl(store::Item_t& result, PlanState& planState) const
  {
    store::Item_t unparsedText;
    store::Item_t uriItem;
    store::Item_t encodingItem;
    zstring uriString;

    PlanIteratorState* state;
    DEFAULT_STACK_INIT(PlanIteratorState, state, planState);

    if(consumeNext(uriItem, theChildren[0].getp(), planState))
    {
      uriItem->getStringValue2(uriString);
      readDocument(uriString, theSctx, planState, loc, result);
      STACK_PUSH(true, state);
    }
    else
      STACK_PUSH(false, state);

    STACK_END(state);
  }

/*******************************************************************************
  14.8.7 fn:unparsed-text-available
********************************************************************************/

  bool FnUnparsedTextAvailableIterator::nextImpl(store::Item_t& result, PlanState& planState) const
  {
    store::Item_t unparsedText;
    store::Item_t uriItem;
    store::Item_t encodingItem;
    zstring uriString;

    PlanIteratorState* state;
    DEFAULT_STACK_INIT(PlanIteratorState, state, planState);

    if(consumeNext(uriItem, theChildren[0].getp(), planState))
    {
      uriItem->getStringValue2(uriString);
      readDocument(uriString, theSctx, planState, loc, unparsedText);
      STACK_PUSH(GENV_ITEMFACTORY->createBoolean(result, !(unparsedText.isNull()) ), state);
    }
    else
      STACK_PUSH(GENV_ITEMFACTORY->createBoolean(result, false), state);

    STACK_END(state);
  }

}//namespace zorba
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "consoleprocess_p.h"

#include "qtcprocess.h"

#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>

#include <QCoreApplication>
#include <QTimer>

#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

namespace Utils {

ConsoleProcessPrivate::ConsoleProcessPrivate() :
    m_mode(ConsoleProcess::Run),
    m_appPid(0),
    m_stubSocket(0),
    m_tempFile(0),
    m_settings(0),
    m_stubConnected(false),
    m_stubPid(0),
    m_stubConnectTimer(0)
{
}

ConsoleProcess::ConsoleProcess(QObject *parent)  :
    QObject(parent), d(new ConsoleProcessPrivate)
{
    connect(&d->m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));

    d->m_process.setProcessChannelMode(QProcess::ForwardedChannels);
}

qint64 ConsoleProcess::applicationMainThreadID() const
{
    QTC_CHECK(false);
    return -1;
}

void ConsoleProcess::setSettings(QSettings *settings)
{
    d->m_settings = settings;
}

bool ConsoleProcess::start(const QString &program, const QString &args)
{
    if (isRunning())
        return false;

    QtcProcess::SplitError perr;
    QtcProcess::Arguments pargs = QtcProcess::prepareArgs(args, &perr, HostOsInfo::hostOs(),
                                                          &d->m_environment, &d->m_workingDir);
    QString pcmd;
    if (perr == QtcProcess::SplitOk) {
        pcmd = program;
    } else {
        if (perr != QtcProcess::FoundMeta) {
            emit processError(tr("Quoting error in command."));
            return false;
        }
        if (d->m_mode == Debug) {
            // FIXME: QTCREATORBUG-2809
            emit processError(tr("Debugging complex shell commands in a terminal"
                                 " is currently not supported."));
            return false;
        }
        pcmd = QLatin1String("/bin/sh");
        pargs = QtcProcess::Arguments::createUnixArgs(QStringList()
                    << QLatin1String("-c")
                    << (QtcProcess::quoteArg(program) + QLatin1Char(' ') + args));
    }

    QtcProcess::SplitError qerr;
    QtcProcess::Arguments xtermArgs = QtcProcess::prepareArgs(terminalEmulator(d->m_settings), &qerr,
                                                              HostOsInfo::hostOs(),
                                                              &d->m_environment, &d->m_workingDir);
    if (qerr != QtcProcess::SplitOk) {
        emit processError(qerr == QtcProcess::BadQuoting
                          ? tr("Quoting error in terminal command.")
                          : tr("Terminal command may not be a shell command."));
        return false;
    }

    const QString err = stubServerListen();
    if (!err.isEmpty()) {
        emit processError(msgCommChannelFailed(err));
        return false;
    }

    QStringList env = d->m_environment.toStringList();
    if (!env.isEmpty()) {
        d->m_tempFile = new QTemporaryFile();
        if (!d->m_tempFile->open()) {
            stubServerShutdown();
            emit processError(msgCannotCreateTempFile(d->m_tempFile->errorString()));
            delete d->m_tempFile;
            d->m_tempFile = 0;
            return false;
        }
        QByteArray contents;
        foreach (const QString &var, env) {
            QByteArray l8b = var.toLocal8Bit();
            contents.append(l8b.constData(), l8b.size() + 1);
        }
        if (d->m_tempFile->write(contents) != contents.size() || !d->m_tempFile->flush()) {
            stubServerShutdown();
            emit processError(msgCannotWriteTempFile());
            delete d->m_tempFile;
            d->m_tempFile = 0;
            return false;
        }
    }

    QString stubPath = QCoreApplication::applicationDirPath();
    if (Utils::HostOsInfo::isMacHost())
        stubPath.append(QLatin1String("/../Resources/qtcreator_process_stub"));
    else
        stubPath.append(QLatin1String("/qtcreator_process_stub"));

    QStringList allArgs = xtermArgs.toUnixArgs();
    allArgs << stubPath
              << modeOption(d->m_mode)
              << d->m_stubServer.fullServerName()
              << msgPromptToClose()
              << workingDirectory()
              << (d->m_tempFile ? d->m_tempFile->fileName() : QString())
              << pcmd << pargs.toUnixArgs();

    QString xterm = allArgs.takeFirst();
    d->m_process.start(xterm, allArgs);
    if (!d->m_process.waitForStarted()) {
        stubServerShutdown();
        emit processError(tr("Cannot start the terminal emulator '%1', change the setting in the "
                             "Environment options.").arg(xterm));
        delete d->m_tempFile;
        d->m_tempFile = 0;
        return false;
    }
    d->m_stubConnectTimer = new QTimer(this);
    connect(d->m_stubConnectTimer, SIGNAL(timeout()), SLOT(stop()));
    d->m_stubConnectTimer->setSingleShot(true);
    d->m_stubConnectTimer->start(10000);
    d->m_executable = program;
    return true;
}

void ConsoleProcess::killProcess()
{
    if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
        d->m_stubSocket->write("k", 1);
        d->m_stubSocket->flush();
    }
    d->m_appPid = 0;
}

void ConsoleProcess::killStub()
{
    if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
        d->m_stubSocket->write("s", 1);
        d->m_stubSocket->flush();
    }
    stubServerShutdown();
    d->m_stubPid = 0;
}

void ConsoleProcess::detachStub()
{
    if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
        d->m_stubSocket->write("d", 1);
        d->m_stubSocket->flush();
    }
    stubServerShutdown();
    d->m_stubPid = 0;
}

void ConsoleProcess::stop()
{
    killProcess();
    killStub();
    if (isRunning()) {
        d->m_process.terminate();
        if (!d->m_process.waitForFinished(1000)) {
            d->m_process.kill();
            d->m_process.waitForFinished();
        }
    }
}

bool ConsoleProcess::isRunning() const
{
    return d->m_process.state() != QProcess::NotRunning
            || (d->m_stubSocket && d->m_stubSocket->isOpen());
}

QString ConsoleProcess::stubServerListen()
{
    // We need to put the socket in a private directory, as some systems simply do not
    // check the file permissions of sockets.
    QString stubFifoDir;
    forever {
        {
            QTemporaryFile tf;
            if (!tf.open())
                return msgCannotCreateTempFile(tf.errorString());
            stubFifoDir = tf.fileName();
        }
        // By now the temp file was deleted again
        d->m_stubServerDir = QFile::encodeName(stubFifoDir);
        if (!::mkdir(d->m_stubServerDir.constData(), 0700))
            break;
        if (errno != EEXIST)
            return msgCannotCreateTempDir(stubFifoDir, QString::fromLocal8Bit(strerror(errno)));
    }
    const QString stubServer  = stubFifoDir + QLatin1String("/stub-socket");
    if (!d->m_stubServer.listen(stubServer)) {
        ::rmdir(d->m_stubServerDir.constData());
        return tr("Cannot create socket '%1': %2").arg(stubServer, d->m_stubServer.errorString());
    }
    return QString();
}

void ConsoleProcess::stubServerShutdown()
{
    if (d->m_stubSocket) {
        readStubOutput(); // we could get the shutdown signal before emptying the buffer
        d->m_stubSocket->disconnect(); // avoid getting queued readyRead signals
        d->m_stubSocket->deleteLater(); // we might be called from the disconnected signal of m_stubSocket
    }
    d->m_stubSocket = 0;
    if (d->m_stubServer.isListening()) {
        d->m_stubServer.close();
        ::rmdir(d->m_stubServerDir.constData());
    }
}

void ConsoleProcess::stubConnectionAvailable()
{
    if (d->m_stubConnectTimer) {
        delete d->m_stubConnectTimer;
        d->m_stubConnectTimer = 0;
    }
    d->m_stubConnected = true;
    emit stubStarted();
    d->m_stubSocket = d->m_stubServer.nextPendingConnection();
    connect(d->m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput()));
    connect(d->m_stubSocket, SIGNAL(disconnected()), SLOT(stubExited()));
}

static QString errorMsg(int code)
{
    return QString::fromLocal8Bit(strerror(code));
}

void ConsoleProcess::readStubOutput()
{
    while (d->m_stubSocket->canReadLine()) {
        QByteArray out = d->m_stubSocket->readLine();
        out.chop(1); // \n
        if (out.startsWith("err:chdir ")) {
            emit processError(msgCannotChangeToWorkDir(workingDirectory(), errorMsg(out.mid(10).toInt())));
        } else if (out.startsWith("err:exec ")) {
            emit processError(msgCannotExecute(d->m_executable, errorMsg(out.mid(9).toInt())));
        } else if (out.startsWith("spid ")) {
            delete d->m_tempFile;
            d->m_tempFile = 0;

            d->m_stubPid = out.mid(4).toInt();
        } else if (out.startsWith("pid ")) {
            d->m_appPid = out.mid(4).toInt();
            emit processStarted();
        } else if (out.startsWith("exit ")) {
            d->m_appStatus = QProcess::NormalExit;
            d->m_appCode = out.mid(5).toInt();
            d->m_appPid = 0;
            emit processStopped(d->m_appCode, d->m_appStatus);
        } else if (out.startsWith("crash ")) {
            d->m_appStatus = QProcess::CrashExit;
            d->m_appCode = out.mid(6).toInt();
            d->m_appPid = 0;
            emit processStopped(d->m_appCode, d->m_appStatus);
        } else {
            emit processError(msgUnexpectedOutput(out));
            d->m_stubPid = 0;
            d->m_process.terminate();
            break;
        }
    }
}

void ConsoleProcess::stubExited()
{
    // The stub exit might get noticed before we read the error status.
    if (d->m_stubSocket && d->m_stubSocket->state() == QLocalSocket::ConnectedState)
        d->m_stubSocket->waitForDisconnected();
    stubServerShutdown();
    d->m_stubPid = 0;
    delete d->m_tempFile;
    d->m_tempFile = 0;
    if (d->m_appPid) {
        d->m_appStatus = QProcess::CrashExit;
        d->m_appCode = -1;
        d->m_appPid = 0;
        emit processStopped(d->m_appCode, d->m_appStatus); // Maybe it actually did not, but keep state consistent
    }
    emit stubStopped();
}

struct Terminal {
    const char *binary;
    const char *options;
};

static const Terminal knownTerminals[] =
{
    {"xterm", "-e"},
    {"aterm", "-e"},
    {"Eterm", "-e"},
    {"rxvt", "-e"},
    {"urxvt", "-e"},
    {"xfce4-terminal", "-x"},
    {"konsole", "-e"},
    {"gnome-terminal", "-x"}
};

QString ConsoleProcess::defaultTerminalEmulator()
{
    if (Utils::HostOsInfo::isMacHost()) {
        QString termCmd = QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/scripts/openTerminal.command");
        if (QFile(termCmd).exists())
            return termCmd.replace(QLatin1Char(' '), QLatin1String("\\ "));
        return QLatin1String("/usr/X11/bin/xterm");
    }
    const Environment env = Environment::systemEnvironment();
    const int terminalCount = int(sizeof(knownTerminals) / sizeof(knownTerminals[0]));
    for (int i = 0; i < terminalCount; ++i) {
        QString result = env.searchInPath(QLatin1String(knownTerminals[i].binary));
        if (!result.isEmpty()) {
            result += QLatin1Char(' ');
            result += QLatin1String(knownTerminals[i].options);
            return result;
        }
    }
    return QLatin1String("xterm -e");
}

QStringList ConsoleProcess::availableTerminalEmulators()
{
    QStringList result;
    const Environment env = Environment::systemEnvironment();
    const int terminalCount = int(sizeof(knownTerminals) / sizeof(knownTerminals[0]));
    for (int i = 0; i < terminalCount; ++i) {
        QString terminal = env.searchInPath(QLatin1String(knownTerminals[i].binary));
        if (!terminal.isEmpty()) {
            terminal += QLatin1Char(' ');
            terminal += QLatin1String(knownTerminals[i].options);
            result.push_back(terminal);
        }
    }
    if (!result.contains(defaultTerminalEmulator()))
        result.append(defaultTerminalEmulator());
    result.sort();
    return result;
}

} // namespace Utils
<commit_msg>ConsoleProcess: Remove assert<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "consoleprocess_p.h"

#include "qtcprocess.h"

#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>

#include <QCoreApplication>
#include <QTimer>

#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

namespace Utils {

ConsoleProcessPrivate::ConsoleProcessPrivate() :
    m_mode(ConsoleProcess::Run),
    m_appPid(0),
    m_stubSocket(0),
    m_tempFile(0),
    m_settings(0),
    m_stubConnected(false),
    m_stubPid(0),
    m_stubConnectTimer(0)
{
}

ConsoleProcess::ConsoleProcess(QObject *parent)  :
    QObject(parent), d(new ConsoleProcessPrivate)
{
    connect(&d->m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));

    d->m_process.setProcessChannelMode(QProcess::ForwardedChannels);
}

qint64 ConsoleProcess::applicationMainThreadID() const
{
    return -1;
}

void ConsoleProcess::setSettings(QSettings *settings)
{
    d->m_settings = settings;
}

bool ConsoleProcess::start(const QString &program, const QString &args)
{
    if (isRunning())
        return false;

    QtcProcess::SplitError perr;
    QtcProcess::Arguments pargs = QtcProcess::prepareArgs(args, &perr, HostOsInfo::hostOs(),
                                                          &d->m_environment, &d->m_workingDir);
    QString pcmd;
    if (perr == QtcProcess::SplitOk) {
        pcmd = program;
    } else {
        if (perr != QtcProcess::FoundMeta) {
            emit processError(tr("Quoting error in command."));
            return false;
        }
        if (d->m_mode == Debug) {
            // FIXME: QTCREATORBUG-2809
            emit processError(tr("Debugging complex shell commands in a terminal"
                                 " is currently not supported."));
            return false;
        }
        pcmd = QLatin1String("/bin/sh");
        pargs = QtcProcess::Arguments::createUnixArgs(QStringList()
                    << QLatin1String("-c")
                    << (QtcProcess::quoteArg(program) + QLatin1Char(' ') + args));
    }

    QtcProcess::SplitError qerr;
    QtcProcess::Arguments xtermArgs = QtcProcess::prepareArgs(terminalEmulator(d->m_settings), &qerr,
                                                              HostOsInfo::hostOs(),
                                                              &d->m_environment, &d->m_workingDir);
    if (qerr != QtcProcess::SplitOk) {
        emit processError(qerr == QtcProcess::BadQuoting
                          ? tr("Quoting error in terminal command.")
                          : tr("Terminal command may not be a shell command."));
        return false;
    }

    const QString err = stubServerListen();
    if (!err.isEmpty()) {
        emit processError(msgCommChannelFailed(err));
        return false;
    }

    QStringList env = d->m_environment.toStringList();
    if (!env.isEmpty()) {
        d->m_tempFile = new QTemporaryFile();
        if (!d->m_tempFile->open()) {
            stubServerShutdown();
            emit processError(msgCannotCreateTempFile(d->m_tempFile->errorString()));
            delete d->m_tempFile;
            d->m_tempFile = 0;
            return false;
        }
        QByteArray contents;
        foreach (const QString &var, env) {
            QByteArray l8b = var.toLocal8Bit();
            contents.append(l8b.constData(), l8b.size() + 1);
        }
        if (d->m_tempFile->write(contents) != contents.size() || !d->m_tempFile->flush()) {
            stubServerShutdown();
            emit processError(msgCannotWriteTempFile());
            delete d->m_tempFile;
            d->m_tempFile = 0;
            return false;
        }
    }

    QString stubPath = QCoreApplication::applicationDirPath();
    if (Utils::HostOsInfo::isMacHost())
        stubPath.append(QLatin1String("/../Resources/qtcreator_process_stub"));
    else
        stubPath.append(QLatin1String("/qtcreator_process_stub"));

    QStringList allArgs = xtermArgs.toUnixArgs();
    allArgs << stubPath
              << modeOption(d->m_mode)
              << d->m_stubServer.fullServerName()
              << msgPromptToClose()
              << workingDirectory()
              << (d->m_tempFile ? d->m_tempFile->fileName() : QString())
              << pcmd << pargs.toUnixArgs();

    QString xterm = allArgs.takeFirst();
    d->m_process.start(xterm, allArgs);
    if (!d->m_process.waitForStarted()) {
        stubServerShutdown();
        emit processError(tr("Cannot start the terminal emulator '%1', change the setting in the "
                             "Environment options.").arg(xterm));
        delete d->m_tempFile;
        d->m_tempFile = 0;
        return false;
    }
    d->m_stubConnectTimer = new QTimer(this);
    connect(d->m_stubConnectTimer, SIGNAL(timeout()), SLOT(stop()));
    d->m_stubConnectTimer->setSingleShot(true);
    d->m_stubConnectTimer->start(10000);
    d->m_executable = program;
    return true;
}

void ConsoleProcess::killProcess()
{
    if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
        d->m_stubSocket->write("k", 1);
        d->m_stubSocket->flush();
    }
    d->m_appPid = 0;
}

void ConsoleProcess::killStub()
{
    if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
        d->m_stubSocket->write("s", 1);
        d->m_stubSocket->flush();
    }
    stubServerShutdown();
    d->m_stubPid = 0;
}

void ConsoleProcess::detachStub()
{
    if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
        d->m_stubSocket->write("d", 1);
        d->m_stubSocket->flush();
    }
    stubServerShutdown();
    d->m_stubPid = 0;
}

void ConsoleProcess::stop()
{
    killProcess();
    killStub();
    if (isRunning()) {
        d->m_process.terminate();
        if (!d->m_process.waitForFinished(1000)) {
            d->m_process.kill();
            d->m_process.waitForFinished();
        }
    }
}

bool ConsoleProcess::isRunning() const
{
    return d->m_process.state() != QProcess::NotRunning
            || (d->m_stubSocket && d->m_stubSocket->isOpen());
}

QString ConsoleProcess::stubServerListen()
{
    // We need to put the socket in a private directory, as some systems simply do not
    // check the file permissions of sockets.
    QString stubFifoDir;
    forever {
        {
            QTemporaryFile tf;
            if (!tf.open())
                return msgCannotCreateTempFile(tf.errorString());
            stubFifoDir = tf.fileName();
        }
        // By now the temp file was deleted again
        d->m_stubServerDir = QFile::encodeName(stubFifoDir);
        if (!::mkdir(d->m_stubServerDir.constData(), 0700))
            break;
        if (errno != EEXIST)
            return msgCannotCreateTempDir(stubFifoDir, QString::fromLocal8Bit(strerror(errno)));
    }
    const QString stubServer  = stubFifoDir + QLatin1String("/stub-socket");
    if (!d->m_stubServer.listen(stubServer)) {
        ::rmdir(d->m_stubServerDir.constData());
        return tr("Cannot create socket '%1': %2").arg(stubServer, d->m_stubServer.errorString());
    }
    return QString();
}

void ConsoleProcess::stubServerShutdown()
{
    if (d->m_stubSocket) {
        readStubOutput(); // we could get the shutdown signal before emptying the buffer
        d->m_stubSocket->disconnect(); // avoid getting queued readyRead signals
        d->m_stubSocket->deleteLater(); // we might be called from the disconnected signal of m_stubSocket
    }
    d->m_stubSocket = 0;
    if (d->m_stubServer.isListening()) {
        d->m_stubServer.close();
        ::rmdir(d->m_stubServerDir.constData());
    }
}

void ConsoleProcess::stubConnectionAvailable()
{
    if (d->m_stubConnectTimer) {
        delete d->m_stubConnectTimer;
        d->m_stubConnectTimer = 0;
    }
    d->m_stubConnected = true;
    emit stubStarted();
    d->m_stubSocket = d->m_stubServer.nextPendingConnection();
    connect(d->m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput()));
    connect(d->m_stubSocket, SIGNAL(disconnected()), SLOT(stubExited()));
}

static QString errorMsg(int code)
{
    return QString::fromLocal8Bit(strerror(code));
}

void ConsoleProcess::readStubOutput()
{
    while (d->m_stubSocket->canReadLine()) {
        QByteArray out = d->m_stubSocket->readLine();
        out.chop(1); // \n
        if (out.startsWith("err:chdir ")) {
            emit processError(msgCannotChangeToWorkDir(workingDirectory(), errorMsg(out.mid(10).toInt())));
        } else if (out.startsWith("err:exec ")) {
            emit processError(msgCannotExecute(d->m_executable, errorMsg(out.mid(9).toInt())));
        } else if (out.startsWith("spid ")) {
            delete d->m_tempFile;
            d->m_tempFile = 0;

            d->m_stubPid = out.mid(4).toInt();
        } else if (out.startsWith("pid ")) {
            d->m_appPid = out.mid(4).toInt();
            emit processStarted();
        } else if (out.startsWith("exit ")) {
            d->m_appStatus = QProcess::NormalExit;
            d->m_appCode = out.mid(5).toInt();
            d->m_appPid = 0;
            emit processStopped(d->m_appCode, d->m_appStatus);
        } else if (out.startsWith("crash ")) {
            d->m_appStatus = QProcess::CrashExit;
            d->m_appCode = out.mid(6).toInt();
            d->m_appPid = 0;
            emit processStopped(d->m_appCode, d->m_appStatus);
        } else {
            emit processError(msgUnexpectedOutput(out));
            d->m_stubPid = 0;
            d->m_process.terminate();
            break;
        }
    }
}

void ConsoleProcess::stubExited()
{
    // The stub exit might get noticed before we read the error status.
    if (d->m_stubSocket && d->m_stubSocket->state() == QLocalSocket::ConnectedState)
        d->m_stubSocket->waitForDisconnected();
    stubServerShutdown();
    d->m_stubPid = 0;
    delete d->m_tempFile;
    d->m_tempFile = 0;
    if (d->m_appPid) {
        d->m_appStatus = QProcess::CrashExit;
        d->m_appCode = -1;
        d->m_appPid = 0;
        emit processStopped(d->m_appCode, d->m_appStatus); // Maybe it actually did not, but keep state consistent
    }
    emit stubStopped();
}

struct Terminal {
    const char *binary;
    const char *options;
};

static const Terminal knownTerminals[] =
{
    {"xterm", "-e"},
    {"aterm", "-e"},
    {"Eterm", "-e"},
    {"rxvt", "-e"},
    {"urxvt", "-e"},
    {"xfce4-terminal", "-x"},
    {"konsole", "-e"},
    {"gnome-terminal", "-x"}
};

QString ConsoleProcess::defaultTerminalEmulator()
{
    if (Utils::HostOsInfo::isMacHost()) {
        QString termCmd = QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/scripts/openTerminal.command");
        if (QFile(termCmd).exists())
            return termCmd.replace(QLatin1Char(' '), QLatin1String("\\ "));
        return QLatin1String("/usr/X11/bin/xterm");
    }
    const Environment env = Environment::systemEnvironment();
    const int terminalCount = int(sizeof(knownTerminals) / sizeof(knownTerminals[0]));
    for (int i = 0; i < terminalCount; ++i) {
        QString result = env.searchInPath(QLatin1String(knownTerminals[i].binary));
        if (!result.isEmpty()) {
            result += QLatin1Char(' ');
            result += QLatin1String(knownTerminals[i].options);
            return result;
        }
    }
    return QLatin1String("xterm -e");
}

QStringList ConsoleProcess::availableTerminalEmulators()
{
    QStringList result;
    const Environment env = Environment::systemEnvironment();
    const int terminalCount = int(sizeof(knownTerminals) / sizeof(knownTerminals[0]));
    for (int i = 0; i < terminalCount; ++i) {
        QString terminal = env.searchInPath(QLatin1String(knownTerminals[i].binary));
        if (!terminal.isEmpty()) {
            terminal += QLatin1Char(' ');
            terminal += QLatin1String(knownTerminals[i].options);
            result.push_back(terminal);
        }
    }
    if (!result.contains(defaultTerminalEmulator()))
        result.append(defaultTerminalEmulator());
    result.sort();
    return result;
}

} // namespace Utils
<|endoftext|>


<commit_before>/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "CameraCalc.h"
#include "JsonHelper.h"
#include "Vehicle.h"
#include "CameraMetaData.h"

#include <QQmlEngine>

const char* CameraCalc::cameraNameName =                    "CameraName";
const char* CameraCalc::valueSetIsDistanceName =            "ValueSetIsDistance";
const char* CameraCalc::distanceToSurfaceName =             "DistanceToSurface";
const char* CameraCalc::distanceToSurfaceRelativeName =     "DistanceToSurfaceRelative";
const char* CameraCalc::imageDensityName =                  "ImageDensity";
const char* CameraCalc::frontalOverlapName =                "FrontalOverlap";
const char* CameraCalc::sideOverlapName =                   "SideOverlap";
const char* CameraCalc::adjustedFootprintFrontalName =      "AdjustedFootprintFrontal";
const char* CameraCalc::adjustedFootprintSideName =         "AdjustedFootprintSide";

const char* CameraCalc::_jsonCameraSpecTypeKey =            "CameraSpecType";

CameraCalc::CameraCalc(Vehicle* vehicle, const QString& settingsGroup, QObject* parent)
    : CameraSpec                    (settingsGroup, parent)
    , _vehicle                      (vehicle)
    , _dirty                        (false)
    , _disableRecalc                (false)
    , _distanceToSurfaceRelative    (true)
    , _metaDataMap                  (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraCalc.FactMetaData.json"), this))
    , _cameraNameFact               (settingsGroup, _metaDataMap[cameraNameName])
    , _valueSetIsDistanceFact       (settingsGroup, _metaDataMap[valueSetIsDistanceName])
    , _distanceToSurfaceFact        (settingsGroup, _metaDataMap[distanceToSurfaceName])
    , _imageDensityFact             (settingsGroup, _metaDataMap[imageDensityName])
    , _frontalOverlapFact           (settingsGroup, _metaDataMap[frontalOverlapName])
    , _sideOverlapFact              (settingsGroup, _metaDataMap[sideOverlapName])
    , _adjustedFootprintSideFact    (settingsGroup, _metaDataMap[adjustedFootprintSideName])
    , _adjustedFootprintFrontalFact (settingsGroup, _metaDataMap[adjustedFootprintFrontalName])
    , _imageFootprintSide           (0)
    , _imageFootprintFrontal        (0)
    , _knownCameraList              (_vehicle->staticCameraList())
{
    QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);

    connect(&_valueSetIsDistanceFact,       &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_distanceToSurfaceFact,        &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_imageDensityFact,             &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_frontalOverlapFact,           &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_sideOverlapFact,              &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_adjustedFootprintSideFact,    &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_adjustedFootprintFrontalFact, &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_cameraNameFact,               &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(this,                           &CameraCalc::distanceToSurfaceRelativeChanged,  this, &CameraCalc::_setDirty);

    connect(&_cameraNameFact,               &Fact::valueChanged,                            this, &CameraCalc::_cameraNameChanged);
    connect(&_cameraNameFact,               &Fact::valueChanged,                            this, &CameraCalc::isManualCameraChanged);
    connect(&_cameraNameFact,               &Fact::valueChanged,                            this, &CameraCalc::isCustomCameraChanged);

    connect(&_distanceToSurfaceFact,    &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(&_imageDensityFact,         &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(&_frontalOverlapFact,       &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(&_sideOverlapFact,          &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(sensorWidth(),              &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(sensorHeight(),             &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(imageWidth(),               &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(imageHeight(),              &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(focalLength(),              &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(landscape(),                &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);

    _cameraNameChanged();

    setDirty(false);
}

void CameraCalc::setDirty(bool dirty)
{
    if (_dirty != dirty) {
        _dirty = dirty;
        emit dirtyChanged(_dirty);
    }
}

void CameraCalc::_cameraNameChanged(void)
{
    if (_disableRecalc) {
        return;
    }

    QString cameraName = _cameraNameFact.rawValue().toString();

    // Validate known camera name
    bool foundKnownCamera = false;
    CameraMetaData* cameraMetaData = nullptr;
    if (!isManualCamera() && !isCustomCamera()) {
        for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
            cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
            if (cameraName == cameraMetaData->name()) {
                foundKnownCamera = true;
                break;
            }
        }

        if (!foundKnownCamera) {
            // This will cause another camera changed signal which will recurse back to this routine
            _cameraNameFact.setRawValue(customCameraName());
            return;
        }
    }

    _disableRecalc = true;
    if (foundKnownCamera) {
        sensorWidth()->setRawValue          (cameraMetaData->sensorWidth());
        sensorHeight()->setRawValue         (cameraMetaData->sensorHeight());
        imageWidth()->setRawValue           (cameraMetaData->imageWidth());
        imageHeight()->setRawValue          (cameraMetaData->imageHeight());
        focalLength()->setRawValue          (cameraMetaData->focalLength());
        landscape()->setRawValue            (cameraMetaData->landscape());
        fixedOrientation()->setRawValue     (cameraMetaData->fixedOrientation());
        minTriggerInterval()->setRawValue   (cameraMetaData->minTriggerInterval());
    } else {
        if (isManualCamera() || isCustomCamera()) {
            // These values are unknown for these types
            fixedOrientation()->setRawValue(false);
            minTriggerInterval()->setRawValue(0);
            if (isManualCamera() && !valueSetIsDistance()->rawValue().toBool()) {
                valueSetIsDistance()->setRawValue(true);
            }
        } else {
            qWarning() << "Internal Error: Not known camera, but now manual or custom either";
        }
    }
    _disableRecalc = false;

    _recalcTriggerDistance();
    _adjustDistanceToSurfaceRelative();
}

void CameraCalc::_recalcTriggerDistance(void)
{
    if (_disableRecalc || isManualCamera()) {
        return;
    }

    _disableRecalc = true;

    double focalLength =    this->focalLength()->rawValue().toDouble();
    double sensorWidth =    this->sensorWidth()->rawValue().toDouble();
    double sensorHeight =   this->sensorHeight()->rawValue().toDouble();
    double imageWidth =     this->imageWidth()->rawValue().toDouble();
    double imageHeight =    this->imageHeight()->rawValue().toDouble();
    double imageDensity =   _imageDensityFact.rawValue().toDouble();

    if (focalLength <= 0 || sensorWidth <= 0 || sensorHeight <= 0 || imageWidth <= 0 || imageHeight <= 0 || imageDensity <= 0) {
        return;
    }

    if (_valueSetIsDistanceFact.rawValue().toBool()) {
        _imageDensityFact.setRawValue((_distanceToSurfaceFact.rawValue().toDouble() * sensorWidth * 100.0) / (imageWidth * focalLength));
    } else {
        _distanceToSurfaceFact.setRawValue((imageWidth * _imageDensityFact.rawValue().toDouble() * focalLength) / (sensorWidth * 100.0));
    }

    imageDensity = _imageDensityFact.rawValue().toDouble();

    if (landscape()->rawValue().toBool()) {
        _imageFootprintSide =       (imageWidth  * imageDensity) / 100.0;
        _imageFootprintFrontal =    (imageHeight * imageDensity) / 100.0;
    } else {
        _imageFootprintSide  =      (imageHeight * imageDensity) / 100.0;
        _imageFootprintFrontal =    (imageWidth  * imageDensity) / 100.0;
    }
    _adjustedFootprintSideFact.setRawValue      (_imageFootprintSide * ((100.0 - _sideOverlapFact.rawValue().toDouble()) / 100.0));
    _adjustedFootprintFrontalFact.setRawValue   (_imageFootprintFrontal * ((100.0 - _frontalOverlapFact.rawValue().toDouble()) / 100.0));

    emit imageFootprintSideChanged      (_imageFootprintSide);
    emit imageFootprintFrontalChanged   (_imageFootprintFrontal);

    _disableRecalc = false;
}

void CameraCalc::save(QJsonObject& json) const
{
    json[JsonHelper::jsonVersionKey] =      1;
    json[adjustedFootprintSideName] =       _adjustedFootprintSideFact.rawValue().toDouble();
    json[adjustedFootprintFrontalName] =    _adjustedFootprintFrontalFact.rawValue().toDouble();
    json[distanceToSurfaceName] =           _distanceToSurfaceFact.rawValue().toDouble();
    json[distanceToSurfaceRelativeName] =   _distanceToSurfaceRelative;
    json[cameraNameName] =                  _cameraNameFact.rawValue().toString();

    if (!isManualCamera()) {
        CameraSpec::save(json);
        json[valueSetIsDistanceName] = _valueSetIsDistanceFact.rawValue().toBool();
        json[imageDensityName] =       _imageDensityFact.rawValue().toDouble();
        json[frontalOverlapName] =     _frontalOverlapFact.rawValue().toDouble();
        json[sideOverlapName] =        _sideOverlapFact.rawValue().toDouble();
    }
}

bool CameraCalc::load(const QJsonObject& json, QString& errorString)
{
    QJsonObject v1Json = json;

    if (!json.contains(JsonHelper::jsonVersionKey)) {
        // Version 0 file. Differences from Version 1 for conversion:
        //  JsonHelper::jsonVersionKey not stored
        //  _jsonCameraSpecTypeKey stores CameraSpecType
        //  _jsonCameraNameKey only set if CameraSpecKnown
        int cameraSpec = v1Json[_jsonCameraSpecTypeKey].toInt(CameraSpecNone);
        if (cameraSpec == CameraSpecCustom) {
            v1Json[cameraNameName] = customCameraName();
        } else if (cameraSpec == CameraSpecNone) {
            v1Json[cameraNameName] = manualCameraName();
        }
        v1Json.remove(_jsonCameraSpecTypeKey);
        v1Json[JsonHelper::jsonVersionKey] = 1;
    }

    int version = v1Json[JsonHelper::jsonVersionKey].toInt();
    if (version != 1) {
        errorString = tr("CameraCalc section version %1 not supported").arg(version);
        return false;
    }

    QList<JsonHelper::KeyValidateInfo> keyInfoList1 = {
        { cameraNameName,                   QJsonValue::String, true },
        { adjustedFootprintSideName,        QJsonValue::Double, true },
        { adjustedFootprintFrontalName,     QJsonValue::Double, true },
        { distanceToSurfaceName,            QJsonValue::Double, true },
        { distanceToSurfaceRelativeName,    QJsonValue::Bool,   true },
    };
    if (!JsonHelper::validateKeys(v1Json, keyInfoList1, errorString)) {
        return false;
    }

    _disableRecalc = true;

    _distanceToSurfaceRelative = v1Json[distanceToSurfaceRelativeName].toBool();

    if (!isManualCamera()) {
        QList<JsonHelper::KeyValidateInfo> keyInfoList2 = {
            { valueSetIsDistanceName,   QJsonValue::Bool,   true },
            { imageDensityName,         QJsonValue::Double, true },
            { frontalOverlapName,       QJsonValue::Double, true },
            { sideOverlapName,          QJsonValue::Double, true },
        };
        if (!JsonHelper::validateKeys(v1Json, keyInfoList2, errorString)) {
            _disableRecalc = false;
            return false;
        }

        _valueSetIsDistanceFact.setRawValue (v1Json[valueSetIsDistanceName].toBool());
        _frontalOverlapFact.setRawValue     (v1Json[frontalOverlapName].toDouble());
        _sideOverlapFact.setRawValue        (v1Json[sideOverlapName].toDouble());
    }

    _cameraNameFact.setRawValue                 (v1Json[cameraNameName].toString());
    _adjustedFootprintSideFact.setRawValue      (v1Json[adjustedFootprintSideName].toDouble());
    _adjustedFootprintFrontalFact.setRawValue   (v1Json[adjustedFootprintFrontalName].toDouble());
    _distanceToSurfaceFact.setRawValue          (v1Json[distanceToSurfaceName].toDouble());
    if (!isManualCamera()) {
        _imageDensityFact.setRawValue(v1Json[imageDensityName].toDouble());

        if (!CameraSpec::load(v1Json, errorString)) {
            _disableRecalc = false;
            return false;
        }
    }

    _disableRecalc = false;

    return true;
}

QString CameraCalc::customCameraName(void)
{
    return tr("Custom Camera");
}

QString CameraCalc::manualCameraName(void)
{
    return tr("Manual (no camera specs)");
}

void CameraCalc::_adjustDistanceToSurfaceRelative(void)
{
    if (!isManualCamera()) {
        setDistanceToSurfaceRelative(true);
    }
}

void CameraCalc::setDistanceToSurfaceRelative(bool distanceToSurfaceRelative)
{
    if (distanceToSurfaceRelative != _distanceToSurfaceRelative) {
        _distanceToSurfaceRelative = distanceToSurfaceRelative;
        emit distanceToSurfaceRelativeChanged(distanceToSurfaceRelative);
    }
}

void CameraCalc::_setDirty(void)
{
    setDirty(true);
}
<commit_msg><commit_after>/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "CameraCalc.h"
#include "JsonHelper.h"
#include "Vehicle.h"
#include "CameraMetaData.h"

#include <QQmlEngine>

const char* CameraCalc::cameraNameName =                    "CameraName";
const char* CameraCalc::valueSetIsDistanceName =            "ValueSetIsDistance";
const char* CameraCalc::distanceToSurfaceName =             "DistanceToSurface";
const char* CameraCalc::distanceToSurfaceRelativeName =     "DistanceToSurfaceRelative";
const char* CameraCalc::imageDensityName =                  "ImageDensity";
const char* CameraCalc::frontalOverlapName =                "FrontalOverlap";
const char* CameraCalc::sideOverlapName =                   "SideOverlap";
const char* CameraCalc::adjustedFootprintFrontalName =      "AdjustedFootprintFrontal";
const char* CameraCalc::adjustedFootprintSideName =         "AdjustedFootprintSide";

const char* CameraCalc::_jsonCameraSpecTypeKey =            "CameraSpecType";

CameraCalc::CameraCalc(Vehicle* vehicle, const QString& settingsGroup, QObject* parent)
    : CameraSpec                    (settingsGroup, parent)
    , _vehicle                      (vehicle)
    , _dirty                        (false)
    , _disableRecalc                (false)
    , _distanceToSurfaceRelative    (true)
    , _metaDataMap                  (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraCalc.FactMetaData.json"), this))
    , _cameraNameFact               (settingsGroup, _metaDataMap[cameraNameName])
    , _valueSetIsDistanceFact       (settingsGroup, _metaDataMap[valueSetIsDistanceName])
    , _distanceToSurfaceFact        (settingsGroup, _metaDataMap[distanceToSurfaceName])
    , _imageDensityFact             (settingsGroup, _metaDataMap[imageDensityName])
    , _frontalOverlapFact           (settingsGroup, _metaDataMap[frontalOverlapName])
    , _sideOverlapFact              (settingsGroup, _metaDataMap[sideOverlapName])
    , _adjustedFootprintSideFact    (settingsGroup, _metaDataMap[adjustedFootprintSideName])
    , _adjustedFootprintFrontalFact (settingsGroup, _metaDataMap[adjustedFootprintFrontalName])
    , _imageFootprintSide           (0)
    , _imageFootprintFrontal        (0)
    , _knownCameraList              (_vehicle->staticCameraList())
{
    QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);

    connect(&_valueSetIsDistanceFact,       &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_distanceToSurfaceFact,        &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_imageDensityFact,             &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_frontalOverlapFact,           &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_sideOverlapFact,              &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_adjustedFootprintSideFact,    &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_adjustedFootprintFrontalFact, &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(&_cameraNameFact,               &Fact::valueChanged,                            this, &CameraCalc::_setDirty);
    connect(this,                           &CameraCalc::distanceToSurfaceRelativeChanged,  this, &CameraCalc::_setDirty);

    connect(&_cameraNameFact,               &Fact::valueChanged,                            this, &CameraCalc::_cameraNameChanged);
    connect(&_cameraNameFact,               &Fact::valueChanged,                            this, &CameraCalc::isManualCameraChanged);
    connect(&_cameraNameFact,               &Fact::valueChanged,                            this, &CameraCalc::isCustomCameraChanged);

    connect(&_distanceToSurfaceFact,    &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(&_imageDensityFact,         &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(&_frontalOverlapFact,       &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(&_sideOverlapFact,          &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(sensorWidth(),              &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(sensorHeight(),             &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(imageWidth(),               &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(imageHeight(),              &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(focalLength(),              &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
    connect(landscape(),                &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);

    _cameraNameChanged();

    setDirty(false);
}

void CameraCalc::setDirty(bool dirty)
{
    if (_dirty != dirty) {
        _dirty = dirty;
        emit dirtyChanged(_dirty);
    }
}

void CameraCalc::_cameraNameChanged(void)
{
    if (_disableRecalc) {
        return;
    }

    QString cameraName = _cameraNameFact.rawValue().toString();

    // Validate known camera name
    bool foundKnownCamera = false;
    CameraMetaData* cameraMetaData = nullptr;
    if (!isManualCamera() && !isCustomCamera()) {
        for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
            cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
            if (cameraName == cameraMetaData->name()) {
                foundKnownCamera = true;
                break;
            }
        }

        if (!foundKnownCamera) {
            // This will cause another camera changed signal which will recurse back to this routine
            _cameraNameFact.setRawValue(customCameraName());
            return;
        }
    }

    _disableRecalc = true;
    if (foundKnownCamera) {
        sensorWidth()->setRawValue          (cameraMetaData->sensorWidth());
        sensorHeight()->setRawValue         (cameraMetaData->sensorHeight());
        imageWidth()->setRawValue           (cameraMetaData->imageWidth());
        imageHeight()->setRawValue          (cameraMetaData->imageHeight());
        focalLength()->setRawValue          (cameraMetaData->focalLength());
        landscape()->setRawValue            (cameraMetaData->landscape());
        fixedOrientation()->setRawValue     (cameraMetaData->fixedOrientation());
        minTriggerInterval()->setRawValue   (cameraMetaData->minTriggerInterval());
    } else {
        if (isManualCamera() || isCustomCamera()) {
            // These values are unknown for these types
            fixedOrientation()->setRawValue(false);
            minTriggerInterval()->setRawValue(0);
            if (isManualCamera() && !valueSetIsDistance()->rawValue().toBool()) {
                valueSetIsDistance()->setRawValue(true);
            }
        } else {
            qWarning() << "Internal Error: Not known camera, but now manual or custom either";
        }
    }
    _disableRecalc = false;

    _recalcTriggerDistance();
    _adjustDistanceToSurfaceRelative();
}

void CameraCalc::_recalcTriggerDistance(void)
{
    if (_disableRecalc || isManualCamera()) {
        return;
    }

    _disableRecalc = true;

    double focalLength =    this->focalLength()->rawValue().toDouble();
    double sensorWidth =    this->sensorWidth()->rawValue().toDouble();
    double sensorHeight =   this->sensorHeight()->rawValue().toDouble();
    double imageWidth =     this->imageWidth()->rawValue().toDouble();
    double imageHeight =    this->imageHeight()->rawValue().toDouble();
    double imageDensity =   _imageDensityFact.rawValue().toDouble();

    if (focalLength <= 0 || sensorWidth <= 0 || sensorHeight <= 0 || imageWidth <= 0 || imageHeight <= 0 || imageDensity <= 0) {
        return;
    }

    if (_valueSetIsDistanceFact.rawValue().toBool()) {
        _imageDensityFact.setRawValue((_distanceToSurfaceFact.rawValue().toDouble() * sensorWidth * 100.0) / (imageWidth * focalLength));
    } else {
        _distanceToSurfaceFact.setRawValue((imageWidth * _imageDensityFact.rawValue().toDouble() * focalLength) / (sensorWidth * 100.0));
    }

    imageDensity = _imageDensityFact.rawValue().toDouble();

    if (landscape()->rawValue().toBool()) {
        _imageFootprintSide =       (imageWidth  * imageDensity) / 100.0;
        _imageFootprintFrontal =    (imageHeight * imageDensity) / 100.0;
    } else {
        _imageFootprintSide  =      (imageHeight * imageDensity) / 100.0;
        _imageFootprintFrontal =    (imageWidth  * imageDensity) / 100.0;
    }
    _adjustedFootprintSideFact.setRawValue      (_imageFootprintSide * ((100.0 - _sideOverlapFact.rawValue().toDouble()) / 100.0));
    _adjustedFootprintFrontalFact.setRawValue   (_imageFootprintFrontal * ((100.0 - _frontalOverlapFact.rawValue().toDouble()) / 100.0));

    emit imageFootprintSideChanged      (_imageFootprintSide);
    emit imageFootprintFrontalChanged   (_imageFootprintFrontal);

    _disableRecalc = false;
}

void CameraCalc::save(QJsonObject& json) const
{
    json[JsonHelper::jsonVersionKey] =      1;
    json[adjustedFootprintSideName] =       _adjustedFootprintSideFact.rawValue().toDouble();
    json[adjustedFootprintFrontalName] =    _adjustedFootprintFrontalFact.rawValue().toDouble();
    json[distanceToSurfaceName] =           _distanceToSurfaceFact.rawValue().toDouble();
    json[distanceToSurfaceRelativeName] =   _distanceToSurfaceRelative;
    json[cameraNameName] =                  _cameraNameFact.rawValue().toString();

    if (!isManualCamera()) {
        CameraSpec::save(json);
        json[valueSetIsDistanceName] = _valueSetIsDistanceFact.rawValue().toBool();
        json[imageDensityName] =       _imageDensityFact.rawValue().toDouble();
        json[frontalOverlapName] =     _frontalOverlapFact.rawValue().toDouble();
        json[sideOverlapName] =        _sideOverlapFact.rawValue().toDouble();
    }
}

bool CameraCalc::load(const QJsonObject& json, QString& errorString)
{
    QJsonObject v1Json = json;

    if (!json.contains(JsonHelper::jsonVersionKey)) {
        // Version 0 file. Differences from Version 1 for conversion:
        //  JsonHelper::jsonVersionKey not stored
        //  _jsonCameraSpecTypeKey stores CameraSpecType
        //  _jsonCameraNameKey only set if CameraSpecKnown
        int cameraSpec = v1Json[_jsonCameraSpecTypeKey].toInt(CameraSpecNone);
        if (cameraSpec == CameraSpecCustom) {
            v1Json[cameraNameName] = customCameraName();
        } else if (cameraSpec == CameraSpecNone) {
            v1Json[cameraNameName] = manualCameraName();
        }
        v1Json.remove(_jsonCameraSpecTypeKey);
        v1Json[JsonHelper::jsonVersionKey] = 1;
    }

    int version = v1Json[JsonHelper::jsonVersionKey].toInt();
    if (version != 1) {
        errorString = tr("CameraCalc section version %1 not supported").arg(version);
        return false;
    }

    QList<JsonHelper::KeyValidateInfo> keyInfoList1 = {
        { cameraNameName,                   QJsonValue::String, true },
        { adjustedFootprintSideName,        QJsonValue::Double, true },
        { adjustedFootprintFrontalName,     QJsonValue::Double, true },
        { distanceToSurfaceName,            QJsonValue::Double, true },
        { distanceToSurfaceRelativeName,    QJsonValue::Bool,   true },
    };
    if (!JsonHelper::validateKeys(v1Json, keyInfoList1, errorString)) {
        return false;
    }

    _disableRecalc = true;

    _distanceToSurfaceRelative = v1Json[distanceToSurfaceRelativeName].toBool();

    _cameraNameFact.setRawValue                 (v1Json[cameraNameName].toString());
    _adjustedFootprintSideFact.setRawValue      (v1Json[adjustedFootprintSideName].toDouble());
    _adjustedFootprintFrontalFact.setRawValue   (v1Json[adjustedFootprintFrontalName].toDouble());
    _distanceToSurfaceFact.setRawValue          (v1Json[distanceToSurfaceName].toDouble());

    if (!isManualCamera()) {
        QList<JsonHelper::KeyValidateInfo> keyInfoList2 = {
            { valueSetIsDistanceName,   QJsonValue::Bool,   true },
            { imageDensityName,         QJsonValue::Double, true },
            { frontalOverlapName,       QJsonValue::Double, true },
            { sideOverlapName,          QJsonValue::Double, true },
        };
        if (!JsonHelper::validateKeys(v1Json, keyInfoList2, errorString)) {
            _disableRecalc = false;
            return false;
        }

        _valueSetIsDistanceFact.setRawValue (v1Json[valueSetIsDistanceName].toBool());
        _frontalOverlapFact.setRawValue     (v1Json[frontalOverlapName].toDouble());
        _sideOverlapFact.setRawValue        (v1Json[sideOverlapName].toDouble());
        _imageDensityFact.setRawValue       (v1Json[imageDensityName].toDouble());

        if (!CameraSpec::load(v1Json, errorString)) {
            _disableRecalc = false;
            return false;
        }
    }

    _disableRecalc = false;

    return true;
}

QString CameraCalc::customCameraName(void)
{
    return tr("Custom Camera");
}

QString CameraCalc::manualCameraName(void)
{
    return tr("Manual (no camera specs)");
}

void CameraCalc::_adjustDistanceToSurfaceRelative(void)
{
    if (!isManualCamera()) {
        setDistanceToSurfaceRelative(true);
    }
}

void CameraCalc::setDistanceToSurfaceRelative(bool distanceToSurfaceRelative)
{
    if (distanceToSurfaceRelative != _distanceToSurfaceRelative) {
        _distanceToSurfaceRelative = distanceToSurfaceRelative;
        emit distanceToSurfaceRelativeChanged(distanceToSurfaceRelative);
    }
}

void CameraCalc::_setDirty(void)
{
    setDirty(true);
}
<|endoftext|>