Merge pull request #4436 from sethalves/persist-entities-as-json

Optionally persist entities as json
This commit is contained in:
Brad Hefta-Gaub 2015-03-17 21:53:17 -07:00
commit a5294ae3fa
20 changed files with 439 additions and 25 deletions

View file

@ -1036,6 +1036,13 @@ void OctreeServer::readConfiguration() {
strcpy(_persistFilename, qPrintable(persistFilename));
qDebug("persistFilename=%s", _persistFilename);
QString persistAsFileType;
if (!readOptionString(QString("persistAsFileType"), settingsSectionObject, persistAsFileType)) {
persistAsFileType = "svo";
}
_persistAsFileType = persistAsFileType;
qDebug() << "persistAsFileType=" << _persistAsFileType;
_persistInterval = OctreePersistThread::DEFAULT_PERSIST_INTERVAL;
readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval);
qDebug() << "persistInterval=" << _persistInterval;
@ -1131,7 +1138,7 @@ void OctreeServer::run() {
// now set up PersistThread
_persistThread = new OctreePersistThread(_tree, _persistFilename, _persistInterval,
_wantBackup, _settings, _debugTimestampNow);
_wantBackup, _settings, _debugTimestampNow, _persistAsFileType);
if (_persistThread) {
_persistThread->initialize(true);
}

View file

@ -157,6 +157,7 @@ protected:
QString _statusHost;
char _persistFilename[MAX_FILENAME_LENGTH];
QString _persistAsFileType;
int _packetsPerClientPerInterval;
int _packetsTotalPerInterval;
Octree* _tree; // this IS a reaveraging tree

View file

@ -325,6 +325,24 @@
"default": "resources/models.svo",
"advanced": true
},
{
"name": "persistAsFileType",
"label": "File format for entity server's persistent data",
"help": "This defines how the entity server will save entities to disk.",
"default": "svo",
"type": "select",
"options": [
{
"value": "svo",
"label": "Entity server persists data as SVO"
},
{
"value": "json",
"label": "Entity server persists data as JSON"
}
],
"advanced": true
},
{
"name": "persistInterval",
"label": "Save Check Interval",

View file

@ -1798,7 +1798,7 @@ bool Application::importEntities(const QString& urlOrFilename) {
url = QUrl::fromLocalFile(urlOrFilename);
}
bool success = _entityClipboard.readFromSVOURL(url.toString());
bool success = _entityClipboard.readFromURL(url.toString());
if (success) {
_entityClipboard.reaverageOctreeElements();
}

View file

@ -187,7 +187,9 @@ void buildStringToShapeTypeLookup() {
}
QString EntityItemProperties::getShapeTypeAsString() const {
return QString(shapeTypeNames[_shapeType]);
if (_shapeType < sizeof(shapeTypeNames) / sizeof(char *))
return QString(shapeTypeNames[_shapeType]);
return QString("none");
}
void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) {

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <VariantMapToScriptValue.h>
#include "EntityScriptingInterface.h"
#include "EntityTree.h"
#include "LightEntityItem.h"

View file

@ -10,13 +10,18 @@
//
#include <PerfStat.h>
#include <QDateTime>
#include <QtScript/QScriptEngine>
#include "EntityTree.h"
#include "EntitySimulation.h"
#include "VariantMapToScriptValue.h"
#include "AddEntityOperator.h"
#include "MovingEntitiesOperator.h"
#include "UpdateEntityOperator.h"
#include "QVariantGLM.h"
#include "RecurseOctreeToMapOperator.h"
EntityTree::EntityTree(bool shouldReaverage) :
Octree(shouldReaverage),
@ -466,7 +471,7 @@ bool EntityTree::findNearPointOperation(OctreeElement* element, void* extraData)
return true; // keep searching in case children have closer entities
}
// if this element doesn't contain the point, then none of it's children can contain the point, so stop searching
// if this element doesn't contain the point, then none of its children can contain the point, so stop searching
return false;
}
@ -1080,3 +1085,41 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData)
return true;
}
bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElement* element) {
entityDescription["Entities"] = QVariantList();
QScriptEngine scriptEngine;
RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine);
recurseTreeWithOperator(&theOperator);
return true;
}
bool EntityTree::readFromMap(QVariantMap& map) {
// map will have a top-level list keyed as "Entities". This will be extracted
// and iterated over. Each member of this list is converted to a QVariantMap, then
// to a QScriptValue, and then to EntityItemProperties. These properties are used
// to add the new entity to the EnitytTree.
QVariantList entitiesQList = map["Entities"].toList();
QScriptEngine scriptEngine;
foreach (QVariant entityVariant, entitiesQList) {
// QVariantMap --> QScriptValue --> EntityItemProperties --> Entity
QVariantMap entityMap = entityVariant.toMap();
QScriptValue entityScriptValue = variantMapToScriptValue(entityMap, scriptEngine);
EntityItemProperties properties;
EntityItemPropertiesFromScriptValue(entityScriptValue, properties);
EntityItemID entityItemID;
if (entityMap.contains("id")) {
entityItemID = EntityItemID(QUuid(entityMap["id"].toString()));
} else {
entityItemID = EntityItemID(QUuid::createUuid());
}
EntityItem* entity = addEntity(entityItemID, properties);
if (!entity) {
qDebug() << "adding Entity failed:" << entityItemID << entity->getType();
}
}
return true;
}

View file

@ -164,6 +164,9 @@ public:
bool wantEditLogging() const { return _wantEditLogging; }
void setWantEditLogging(bool value) { _wantEditLogging = value; }
bool writeToMap(QVariantMap& entityDescription, OctreeElement* element);
bool readFromMap(QVariantMap& entityDescription);
signals:
void deletingEntity(const EntityItemID& entityID);
void addingEntity(const EntityItemID& entityID);

View file

@ -0,0 +1,54 @@
//
// RecurseOctreeToMapOperator.cpp
// libraries/entities/src
//
// Created by Seth Alves on 3/16/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RecurseOctreeToMapOperator.h"
RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine) :
RecurseOctreeOperator(),
_map(map),
_top(top),
_engine(engine)
{
// if some element "top" was given, only save information for that element and it's children.
if (_top) {
_withinTop = false;
} else {
// top was NULL, export entire tree.
_withinTop = true;
}
};
bool RecurseOctreeToMapOperator::preRecursion(OctreeElement* element) {
if (element == _top) {
_withinTop = true;
}
return true;
}
bool RecurseOctreeToMapOperator::postRecursion(OctreeElement* element) {
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
const QList<EntityItem*>& entities = entityTreeElement->getEntities();
QVariantList entitiesQList = qvariant_cast<QVariantList>(_map["Entities"]);
foreach (EntityItem* entityItem, entities) {
EntityItemProperties properties = entityItem->getProperties();
QScriptValue qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties);
entitiesQList << qScriptValues.toVariant();
}
_map["Entities"] = entitiesQList;
if (element == _top) {
_withinTop = false;
}
return true;
}

View file

@ -0,0 +1,24 @@
//
// RecurseOctreeToMapOperator.h
// libraries/entities/src
//
// Created by Seth Alves on 3/16/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityTree.h"
class RecurseOctreeToMapOperator : public RecurseOctreeOperator {
public:
RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine);
bool preRecursion(OctreeElement* element);
bool postRecursion(OctreeElement* element);
private:
QVariantMap& _map;
OctreeElement *_top;
QScriptEngine *_engine;
bool _withinTop;
};

View file

@ -27,6 +27,10 @@
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QVector>
#include <QFile>
#include <QJsonDocument>
#include <QFileInfo>
#include <QString>
#include <GeometryUtil.h>
#include <LogHandler.h>
@ -35,6 +39,7 @@
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <Shape.h>
#include <PathUtils.h>
#include "CoverageMap.h"
#include "OctreeConstants.h"
@ -42,6 +47,9 @@
#include "Octree.h"
#include "ViewFrustum.h"
QVector<QString> PERSIST_EXTENSIONS = {"svo", "json"};
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) {
return voxelSizeScale / powf(2, renderLevel);
}
@ -1841,21 +1849,22 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
return bytesAtThisLevel;
}
bool Octree::readFromSVOFile(const char* fileName) {
bool Octree::readFromFile(const char* fileName) {
bool fileOk = false;
QFile file(fileName);
QString qFileName = findMostRecentFileExtension(fileName, PERSIST_EXTENSIONS);
QFile file(qFileName);
fileOk = file.open(QIODevice::ReadOnly);
if(fileOk) {
QDataStream fileInputStream(&file);
QFileInfo fileInfo(fileName);
QFileInfo fileInfo(qFileName);
unsigned long fileLength = fileInfo.size();
emit importSize(1.0f, 1.0f, 1.0f);
emit importProgress(0);
qDebug("Loading file %s...", fileName);
qDebug() << "Loading file" << qFileName << "...";
fileOk = readFromStream(fileLength, fileInputStream);
@ -1866,14 +1875,14 @@ bool Octree::readFromSVOFile(const char* fileName) {
return fileOk;
}
bool Octree::readFromSVOURL(const QString& urlString) {
bool Octree::readFromURL(const QString& urlString) {
bool readOk = false;
// determine if this is a local file or a network resource
QUrl url(urlString);
if (url.isLocalFile()) {
readOk = readFromSVOFile(qPrintable(url.toLocalFile()));
readOk = readFromFile(qPrintable(url.toLocalFile()));
} else {
QNetworkRequest request;
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
@ -1899,6 +1908,23 @@ bool Octree::readFromSVOURL(const QString& urlString) {
bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream) {
// decide if this is SVO or JSON
QIODevice *device = inputStream.device();
char firstChar;
device->getChar(&firstChar);
device->ungetChar(firstChar);
if (firstChar == (char) PacketTypeEntityData) {
return readSVOFromStream(streamLength, inputStream);
} else {
return readJSONFromStream(streamLength, inputStream);
}
}
bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStream) {
bool fileOk = false;
PacketVersion gotVersion = 0;
@ -2026,6 +2052,53 @@ bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream
return fileOk;
}
bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputStream) {
char *rawData = new char[streamLength];
inputStream.readRawData(rawData, streamLength);
QJsonDocument d = QJsonDocument::fromJson(rawData);
QVariant v = d.toVariant();
QVariantMap m = v.toMap();
readFromMap(m);
delete rawData;
return true;
}
void Octree::writeToFile(const char* fileName, OctreeElement* element, QString persistAsFileType) {
// make the sure file extension makes sense
QString qFileName = fileNameWithoutExtension(QString(fileName), PERSIST_EXTENSIONS) + "." + persistAsFileType;
QByteArray byteArray = qFileName.toUtf8();
const char* cFileName = byteArray.constData();
if (persistAsFileType == "svo") {
writeToSVOFile(fileName, element);
} else if (persistAsFileType == "json") {
writeToJSONFile(cFileName, element);
} else {
qDebug() << "unable to write octree to file of type" << persistAsFileType;
}
}
void Octree::writeToJSONFile(const char* fileName, OctreeElement* element) {
QFile persistFile(fileName);
QVariantMap entityDescription;
qDebug("Saving to file %s...", fileName);
OctreeElement* top;
if (element) {
top = element;
} else {
top = _rootElement;
}
bool entityDescriptionSuccess = writeToMap(entityDescription, top);
if (entityDescriptionSuccess && persistFile.open(QIODevice::WriteOnly)) {
persistFile.write(QJsonDocument::fromVariant(entityDescription).toJson());
} else {
qCritical("Could not write to JSON description of entities.");
}
}
void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) {
std::ofstream file(fileName, std::ios::out|std::ios::binary);

View file

@ -35,6 +35,9 @@ class Shape;
#include <QObject>
#include <QReadWriteLock>
extern QVector<QString> PERSIST_EXTENSIONS;
/// derive from this class to use the Octree::recurseTreeWithOperator() method
class RecurseOctreeOperator {
public:
@ -324,13 +327,19 @@ public:
// Note: this assumes the fileFormat is the HIO individual voxels code files
void loadOctreeFile(const char* fileName, bool wantColorRandomizer);
// these will read/write files that match the wireformat, excluding the 'V' leading
// Octree exporters
void writeToFile(const char* filename, OctreeElement* element = NULL, QString persistAsFileType = "svo");
void writeToJSONFile(const char* filename, OctreeElement* element = NULL);
void writeToSVOFile(const char* filename, OctreeElement* element = NULL);
virtual bool writeToMap(QVariantMap& entityDescription, OctreeElement* element) = 0;
bool readFromSVOFile(const char* filename);
bool readFromSVOURL(const QString& url); // will support file urls as well...
// Octree importers
bool readFromFile(const char* filename);
bool readFromURL(const QString& url); // will support file urls as well...
bool readFromStream(unsigned long streamLength, QDataStream& inputStream);
bool readSVOFromStream(unsigned long streamLength, QDataStream& inputStream);
bool readJSONFromStream(unsigned long streamLength, QDataStream& inputStream);
virtual bool readFromMap(QVariantMap& entityDescription) = 0;
unsigned long getOctreeElementsCount();

View file

@ -20,16 +20,19 @@
#include <QFile>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <PerfStat.h>
#include <SharedUtil.h>
#include <PathUtils.h>
#include "OctreePersistThread.h"
const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, int persistInterval,
bool wantBackup, const QJsonObject& settings, bool debugTimestampNow) :
bool wantBackup, const QJsonObject& settings, bool debugTimestampNow,
QString persistAsFileType) :
_tree(tree),
_filename(filename),
_persistInterval(persistInterval),
@ -38,9 +41,14 @@ OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename,
_lastCheck(0),
_wantBackup(wantBackup),
_debugTimestampNow(debugTimestampNow),
_lastTimeDebug(0)
_lastTimeDebug(0),
_persistAsFileType(persistAsFileType)
{
parseSettings(settings);
// in case the persist filename has an extension that doesn't match the file type
QString sansExt = fileNameWithoutExtension(_filename, PERSIST_EXTENSIONS);
_filename = sansExt + "." + _persistAsFileType;
}
void OctreePersistThread::parseSettings(const QJsonObject& settings) {
@ -140,7 +148,7 @@ bool OctreePersistThread::process() {
qDebug() << "Loading Octree... lock file removed:" << lockFileName;
}
persistantFileRead = _tree->readFromSVOFile(_filename.toLocal8Bit().constData());
persistantFileRead = _tree->readFromFile(qPrintable(_filename.toLocal8Bit()));
_tree->pruneTree();
}
_tree->unlock();
@ -242,9 +250,7 @@ void OctreePersistThread::persist() {
if(lockFile.is_open()) {
qDebug() << "saving Octree lock file created at:" << lockFileName;
qDebug() << "saving Octree to file " << _filename << "...";
_tree->writeToSVOFile(qPrintable(_filename));
_tree->writeToFile(qPrintable(_filename), NULL, _persistAsFileType);
time(&_lastPersistTime);
_tree->clearDirtyBit(); // tree is clean after saving
qDebug() << "DONE saving Octree to file...";
@ -346,8 +352,8 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) {
QString backupExtensionNplusOne = rule.extensionFormat;
backupExtensionN.replace(QString("%N"), QString::number(n));
backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1));
QString backupFilenameN = _filename + backupExtensionN;
QString backupFilenameN = findMostRecentFileExtension(_filename, PERSIST_EXTENSIONS) + backupExtensionN;
QString backupFilenameNplusOne = _filename + backupExtensionNplusOne;
QFile backupFileN(backupFilenameN);

View file

@ -34,8 +34,8 @@ public:
static const int DEFAULT_PERSIST_INTERVAL;
OctreePersistThread(Octree* tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL,
bool wantBackup = false, const QJsonObject& settings = QJsonObject(),
bool debugTimestampNow = false);
bool wantBackup = false, const QJsonObject& settings = QJsonObject(),
bool debugTimestampNow = false, QString persistAsFileType="svo");
bool isInitialLoadComplete() const { return _initialLoadComplete; }
quint64 getLoadElapsedTime() const { return _loadTimeUSecs; }
@ -72,6 +72,8 @@ private:
bool _debugTimestampNow;
quint64 _lastTimeDebug;
QString _persistAsFileType;
};
#endif // hifi_OctreePersistThread_h

View file

@ -11,6 +11,9 @@
#include <QCoreApplication>
#include <QString>
#include <QVector>
#include <QDateTime>
#include <QFileInfo>
#include "PathUtils.h"
@ -23,3 +26,30 @@ QString& PathUtils::resourcesPath() {
#endif
return staticResourcePath;
}
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions) {
foreach (const QString possibleExtension, possibleExtensions) {
if (fileName.endsWith(possibleExtension) ||
fileName.endsWith(possibleExtension.toUpper()) ||
fileName.endsWith(possibleExtension.toLower())) {
return fileName.left(fileName.count() - possibleExtension.count() - 1);
}
}
return fileName;
}
QString findMostRecentFileExtension(const QString& originalFileName, QVector<QString> possibleExtensions) {
QString sansExt = fileNameWithoutExtension(originalFileName, possibleExtensions);
QString newestFileName = originalFileName;
QDateTime newestTime = QDateTime::fromMSecsSinceEpoch(0);
foreach (QString possibleExtension, possibleExtensions) {
QString fileName = sansExt + "." + possibleExtension;
QFileInfo fileInfo(fileName);
if (fileInfo.exists() && fileInfo.lastModified() > newestTime) {
newestFileName = fileName;
newestTime = fileInfo.lastModified();
}
}
return newestFileName;
}

View file

@ -19,4 +19,7 @@ namespace PathUtils {
QString& resourcesPath();
}
#endif // hifi_PathUtils_h
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions);
QString findMostRecentFileExtension(const QString& originalFileName, QVector<QString> possibleExtensions);
#endif // hifi_PathUtils_h

View file

@ -0,0 +1,48 @@
//
// QVariantGLM.cpp
// libraries/shared/src
//
// Created by Seth Alves on 3/9/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "QVariantGLM.h"
#include "OctalCode.h"
QVariantList glmToQList(const glm::vec3& g) {
return QVariantList() << g[0] << g[1] << g[2];
}
QVariantList glmToQList(const glm::quat& g) {
return QVariantList() << g.x << g.y << g.z << g.w;
}
QVariantList rgbColorToQList(rgbColor& v) {
return QVariantList() << (int)(v[0]) << (int)(v[1]) << (int)(v[2]);
}
glm::vec3 qListToGlmVec3(const QVariant q) {
QVariantList qList = q.toList();
return glm::vec3(qList[RED_INDEX].toFloat(), qList[GREEN_INDEX].toFloat(), qList[BLUE_INDEX].toFloat());
}
glm::quat qListToGlmQuat(const QVariant q) {
QVariantList qList = q.toList();
float x = qList[0].toFloat();
float y = qList[1].toFloat();
float z = qList[2].toFloat();
float w = qList[3].toFloat();
return glm::quat(w, x, y, z);
}
void qListtoRgbColor(const QVariant q, rgbColor returnValue) {
QVariantList qList = q.toList();
returnValue[RED_INDEX] = qList[RED_INDEX].toInt();
returnValue[GREEN_INDEX] = qList[GREEN_INDEX].toInt();
returnValue[BLUE_INDEX] = qList[BLUE_INDEX].toInt();
}

View file

@ -0,0 +1,26 @@
//
// QVariantGLM.h
// libraries/shared/src
//
// Created by Seth Alves on 3/9/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QList>
#include <QVariant>
#include <QVariantList>
#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
#include "SharedUtil.h"
QVariantList glmToQList(const glm::vec3& g);
QVariantList glmToQList(const glm::quat& g);
QVariantList rgbColorToQList(rgbColor& v);
glm::vec3 qListToGlmVec3(const QVariant q);
glm::quat qListToGlmQuat(const QVariant q);
void qListtoRgbColor(const QVariant q, rgbColor returnValue);

View file

@ -0,0 +1,47 @@
//
// VariantMapToScriptValue.cpp
// libraries/shared/src/
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDebug>
#include "VariantMapToScriptValue.h"
QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine) {
QScriptValue scriptValue = scriptEngine.newObject();
for (QVariantMap::const_iterator iter = variantMap.begin(); iter != variantMap.end(); ++iter) {
QString key = iter.key();
QVariant qValue = iter.value();
switch(qValue.type()) {
case QVariant::Bool:
scriptValue.setProperty(key, qValue.toBool());
break;
case QVariant::Int:
scriptValue.setProperty(key, qValue.toInt());
break;
case QVariant::Double:
scriptValue.setProperty(key, qValue.toDouble());
break;
case QVariant::String: {
scriptValue.setProperty(key, scriptEngine.newVariant(qValue));
break;
}
case QVariant::Map: {
QVariantMap childMap = qValue.toMap();
scriptValue.setProperty(key, variantMapToScriptValue(childMap, scriptEngine));
break;
}
default:
qDebug() << "unhandled QScript type" << qValue.type();
}
}
return scriptValue;
}

View file

@ -0,0 +1,16 @@
//
// VariantMapToScriptValue.h
// libraries/shared/src/
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QVariant>
#include <QScriptValue>
#include <QScriptEngine>
QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine);