HoloLens: Added support for session pin code and bearer authentication. Improved session management and error handling. Added some dignostic timings for sending of array data.

This commit is contained in:
sigurdp 2018-12-18 22:08:57 +01:00
parent 5ed13d3530
commit 0e910e7391
8 changed files with 255 additions and 31 deletions

View File

@ -10,6 +10,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RicHoloLensRestClient.h
${CMAKE_CURRENT_LIST_DIR}/RicHoloLensServerSettings.h
${CMAKE_CURRENT_LIST_DIR}/RicHoloLensCreateSessionUi.h
${CMAKE_CURRENT_LIST_DIR}/RicHoloLensSession.h
${CMAKE_CURRENT_LIST_DIR}/RicHoloLensSessionObserver.h
${CMAKE_CURRENT_LIST_DIR}/RicHoloLensSessionManager.h
${CMAKE_CURRENT_LIST_DIR}/RicHoloLensCreateDummyFileBackedSessionFeature.h

View File

@ -23,6 +23,13 @@
#include <QNetworkRequest>
#include <QSslConfiguration>
#include <QDateTime>
// For getting time stamps for round-trip timing
#if defined (__linux__)
#include <sys/time.h>
#include <ctime>
#endif
#ifndef QT_NO_OPENSSL
@ -59,7 +66,7 @@ void RicHoloLensRestClient::clearResponseHandler()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient::createSession()
void RicHoloLensRestClient::createSession(const QByteArray& sessionPinCode)
{
const QString url = m_serverUrl + "/sessions/create/" + m_sessionName;
cvf::Trace::show("createSession: POST on url: %s", url.toLatin1().constData());
@ -67,6 +74,7 @@ void RicHoloLensRestClient::createSession()
QNetworkRequest request(url);
//request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/x-www-form-urlencoded"));
request.setRawHeader("PinCode", sessionPinCode);
#ifdef EXPERIMENTAL_SSL_SUPPORT
// NOTE !!!
@ -106,9 +114,22 @@ void RicHoloLensRestClient::slotCreateSessionFinished()
if (detectAndHandleErrorReply("createSession", reply))
{
reply->deleteLater();
m_responseHandler->handleFailedCreateSession();
return;
}
const QByteArray serverData = reply->readAll();
//cvf::Trace::show(" serverResponse: %s", serverData.constData());
// Currently we get the bearer token back in the response wholesale
// The format we get is typically: "Bearer <the token>"
// For example: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
// Presumably the format of the response will change, but for now just strip away the starting "Bearer " string and consider the rest as the actual token
m_bearerToken = serverData;
m_bearerToken.replace("Bearer ", "");
reply->deleteLater();
cvf::Trace::show("createSession OK");
@ -129,6 +150,7 @@ void RicHoloLensRestClient::deleteSession()
QNetworkRequest request(url);
//request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/x-www-form-urlencoded"));
addBearerAuthenticationHeaderToRequest(&request);
QNetworkReply* reply = m_accessManager.deleteResource(request);
connect(reply, SIGNAL(finished()), SLOT(slotDeleteSessionFinished()));
@ -168,13 +190,17 @@ void RicHoloLensRestClient::sendMetaData(int metaDataSequenceNumber, const QStri
const QString url = m_serverUrl + "/sessions/" + m_sessionName + "/metadata";
cvf::Trace::show("sendMetaData (metaDataSequenceNumber=%d): POST on url: %s", metaDataSequenceNumber, url.toLatin1().constData());
const qint64 sendStartTimeStamp_ms = getCurrentTimeStamp_ms();
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
addBearerAuthenticationHeaderToRequest(&request);
const QByteArray jsonByteArr = jsonMetaDataString.toLatin1();
QNetworkReply* reply = m_accessManager.post(request, jsonByteArr);
reply->setProperty("metaDataSequenceNumber", QVariant(metaDataSequenceNumber));
reply->setProperty("holo_metaDataSequenceNumber", QVariant(metaDataSequenceNumber));
reply->setProperty("holo_sendStartTimeStamp_ms", QVariant(sendStartTimeStamp_ms));
connect(reply, SIGNAL(finished()), SLOT(slotSendMetaDataFinished()));
@ -202,19 +228,32 @@ void RicHoloLensRestClient::slotSendMetaDataFinished()
int metaDataSequenceNumber = -1;
{
QVariant var = reply->property("metaDataSequenceNumber");
QVariant var = reply->property("holo_metaDataSequenceNumber");
if (var.type() == QVariant::Int)
{
metaDataSequenceNumber = var.toInt();
}
}
double elapsedTime_s = -1;
{
QVariant var = reply->property("holo_sendStartTimeStamp_ms");
if (var.type() == QVariant::LongLong)
{
const qint64 startTimeStamp_ms = var.toLongLong();
elapsedTime_s = (getCurrentTimeStamp_ms() - startTimeStamp_ms)/1000.0;
}
}
const QByteArray serverData = reply->readAll();
//cvf::Trace::show(" serverResponse: %s", serverData.constData());
reply->deleteLater();
cvf::Trace::show("sendMetaData (metaDataSequenceNumber=%d) OK", metaDataSequenceNumber);
cvf::Trace::show("sendMetaData (metaDataSequenceNumber=%d) OK, elapsedTime=%.2fs", metaDataSequenceNumber, elapsedTime_s);
if (m_responseHandler)
{
m_responseHandler->handleSuccessfulSendMetaData(metaDataSequenceNumber);
m_responseHandler->handleSuccessfulSendMetaData(metaDataSequenceNumber, serverData);
}
}
@ -226,12 +265,20 @@ void RicHoloLensRestClient::sendBinaryData(const QByteArray& binaryDataArr)
const QString url = m_serverUrl + "/sessions/" + m_sessionName + "/data";
cvf::Trace::show("sendBinaryData: POST on url: %s", url.toLatin1().constData());
const qint64 sendStartTimeStamp_ms = getCurrentTimeStamp_ms();
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
addBearerAuthenticationHeaderToRequest(&request);
QNetworkReply* reply = m_accessManager.post(request, binaryDataArr);
reply->setProperty("holo_sendStartTimeStamp_ms", QVariant(sendStartTimeStamp_ms));
connect(reply, SIGNAL(finished()), SLOT(slotSendBinaryDataFinished()));
// Debugging!
connect(reply, SIGNAL(uploadProgress(qint64, qint64)), SLOT(slotDbgUploadProgress(qint64, qint64)));
#ifdef EXPERIMENTAL_SSL_SUPPORT
connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)), SLOT(slotSslErrors(const QList<QSslError>&)));
#endif
@ -254,9 +301,50 @@ void RicHoloLensRestClient::slotSendBinaryDataFinished()
return;
}
double elapsedTime_s = -1;
{
QVariant var = reply->property("holo_sendStartTimeStamp_ms");
if (var.type() == QVariant::LongLong)
{
const qint64 startTimeStamp_ms = var.toLongLong();
elapsedTime_s = (getCurrentTimeStamp_ms() - startTimeStamp_ms)/1000.0;
}
}
reply->deleteLater();
cvf::Trace::show("sendBinaryData OK");
cvf::Trace::show("sendBinaryData OK, elapsedTime=%.2fs", elapsedTime_s);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient::slotDbgUploadProgress(qint64 bytesSent, qint64 bytesTotal)
{
static int sl_lastPct = -1;
int pct = 0;
if (bytesTotal > 0)
{
pct = static_cast<int>(100*(bytesSent/static_cast<double>(bytesTotal)));
}
if (pct % 10 == 0 && pct != sl_lastPct)
{
double elapsedTime_s = -1;
QNetworkReply* reply = dynamic_cast<QNetworkReply*>(sender());
if (reply)
{
QVariant var = reply->property("holo_sendStartTimeStamp_ms");
if (var.type() == QVariant::LongLong)
{
const qint64 startTimeStamp_ms = var.toLongLong();
elapsedTime_s = (getCurrentTimeStamp_ms() - startTimeStamp_ms)/1000.0;
}
}
cvf::Trace::show("Sending progress: %3d%%, %.2f/%.2fMB (elapsedTime=%.2fs)", pct, bytesSent/(1024.0*1024.0), bytesTotal/(1024.0*1024.0), elapsedTime_s);
sl_lastPct = pct;
}
}
//--------------------------------------------------------------------------------------------------
@ -273,6 +361,16 @@ void RicHoloLensRestClient::slotSslErrors(const QList<QSslError>& errors)
#endif
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient::addBearerAuthenticationHeaderToRequest(QNetworkRequest* request) const
{
CVF_ASSERT(request);
request->setRawHeader("Authorization", "Bearer " + m_bearerToken);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -358,3 +456,23 @@ QString RicHoloLensRestClient::networkErrorCodeAsString(QNetworkReply::NetworkEr
return "UnknownErrorCode";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
qint64 RicHoloLensRestClient::getCurrentTimeStamp_ms()
{
#if QT_VERSION >= 0x040700
const qint64 timeStamp_ms = QDateTime::currentMSecsSinceEpoch();
return timeStamp_ms;
#elif defined(__linux__)
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
{
return static_cast<qint64>(ts.tv_sec*1000 + ts.tv_nsec/1000000);
}
#endif
return 0;
}

View File

@ -34,7 +34,9 @@ class RicHoloLensRestResponseHandler
{
public:
virtual void handleSuccessfulCreateSession() = 0;
virtual void handleSuccessfulSendMetaData(int metaDataSequenceNumber) = 0;
virtual void handleFailedCreateSession() = 0;
virtual void handleSuccessfulSendMetaData(int metaDataSequenceNumber, const QByteArray& jsonServerResponseString) = 0;
virtual void handleError(const QString& errMsg, const QString& url, const QString& serverData) = 0;
};
@ -54,20 +56,23 @@ public:
void clearResponseHandler();
void createSession();
void createSession(const QByteArray& sessionPinCode);
void deleteSession();
void sendMetaData(int metaDataSequenceNumber, const QString& jsonMetaDataString);
void sendBinaryData(const QByteArray& binaryDataArr);
private:
void addBearerAuthenticationHeaderToRequest(QNetworkRequest* request) const;
bool detectAndHandleErrorReply(QString operationName, QNetworkReply* reply);
static QString networkErrorCodeAsString(QNetworkReply::NetworkError nwErr);
static qint64 getCurrentTimeStamp_ms();
private slots:
void slotCreateSessionFinished();
void slotDeleteSessionFinished();
void slotSendMetaDataFinished();
void slotSendBinaryDataFinished();
void slotDbgUploadProgress(qint64 bytesSent, qint64 bytesTotal);
void slotSslErrors(const QList<QSslError>& errors);
@ -76,4 +81,6 @@ private:
QString m_serverUrl;
QString m_sessionName;
RicHoloLensRestResponseHandler* m_responseHandler;
QByteArray m_bearerToken;
};

View File

@ -17,7 +17,7 @@
/////////////////////////////////////////////////////////////////////////////////
#include "RicHoloLensSession.h"
#include "RicHoloLensSessionManager.h"
#include "RicHoloLensSessionObserver.h"
#include "RiaLogging.h"
#include "RiaPreferences.h"
@ -32,7 +32,6 @@
#include <QDir>
//==================================================================================================
//
//
@ -45,6 +44,7 @@
RicHoloLensSession::RicHoloLensSession()
: m_isSessionValid(false),
m_lastExtractionMetaDataSequenceNumber(-1),
m_sessionObserver(nullptr),
m_dbgEnableFileExport(false)
{
}
@ -60,12 +60,14 @@ RicHoloLensSession::~RicHoloLensSession()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicHoloLensSession* RicHoloLensSession::createSession(const QString& serverUrl, const QString& sessionName)
RicHoloLensSession* RicHoloLensSession::createSession(const QString& serverUrl, const QString& sessionName, const QByteArray& sessionPinCode, RicHoloLensSessionObserver* sessionObserver)
{
RicHoloLensSession* newSession = new RicHoloLensSession;
newSession->m_restClient = new RicHoloLensRestClient(serverUrl, sessionName, newSession);
newSession->m_restClient->createSession();
newSession->m_restClient->createSession(sessionPinCode);
newSession->m_sessionObserver = sessionObserver;
// For now, leave this on!!!
// We probably want to export this as a preference parameter
@ -127,22 +129,29 @@ void RicHoloLensSession::updateSessionDataFromView(const RimGridView& activeView
{
RiaLogging::info("HoloLens: Updating visualization data");
QString modelMetaJsonStr;
std::vector<int> allReferencedPacketIds;
// Note
// Currently we clear the packet directory each time we update the visualization data
// This means we do no caching of actual packet data and we assume that the server will ask for data
// packets/arrays right after having received updated meta data
m_packetDirectory.clear();
VdeVizDataExtractor extractor(activeView);
QString modelMetaJsonStr;
std::vector<int> allReferencedPacketIds;
extractor.extractViewContents(&modelMetaJsonStr, &allReferencedPacketIds, &m_packetDirectory);
m_lastExtractionMetaDataSequenceNumber++;
m_lastExtractionAllReferencedPacketIdsArr = allReferencedPacketIds;
if (m_restClient)
{
RiaLogging::info(QString("HoloLens: Sending updated meta data to sharing server (sequenceNumber=%1)").arg(m_lastExtractionMetaDataSequenceNumber));
m_restClient->sendMetaData(m_lastExtractionMetaDataSequenceNumber, modelMetaJsonStr);
}
// Debug export to file
if (m_dbgEnableFileExport)
{
@ -181,15 +190,24 @@ void RicHoloLensSession::handleSuccessfulCreateSession()
RiaLogging::info("HoloLens: Session successfully created");
m_isSessionValid = true;
// Slight hack here - reaching out to the manager to update GUI
// We should really just be notifying the manager that our state has changed
RicHoloLensSessionManager::refreshToolbarState();
notifyObserver(RicHoloLensSessionObserver::CreateSessionSucceeded);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensSession::handleFailedCreateSession()
{
RiaLogging::error("HoloLens: Failed to create session");
m_isSessionValid = false;
notifyObserver(RicHoloLensSessionObserver::CreateSessionFailed);
}
//--------------------------------------------------------------------------------------------------
/// Handle the server response we receive after sending new meta data
//--------------------------------------------------------------------------------------------------
void RicHoloLensSession::handleSuccessfulSendMetaData(int metaDataSequenceNumber)
void RicHoloLensSession::handleSuccessfulSendMetaData(int metaDataSequenceNumber, const QByteArray& /*jsonServerResponseString*/)
{
RiaLogging::info(QString("HoloLens: Processing server response to meta data (sequenceNumber=%1)").arg(metaDataSequenceNumber));
@ -229,5 +247,19 @@ void RicHoloLensSession::handleError(const QString& errMsg, const QString& url,
fullMsg += "\n url: " + url;
RiaLogging::error(fullMsg);
// It is probably not correct to always consider an error a state change, but for now
notifyObserver(RicHoloLensSessionObserver::GeneralError);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensSession::notifyObserver(RicHoloLensSessionObserver::Notification notification)
{
if (m_sessionObserver)
{
m_sessionObserver->handleSessionNotification(this, notification);
}
}

View File

@ -18,6 +18,7 @@
#pragma once
#include "RicHoloLensSessionObserver.h"
#include "RicHoloLensRestClient.h"
#include "VdePacketDirectory.h"
@ -30,7 +31,6 @@
class RimGridView;
//==================================================================================================
//
//
@ -41,7 +41,7 @@ class RicHoloLensSession : public QObject, private RicHoloLensRestResponseHandle
public:
~RicHoloLensSession();
static RicHoloLensSession* createSession(const QString& serverUrl, const QString& sessionName);
static RicHoloLensSession* createSession(const QString& serverUrl, const QString& sessionName, const QByteArray& sessionPinCode, RicHoloLensSessionObserver* sessionObserver);
static RicHoloLensSession* createDummyFileBackedSession();
void destroySession();
@ -53,9 +53,12 @@ private:
RicHoloLensSession();
virtual void handleSuccessfulCreateSession() override;
virtual void handleSuccessfulSendMetaData(int metaDataSequenceNumber) override;
virtual void handleFailedCreateSession() override;
virtual void handleSuccessfulSendMetaData(int metaDataSequenceNumber, const QByteArray& jsonServerResponseString) override;
virtual void handleError(const QString& errMsg, const QString& url, const QString& serverData) override;
void notifyObserver(RicHoloLensSessionObserver::Notification notification);
private:
bool m_isSessionValid;
QPointer<RicHoloLensRestClient> m_restClient;
@ -64,8 +67,7 @@ private:
std::vector<int> m_lastExtractionAllReferencedPacketIdsArr;
VdePacketDirectory m_packetDirectory;
RicHoloLensSessionObserver* m_sessionObserver;
bool m_dbgEnableFileExport;
};

View File

@ -50,7 +50,7 @@ RicHoloLensSessionManager* RicHoloLensSessionManager::instance()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicHoloLensSessionManager::createSession(const QString& serverUrl, const QString& sessionName, const QString& /*sessionPinCode*/)
bool RicHoloLensSessionManager::createSession(const QString& serverUrl, const QString& sessionName, const QString& sessionPinCode)
{
if (m_session)
{
@ -58,8 +58,8 @@ bool RicHoloLensSessionManager::createSession(const QString& serverUrl, const QS
return false;
}
RiaLogging::info(QString("Creating HoloLens session: '%1', server url: %2").arg(sessionName).arg(serverUrl));
m_session = RicHoloLensSession::createSession(serverUrl, sessionName);
RiaLogging::info(QString("Creating HoloLens session: '%1' with pin code: %2, server url: %3").arg(sessionName).arg(sessionPinCode).arg(serverUrl));
m_session = RicHoloLensSession::createSession(serverUrl, sessionName, sessionPinCode.toLatin1(), this);
refreshToolbarState();
@ -96,11 +96,14 @@ void RicHoloLensSessionManager::terminateSession()
}
RiaLogging::info("Terminating HoloLens session");
RicHoloLensSession* sessionToDelete = m_session;
m_session->destroySession();
m_session->deleteLater();
m_session = nullptr;
refreshToolbarState();
m_session->deleteLater();
}
//--------------------------------------------------------------------------------------------------
@ -125,3 +128,19 @@ void RicHoloLensSessionManager::refreshToolbarState()
caf::CmdFeatureManager::instance()->refreshEnabledState(commandIds);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensSessionManager::handleSessionNotification(const RicHoloLensSession* session, Notification notification)
{
if (notification == RicHoloLensSessionObserver::CreateSessionFailed)
{
if (m_session && m_session == session)
terminateSession();
}
else
{
refreshToolbarState();
}
}

View File

@ -18,20 +18,19 @@
#pragma once
#include "RicHoloLensRestClient.h"
#include "RicHoloLensSessionObserver.h"
#include <QPointer>
class RicHoloLensSession;
//==================================================================================================
//
//
//
//==================================================================================================
class RicHoloLensSessionManager
class RicHoloLensSessionManager : private RicHoloLensSessionObserver
{
public:
static RicHoloLensSessionManager* instance();
@ -44,6 +43,9 @@ public:
static void refreshToolbarState();
private:
virtual void handleSessionNotification(const RicHoloLensSession* session, Notification notification) override;
private:
RicHoloLensSessionManager();

View File

@ -0,0 +1,43 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2018 Statoil ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
class RicHoloLensSession;
//==================================================================================================
//
//
//
//==================================================================================================
class RicHoloLensSessionObserver
{
public:
enum Notification
{
CreateSessionSucceeded,
CreateSessionFailed,
GeneralError
};
public:
virtual ~RicHoloLensSessionObserver() {}
virtual void handleSessionNotification(const RicHoloLensSession* session, Notification notification) = 0;
};