mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-18 10:59:18 -06:00
Feature/documentation topic (#822)
- Implement documentation topic that can be used to query documentation using the network API. - Implement a way to pass arguments to lua scripts using json (rather than formatting entire lua string clientside) - Implement ability to attach callback to lua script executions - Implement abillity to transport return values from lua scripts back to network API clients. - Do not initialize server interface on slave nodes. - Implement Dictionary -> json converter using nlohmann json library
This commit is contained in:
@@ -33,4 +33,9 @@
|
||||
<User eyeSeparation="0.065">
|
||||
<Pos x="0.0" y="0.0" z="4.0" />
|
||||
</User>
|
||||
</Cluster>
|
||||
</Cluster>
|
||||
<!--
|
||||
Usage:
|
||||
for master: <executable> -local 0
|
||||
for slave: <executable> -local 1 --slave
|
||||
-->
|
||||
|
||||
@@ -82,7 +82,6 @@ public:
|
||||
*/
|
||||
void writeDocumentation(const std::string& filename);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* This abstract method is used by concrete subclasses to provide the actual data that
|
||||
* is used in the documentation written by this DocumentationGenerator class. The JSON
|
||||
|
||||
@@ -39,7 +39,6 @@ class DownloadManager;
|
||||
class LuaConsole;
|
||||
class MissionManager;
|
||||
class ModuleEngine;
|
||||
class NetworkEngine;
|
||||
class OpenSpaceEngine;
|
||||
class ParallelPeer;
|
||||
class RaycasterManager;
|
||||
@@ -75,7 +74,6 @@ DownloadManager& gDownloadManager();
|
||||
LuaConsole& gLuaConsole();
|
||||
MissionManager& gMissionManager();
|
||||
ModuleEngine& gModuleEngine();
|
||||
NetworkEngine& gNetworkEngine();
|
||||
OpenSpaceEngine& gOpenSpaceEngine();
|
||||
ParallelPeer& gParallelPeer();
|
||||
RaycasterManager& gRaycasterManager();
|
||||
@@ -106,7 +104,6 @@ static DownloadManager& downloadManager = detail::gDownloadManager();
|
||||
static LuaConsole& luaConsole = detail::gLuaConsole();
|
||||
static MissionManager& missionManager = detail::gMissionManager();
|
||||
static ModuleEngine& moduleEngine = detail::gModuleEngine();
|
||||
static NetworkEngine& networkEngine = detail::gNetworkEngine();
|
||||
static OpenSpaceEngine& openSpaceEngine = detail::gOpenSpaceEngine();
|
||||
static ParallelPeer& parallelPeer = detail::gParallelPeer();
|
||||
static RaycasterManager& raycasterManager = detail::gRaycasterManager();
|
||||
|
||||
@@ -69,11 +69,12 @@ public:
|
||||
static scripting::LuaLibrary luaLibrary();
|
||||
|
||||
void keyboardCallback(Key key, KeyModifier modifier, KeyAction action);
|
||||
|
||||
std::string generateJson() const override;
|
||||
|
||||
const std::multimap<KeyWithModifier, KeyInformation>& keyBindings() const;
|
||||
|
||||
private:
|
||||
std::string generateJson() const override;
|
||||
|
||||
std::multimap<KeyWithModifier, KeyInformation> _keyLua;
|
||||
};
|
||||
|
||||
@@ -53,8 +53,6 @@ class SceneManager;
|
||||
class ScreenLog;
|
||||
class ScreenSpaceRenderable;
|
||||
struct ShutdownInformation;
|
||||
class Syncable;
|
||||
class SyncBuffer;
|
||||
|
||||
class RenderEngine : public properties::PropertyOwner {
|
||||
public:
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
#include <ghoul/lua/luastate.h>
|
||||
#include <ghoul/misc/boolean.h>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
|
||||
namespace openspace { class SyncBuffer; }
|
||||
|
||||
@@ -47,8 +50,15 @@ namespace openspace::scripting {
|
||||
*/
|
||||
class ScriptEngine : public Syncable, public DocumentationGenerator {
|
||||
public:
|
||||
using ScriptCallback = std::optional<std::function<void(ghoul::Dictionary)>>;
|
||||
BooleanType(RemoteScripting);
|
||||
|
||||
struct QueueItem {
|
||||
std::string script;
|
||||
RemoteScripting remoteScripting;
|
||||
ScriptCallback callback;
|
||||
};
|
||||
|
||||
static constexpr const char* OpenSpaceLibraryName = "openspace";
|
||||
|
||||
ScriptEngine();
|
||||
@@ -71,7 +81,7 @@ public:
|
||||
void addLibrary(LuaLibrary library);
|
||||
bool hasLibrary(const std::string& name);
|
||||
|
||||
bool runScript(const std::string& script);
|
||||
bool runScript(const std::string& script, ScriptCallback callback = ScriptCallback());
|
||||
bool runScriptFile(const std::string& filename);
|
||||
|
||||
bool writeLog(const std::string& script);
|
||||
@@ -81,13 +91,12 @@ public:
|
||||
virtual void decode(SyncBuffer* syncBuffer) override;
|
||||
virtual void postSync(bool isMaster) override;
|
||||
|
||||
void queueScript(const std::string &script, RemoteScripting remoteScripting);
|
||||
|
||||
void setLogFile(const std::string& filename, const std::string& type);
|
||||
|
||||
std::vector<std::string> cachedScripts();
|
||||
void queueScript(const std::string& script, RemoteScripting remoteScripting,
|
||||
ScriptCallback cb = ScriptCallback());
|
||||
|
||||
std::vector<std::string> allLuaFunctions() const;
|
||||
|
||||
std::string generateJson() const override;
|
||||
|
||||
private:
|
||||
BooleanType(Replace);
|
||||
@@ -100,23 +109,20 @@ private:
|
||||
void addBaseLibrary();
|
||||
void remapPrintFunction();
|
||||
|
||||
std::string generateJson() const override;
|
||||
|
||||
ghoul::lua::LuaState _state;
|
||||
std::vector<LuaLibrary> _registeredLibraries;
|
||||
|
||||
std::queue<QueueItem> _incomingScripts;
|
||||
|
||||
//sync variables
|
||||
std::mutex _mutex;
|
||||
std::vector<std::pair<std::string, bool>> _queuedScripts;
|
||||
std::vector<std::string> _receivedScripts;
|
||||
std::string _currentSyncedScript;
|
||||
// Slave scripts are mutex protected since decode and rendering may
|
||||
// happen asynchronously.
|
||||
std::mutex _slaveScriptsMutex;
|
||||
std::queue<std::string> _slaveScriptQueue;
|
||||
std::queue<QueueItem> _masterScriptQueue;
|
||||
|
||||
//parallel variables
|
||||
//std::map<std::string, std::map<std::string, std::string>> _cachedScripts;
|
||||
//std::mutex _cachedScriptsMutex;
|
||||
std::vector<std::string> _scriptsToSync;
|
||||
|
||||
//logging variables
|
||||
// Logging variables
|
||||
bool _logFileExists = false;
|
||||
bool _logScripts = true;
|
||||
std::string _logType;
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
namespace openspace {
|
||||
|
||||
class SceneGraphNode;
|
||||
class SyncBuffer;
|
||||
|
||||
/**
|
||||
* This class still needs some more love. Suggested improvements:
|
||||
|
||||
@@ -110,9 +110,9 @@ public:
|
||||
*/
|
||||
template <class T>
|
||||
ghoul::TemplateFactory<T>* factory() const;
|
||||
|
||||
private:
|
||||
|
||||
std::string generateJson() const override;
|
||||
private:
|
||||
|
||||
/// Singleton member for the Factory Manager
|
||||
static FactoryManager* _manager;
|
||||
|
||||
@@ -33,6 +33,7 @@ set(HEADER_FILES
|
||||
include/serverinterface.h
|
||||
include/topics/authorizationtopic.h
|
||||
include/topics/bouncetopic.h
|
||||
include/topics/documentationtopic.h
|
||||
include/topics/getpropertytopic.h
|
||||
include/topics/luascripttopic.h
|
||||
include/topics/setpropertytopic.h
|
||||
@@ -53,6 +54,7 @@ set(SOURCE_FILES
|
||||
src/serverinterface.cpp
|
||||
src/topics/authorizationtopic.cpp
|
||||
src/topics/bouncetopic.cpp
|
||||
src/topics/documentationtopic.cpp
|
||||
src/topics/getpropertytopic.cpp
|
||||
src/topics/luascripttopic.cpp
|
||||
src/topics/setpropertytopic.cpp
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <openspace/json.h>
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/misc/dictionary.h>
|
||||
|
||||
namespace openspace::properties {
|
||||
|
||||
@@ -53,6 +54,13 @@ void to_json(nlohmann::json& j, const Renderable* pR);
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
namespace ghoul {
|
||||
|
||||
void to_json(nlohmann::json& j, const Dictionary& d);
|
||||
void to_json(nlohmann::json& j, const Dictionary* d);
|
||||
|
||||
} // namespace ghoul
|
||||
|
||||
namespace glm {
|
||||
|
||||
void to_json(nlohmann::json& j, const dvec3& v);
|
||||
|
||||
@@ -22,58 +22,22 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_CORE___NETWORKENGINE___H__
|
||||
#define __OPENSPACE_CORE___NETWORKENGINE___H__
|
||||
#ifndef __OPENSPACE_MODULE_SERVER___DOCUMENTATION_TOPIC___H__
|
||||
#define __OPENSPACE_MODULE_SERVER___DOCUMENTATION_TOPIC___H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <modules/server/include/topics/topic.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class NetworkEngine {
|
||||
class DocumentationTopic : public Topic {
|
||||
public:
|
||||
using MessageIdentifier = uint16_t;
|
||||
DocumentationTopic() = default;
|
||||
virtual ~DocumentationTopic() = default;
|
||||
|
||||
NetworkEngine();
|
||||
|
||||
// Receiving messages
|
||||
bool handleMessage(const std::string& message);
|
||||
|
||||
// Sending messages
|
||||
void publishStatusMessage();
|
||||
void publishIdentifierMappingMessage();
|
||||
void publishMessage(MessageIdentifier identifier, std::vector<char> message);
|
||||
void sendMessages();
|
||||
|
||||
// Initial Connection Messages
|
||||
void setInitialConnectionMessage(MessageIdentifier identifier,
|
||||
std::vector<char> message);
|
||||
void sendInitialInformation();
|
||||
|
||||
// Background
|
||||
MessageIdentifier identifier(std::string name);
|
||||
|
||||
private:
|
||||
std::map<std::string, MessageIdentifier> _identifiers;
|
||||
MessageIdentifier _lastAssignedIdentifier = MessageIdentifier(-1);
|
||||
|
||||
struct Message {
|
||||
MessageIdentifier identifer;
|
||||
std::vector<char> body;
|
||||
};
|
||||
std::vector<Message> _messagesToSend;
|
||||
|
||||
std::vector<Message> _initialConnectionMessages;
|
||||
|
||||
bool _shouldPublishStatusMessage = true;
|
||||
|
||||
MessageIdentifier _statusMessageIdentifier;
|
||||
MessageIdentifier _identifierMappingIdentifier;
|
||||
MessageIdentifier _initialMessageFinishedIdentifier;
|
||||
void handleJson(const nlohmann::json& json) override;
|
||||
bool isDone() const override;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_CORE___NETWORKENGINE___H__
|
||||
#endif // __OPENSPACE_MODULE_SERVER___DOCUMENTATION_TOPIC___H__
|
||||
@@ -36,6 +36,10 @@ public:
|
||||
|
||||
void handleJson(const nlohmann::json& json) override;
|
||||
bool isDone() const override;
|
||||
private:
|
||||
void runScript(const std::string& script, bool returnValue);
|
||||
|
||||
bool _waitingForReturnValue = true;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <modules/server/include/connection.h>
|
||||
#include <modules/server/include/topics/topic.h>
|
||||
#include <openspace/engine/globalscallbacks.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <ghoul/io/socket/socket.h>
|
||||
#include <ghoul/io/socket/tcpsocketserver.h>
|
||||
@@ -86,7 +88,10 @@ void ServerModule::internalInitialize(const ghoul::Dictionary& configuration) {
|
||||
std::unique_ptr<ServerInterface> serverInterface =
|
||||
ServerInterface::createFromDictionary(interfaceDictionary);
|
||||
|
||||
serverInterface->initialize();
|
||||
|
||||
if (global::windowDelegate.isMaster()) {
|
||||
serverInterface->initialize();
|
||||
}
|
||||
|
||||
_interfaceOwner.addPropertySubOwner(serverInterface.get());
|
||||
|
||||
@@ -101,6 +106,10 @@ void ServerModule::internalInitialize(const ghoul::Dictionary& configuration) {
|
||||
}
|
||||
|
||||
void ServerModule::preSync() {
|
||||
if (!global::windowDelegate.isMaster()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up new connections.
|
||||
for (std::unique_ptr<ServerInterface>& serverInterface : _interfaces) {
|
||||
if (!serverInterface->isEnabled()) {
|
||||
@@ -165,7 +174,9 @@ void ServerModule::cleanUpFinishedThreads() {
|
||||
|
||||
void ServerModule::disconnectAll() {
|
||||
for (std::unique_ptr<ServerInterface>& serverInterface : _interfaces) {
|
||||
serverInterface->deinitialize();
|
||||
if (global::windowDelegate.isMaster()) {
|
||||
serverInterface->deinitialize();
|
||||
}
|
||||
}
|
||||
|
||||
for (ConnectionData& connectionData : _connections) {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <modules/server/include/topics/authorizationtopic.h>
|
||||
#include <modules/server/include/topics/bouncetopic.h>
|
||||
#include <modules/server/include/topics/documentationtopic.h>
|
||||
#include <modules/server/include/topics/getpropertytopic.h>
|
||||
#include <modules/server/include/topics/luascripttopic.h>
|
||||
#include <modules/server/include/topics/setpropertytopic.h>
|
||||
@@ -52,6 +53,7 @@ namespace {
|
||||
|
||||
constexpr const char* VersionTopicKey = "version";
|
||||
constexpr const char* AuthenticationTopicKey = "authorize";
|
||||
constexpr const char* DocumentationTopicKey = "documentation";
|
||||
constexpr const char* GetPropertyTopicKey = "get";
|
||||
constexpr const char* LuaScriptTopicKey = "luascript";
|
||||
constexpr const char* SetPropertyTopicKey = "set";
|
||||
@@ -81,6 +83,7 @@ Connection::Connection(std::unique_ptr<ghoul::io::Socket> s,
|
||||
}
|
||||
);
|
||||
|
||||
_topicFactory.registerClass<DocumentationTopic>(DocumentationTopicKey);
|
||||
_topicFactory.registerClass<GetPropertyTopic>(GetPropertyTopicKey);
|
||||
_topicFactory.registerClass<LuaScriptTopic>(LuaScriptTopicKey);
|
||||
_topicFactory.registerClass<SetPropertyTopic>(SetPropertyTopicKey);
|
||||
|
||||
@@ -68,6 +68,68 @@ void to_json(json& j, const PropertyOwner* p) {
|
||||
|
||||
} // namespace openspace::properties
|
||||
|
||||
namespace ghoul {
|
||||
|
||||
void to_json(json& j, const Dictionary& dictionary) {
|
||||
json object;
|
||||
for (const std::string& key : dictionary.keys()) {
|
||||
if (dictionary.hasValue<glm::vec4>(key)) {
|
||||
const glm::vec4 v = dictionary.value<glm::vec4>(key);
|
||||
object[key] = json::array({ v[0], v[1], v[2], v[3] });
|
||||
}
|
||||
else if (dictionary.hasValue<glm::vec3>(key)) {
|
||||
const glm::vec3 v = dictionary.value<glm::vec3>(key);
|
||||
object[key] = json::array({ v[0], v[1], v[2] });
|
||||
}
|
||||
else if (dictionary.hasValue<glm::vec2>(key)) {
|
||||
const glm::vec2 v = dictionary.value<glm::vec2>(key);
|
||||
object[key] = json::array({ v[0], v[1] });
|
||||
}
|
||||
else if (dictionary.hasValue<glm::dvec4>(key)) {
|
||||
const glm::dvec4 v = dictionary.value<glm::dvec4>(key);
|
||||
object[key] = json::array({ v[0], v[1], v[2], v[3] });
|
||||
}
|
||||
else if (dictionary.hasValue<glm::dvec3>(key)) {
|
||||
const glm::dvec3 v = dictionary.value<glm::dvec3>(key);
|
||||
object[key] = json::array({ v[0], v[1], v[2] });
|
||||
}
|
||||
else if (dictionary.hasValue<glm::dvec2>(key)) {
|
||||
const glm::dvec2 v = dictionary.value<glm::dvec2>(key);
|
||||
object[key] = json::array({ v[0], v[1] });
|
||||
}
|
||||
else if (dictionary.hasValue<float>(key)) {
|
||||
object[key] = dictionary.value<float>(key);
|
||||
}
|
||||
else if (dictionary.hasValue<double>(key)) {
|
||||
object[key] = dictionary.value<double>(key);
|
||||
}
|
||||
else if (dictionary.hasValue<int>(key)) {
|
||||
object[key] = dictionary.value<int>(key);
|
||||
}
|
||||
else if (dictionary.hasValue<unsigned int>(key)) {
|
||||
object[key] = dictionary.value<unsigned int>(key);
|
||||
}
|
||||
else if (dictionary.hasValue<std::string>(key)) {
|
||||
object[key] = dictionary.value<std::string>(key);
|
||||
}
|
||||
else if (dictionary.hasValue<Dictionary>(key)) {
|
||||
json child;
|
||||
to_json(child, dictionary.value<Dictionary>(key));
|
||||
object[key] = child;
|
||||
}
|
||||
else {
|
||||
object[key] = nullptr;
|
||||
}
|
||||
}
|
||||
j = object;
|
||||
}
|
||||
|
||||
void to_json(json& j, const Dictionary* d) {
|
||||
j = *d;
|
||||
}
|
||||
|
||||
} // namespace ghoul
|
||||
|
||||
namespace openspace {
|
||||
|
||||
void to_json(json& j, const SceneGraphNode& n) {
|
||||
|
||||
72
modules/server/src/topics/documentationtopic.cpp
Normal file
72
modules/server/src/topics/documentationtopic.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2019 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/server/include/topics/documentationtopic.h>
|
||||
|
||||
#include <modules/server/include/connection.h>
|
||||
#include <modules/server/include/jsonconverters.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <openspace/util/factorymanager.h>
|
||||
#include <openspace/interaction/keybindingmanager.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "DocumentationTopic";
|
||||
constexpr const char* KeyType = "type";
|
||||
constexpr const char* TypeLua = "lua";
|
||||
constexpr const char* TypeFactories = "factories";
|
||||
constexpr const char* TypeKeyboard = "keyboard";
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
void DocumentationTopic::handleJson(const nlohmann::json& json) {
|
||||
std::string requestedType = json.at(KeyType).get<std::string>();
|
||||
|
||||
nlohmann::json response;
|
||||
|
||||
// @emiax: Proposed future refector.
|
||||
// Do not parse generated json. Instead implement ability to get
|
||||
// ghoul::Dictionary objects from ScriptEngine, FactoryManager, and KeybindingManager.
|
||||
if (requestedType == TypeLua) {
|
||||
response = json::parse(global::scriptEngine.generateJson());
|
||||
} else if (requestedType == TypeFactories) {
|
||||
response = json::parse(FactoryManager::ref().generateJson());
|
||||
} else if (requestedType == TypeKeyboard) {
|
||||
response = json::parse(global::keybindingManager.generateJson());
|
||||
}
|
||||
|
||||
_connection->sendJson(wrappedPayload(response));
|
||||
}
|
||||
|
||||
bool DocumentationTopic::isDone() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
@@ -24,25 +24,137 @@
|
||||
|
||||
#include <modules/server/include/topics/luascripttopic.h>
|
||||
|
||||
#include <modules/server/include/jsonconverters.h>
|
||||
#include <modules/server/include/connection.h>
|
||||
#include <openspace/json.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/dictionary.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char* ScriptKey = "script";
|
||||
constexpr const char* KeyScript = "script";
|
||||
constexpr const char* KeyFunction = "function";
|
||||
constexpr const char* KeyArguments = "arguments";
|
||||
constexpr const char* KeyReturn = "return";
|
||||
constexpr const char* TypeKey = "type";
|
||||
constexpr const char* _loggerCat = "LuaScriptTopic";
|
||||
|
||||
std::string formatLua(const nlohmann::json::const_iterator& it);
|
||||
|
||||
std::string escapeLuaString(const std::string& s) {
|
||||
std::string output;
|
||||
for (const char& c : s) {
|
||||
switch (c) {
|
||||
case '\a': output += "\\\a"; break;
|
||||
case '\b': output += "\\\b"; break;
|
||||
case '\f': output += "\\\f"; break;
|
||||
case '\n' : output += "\\\n"; break;
|
||||
case '\r' : output += "\\\r"; break;
|
||||
case '\t' : output += "\\\t"; break;
|
||||
case '\v' : output += "\\\v"; break;
|
||||
case '\\' : output += "\\\\"; break;
|
||||
case '"' : output += "\\\""; break;
|
||||
case '\'' : output += "\\'"; break;
|
||||
default: output += c;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string formatLuaString(const std::string& s) {
|
||||
return "\"" + escapeLuaString(s) + "\"";
|
||||
}
|
||||
|
||||
std::string formatKeyValuePair(const nlohmann::json::const_iterator& it) {
|
||||
return "[" + formatLuaString(it.key()) + "] = " + formatLua(it);
|
||||
}
|
||||
|
||||
std::string formatLuaTable(const nlohmann::json& json) {
|
||||
std::string output = "{";
|
||||
auto it = json.begin();
|
||||
for (size_t i = 0; i < json.size(); ++i, ++it) {
|
||||
output += formatKeyValuePair(it);
|
||||
if (i < json.size() - 1) {
|
||||
output += ",";
|
||||
}
|
||||
}
|
||||
return output + "}";
|
||||
}
|
||||
|
||||
std::string formatLua(const nlohmann::json::const_iterator& it) {
|
||||
if (it->is_object()) {
|
||||
return formatLuaTable(it->get<nlohmann::json>());
|
||||
}
|
||||
if (it->is_number()) {
|
||||
return fmt::format("{:E}", it->get<double>());
|
||||
}
|
||||
if (it->is_string()) {
|
||||
return formatLuaString(it->get<std::string>());
|
||||
}
|
||||
if (it->is_boolean()) {
|
||||
return it->get<bool>() ? "true" : "false";
|
||||
}
|
||||
if (it->is_null()) {
|
||||
return "nil";
|
||||
}
|
||||
throw ghoul::lua::LuaFormatException("Format error.");
|
||||
}
|
||||
|
||||
std::string generateScript(const std::string& function,
|
||||
const std::vector<std::string>& args)
|
||||
{
|
||||
std::string script = "return " + function + "(";
|
||||
auto it = args.begin();
|
||||
for (size_t i = 0; i < args.size(); ++i, ++it) {
|
||||
script += *it;
|
||||
if (i < args.size() - 1) {
|
||||
script += ",";
|
||||
}
|
||||
}
|
||||
return script + ")";
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
void LuaScriptTopic::handleJson(const nlohmann::json& json) {
|
||||
try {
|
||||
std::string script = json.at(ScriptKey).get<std::string>();
|
||||
global::scriptEngine.queueScript(
|
||||
std::move(script),
|
||||
scripting::ScriptEngine::RemoteScripting::No
|
||||
);
|
||||
nlohmann::json::const_iterator script = json.find(KeyScript);
|
||||
nlohmann::json::const_iterator function = json.find(KeyFunction);
|
||||
|
||||
if (script != json.end() && script->is_string()) {
|
||||
std::string luaScript = script->get<std::string>();
|
||||
nlohmann::json::const_iterator ret = json.find(KeyReturn);
|
||||
bool shouldReturn = (ret != json.end()) &&
|
||||
ret->is_boolean() &&
|
||||
ret->get<bool>();
|
||||
|
||||
runScript(luaScript, shouldReturn);
|
||||
}
|
||||
else if (function != json.end() && function->is_string()) {
|
||||
std::string luaFunction = function->get<std::string>();
|
||||
nlohmann::json::const_iterator ret = json.find(KeyReturn);
|
||||
bool shouldReturn = (ret != json.end()) &&
|
||||
ret->is_boolean() &&
|
||||
ret->get<bool>();
|
||||
|
||||
nlohmann::json::const_iterator args = json.find(KeyArguments);
|
||||
if (!args->is_array()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> formattedArgs;
|
||||
formattedArgs.reserve(args->size());
|
||||
for (auto it = args->begin(); it != args->end(); ++it) {
|
||||
formattedArgs.push_back(formatLua(it));
|
||||
}
|
||||
|
||||
std::string luaScript = generateScript(luaFunction, formattedArgs);
|
||||
runScript(luaScript, shouldReturn);
|
||||
}
|
||||
}
|
||||
catch (const std::out_of_range& e) {
|
||||
LERROR("Could not run script -- key or value is missing in payload");
|
||||
@@ -50,8 +162,29 @@ void LuaScriptTopic::handleJson(const nlohmann::json& json) {
|
||||
}
|
||||
}
|
||||
|
||||
void LuaScriptTopic::runScript(const std::string& script, bool shouldReturn) {
|
||||
scripting::ScriptEngine::ScriptCallback callback;
|
||||
if (shouldReturn) {
|
||||
callback = [this](ghoul::Dictionary data) {
|
||||
nlohmann::json j = data;
|
||||
_connection->sendJson(wrappedPayload(j));
|
||||
_waitingForReturnValue = false;
|
||||
};
|
||||
_waitingForReturnValue = true;
|
||||
}
|
||||
else {
|
||||
_waitingForReturnValue = false;
|
||||
}
|
||||
|
||||
global::scriptEngine.queueScript(
|
||||
std::move(script),
|
||||
scripting::ScriptEngine::RemoteScripting::No,
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
bool LuaScriptTopic::isDone() const {
|
||||
return true;
|
||||
return !_waitingForReturnValue;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -68,10 +68,11 @@ void SubscriptionTopic::resetCallbacks() {
|
||||
}
|
||||
|
||||
void SubscriptionTopic::handleJson(const nlohmann::json& json) {
|
||||
std::string key = json.at(PropertyKey).get<std::string>();
|
||||
const std::string& event = json.at(EventKey).get<std::string>();
|
||||
|
||||
if (event == StartSubscription) {
|
||||
std::string key = json.at(PropertyKey).get<std::string>();
|
||||
|
||||
_prop = property(key);
|
||||
resetCallbacks();
|
||||
|
||||
|
||||
@@ -37,8 +37,6 @@
|
||||
#include <fstream>
|
||||
|
||||
namespace {
|
||||
constexpr const char* PlaybookIdentifierName = "HongKang";
|
||||
|
||||
double ephemerisTimeFromMissionElapsedTime(double met, double metReference) {
|
||||
const double referenceET = openspace::SpiceManager::ref().ephemerisTimeFromDate(
|
||||
"2015-07-14T11:50:00.00"
|
||||
@@ -144,13 +142,11 @@ bool HongKangParser::create() {
|
||||
}
|
||||
size_t position = _fileName.find_last_of('.') + 1;
|
||||
if (position == 0 || position == std::string::npos) {
|
||||
sendPlaybookInformation(PlaybookIdentifierName);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& extension = ghoul::filesystem::File(_fileName).fileExtension();
|
||||
if (extension != "txt") {
|
||||
sendPlaybookInformation(PlaybookIdentifierName);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -279,7 +275,6 @@ bool HongKangParser::create() {
|
||||
}
|
||||
}
|
||||
|
||||
sendPlaybookInformation(PlaybookIdentifierName);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "InstrumentTimesParser";
|
||||
|
||||
constexpr const char* PlaybookIdentifierName = "InstrumentTimesParser";
|
||||
constexpr const char* KeyTargetBody = "Target.Body";
|
||||
constexpr const char* KeyInstruments = "Instruments";
|
||||
constexpr const char* KeyInstrument = "Instrument";
|
||||
@@ -156,7 +155,6 @@ bool InstrumentTimesParser::create() {
|
||||
}
|
||||
);
|
||||
|
||||
sendPlaybookInformation(PlaybookIdentifierName);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,6 @@ namespace {
|
||||
constexpr const char* _loggerCat = "LabelParser";
|
||||
constexpr const char* keySpecs = "Read";
|
||||
constexpr const char* keyConvert = "Convert";
|
||||
|
||||
constexpr const char* PlaybookIdentifierName = "LabelParser";
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
@@ -325,7 +323,6 @@ bool LabelParser::create() {
|
||||
for (const std::pair<const std::string, ImageSubset>& target : _subsetMap) {
|
||||
_instrumentTimes.emplace_back(lblName, _subsetMap[target.first]._range);
|
||||
}
|
||||
sendPlaybookInformation(PlaybookIdentifierName);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,6 @@
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "SequenceParser";
|
||||
|
||||
constexpr const char* PlaybookIdentifierName = "Playbook";
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
@@ -62,134 +60,4 @@ std::map<std::string, std::unique_ptr<Decoder>>& SequenceParser::translations()
|
||||
return _fileTranslation;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void writeToBuffer(std::vector<char>& buffer, size_t& currentWriteLocation, T value) {
|
||||
if ((currentWriteLocation + sizeof(T)) > buffer.size()) {
|
||||
buffer.resize(2 * buffer.size());
|
||||
}
|
||||
|
||||
std::memmove(
|
||||
buffer.data() + currentWriteLocation,
|
||||
reinterpret_cast<const void*>(&value),
|
||||
sizeof(T)
|
||||
);
|
||||
currentWriteLocation += sizeof(T);
|
||||
}
|
||||
|
||||
template <>
|
||||
void writeToBuffer<std::string>(std::vector<char>& buffer, size_t& currentWriteLocation,
|
||||
std::string value)
|
||||
{
|
||||
if ((currentWriteLocation + sizeof(uint8_t) + value.size()) > buffer.size()) {
|
||||
buffer.resize(2 * buffer.size());
|
||||
}
|
||||
|
||||
uint8_t length = static_cast<uint8_t>(value.size());
|
||||
std::memcpy(buffer.data() + currentWriteLocation, &length, sizeof(uint8_t));
|
||||
currentWriteLocation += sizeof(uint8_t);
|
||||
|
||||
std::memmove(buffer.data() + currentWriteLocation, value.data(), length);
|
||||
currentWriteLocation += length;
|
||||
}
|
||||
|
||||
void SequenceParser::sendPlaybookInformation(const std::string& name) {
|
||||
std::string fullName = std::string(PlaybookIdentifierName) + "_" + name;
|
||||
_messageIdentifier = global::networkEngine.identifier(fullName);
|
||||
|
||||
std::vector<char> buffer(1024);
|
||||
size_t currentWriteLocation = 0;
|
||||
|
||||
// Protocol:
|
||||
// 4 bytes: Total number of bytes sent
|
||||
// 1 byte : Number of Targets (i)
|
||||
// i times: 1 byte (id), 1 byte (length j of name), j bytes (name)
|
||||
// 1 byte : Number of Instruments (i)
|
||||
// i times: 1 byte (id), 1 byte (length j of name), j bytes (name)
|
||||
// 4 byte: Number (n) of images
|
||||
// n times: 8 byte (beginning time), 8 byte (ending time), 1 byte (target id),
|
||||
// 2 byte (instrument id)
|
||||
|
||||
std::map<std::string, uint8_t> targetMap;
|
||||
uint8_t currentTargetId = 0;
|
||||
for (const std::pair<const std::string, ImageSubset>& target : _subsetMap) {
|
||||
if (targetMap.find(target.first) == targetMap.end()) {
|
||||
targetMap[target.first] = currentTargetId++;
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, uint16_t> instrumentMap;
|
||||
uint16_t currentInstrumentId = 1;
|
||||
for (std::pair<const std::string, ImageSubset>& target : _subsetMap) {
|
||||
for (const Image& image : target.second._subset) {
|
||||
for (const std::string& instrument : image.activeInstruments) {
|
||||
if (instrumentMap.find(instrument) == instrumentMap.end()) {
|
||||
instrumentMap[instrument] = currentInstrumentId;
|
||||
currentInstrumentId = currentInstrumentId << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeToBuffer(buffer, currentWriteLocation, uint8_t(targetMap.size()));
|
||||
for (const std::pair<std::string, uint8_t>& p : targetMap) {
|
||||
writeToBuffer(buffer, currentWriteLocation, p.second);
|
||||
writeToBuffer(buffer, currentWriteLocation, p.first);
|
||||
}
|
||||
|
||||
writeToBuffer(buffer, currentWriteLocation, uint8_t(instrumentMap.size()));
|
||||
for (const std::pair<std::string, uint16_t>& p : instrumentMap) {
|
||||
writeToBuffer(buffer, currentWriteLocation, p.second);
|
||||
writeToBuffer(buffer, currentWriteLocation, p.first);
|
||||
}
|
||||
|
||||
uint32_t allImages = 0;
|
||||
for (const std::pair<const std::string, ImageSubset>& target : _subsetMap) {
|
||||
allImages += static_cast<uint32_t>(target.second._subset.size());
|
||||
}
|
||||
writeToBuffer(buffer, currentWriteLocation, allImages);
|
||||
|
||||
for (const std::pair<const std::string, ImageSubset>& target : _subsetMap) {
|
||||
for (const Image& image : target.second._subset){
|
||||
writeToBuffer(buffer, currentWriteLocation, image.timeRange.start);
|
||||
writeToBuffer(buffer, currentWriteLocation, image.timeRange.end);
|
||||
|
||||
std::string timeBegin = SpiceManager::ref().dateFromEphemerisTime(
|
||||
image.timeRange.start
|
||||
);
|
||||
std::string timeEnd = SpiceManager::ref().dateFromEphemerisTime(
|
||||
image.timeRange.end
|
||||
);
|
||||
|
||||
writeToBuffer(buffer, currentWriteLocation, timeBegin);
|
||||
writeToBuffer(buffer, currentWriteLocation, timeEnd);
|
||||
|
||||
uint8_t targetId = targetMap[target.first];
|
||||
writeToBuffer(buffer, currentWriteLocation, targetId);
|
||||
uint16_t totalInstrumentId = 0;
|
||||
if (image.activeInstruments.empty()) {
|
||||
LERROR("Image had no active instruments");
|
||||
}
|
||||
|
||||
for (const std::string& instrument : image.activeInstruments) {
|
||||
uint16_t thisInstrumentId = instrumentMap[instrument];
|
||||
totalInstrumentId |= thisInstrumentId;
|
||||
}
|
||||
writeToBuffer(buffer, currentWriteLocation, totalInstrumentId);
|
||||
}
|
||||
}
|
||||
|
||||
union {
|
||||
uint32_t value;
|
||||
std::array<char, sizeof(uint32_t)> data;
|
||||
} sizeBuffer = {};
|
||||
sizeBuffer.value = static_cast<uint32_t>(currentWriteLocation);
|
||||
buffer.insert(buffer.begin(), sizeBuffer.data.begin(), sizeBuffer.data.end());
|
||||
currentWriteLocation += sizeof(uint32_t);
|
||||
|
||||
buffer.resize(currentWriteLocation);
|
||||
|
||||
//OsEng.networkEngine()->publishMessage(PlaybookIdentifier, buffer);
|
||||
global::networkEngine.setInitialConnectionMessage(_messageIdentifier, buffer);
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#ifndef __OPENSPACE_MODULE_SPACECRAFTINSTRUMENTS___SEQUENCEPARSER___H__
|
||||
#define __OPENSPACE_MODULE_SPACECRAFTINSTRUMENTS___SEQUENCEPARSER___H__
|
||||
|
||||
#include <openspace/network/networkengine.h>
|
||||
#include <modules/spacecraftinstruments/util/decoder.h>
|
||||
#include <openspace/util/timerange.h>
|
||||
#include <modules/spacecraftinstruments/util/image.h>
|
||||
@@ -48,16 +47,12 @@ public:
|
||||
const std::vector<double>& getCaptureProgression() const;
|
||||
|
||||
protected:
|
||||
void sendPlaybookInformation(const std::string& name);
|
||||
|
||||
std::map<std::string, ImageSubset> _subsetMap;
|
||||
std::vector<std::pair<std::string, TimeRange>> _instrumentTimes;
|
||||
std::vector<std::pair<double, std::string>> _targetTimes;
|
||||
std::vector<double> _captureProgression;
|
||||
|
||||
std::map<std::string, std::unique_ptr<Decoder>> _fileTranslation;
|
||||
|
||||
NetworkEngine::MessageIdentifier _messageIdentifier;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -63,7 +63,6 @@ set(OPENSPACE_SOURCE
|
||||
${OPENSPACE_BASE_DIR}/src/mission/mission.cpp
|
||||
${OPENSPACE_BASE_DIR}/src/mission/missionmanager.cpp
|
||||
${OPENSPACE_BASE_DIR}/src/mission/missionmanager_lua.inl
|
||||
${OPENSPACE_BASE_DIR}/src/network/networkengine.cpp
|
||||
${OPENSPACE_BASE_DIR}/src/network/parallelconnection.cpp
|
||||
${OPENSPACE_BASE_DIR}/src/network/parallelpeer.cpp
|
||||
${OPENSPACE_BASE_DIR}/src/network/parallelpeer_lua.inl
|
||||
@@ -245,7 +244,6 @@ set(OPENSPACE_HEADER
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/interaction/shortcutmanager.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/mission/mission.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/mission/missionmanager.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/network/networkengine.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/network/parallelconnection.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/network/parallelpeer.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/network/parallelserver.h
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#include <openspace/interaction/sessionrecording.h>
|
||||
#include <openspace/interaction/shortcutmanager.h>
|
||||
#include <openspace/mission/missionmanager.h>
|
||||
#include <openspace/network/networkengine.h>
|
||||
#include <openspace/network/parallelpeer.h>
|
||||
#include <openspace/performance/performancemanager.h>
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
@@ -94,11 +93,6 @@ ModuleEngine& gModuleEngine() {
|
||||
return g;
|
||||
}
|
||||
|
||||
NetworkEngine& gNetworkEngine() {
|
||||
static NetworkEngine g;
|
||||
return g;
|
||||
}
|
||||
|
||||
OpenSpaceEngine& gOpenSpaceEngine() {
|
||||
static OpenSpaceEngine g;
|
||||
return g;
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#include <openspace/interaction/sessionrecording.h>
|
||||
#include <openspace/interaction/navigationhandler.h>
|
||||
#include <openspace/interaction/orbitalnavigator.h>
|
||||
#include <openspace/network/networkengine.h>
|
||||
#include <openspace/network/parallelpeer.h>
|
||||
#include <openspace/performance/performancemeasurement.h>
|
||||
#include <openspace/performance/performancemanager.h>
|
||||
@@ -1246,9 +1245,6 @@ void OpenSpaceEngine::mouseScrollWheelCallback(double posX, double posY) {
|
||||
|
||||
std::vector<char> OpenSpaceEngine::encode() {
|
||||
std::vector<char> buffer = global::syncEngine.encodeSyncables();
|
||||
global::networkEngine.publishStatusMessage();
|
||||
global::networkEngine.sendMessages();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ SyncEngine::SyncEngine(unsigned int syncBufferSize)
|
||||
ghoul_assert(syncBufferSize > 0, "syncBufferSize must be bigger than 0");
|
||||
}
|
||||
|
||||
// should be called on sgct master
|
||||
// Should be called on sgct master
|
||||
std::vector<char> SyncEngine::encodeSyncables() {
|
||||
for (Syncable* syncable : _syncables) {
|
||||
syncable->encode(&_syncBuffer);
|
||||
@@ -45,17 +45,9 @@ std::vector<char> SyncEngine::encodeSyncables() {
|
||||
std::vector<char> data = _syncBuffer.data();
|
||||
_syncBuffer.reset();
|
||||
return data;
|
||||
|
||||
//_dataStream.resize(_encodeOffset);
|
||||
//_synchronizationBuffer->setVal(_dataStream);
|
||||
//sgct::SharedData::instance()->writeVector(_synchronizationBuffer.get());
|
||||
//_dataStream.resize(_n);
|
||||
//_encodeOffset = 0;
|
||||
//_decodeOffset = 0;
|
||||
//_syncBuffer.write();
|
||||
}
|
||||
|
||||
//should be called on sgct slaves
|
||||
// Should be called on sgct slaves
|
||||
void SyncEngine::decodeSyncables(std::vector<char> data) {
|
||||
_syncBuffer.setData(std::move(data));
|
||||
for (Syncable* syncable : _syncables) {
|
||||
@@ -78,14 +70,14 @@ void SyncEngine::postSynchronization(IsMaster isMaster) {
|
||||
}
|
||||
|
||||
void SyncEngine::addSyncable(Syncable* syncable) {
|
||||
ghoul_assert(syncable, "synable must not be nullptr");
|
||||
ghoul_assert(syncable, "Syncable must not be nullptr");
|
||||
|
||||
_syncables.push_back(syncable);
|
||||
}
|
||||
|
||||
void SyncEngine::addSyncables(const std::vector<Syncable*>& syncables) {
|
||||
for (Syncable* syncable : syncables) {
|
||||
ghoul_assert(syncable, "syncables must not contain any nullptr");
|
||||
ghoul_assert(syncable, "Syncables must not contain any nullptr");
|
||||
addSyncable(syncable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2019 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <openspace/network/networkengine.h>
|
||||
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <openspace/util/timemanager.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "NetworkEngine";
|
||||
|
||||
constexpr const char* StatusMessageIdentifierName = "StatusMessage";
|
||||
constexpr const char* MappingIdentifierIdentifierName = "IdentifierMapping";
|
||||
constexpr const char* InitialMessageFinishedIdentifierName = "InitialMessageFinished";
|
||||
|
||||
constexpr const char MessageTypeLuaScript = '0';
|
||||
constexpr const char MessageTypeExternalControlConnected = '1';
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
NetworkEngine::NetworkEngine() {
|
||||
static_assert(
|
||||
sizeof(MessageIdentifier) == 2,
|
||||
"MessageIdentifier has to be 2 bytes or dependent applications will break"
|
||||
);
|
||||
_statusMessageIdentifier = identifier(StatusMessageIdentifierName);
|
||||
_identifierMappingIdentifier = identifier(MappingIdentifierIdentifierName);
|
||||
_initialMessageFinishedIdentifier = identifier(InitialMessageFinishedIdentifierName);
|
||||
}
|
||||
|
||||
bool NetworkEngine::handleMessage(const std::string& message) {
|
||||
// The first byte determines the type of message
|
||||
const char type = message[0];
|
||||
switch (type) {
|
||||
case MessageTypeLuaScript: // LuaScript
|
||||
global::scriptEngine.queueScript(
|
||||
message.substr(1),
|
||||
scripting::ScriptEngine::RemoteScripting::No
|
||||
);
|
||||
return true;
|
||||
case MessageTypeExternalControlConnected:
|
||||
publishIdentifierMappingMessage();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
sendInitialInformation();
|
||||
return true;
|
||||
default:
|
||||
LERROR(fmt::format("Unknown type '{}'", type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkEngine::publishStatusMessage() {
|
||||
if (!_shouldPublishStatusMessage ||
|
||||
!global::windowDelegate.isExternalControlConnected())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Protocol:
|
||||
// 8 bytes: time as a ET double
|
||||
// 24 bytes: time as a UTC string
|
||||
// 8 bytes: delta time as double
|
||||
// Total: 40
|
||||
|
||||
const Time& currentTime = global::timeManager.time();
|
||||
|
||||
uint16_t messageSize = 0;
|
||||
|
||||
const double time = currentTime.j2000Seconds();
|
||||
const std::string timeString = currentTime.UTC();
|
||||
double delta = global::timeManager.deltaTime();
|
||||
|
||||
|
||||
messageSize += sizeof(time);
|
||||
messageSize += static_cast<uint16_t>(timeString.length());
|
||||
messageSize += sizeof(delta);
|
||||
|
||||
ghoul_assert(messageSize == 40, "Message size is not correct");
|
||||
|
||||
unsigned int currentLocation = 0;
|
||||
std::vector<char> buffer(messageSize);
|
||||
|
||||
std::memmove(buffer.data() + currentLocation, &time, sizeof(time));
|
||||
currentLocation += sizeof(time);
|
||||
std::memmove(
|
||||
buffer.data() + currentLocation,
|
||||
timeString.c_str(), timeString.length()
|
||||
);
|
||||
currentLocation += static_cast<unsigned int>(timeString.length());
|
||||
std::memmove(buffer.data() + currentLocation, &delta, sizeof(delta));
|
||||
|
||||
publishMessage(_statusMessageIdentifier, std::move(buffer));
|
||||
}
|
||||
|
||||
void NetworkEngine::publishIdentifierMappingMessage() {
|
||||
size_t bufferSize = sizeof(uint16_t);
|
||||
for (const std::pair<std::string, MessageIdentifier>& i : _identifiers) {
|
||||
bufferSize += sizeof(MessageIdentifier);
|
||||
bufferSize += i.first.size() + 1; // +1 for \0 terminating character
|
||||
}
|
||||
|
||||
std::vector<char> buffer(bufferSize);
|
||||
size_t currentWritingPosition = 0;
|
||||
uint16_t size = static_cast<uint16_t>(_identifiers.size());
|
||||
std::memcpy(buffer.data(), &size, sizeof(uint16_t));
|
||||
currentWritingPosition += sizeof(uint16_t);
|
||||
for (const std::pair<std::string, MessageIdentifier>& i : _identifiers) {
|
||||
std::memcpy(
|
||||
buffer.data() + currentWritingPosition,
|
||||
&(i.second), sizeof(MessageIdentifier)
|
||||
);
|
||||
currentWritingPosition += sizeof(MessageIdentifier);
|
||||
uint8_t stringSize = static_cast<uint8_t>(i.first.size());
|
||||
std::memcpy(buffer.data() + currentWritingPosition, &stringSize, sizeof(uint8_t));
|
||||
currentWritingPosition += sizeof(uint8_t);
|
||||
std::memcpy(buffer.data() + currentWritingPosition, i.first.data(), stringSize);
|
||||
currentWritingPosition += i.first.size();
|
||||
}
|
||||
|
||||
publishMessage(_identifierMappingIdentifier, std::move(buffer));
|
||||
}
|
||||
|
||||
|
||||
NetworkEngine::MessageIdentifier NetworkEngine::identifier(std::string name) {
|
||||
auto i = _identifiers.find(name);
|
||||
if (i != _identifiers.end()) {
|
||||
return i->second;
|
||||
}
|
||||
else {
|
||||
_lastAssignedIdentifier++;
|
||||
MessageIdentifier result = _lastAssignedIdentifier;
|
||||
|
||||
_identifiers[std::move(name)] = result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkEngine::publishMessage(MessageIdentifier identifier,
|
||||
std::vector<char> message)
|
||||
{
|
||||
_messagesToSend.push_back({ std::move(identifier), std::move(message) });
|
||||
}
|
||||
|
||||
void NetworkEngine::sendMessages() {
|
||||
if (!global::windowDelegate.isExternalControlConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Message& m : _messagesToSend) {
|
||||
// Protocol:
|
||||
// 2 bytes: type of message as uint16_t
|
||||
// Rest of payload depending on the message type
|
||||
|
||||
union {
|
||||
MessageIdentifier value;
|
||||
std::array<char, 2> data;
|
||||
} identifier = {};
|
||||
identifier.value = m.identifer;
|
||||
|
||||
// Prepending the message identifier to the front
|
||||
m.body.insert(m.body.begin(), identifier.data.begin(), identifier.data.end());
|
||||
global::windowDelegate.sendMessageToExternalControl(m.body);
|
||||
}
|
||||
|
||||
_messagesToSend.clear();
|
||||
}
|
||||
|
||||
void NetworkEngine::sendInitialInformation() {
|
||||
constexpr const int SleepTime = 250;
|
||||
_shouldPublishStatusMessage = false;
|
||||
for (const Message& m : _initialConnectionMessages) {
|
||||
union {
|
||||
MessageIdentifier value;
|
||||
std::array<char, 2> data;
|
||||
} identifier = {};
|
||||
identifier.value = m.identifer;
|
||||
|
||||
std::vector<char> payload = m.body;
|
||||
payload.insert(payload.begin(), identifier.data.begin(), identifier.data.end());
|
||||
global::windowDelegate.sendMessageToExternalControl(payload);
|
||||
LINFO(fmt::format(
|
||||
"Sent initial message: (s={}) [i={}]", m.body.size(), identifier.value
|
||||
));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(SleepTime));
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(4 * SleepTime));
|
||||
|
||||
// Send finished message
|
||||
union {
|
||||
MessageIdentifier value;
|
||||
std::array<char, 2> data;
|
||||
} identifier = {};
|
||||
identifier.value = _initialMessageFinishedIdentifier;
|
||||
|
||||
std::vector<char> d;
|
||||
d.insert(d.begin(), identifier.data.begin(), identifier.data.end());
|
||||
|
||||
global::windowDelegate.sendMessageToExternalControl(d);
|
||||
_shouldPublishStatusMessage = true;
|
||||
}
|
||||
|
||||
void NetworkEngine::setInitialConnectionMessage(MessageIdentifier identifier,
|
||||
std::vector<char> message)
|
||||
{
|
||||
// Add check if a MessageIdentifier already exists ---abock
|
||||
_initialConnectionMessages.push_back({std::move(identifier), std::move(message)});
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
@@ -161,7 +161,7 @@ bool ScriptEngine::hasLibrary(const std::string& name) {
|
||||
return (it != _registeredLibraries.end());
|
||||
}
|
||||
|
||||
bool ScriptEngine::runScript(const std::string& script) {
|
||||
bool ScriptEngine::runScript(const std::string& script, ScriptCallback callback) {
|
||||
if (script.empty()) {
|
||||
LWARNING("Script was empty");
|
||||
return false;
|
||||
@@ -173,7 +173,13 @@ bool ScriptEngine::runScript(const std::string& script) {
|
||||
}
|
||||
|
||||
try {
|
||||
ghoul::lua::runScript(_state, script);
|
||||
if (callback) {
|
||||
ghoul::Dictionary returnValue =
|
||||
ghoul::lua::loadArrayDictionaryFromString(script, _state);
|
||||
callback.value()(returnValue);
|
||||
} else {
|
||||
ghoul::lua::runScript(_state, script);
|
||||
}
|
||||
}
|
||||
catch (const ghoul::lua::LuaLoadingException& e) {
|
||||
LERRORC(e.component, e.message);
|
||||
@@ -183,6 +189,10 @@ bool ScriptEngine::runScript(const std::string& script) {
|
||||
LERRORC(e.component, e.message);
|
||||
return false;
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
LERRORC(e.component, e.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -639,72 +649,82 @@ void ScriptEngine::preSync(bool isMaster) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mutex.lock();
|
||||
if (!_queuedScripts.empty()) {
|
||||
_currentSyncedScript = _queuedScripts.back().first;
|
||||
const bool remoteScripting = _queuedScripts.back().second;
|
||||
std::lock_guard<std::mutex> guard(_slaveScriptsMutex);
|
||||
while (!_incomingScripts.empty()) {
|
||||
QueueItem item = std::move(_incomingScripts.front());
|
||||
_incomingScripts.pop();
|
||||
|
||||
_scriptsToSync.push_back(item.script);
|
||||
const bool remoteScripting = item.remoteScripting;
|
||||
|
||||
// Not really a received script but the master also needs to run the script...
|
||||
_receivedScripts.push_back(_currentSyncedScript);
|
||||
_queuedScripts.pop_back();
|
||||
_masterScriptQueue.push(item);
|
||||
|
||||
if (global::parallelPeer.isHost() && remoteScripting) {
|
||||
global::parallelPeer.sendScript(_currentSyncedScript);
|
||||
global::parallelPeer.sendScript(item.script);
|
||||
}
|
||||
if (global::sessionRecording.isRecording()) {
|
||||
global::sessionRecording.saveScriptKeyframe(_currentSyncedScript);
|
||||
global::sessionRecording.saveScriptKeyframe(item.script);
|
||||
}
|
||||
}
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
void ScriptEngine::encode(SyncBuffer* syncBuffer) {
|
||||
syncBuffer->encode(_currentSyncedScript);
|
||||
_currentSyncedScript.clear();
|
||||
size_t nScripts = _scriptsToSync.size();
|
||||
syncBuffer->encode(nScripts);
|
||||
for (const std::string& s : _scriptsToSync) {
|
||||
syncBuffer->encode(s);
|
||||
}
|
||||
_scriptsToSync.clear();
|
||||
}
|
||||
|
||||
void ScriptEngine::decode(SyncBuffer* syncBuffer) {
|
||||
syncBuffer->decode(_currentSyncedScript);
|
||||
std::lock_guard<std::mutex> guard(_slaveScriptsMutex);
|
||||
size_t nScripts;
|
||||
syncBuffer->decode(nScripts);
|
||||
|
||||
if (!_currentSyncedScript.empty()) {
|
||||
_mutex.lock();
|
||||
_receivedScripts.push_back(_currentSyncedScript);
|
||||
_mutex.unlock();
|
||||
for (size_t i = 0; i < nScripts; ++i) {
|
||||
std::string script;
|
||||
syncBuffer->decode(script);
|
||||
_slaveScriptQueue.push(std::move(script));
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::postSync(bool) {
|
||||
std::vector<std::string> scripts;
|
||||
|
||||
_mutex.lock();
|
||||
scripts.assign(_receivedScripts.begin(), _receivedScripts.end());
|
||||
_receivedScripts.clear();
|
||||
_mutex.unlock();
|
||||
|
||||
while (!scripts.empty()) {
|
||||
try {
|
||||
runScript(scripts.back());
|
||||
void ScriptEngine::postSync(bool isMaster) {
|
||||
if (isMaster) {
|
||||
while (!_masterScriptQueue.empty()) {
|
||||
std::string script = std::move(_masterScriptQueue.front().script);
|
||||
ScriptCallback callback = std::move(_masterScriptQueue.front().callback);
|
||||
_masterScriptQueue.pop();
|
||||
try {
|
||||
runScript(script, callback);
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
LERRORC(e.component, e.message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
LERRORC(e.component, e.message);
|
||||
} else {
|
||||
std::lock_guard<std::mutex> guard(_slaveScriptsMutex);
|
||||
while (!_slaveScriptQueue.empty()) {
|
||||
try {
|
||||
runScript(_slaveScriptQueue.front());
|
||||
_slaveScriptQueue.pop();
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
LERRORC(e.component, e.message);
|
||||
}
|
||||
}
|
||||
scripts.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::queueScript(const std::string& script,
|
||||
ScriptEngine::RemoteScripting remoteScripting)
|
||||
ScriptEngine::RemoteScripting remoteScripting,
|
||||
ScriptCallback callback)
|
||||
{
|
||||
if (script.empty()) {
|
||||
return;
|
||||
if (!script.empty()) {
|
||||
_incomingScripts.push({ script, remoteScripting, callback });
|
||||
}
|
||||
|
||||
_mutex.lock();
|
||||
_queuedScripts.insert(
|
||||
_queuedScripts.begin(),
|
||||
std::make_pair(script, remoteScripting)
|
||||
);
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
} // namespace openspace::scripting
|
||||
|
||||
@@ -84,20 +84,4 @@ void SyncBuffer::reset() {
|
||||
_decodeOffset = 0;
|
||||
}
|
||||
|
||||
//void SyncBuffer::write() {
|
||||
// _dataStream.resize(_encodeOffset);
|
||||
// _synchronizationBuffer->setVal(_dataStream);
|
||||
// sgct::SharedData::instance()->writeVector(_synchronizationBuffer.get());
|
||||
// _dataStream.resize(_n);
|
||||
// _encodeOffset = 0;
|
||||
// _decodeOffset = 0;
|
||||
//}
|
||||
//
|
||||
//void SyncBuffer::read() {
|
||||
// sgct::SharedData::instance()->readVector(_synchronizationBuffer.get());
|
||||
// _dataStream = _synchronizationBuffer->getVal();
|
||||
// _encodeOffset = 0;
|
||||
// _decodeOffset = 0;
|
||||
//}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
Reference in New Issue
Block a user