From 53e07d90e1ee300a4b47d011d64faa4fd2bc8798 Mon Sep 17 00:00:00 2001 From: Emil Axelsson Date: Wed, 3 Apr 2019 10:30:28 +0200 Subject: [PATCH] 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 --- config/two_nodes.xml | 7 +- .../documentation/documentationgenerator.h | 1 - include/openspace/engine/globals.h | 3 - .../openspace/interaction/keybindingmanager.h | 3 +- include/openspace/rendering/renderengine.h | 2 - include/openspace/scripting/scriptengine.h | 40 +-- include/openspace/util/camera.h | 1 - include/openspace/util/factorymanager.h | 4 +- modules/server/CMakeLists.txt | 2 + modules/server/include/jsonconverters.h | 8 + .../include/topics/documentationtopic.h | 54 +--- .../server/include/topics/luascripttopic.h | 4 + modules/server/servermodule.cpp | 15 +- modules/server/src/connection.cpp | 3 + modules/server/src/jsonconverters.cpp | 62 +++++ .../server/src/topics/documentationtopic.cpp | 72 ++++++ modules/server/src/topics/luascripttopic.cpp | 147 ++++++++++- .../server/src/topics/subscriptiontopic.cpp | 3 +- .../util/hongkangparser.cpp | 5 - .../util/instrumenttimesparser.cpp | 2 - .../util/labelparser.cpp | 3 - .../util/sequenceparser.cpp | 132 ---------- .../util/sequenceparser.h | 5 - src/CMakeLists.txt | 2 - src/engine/globals.cpp | 6 - src/engine/openspaceengine.cpp | 4 - src/engine/syncengine.cpp | 16 +- src/network/networkengine.cpp | 238 ------------------ src/scripting/scriptengine.cpp | 104 ++++---- src/util/syncbuffer.cpp | 16 -- 30 files changed, 414 insertions(+), 550 deletions(-) rename include/openspace/network/networkengine.h => modules/server/include/topics/documentationtopic.h (61%) create mode 100644 modules/server/src/topics/documentationtopic.cpp delete mode 100644 src/network/networkengine.cpp diff --git a/config/two_nodes.xml b/config/two_nodes.xml index b4ae20f8c0..caa62075ea 100644 --- a/config/two_nodes.xml +++ b/config/two_nodes.xml @@ -33,4 +33,9 @@ - \ No newline at end of file + + diff --git a/include/openspace/documentation/documentationgenerator.h b/include/openspace/documentation/documentationgenerator.h index 92fd1cd910..ad255aaaed 100644 --- a/include/openspace/documentation/documentationgenerator.h +++ b/include/openspace/documentation/documentationgenerator.h @@ -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 diff --git a/include/openspace/engine/globals.h b/include/openspace/engine/globals.h index 9d55621446..81b5210534 100644 --- a/include/openspace/engine/globals.h +++ b/include/openspace/engine/globals.h @@ -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(); diff --git a/include/openspace/interaction/keybindingmanager.h b/include/openspace/interaction/keybindingmanager.h index 967f2c0f2e..acaa00df91 100644 --- a/include/openspace/interaction/keybindingmanager.h +++ b/include/openspace/interaction/keybindingmanager.h @@ -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& keyBindings() const; private: - std::string generateJson() const override; std::multimap _keyLua; }; diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index b1a5d0c878..fd71f3880d 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -53,8 +53,6 @@ class SceneManager; class ScreenLog; class ScreenSpaceRenderable; struct ShutdownInformation; -class Syncable; -class SyncBuffer; class RenderEngine : public properties::PropertyOwner { public: diff --git a/include/openspace/scripting/scriptengine.h b/include/openspace/scripting/scriptengine.h index 5f3f4fce9f..26d750ecfd 100644 --- a/include/openspace/scripting/scriptengine.h +++ b/include/openspace/scripting/scriptengine.h @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include namespace openspace { class SyncBuffer; } @@ -47,8 +50,15 @@ namespace openspace::scripting { */ class ScriptEngine : public Syncable, public DocumentationGenerator { public: + using ScriptCallback = std::optional>; 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 cachedScripts(); + void queueScript(const std::string& script, RemoteScripting remoteScripting, + ScriptCallback cb = ScriptCallback()); std::vector 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 _registeredLibraries; + std::queue _incomingScripts; - //sync variables - std::mutex _mutex; - std::vector> _queuedScripts; - std::vector _receivedScripts; - std::string _currentSyncedScript; + // Slave scripts are mutex protected since decode and rendering may + // happen asynchronously. + std::mutex _slaveScriptsMutex; + std::queue _slaveScriptQueue; + std::queue _masterScriptQueue; - //parallel variables - //std::map> _cachedScripts; - //std::mutex _cachedScriptsMutex; + std::vector _scriptsToSync; - //logging variables + // Logging variables bool _logFileExists = false; bool _logScripts = true; std::string _logType; diff --git a/include/openspace/util/camera.h b/include/openspace/util/camera.h index fb78c1becd..da511165f1 100644 --- a/include/openspace/util/camera.h +++ b/include/openspace/util/camera.h @@ -33,7 +33,6 @@ namespace openspace { class SceneGraphNode; -class SyncBuffer; /** * This class still needs some more love. Suggested improvements: diff --git a/include/openspace/util/factorymanager.h b/include/openspace/util/factorymanager.h index 1bdc5202c2..1f3dad0941 100644 --- a/include/openspace/util/factorymanager.h +++ b/include/openspace/util/factorymanager.h @@ -110,9 +110,9 @@ public: */ template ghoul::TemplateFactory* factory() const; - -private: + std::string generateJson() const override; +private: /// Singleton member for the Factory Manager static FactoryManager* _manager; diff --git a/modules/server/CMakeLists.txt b/modules/server/CMakeLists.txt index ca749b3885..5b0a61fc70 100644 --- a/modules/server/CMakeLists.txt +++ b/modules/server/CMakeLists.txt @@ -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 diff --git a/modules/server/include/jsonconverters.h b/modules/server/include/jsonconverters.h index 72cce8d49a..75286a3d35 100644 --- a/modules/server/include/jsonconverters.h +++ b/modules/server/include/jsonconverters.h @@ -27,6 +27,7 @@ #include #include +#include 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); diff --git a/include/openspace/network/networkengine.h b/modules/server/include/topics/documentationtopic.h similarity index 61% rename from include/openspace/network/networkengine.h rename to modules/server/include/topics/documentationtopic.h index 1e0d774760..0733cad4eb 100644 --- a/include/openspace/network/networkengine.h +++ b/modules/server/include/topics/documentationtopic.h @@ -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 -#include -#include -#include +#include 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 message); - void sendMessages(); - - // Initial Connection Messages - void setInitialConnectionMessage(MessageIdentifier identifier, - std::vector message); - void sendInitialInformation(); - - // Background - MessageIdentifier identifier(std::string name); - -private: - std::map _identifiers; - MessageIdentifier _lastAssignedIdentifier = MessageIdentifier(-1); - - struct Message { - MessageIdentifier identifer; - std::vector body; - }; - std::vector _messagesToSend; - - std::vector _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__ diff --git a/modules/server/include/topics/luascripttopic.h b/modules/server/include/topics/luascripttopic.h index ecff03e434..6ae0f8f039 100644 --- a/modules/server/include/topics/luascripttopic.h +++ b/modules/server/include/topics/luascripttopic.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 diff --git a/modules/server/servermodule.cpp b/modules/server/servermodule.cpp index 9be0afda7f..9de969f412 100644 --- a/modules/server/servermodule.cpp +++ b/modules/server/servermodule.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -86,7 +88,10 @@ void ServerModule::internalInitialize(const ghoul::Dictionary& configuration) { std::unique_ptr 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 : _interfaces) { if (!serverInterface->isEnabled()) { @@ -165,7 +174,9 @@ void ServerModule::cleanUpFinishedThreads() { void ServerModule::disconnectAll() { for (std::unique_ptr& serverInterface : _interfaces) { - serverInterface->deinitialize(); + if (global::windowDelegate.isMaster()) { + serverInterface->deinitialize(); + } } for (ConnectionData& connectionData : _connections) { diff --git a/modules/server/src/connection.cpp b/modules/server/src/connection.cpp index ca2330307a..364ab1d427 100644 --- a/modules/server/src/connection.cpp +++ b/modules/server/src/connection.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -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 s, } ); + _topicFactory.registerClass(DocumentationTopicKey); _topicFactory.registerClass(GetPropertyTopicKey); _topicFactory.registerClass(LuaScriptTopicKey); _topicFactory.registerClass(SetPropertyTopicKey); diff --git a/modules/server/src/jsonconverters.cpp b/modules/server/src/jsonconverters.cpp index 5175443ea9..f6ec35d94b 100644 --- a/modules/server/src/jsonconverters.cpp +++ b/modules/server/src/jsonconverters.cpp @@ -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(key)) { + const glm::vec4 v = dictionary.value(key); + object[key] = json::array({ v[0], v[1], v[2], v[3] }); + } + else if (dictionary.hasValue(key)) { + const glm::vec3 v = dictionary.value(key); + object[key] = json::array({ v[0], v[1], v[2] }); + } + else if (dictionary.hasValue(key)) { + const glm::vec2 v = dictionary.value(key); + object[key] = json::array({ v[0], v[1] }); + } + else if (dictionary.hasValue(key)) { + const glm::dvec4 v = dictionary.value(key); + object[key] = json::array({ v[0], v[1], v[2], v[3] }); + } + else if (dictionary.hasValue(key)) { + const glm::dvec3 v = dictionary.value(key); + object[key] = json::array({ v[0], v[1], v[2] }); + } + else if (dictionary.hasValue(key)) { + const glm::dvec2 v = dictionary.value(key); + object[key] = json::array({ v[0], v[1] }); + } + else if (dictionary.hasValue(key)) { + object[key] = dictionary.value(key); + } + else if (dictionary.hasValue(key)) { + object[key] = dictionary.value(key); + } + else if (dictionary.hasValue(key)) { + object[key] = dictionary.value(key); + } + else if (dictionary.hasValue(key)) { + object[key] = dictionary.value(key); + } + else if (dictionary.hasValue(key)) { + object[key] = dictionary.value(key); + } + else if (dictionary.hasValue(key)) { + json child; + to_json(child, dictionary.value(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) { diff --git a/modules/server/src/topics/documentationtopic.cpp b/modules/server/src/topics/documentationtopic.cpp new file mode 100644 index 0000000000..67dae07350 --- /dev/null +++ b/modules/server/src/topics/documentationtopic.cpp @@ -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 + +#include +#include +#include + +#include +#include +#include +#include + + +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(); + + 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 diff --git a/modules/server/src/topics/luascripttopic.cpp b/modules/server/src/topics/luascripttopic.cpp index f81a425c3a..dd4f1fe81d 100644 --- a/modules/server/src/topics/luascripttopic.cpp +++ b/modules/server/src/topics/luascripttopic.cpp @@ -24,25 +24,137 @@ #include +#include +#include #include #include #include #include +#include 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()); + } + if (it->is_number()) { + return fmt::format("{:E}", it->get()); + } + if (it->is_string()) { + return formatLuaString(it->get()); + } + if (it->is_boolean()) { + return it->get() ? "true" : "false"; + } + if (it->is_null()) { + return "nil"; + } + throw ghoul::lua::LuaFormatException("Format error."); + } + + std::string generateScript(const std::string& function, + const std::vector& 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(); - 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(); + nlohmann::json::const_iterator ret = json.find(KeyReturn); + bool shouldReturn = (ret != json.end()) && + ret->is_boolean() && + ret->get(); + + runScript(luaScript, shouldReturn); + } + else if (function != json.end() && function->is_string()) { + std::string luaFunction = function->get(); + nlohmann::json::const_iterator ret = json.find(KeyReturn); + bool shouldReturn = (ret != json.end()) && + ret->is_boolean() && + ret->get(); + + nlohmann::json::const_iterator args = json.find(KeyArguments); + if (!args->is_array()) { + return; + } + + std::vector 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 diff --git a/modules/server/src/topics/subscriptiontopic.cpp b/modules/server/src/topics/subscriptiontopic.cpp index 3ec9e3bfb6..20933fa4e1 100644 --- a/modules/server/src/topics/subscriptiontopic.cpp +++ b/modules/server/src/topics/subscriptiontopic.cpp @@ -68,10 +68,11 @@ void SubscriptionTopic::resetCallbacks() { } void SubscriptionTopic::handleJson(const nlohmann::json& json) { - std::string key = json.at(PropertyKey).get(); const std::string& event = json.at(EventKey).get(); if (event == StartSubscription) { + std::string key = json.at(PropertyKey).get(); + _prop = property(key); resetCallbacks(); diff --git a/modules/spacecraftinstruments/util/hongkangparser.cpp b/modules/spacecraftinstruments/util/hongkangparser.cpp index 351f2d54fb..d5dbcd7890 100644 --- a/modules/spacecraftinstruments/util/hongkangparser.cpp +++ b/modules/spacecraftinstruments/util/hongkangparser.cpp @@ -37,8 +37,6 @@ #include 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; } diff --git a/modules/spacecraftinstruments/util/instrumenttimesparser.cpp b/modules/spacecraftinstruments/util/instrumenttimesparser.cpp index fc872e0ffe..da373f9d30 100644 --- a/modules/spacecraftinstruments/util/instrumenttimesparser.cpp +++ b/modules/spacecraftinstruments/util/instrumenttimesparser.cpp @@ -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; } diff --git a/modules/spacecraftinstruments/util/labelparser.cpp b/modules/spacecraftinstruments/util/labelparser.cpp index 726393a263..d450b767f0 100644 --- a/modules/spacecraftinstruments/util/labelparser.cpp +++ b/modules/spacecraftinstruments/util/labelparser.cpp @@ -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& target : _subsetMap) { _instrumentTimes.emplace_back(lblName, _subsetMap[target.first]._range); } - sendPlaybookInformation(PlaybookIdentifierName); return true; } diff --git a/modules/spacecraftinstruments/util/sequenceparser.cpp b/modules/spacecraftinstruments/util/sequenceparser.cpp index 2eda0c1563..0739d76fdc 100644 --- a/modules/spacecraftinstruments/util/sequenceparser.cpp +++ b/modules/spacecraftinstruments/util/sequenceparser.cpp @@ -33,8 +33,6 @@ namespace { constexpr const char* _loggerCat = "SequenceParser"; - - constexpr const char* PlaybookIdentifierName = "Playbook"; } // namespace namespace openspace { @@ -62,134 +60,4 @@ std::map>& SequenceParser::translations() return _fileTranslation; } -template -void writeToBuffer(std::vector& buffer, size_t& currentWriteLocation, T value) { - if ((currentWriteLocation + sizeof(T)) > buffer.size()) { - buffer.resize(2 * buffer.size()); - } - - std::memmove( - buffer.data() + currentWriteLocation, - reinterpret_cast(&value), - sizeof(T) - ); - currentWriteLocation += sizeof(T); -} - -template <> -void writeToBuffer(std::vector& 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(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 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 targetMap; - uint8_t currentTargetId = 0; - for (const std::pair& target : _subsetMap) { - if (targetMap.find(target.first) == targetMap.end()) { - targetMap[target.first] = currentTargetId++; - } - } - - std::map instrumentMap; - uint16_t currentInstrumentId = 1; - for (std::pair& 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& p : targetMap) { - writeToBuffer(buffer, currentWriteLocation, p.second); - writeToBuffer(buffer, currentWriteLocation, p.first); - } - - writeToBuffer(buffer, currentWriteLocation, uint8_t(instrumentMap.size())); - for (const std::pair& p : instrumentMap) { - writeToBuffer(buffer, currentWriteLocation, p.second); - writeToBuffer(buffer, currentWriteLocation, p.first); - } - - uint32_t allImages = 0; - for (const std::pair& target : _subsetMap) { - allImages += static_cast(target.second._subset.size()); - } - writeToBuffer(buffer, currentWriteLocation, allImages); - - for (const std::pair& 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 data; - } sizeBuffer = {}; - sizeBuffer.value = static_cast(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 diff --git a/modules/spacecraftinstruments/util/sequenceparser.h b/modules/spacecraftinstruments/util/sequenceparser.h index fada30e731..d3881f9468 100644 --- a/modules/spacecraftinstruments/util/sequenceparser.h +++ b/modules/spacecraftinstruments/util/sequenceparser.h @@ -25,7 +25,6 @@ #ifndef __OPENSPACE_MODULE_SPACECRAFTINSTRUMENTS___SEQUENCEPARSER___H__ #define __OPENSPACE_MODULE_SPACECRAFTINSTRUMENTS___SEQUENCEPARSER___H__ -#include #include #include #include @@ -48,16 +47,12 @@ public: const std::vector& getCaptureProgression() const; protected: - void sendPlaybookInformation(const std::string& name); - std::map _subsetMap; std::vector> _instrumentTimes; std::vector> _targetTimes; std::vector _captureProgression; std::map> _fileTranslation; - - NetworkEngine::MessageIdentifier _messageIdentifier; }; } // namespace openspace diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a7d3d12785..7111bb5554 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/engine/globals.cpp b/src/engine/globals.cpp index 586ab40cd4..5885a2b2bb 100644 --- a/src/engine/globals.cpp +++ b/src/engine/globals.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -94,11 +93,6 @@ ModuleEngine& gModuleEngine() { return g; } -NetworkEngine& gNetworkEngine() { - static NetworkEngine g; - return g; -} - OpenSpaceEngine& gOpenSpaceEngine() { static OpenSpaceEngine g; return g; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 2498b7cdfe..48e7707a96 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -1246,9 +1245,6 @@ void OpenSpaceEngine::mouseScrollWheelCallback(double posX, double posY) { std::vector OpenSpaceEngine::encode() { std::vector buffer = global::syncEngine.encodeSyncables(); - global::networkEngine.publishStatusMessage(); - global::networkEngine.sendMessages(); - return buffer; } diff --git a/src/engine/syncengine.cpp b/src/engine/syncengine.cpp index f1b35d1f06..65e470ac82 100644 --- a/src/engine/syncengine.cpp +++ b/src/engine/syncengine.cpp @@ -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 SyncEngine::encodeSyncables() { for (Syncable* syncable : _syncables) { syncable->encode(&_syncBuffer); @@ -45,17 +45,9 @@ std::vector SyncEngine::encodeSyncables() { std::vector 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 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& 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); } } diff --git a/src/network/networkengine.cpp b/src/network/networkengine.cpp deleted file mode 100644 index 6022af5288..0000000000 --- a/src/network/networkengine.cpp +++ /dev/null @@ -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 - -#include -#include -#include -#include -#include -#include -#include -#include - -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(timeString.length()); - messageSize += sizeof(delta); - - ghoul_assert(messageSize == 40, "Message size is not correct"); - - unsigned int currentLocation = 0; - std::vector 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(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& i : _identifiers) { - bufferSize += sizeof(MessageIdentifier); - bufferSize += i.first.size() + 1; // +1 for \0 terminating character - } - - std::vector buffer(bufferSize); - size_t currentWritingPosition = 0; - uint16_t size = static_cast(_identifiers.size()); - std::memcpy(buffer.data(), &size, sizeof(uint16_t)); - currentWritingPosition += sizeof(uint16_t); - for (const std::pair& i : _identifiers) { - std::memcpy( - buffer.data() + currentWritingPosition, - &(i.second), sizeof(MessageIdentifier) - ); - currentWritingPosition += sizeof(MessageIdentifier); - uint8_t stringSize = static_cast(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 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 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 data; - } identifier = {}; - identifier.value = m.identifer; - - std::vector 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 data; - } identifier = {}; - identifier.value = _initialMessageFinishedIdentifier; - - std::vector d; - d.insert(d.begin(), identifier.data.begin(), identifier.data.end()); - - global::windowDelegate.sendMessageToExternalControl(d); - _shouldPublishStatusMessage = true; -} - -void NetworkEngine::setInitialConnectionMessage(MessageIdentifier identifier, - std::vector message) -{ - // Add check if a MessageIdentifier already exists ---abock - _initialConnectionMessages.push_back({std::move(identifier), std::move(message)}); -} - -} // namespace openspace diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index 2fb4ee95c3..05491a37db 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -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 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 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 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 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 diff --git a/src/util/syncbuffer.cpp b/src/util/syncbuffer.cpp index 93ad822357..f89204c542 100644 --- a/src/util/syncbuffer.cpp +++ b/src/util/syncbuffer.cpp @@ -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