mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-01 17:20:09 -06:00
Refactored NetworkEngine and PointDataMessageHandler into Data-Oriented Design to simplify development
This commit is contained in:
@@ -26,9 +26,9 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
|
||||
|
||||
set(HEADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/softwareintegrationmodule.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pointdatamessagehandler.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/messagehandler.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/network/network.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/network/softwareconnection.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/network/networkengine.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepointscloud.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/utils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/syncablefloatdatastorage.h
|
||||
@@ -41,9 +41,9 @@ source_group("Header Files" FILES ${HEADER_FILES})
|
||||
set(SOURCE_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/softwareintegrationmodule.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/softwareintegrationmodule_lua.inl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pointdatamessagehandler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/messagehandler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/network/network.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/network/softwareconnection.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/network/networkengine.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepointscloud.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/syncablefloatdatastorage.cpp
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/softwareintegration/pointdatamessagehandler.h>
|
||||
#include <modules/softwareintegration/messagehandler.h>
|
||||
#include <modules/softwareintegration/softwareintegrationmodule.h>
|
||||
#include <modules/softwareintegration/utils.h>
|
||||
|
||||
@@ -41,14 +41,219 @@
|
||||
#include <iomanip>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "PDatMessHand";
|
||||
|
||||
constexpr const char* _loggerCat = "PDatMessHand";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
namespace openspace::softwareintegration::network {
|
||||
|
||||
using namespace softwareintegration;
|
||||
// Anonymous namespace
|
||||
namespace {
|
||||
|
||||
void PointDataMessageHandler::handlePointDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
CallbackMap callbacks{};
|
||||
std::mutex callbacksMutex{};
|
||||
size_t callbacksRetries{0};
|
||||
|
||||
const Renderable* getRenderable(const std::string& identifier) {
|
||||
return renderable(identifier);
|
||||
}
|
||||
|
||||
void checkRenderable(
|
||||
const std::vector<char>& message, size_t& messageOffset,
|
||||
std::shared_ptr<SoftwareConnection> connection, std::string& identifier
|
||||
) {
|
||||
std::string guiName;
|
||||
|
||||
try {
|
||||
// The following order of creating variables is the exact order they are received
|
||||
// in the message. If the order is not the same, the global variable
|
||||
// 'message offset' will be wrong
|
||||
identifier = simp::readString(message, messageOffset);
|
||||
guiName = simp::readString(message, messageOffset);
|
||||
}
|
||||
catch (const simp::SimpError& err) {
|
||||
LERROR(fmt::format("Error when reading identifier and guiName from message: {}", err.message));
|
||||
return;
|
||||
}
|
||||
|
||||
connection->addSceneGraphNode(identifier);
|
||||
|
||||
auto r = renderable(identifier);
|
||||
bool hasCallbacks = false;
|
||||
{
|
||||
std::lock_guard guard(callbacksMutex);
|
||||
hasCallbacks = callbacks.count(identifier) > 0;
|
||||
}
|
||||
if (!r && !hasCallbacks) {
|
||||
LDEBUG(fmt::format("No renderable with identifier '{}' was found. Creating it.", identifier));
|
||||
|
||||
// Create a renderable, since it didn't exist
|
||||
using namespace std::string_literals;
|
||||
ghoul::Dictionary renderablePointsCloud;
|
||||
renderablePointsCloud.setValue("Type", "RenderablePointsCloud"s);
|
||||
renderablePointsCloud.setValue("Identifier", identifier);
|
||||
renderablePointsCloud.setValue("Name", guiName);
|
||||
|
||||
ghoul::Dictionary gui;
|
||||
gui.setValue("Name", guiName);
|
||||
gui.setValue("Path", "/Software Integration"s);
|
||||
|
||||
ghoul::Dictionary node;
|
||||
node.setValue("Identifier", identifier);
|
||||
node.setValue("Renderable", renderablePointsCloud);
|
||||
node.setValue("GUI", gui);
|
||||
|
||||
global::scriptEngine->queueScript(
|
||||
"openspace.addSceneGraphNode(" + ghoul::formatLua(node) + ")"
|
||||
"openspace.setPropertyValueSingle('Modules.CefWebGui.Reload', nil)", // Reload WebGUI so that SoftwareIntegration GUI appears
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void addCallback(
|
||||
const std::string& identifier,
|
||||
const Callback& newCallback
|
||||
) {
|
||||
std::lock_guard guard(callbacksMutex);
|
||||
auto it = callbacks.find(identifier);
|
||||
if (it == callbacks.end()) {
|
||||
CallbackList newCallbackList{ newCallback };
|
||||
callbacks.emplace(identifier, newCallbackList);
|
||||
}
|
||||
else {
|
||||
it->second.push_back(newCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void onFixedColorChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
) {
|
||||
if (!connection->isConnected()) {
|
||||
connection->removePropertySubscription(property->identifier(), identifier);
|
||||
return;
|
||||
}
|
||||
|
||||
// auto propertySubscription = connection->getPropertySubscription(identifier, property->identifier());
|
||||
// if (!propertySubscription) return;
|
||||
// if (!propertySubscription->shouldSendMessage) {
|
||||
// propertySubscription->shouldSendMessage = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
glm::vec4 color = std::any_cast<glm::vec4>(property->get());
|
||||
|
||||
const std::string message = simp::formatColorMessage(identifier, color);
|
||||
connection->sendMessage(message);
|
||||
}
|
||||
|
||||
void onOpacityChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
) {
|
||||
if (!connection->isConnected()) {
|
||||
connection->removePropertySubscription(property->identifier(), identifier);
|
||||
return;
|
||||
}
|
||||
|
||||
// auto propertySubscription = connection->getPropertySubscription(identifier, property->identifier());
|
||||
// if (!propertySubscription) return;
|
||||
// if (!propertySubscription->shouldSendMessage) {
|
||||
// propertySubscription->shouldSendMessage = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
float value = std::any_cast<float>(property->get());
|
||||
std::string hex_value = simp::floatToHex(value);
|
||||
|
||||
const std::string message = simp::formatUpdateMessage(simp::MessageType::Opacity, identifier, hex_value);
|
||||
connection->sendMessage(message);
|
||||
}
|
||||
|
||||
void onFixedPointSizeChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
) {
|
||||
if (!connection->isConnected()) {
|
||||
connection->removePropertySubscription(property->identifier(), identifier);
|
||||
return;
|
||||
}
|
||||
|
||||
// auto propertySubscription = connection->getPropertySubscription(identifier, property->identifier());
|
||||
// if (!propertySubscription) return;
|
||||
// if (!propertySubscription->shouldSendMessage) {
|
||||
// propertySubscription->shouldSendMessage = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
float value = std::any_cast<float>(property->get());
|
||||
std::string hex_value = simp::floatToHex(value);
|
||||
|
||||
const std::string message = simp::formatUpdateMessage(simp::MessageType::FixedSize, identifier, hex_value);
|
||||
connection->sendMessage(message);
|
||||
}
|
||||
|
||||
void onVisibilityChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
) {
|
||||
if (!connection->isConnected()) {
|
||||
connection->removePropertySubscription(property->identifier(), identifier);
|
||||
return;
|
||||
}
|
||||
|
||||
// auto propertySubscription = connection->getPropertySubscription(identifier, property->identifier());
|
||||
// if (!propertySubscription) return;
|
||||
// if (!propertySubscription->shouldSendMessage) {
|
||||
// propertySubscription->shouldSendMessage = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
bool isVisible = std::any_cast<bool>(property->get());
|
||||
std::string_view visibilityFlag = isVisible ? "T" : "F";
|
||||
|
||||
const std::string message = simp::formatUpdateMessage(simp::MessageType::Visibility, identifier, visibilityFlag);
|
||||
connection->sendMessage(message);
|
||||
}
|
||||
|
||||
// TODO: Move to SIMP / use distanceconversion
|
||||
void convertToMeterPerSecond(simp::LengthUnit currLengthUnit, std::vector<float>& data) {
|
||||
// distanceconversion::convertDistance
|
||||
float multiplier = 1.0;
|
||||
switch (currLengthUnit) {
|
||||
case simp::LengthUnit::km:
|
||||
multiplier = 1000.0;
|
||||
break;
|
||||
case simp::LengthUnit::AU:
|
||||
multiplier = static_cast<float>(distanceconstants::AstronomicalUnit);
|
||||
break;
|
||||
case simp::LengthUnit::lyr:
|
||||
multiplier = static_cast<float>(distanceconstants::LightYear);
|
||||
break;
|
||||
case simp::LengthUnit::pc:
|
||||
multiplier = static_cast<float>(distanceconstants::Parsec);
|
||||
break;
|
||||
case simp::LengthUnit::kpc:
|
||||
multiplier = static_cast<float>(distanceconstants::Parsec * 1.0e3);
|
||||
break;
|
||||
case simp::LengthUnit::Mpc:
|
||||
multiplier = static_cast<float>(distanceconstants::Parsec * 1.0e6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::transform(std::begin(data), std::end(data), std::begin(data),
|
||||
[multiplier] (float f) {return f * multiplier;});
|
||||
}
|
||||
|
||||
void handlePointDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
@@ -86,8 +291,8 @@ void PointDataMessageHandler::handlePointDataMessage(const std::vector<char>& me
|
||||
addCallback(identifier, { reanchorCallback, { storage::Key::DataPoints }, "reanchorCallback" });
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleVelocityDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
LWARNING(fmt::format("PointDataMessageHandler::handleVelocityDataMessage()"));
|
||||
void handleVelocityDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
LWARNING(fmt::format("handleVelocityDataMessage()"));
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
@@ -139,10 +344,11 @@ void PointDataMessageHandler::handleVelocityDataMessage(const std::vector<char>&
|
||||
auto module = global::moduleEngine->module<SoftwareIntegrationModule>();
|
||||
module->storeData(identifier, storage::Key::VelocityData, std::move(velocities));
|
||||
|
||||
auto velNaNModeCallback = [this, identifier, velNaNMode, velNaNColor, connection] {
|
||||
auto velNaNModeCallback = [identifier, velNaNMode, velNaNColor, connection] {
|
||||
if (velNaNMode == simp::NaNRenderMode::Color) {
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
if (!r) return;
|
||||
|
||||
// Get velNaNColor of renderable
|
||||
properties::Property* velNaNColorProperty = r->property("VelNaNColor");
|
||||
@@ -182,7 +388,7 @@ void PointDataMessageHandler::handleVelocityDataMessage(const std::vector<char>&
|
||||
addCallback(identifier, { enableMotionCallback, { storage::Key::VelocityData }, "Enable motion mode, wait for VelocityData" });
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleFixedColorMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
void handleFixedColorMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
@@ -207,7 +413,7 @@ void PointDataMessageHandler::handleFixedColorMessage(const std::vector<char>& m
|
||||
// Create weak_ptr, safer than shared_ptr for lambdas
|
||||
std::weak_ptr<SoftwareConnection> connWeakPtr{ connection };
|
||||
|
||||
auto setFixedColorCallback = [this, identifier, color, colorProperty, connWeakPtr] {
|
||||
auto setFixedColorCallback = [identifier, color, colorProperty, connWeakPtr] {
|
||||
if (!colorProperty || connWeakPtr.expired()) return;
|
||||
// auto conn = connWeakPtr.lock()
|
||||
|
||||
@@ -239,14 +445,14 @@ void PointDataMessageHandler::handleFixedColorMessage(const std::vector<char>& m
|
||||
addCallback(identifier, { setFixedColorCallback, {}, "handleFixedColorMessage" });
|
||||
|
||||
// Create and set onChange for color
|
||||
auto updateColor = [this, colorProperty, identifier, connWeakPtr] {
|
||||
auto updateColor = [colorProperty, identifier, connWeakPtr] {
|
||||
if (!colorProperty || connWeakPtr.expired()) return;
|
||||
onFixedColorChange(colorProperty, identifier, connWeakPtr.lock());
|
||||
};
|
||||
connection->addPropertySubscription(colorProperty->identifier(), identifier, updateColor);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleColormapMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
void handleColormapMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
@@ -285,9 +491,10 @@ void PointDataMessageHandler::handleColormapMessage(const std::vector<char>& mes
|
||||
auto module = global::moduleEngine->module<SoftwareIntegrationModule>();
|
||||
module->storeData(identifier, storage::Key::Colormap, std::move(colorMap));
|
||||
|
||||
auto colormapLimitsCallback = [this, identifier, min, max, connection] {
|
||||
auto colormapLimitsCallback = [identifier, min, max, connection] {
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
if (!r) return;
|
||||
|
||||
properties::Property* colormapMinProperty = r->property("ColormapMin");
|
||||
// auto minPropertySub = connection->getPropertySubscription(identifier, colormapMinProperty->identifier());
|
||||
@@ -324,10 +531,11 @@ void PointDataMessageHandler::handleColormapMessage(const std::vector<char>& mes
|
||||
};
|
||||
addCallback(identifier, { colormapLimitsCallback, {}, "colormapLimitsCallback" });
|
||||
|
||||
auto cmapNaNModeCallback = [this, identifier, cmapNaNMode, cmapNaNColor, connection] {
|
||||
auto cmapNaNModeCallback = [identifier, cmapNaNMode, cmapNaNColor, connection] {
|
||||
if (cmapNaNMode == simp::NaNRenderMode::Color) {
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
if (!r) return;
|
||||
|
||||
// Get cmapNaNColor of renderable
|
||||
properties::Property* cmapNaNColorProperty = r->property("CmapNaNColor");
|
||||
@@ -355,7 +563,7 @@ void PointDataMessageHandler::handleColormapMessage(const std::vector<char>& mes
|
||||
};
|
||||
addCallback(identifier, { cmapNaNModeCallback, {}, "cmapNaNModeCallback" });
|
||||
|
||||
auto enableColormapCallback = [this, identifier] {
|
||||
auto enableColormapCallback = [identifier] {
|
||||
global::scriptEngine->queueScript(
|
||||
fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.ColormapEnabled', {});",
|
||||
@@ -369,7 +577,7 @@ void PointDataMessageHandler::handleColormapMessage(const std::vector<char>& mes
|
||||
addCallback(identifier, { enableColormapCallback, std::move(dataToWaitFor), "enableColormapCallback" });
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleAttributeDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
void handleAttributeDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
@@ -409,7 +617,7 @@ void PointDataMessageHandler::handleAttributeDataMessage(const std::vector<char>
|
||||
std::string callbackDescription = "handleAttributeDataMessage, key=" + storage::getStorageKeyString(key);
|
||||
switch (key) {
|
||||
case storage::Key::ColormapAttrData : {
|
||||
auto callback = [this, identifier] {
|
||||
auto callback = [identifier] {
|
||||
global::scriptEngine->queueScript(
|
||||
fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.ColormapEnabled', {});",
|
||||
@@ -422,7 +630,7 @@ void PointDataMessageHandler::handleAttributeDataMessage(const std::vector<char>
|
||||
break;
|
||||
}
|
||||
case storage::Key::LinearSizeAttrData: {
|
||||
auto callback = [this, identifier] {
|
||||
auto callback = [identifier] {
|
||||
global::scriptEngine->queueScript(
|
||||
fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.LinearSizeEnabled', {});",
|
||||
@@ -439,7 +647,7 @@ void PointDataMessageHandler::handleAttributeDataMessage(const std::vector<char>
|
||||
}
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleOpacityMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
void handleOpacityMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
@@ -464,7 +672,7 @@ void PointDataMessageHandler::handleOpacityMessage(const std::vector<char>& mess
|
||||
// Create weak_ptr, safer than shared_ptr for lambdas
|
||||
std::weak_ptr<SoftwareConnection> connWeakPtr{ connection };
|
||||
|
||||
auto callback = [this, identifier, opacity, opacityProperty, connWeakPtr] {
|
||||
auto callback = [identifier, opacity, opacityProperty, connWeakPtr] {
|
||||
if (!opacityProperty || connWeakPtr.expired()) return;
|
||||
// auto conn = connWeakPtr.lock()
|
||||
|
||||
@@ -490,14 +698,14 @@ void PointDataMessageHandler::handleOpacityMessage(const std::vector<char>& mess
|
||||
addCallback(identifier, { callback, {}, "handleOpacityMessage" });
|
||||
|
||||
// Create and set onChange for opacity
|
||||
auto updateOpacity = [this, opacityProperty, identifier, connWeakPtr] {
|
||||
auto updateOpacity = [opacityProperty, identifier, connWeakPtr] {
|
||||
if (!opacityProperty || connWeakPtr.expired()) return;
|
||||
onOpacityChange(opacityProperty, identifier, connWeakPtr.lock());
|
||||
};
|
||||
connection->addPropertySubscription(opacityProperty->identifier(), identifier, updateOpacity);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleFixedPointSizeMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
void handleFixedPointSizeMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
@@ -522,7 +730,7 @@ void PointDataMessageHandler::handleFixedPointSizeMessage(const std::vector<char
|
||||
// Create weak_ptr, safer than shared_ptr for lambdas
|
||||
std::weak_ptr<SoftwareConnection> connWeakPtr{ connection };
|
||||
|
||||
auto callback = [this, identifier, size, sizeProperty, connWeakPtr] {
|
||||
auto callback = [identifier, size, sizeProperty, connWeakPtr] {
|
||||
if (!sizeProperty || connWeakPtr.expired()) return;
|
||||
// auto conn = connWeakPtr.lock()
|
||||
|
||||
@@ -556,14 +764,14 @@ void PointDataMessageHandler::handleFixedPointSizeMessage(const std::vector<char
|
||||
};
|
||||
addCallback(identifier, { callback, {}, "handleFixedPointSizeMessage" });
|
||||
|
||||
auto updateSize = [this, sizeProperty, identifier, connWeakPtr] {
|
||||
auto updateSize = [sizeProperty, identifier, connWeakPtr] {
|
||||
if (!sizeProperty || connWeakPtr.expired()) return;
|
||||
onFixedPointSizeChange(sizeProperty, identifier, connWeakPtr.lock());
|
||||
};
|
||||
connection->addPropertySubscription(sizeProperty->identifier(), identifier, updateSize);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleLinearPointSizeMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
void handleLinearPointSizeMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
@@ -582,9 +790,10 @@ void PointDataMessageHandler::handleLinearPointSizeMessage(const std::vector<cha
|
||||
return;
|
||||
}
|
||||
|
||||
auto linearSizeCallback = [this, identifier, size, min, max] {
|
||||
auto linearSizeCallback = [identifier, size, min, max] {
|
||||
// Get renderable
|
||||
auto r = getRenderable(identifier);
|
||||
if (!r) return;
|
||||
|
||||
// Get size from renderable
|
||||
properties::Property* sizeProperty = r->property("Size");
|
||||
@@ -628,7 +837,7 @@ void PointDataMessageHandler::handleLinearPointSizeMessage(const std::vector<cha
|
||||
};
|
||||
addCallback(identifier, { linearSizeCallback, {}, "linearSizeCallback" });
|
||||
|
||||
auto enableLinearSizeCallback = [this, identifier] {
|
||||
auto enableLinearSizeCallback = [identifier] {
|
||||
global::scriptEngine->queueScript(
|
||||
fmt::format(
|
||||
"openspace.setPropertyValueSingle('Scene.{}.Renderable.LinearSizeEnabled', {});",
|
||||
@@ -647,7 +856,7 @@ void PointDataMessageHandler::handleLinearPointSizeMessage(const std::vector<cha
|
||||
);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleVisibilityMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
void handleVisibilityMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection) {
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
@@ -674,7 +883,7 @@ void PointDataMessageHandler::handleVisibilityMessage(const std::vector<char>& m
|
||||
|
||||
const bool visibility = visibilityMessage == "T";
|
||||
|
||||
auto callback = [this, identifier, visibility, visibilityProperty, connWeakPtr] {
|
||||
auto callback = [identifier, visibility, visibilityProperty, connWeakPtr] {
|
||||
if (!visibilityProperty || connWeakPtr.expired()) return;
|
||||
// auto conn = connWeakPtr.lock()
|
||||
|
||||
@@ -702,14 +911,14 @@ void PointDataMessageHandler::handleVisibilityMessage(const std::vector<char>& m
|
||||
addCallback(identifier, { callback, {}, "handleVisibilityMessage" });
|
||||
|
||||
// Create and set onChange for visibility
|
||||
auto toggleVisibility = [this, visibilityProperty, identifier, connWeakPtr] {
|
||||
auto toggleVisibility = [visibilityProperty, identifier, connWeakPtr] {
|
||||
if (!visibilityProperty || connWeakPtr.expired()) return;
|
||||
onVisibilityChange(visibilityProperty, identifier, connWeakPtr.lock());
|
||||
};
|
||||
connection->addPropertySubscription(visibilityProperty->identifier(), identifier, toggleVisibility);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::handleRemoveSGNMessage(const std::vector<char>& message,std::shared_ptr<SoftwareConnection> connection) {
|
||||
void handleRemoveSGNMessage(const std::vector<char>& message,std::shared_ptr<SoftwareConnection> connection) {
|
||||
size_t messageOffset = 0;
|
||||
std::string identifier;
|
||||
|
||||
@@ -742,12 +951,95 @@ void PointDataMessageHandler::handleRemoveSGNMessage(const std::vector<char>& me
|
||||
LDEBUG(fmt::format("Scene graph node '{}' removed.", identifier));
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::postSync() {
|
||||
std::lock_guard guard(_onceNodeExistsCallbacksMutex);
|
||||
} // namespace
|
||||
|
||||
void handleMessage(IncomingMessage& incomingMessage) {
|
||||
if(incomingMessage.connection.expired()) {
|
||||
LDEBUG(fmt::format("Trying to handle message from disconnected peer. Aborting."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto connectionPtr = incomingMessage.connection.lock();
|
||||
|
||||
const simp::MessageType messageType = incomingMessage.type;
|
||||
std::vector<char>& message = incomingMessage.content;
|
||||
|
||||
switch (messageType) {
|
||||
case simp::MessageType::Connection: {
|
||||
LDEBUG(fmt::format("Message recieved... Connection: {}", connectionPtr->id()));
|
||||
size_t offset = 0;
|
||||
const std::string software = simp::readString(message, offset);
|
||||
|
||||
// Send back message to software to complete handshake
|
||||
connectionPtr->sendMessage(simp::formatConnectionMessage(software));
|
||||
LINFO(fmt::format("OpenSpace has connected with {} through socket", software));
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::PointData: {
|
||||
LDEBUG("Message recieved.. Point data");
|
||||
handlePointDataMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::VelocityData: {
|
||||
LDEBUG("Message recieved... Velocity data");
|
||||
handleVelocityDataMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::RemoveSceneGraphNode: {
|
||||
LDEBUG(fmt::format("Message recieved.. Remove SGN"));
|
||||
handleRemoveSGNMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Color: {
|
||||
LDEBUG(fmt::format("Message recieved.. New color"));
|
||||
handleFixedColorMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Colormap: {
|
||||
LDEBUG(fmt::format("Message recieved.. New colormap"));
|
||||
handleColormapMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::AttributeData: {
|
||||
LDEBUG(fmt::format("Message recieved.. New attribute data"));
|
||||
handleAttributeDataMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Opacity: {
|
||||
LDEBUG(fmt::format("Message recieved.. New Opacity"));
|
||||
handleOpacityMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::FixedSize: {
|
||||
LDEBUG(fmt::format("Message recieved.. New size"));
|
||||
handleFixedPointSizeMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::LinearSize: {
|
||||
LDEBUG(fmt::format("Message recieved.. New linear size"));
|
||||
handleLinearPointSizeMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Visibility: {
|
||||
LDEBUG(fmt::format("Message recieved.. New visibility"));
|
||||
handleVisibilityMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LERROR(fmt::format(
|
||||
"Unsupported message type: {}", incomingMessage.rawMessageType
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void postSyncCallbacks() {
|
||||
std::lock_guard guard(callbacksMutex);
|
||||
// Check if the scene graph node has been created.
|
||||
// If so, call the corresponding callback functions to set up any subscriptions
|
||||
auto callbackMapIt = _onceNodeExistsCallbacks.begin();
|
||||
while (callbackMapIt != _onceNodeExistsCallbacks.end()) {
|
||||
auto callbackMapIt = callbacks.begin();
|
||||
while (callbackMapIt != callbacks.end()) {
|
||||
auto& [identifier, callbackList] = *callbackMapIt;
|
||||
|
||||
try {
|
||||
@@ -779,225 +1071,19 @@ void PointDataMessageHandler::postSync() {
|
||||
}
|
||||
|
||||
if (callbackList.empty()) {
|
||||
callbackMapIt = _onceNodeExistsCallbacks.erase(callbackMapIt);
|
||||
_onceNodeExistsCallbacksRetries = 0;
|
||||
callbackMapIt = callbacks.erase(callbackMapIt);
|
||||
callbacksRetries = 0;
|
||||
} else {
|
||||
callbackMapIt++;
|
||||
}
|
||||
}
|
||||
catch(std::exception &err) {
|
||||
++_onceNodeExistsCallbacksRetries;
|
||||
ghoul_assert(_onceNodeExistsCallbacksRetries < 10, "Too many callback retries");
|
||||
++callbacksRetries;
|
||||
ghoul_assert(callbacksRetries < 10, "Too many callback retries");
|
||||
LDEBUG(fmt::format("Error when trying to run callback: {}", err.what()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Renderable* PointDataMessageHandler::getRenderable(const std::string& identifier) {
|
||||
return renderable(identifier);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::checkRenderable(
|
||||
const std::vector<char>& message, size_t& messageOffset,
|
||||
std::shared_ptr<SoftwareConnection> connection, std::string& identifier
|
||||
) {
|
||||
std::string guiName;
|
||||
|
||||
try {
|
||||
// The following order of creating variables is the exact order they are received
|
||||
// in the message. If the order is not the same, the global variable
|
||||
// 'message offset' will be wrong
|
||||
identifier = simp::readString(message, messageOffset);
|
||||
guiName = simp::readString(message, messageOffset);
|
||||
}
|
||||
catch (const simp::SimpError& err) {
|
||||
LERROR(fmt::format("Error when reading identifier and guiName from message: {}", err.message));
|
||||
return;
|
||||
}
|
||||
|
||||
connection->addSceneGraphNode(identifier);
|
||||
|
||||
const Renderable* r = renderable(identifier);
|
||||
bool hasCallbacks = false;
|
||||
{
|
||||
std::lock_guard guard(_onceNodeExistsCallbacksMutex);
|
||||
hasCallbacks = _onceNodeExistsCallbacks.count(identifier) > 0;
|
||||
}
|
||||
if (!r && !hasCallbacks) {
|
||||
LDEBUG(fmt::format("No renderable with identifier '{}' was found. Creating it.", identifier));
|
||||
|
||||
// Create a renderable, since it didn't exist
|
||||
using namespace std::string_literals;
|
||||
ghoul::Dictionary renderablePointsCloud;
|
||||
renderablePointsCloud.setValue("Type", "RenderablePointsCloud"s);
|
||||
renderablePointsCloud.setValue("Identifier", identifier);
|
||||
renderablePointsCloud.setValue("Name", guiName);
|
||||
|
||||
ghoul::Dictionary gui;
|
||||
gui.setValue("Name", guiName);
|
||||
gui.setValue("Path", "/Software Integration"s);
|
||||
|
||||
ghoul::Dictionary node;
|
||||
node.setValue("Identifier", identifier);
|
||||
node.setValue("Renderable", renderablePointsCloud);
|
||||
node.setValue("GUI", gui);
|
||||
|
||||
global::scriptEngine->queueScript(
|
||||
"openspace.addSceneGraphNode(" + ghoul::formatLua(node) + ")"
|
||||
"openspace.setPropertyValueSingle('Modules.CefWebGui.Reload', nil)", // Reload WebGUI so that SoftwareIntegration GUI appears
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::addCallback(
|
||||
const std::string& identifier,
|
||||
const Callback& newCallback
|
||||
) {
|
||||
std::lock_guard guard(_onceNodeExistsCallbacksMutex);
|
||||
auto it = _onceNodeExistsCallbacks.find(identifier);
|
||||
if (it == _onceNodeExistsCallbacks.end()) {
|
||||
CallbackList newCallbackList{ newCallback };
|
||||
_onceNodeExistsCallbacks.emplace(identifier, newCallbackList);
|
||||
}
|
||||
else {
|
||||
it->second.push_back(newCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::onFixedColorChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
) {
|
||||
if (!connection->isConnected()) {
|
||||
SoftwareConnection::PointDataMessageHandlerFriends::removePropertySubscription(
|
||||
connection, property->identifier(), identifier
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// auto propertySubscription = connection->getPropertySubscription(identifier, property->identifier());
|
||||
// if (!propertySubscription) return;
|
||||
// if (!propertySubscription->shouldSendMessage) {
|
||||
// propertySubscription->shouldSendMessage = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
glm::vec4 color = std::any_cast<glm::vec4>(property->get());
|
||||
|
||||
const std::string message = simp::formatColorMessage(identifier, color);
|
||||
connection->sendMessage(message);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::onOpacityChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
) {
|
||||
if (!connection->isConnected()) {
|
||||
SoftwareConnection::PointDataMessageHandlerFriends::removePropertySubscription(
|
||||
connection, property->identifier(), identifier
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// auto propertySubscription = connection->getPropertySubscription(identifier, property->identifier());
|
||||
// if (!propertySubscription) return;
|
||||
// if (!propertySubscription->shouldSendMessage) {
|
||||
// propertySubscription->shouldSendMessage = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
float value = std::any_cast<float>(property->get());
|
||||
std::string hex_value = simp::floatToHex(value);
|
||||
|
||||
const std::string message = simp::formatUpdateMessage(simp::MessageType::Opacity, identifier, hex_value);
|
||||
connection->sendMessage(message);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::onFixedPointSizeChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
) {
|
||||
if (!connection->isConnected()) {
|
||||
SoftwareConnection::PointDataMessageHandlerFriends::removePropertySubscription(
|
||||
connection, property->identifier(), identifier
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// auto propertySubscription = connection->getPropertySubscription(identifier, property->identifier());
|
||||
// if (!propertySubscription) return;
|
||||
// if (!propertySubscription->shouldSendMessage) {
|
||||
// propertySubscription->shouldSendMessage = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
float value = std::any_cast<float>(property->get());
|
||||
std::string hex_value = simp::floatToHex(value);
|
||||
|
||||
const std::string message = simp::formatUpdateMessage(simp::MessageType::FixedSize, identifier, hex_value);
|
||||
connection->sendMessage(message);
|
||||
}
|
||||
|
||||
void PointDataMessageHandler::onVisibilityChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
) {
|
||||
if (!connection->isConnected()) {
|
||||
SoftwareConnection::PointDataMessageHandlerFriends::removePropertySubscription(
|
||||
connection, property->identifier(), identifier
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// auto propertySubscription = connection->getPropertySubscription(identifier, property->identifier());
|
||||
// if (!propertySubscription) return;
|
||||
// if (!propertySubscription->shouldSendMessage) {
|
||||
// propertySubscription->shouldSendMessage = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
bool isVisible = std::any_cast<bool>(property->get());
|
||||
std::string_view visibilityFlag = isVisible ? "T" : "F";
|
||||
|
||||
const std::string message = simp::formatUpdateMessage(simp::MessageType::Visibility, identifier, visibilityFlag);
|
||||
connection->sendMessage(message);
|
||||
}
|
||||
|
||||
// TODO: Move to SIMP / use distanceconversion
|
||||
void PointDataMessageHandler::convertToMeterPerSecond(simp::LengthUnit currLengthUnit, std::vector<float>& data) {
|
||||
// distanceconversion::convertDistance
|
||||
float multiplier = 1.0;
|
||||
switch (currLengthUnit) {
|
||||
case simp::LengthUnit::km:
|
||||
multiplier = 1000.0;
|
||||
break;
|
||||
case simp::LengthUnit::AU:
|
||||
multiplier = static_cast<float>(distanceconstants::AstronomicalUnit);
|
||||
break;
|
||||
case simp::LengthUnit::lyr:
|
||||
multiplier = static_cast<float>(distanceconstants::LightYear);
|
||||
break;
|
||||
case simp::LengthUnit::pc:
|
||||
multiplier = static_cast<float>(distanceconstants::Parsec);
|
||||
break;
|
||||
case simp::LengthUnit::kpc:
|
||||
multiplier = static_cast<float>(distanceconstants::Parsec * 1.0e3);
|
||||
break;
|
||||
case simp::LengthUnit::Mpc:
|
||||
multiplier = static_cast<float>(distanceconstants::Parsec * 1.0e6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::transform(std::begin(data), std::end(data), std::begin(data),
|
||||
[multiplier] (float f) {return f * multiplier;});
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
} // namespace openspace::softwareintegration::messagehandler
|
||||
50
modules/softwareintegration/messagehandler.h
Normal file
50
modules/softwareintegration/messagehandler.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_SOFTWAREINTEGRATION___MESSAGEHANDLER___H__
|
||||
#define __OPENSPACE_MODULE_SOFTWAREINTEGRATION___MESSAGEHANDLER___H__
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
|
||||
#include <modules/softwareintegration/network/softwareconnection.h>
|
||||
|
||||
namespace openspace::softwareintegration::network {
|
||||
|
||||
struct Callback {
|
||||
std::function<void()> function;
|
||||
std::vector<softwareintegration::storage::Key> waitForData = {};
|
||||
std::string description = "???"; // To help debugging. Maybe remove?
|
||||
};
|
||||
using CallbackList = std::vector<Callback>;
|
||||
using CallbackMap = std::unordered_map<std::string, CallbackList>;
|
||||
|
||||
void postSyncCallbacks();
|
||||
|
||||
void handleMessage(IncomingMessage& incomingMessage);
|
||||
|
||||
} // namespace openspace::softwareintegration::messagehandler
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SOFTWAREINTEGRATION___MESSAGEHANDLER___H__
|
||||
132
modules/softwareintegration/network/network.cpp
Normal file
132
modules/softwareintegration/network/network.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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/softwareintegration/network/network.h>
|
||||
|
||||
#include <modules/softwareintegration/messagehandler.h>
|
||||
#include <modules/softwareintegration/utils.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/globalscallbacks.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* _loggerCat = "NetworkEngine";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace openspace::softwareintegration::network {
|
||||
|
||||
namespace {
|
||||
|
||||
void eventLoop(std::weak_ptr<NetworkState> networkStateWeakPtr) {
|
||||
while (!networkStateWeakPtr.expired()) {
|
||||
auto networkState = networkStateWeakPtr.lock();
|
||||
if (networkState->shouldStopThreads) break;
|
||||
// The call to "pop" below will block execution
|
||||
// on this thread until interrupt is called
|
||||
try {
|
||||
auto pm = networkState->incomingMessages.pop();
|
||||
handleMessage(pm);
|
||||
}
|
||||
catch (const ghoul::RuntimeError&) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void serverLoop(std::weak_ptr<NetworkState> networkStateWeakPtr) {
|
||||
while (!networkStateWeakPtr.expired()) {
|
||||
auto networkState = networkStateWeakPtr.lock();
|
||||
if (networkState->shouldStopThreads) break;
|
||||
std::unique_ptr<ghoul::io::TcpSocket> socket = networkState->server.awaitPendingTcpSocket();
|
||||
|
||||
if (!socket) return;
|
||||
|
||||
socket->startStreams();
|
||||
|
||||
auto p = std::make_shared<SoftwareConnection>(std::move(socket));
|
||||
std::lock_guard guard(networkState->softwareConnectionsMutex);
|
||||
auto [it, peerInserted] = networkState->softwareConnections.emplace(p->id(), std::move(p));
|
||||
|
||||
if (peerInserted) {
|
||||
auto connectionWeak = std::weak_ptr<SoftwareConnection>{ it->second };
|
||||
auto thread = std::thread{
|
||||
[connectionWeak, networkStateWeakPtr] {
|
||||
connection::eventLoop(connectionWeak, networkStateWeakPtr);
|
||||
}
|
||||
};
|
||||
it->second->setThread(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<NetworkState> serve(const int port) {
|
||||
auto networkState = std::make_shared<NetworkState>();
|
||||
|
||||
// 4700, is the defualt port where the tcp socket will be opened to the ext. software
|
||||
networkState->server.listen(port);
|
||||
|
||||
std::weak_ptr<NetworkState> networkStateWeakPtr = networkState;
|
||||
networkState->serverThread = std::thread{ [networkStateWeakPtr] {
|
||||
serverLoop(networkStateWeakPtr);
|
||||
} };
|
||||
|
||||
networkState->eventLoopThread = std::thread{ [networkStateWeakPtr] {
|
||||
eventLoop(networkStateWeakPtr);
|
||||
} };
|
||||
|
||||
return networkState;
|
||||
};
|
||||
|
||||
void stopServer(std::shared_ptr<NetworkState> networkState) {
|
||||
networkState->shouldStopThreads = true;
|
||||
|
||||
networkState->incomingMessages.interrupt();
|
||||
|
||||
networkState->server.close();
|
||||
|
||||
{
|
||||
std::lock_guard guardSoftwareConnections(networkState->softwareConnectionsMutex);
|
||||
networkState->softwareConnections.clear();
|
||||
}
|
||||
|
||||
if (networkState->serverThread.joinable()) {
|
||||
networkState->serverThread.join();
|
||||
}
|
||||
if (networkState->eventLoopThread.joinable()) {
|
||||
networkState->eventLoopThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
SoftwareConnectionLostError::SoftwareConnectionLostError(const std::string& msg)
|
||||
: ghoul::RuntimeError(fmt::format("{}{}", "Software connection lost", msg), "SoftwareConnection")
|
||||
{}
|
||||
|
||||
} // namespace openspace::softwareintegration::network
|
||||
@@ -26,54 +26,45 @@
|
||||
#define __OPENSPACE_MODULE_SOFTWAREINTEGRATION___NETWORKENGINE___H__
|
||||
|
||||
#include <modules/softwareintegration/network/softwareconnection.h>
|
||||
#include <modules/softwareintegration/pointdatamessagehandler.h>
|
||||
#include <modules/softwareintegration/interruptibleconcurrentqueue.h>
|
||||
#include <modules/softwareintegration/utils.h>
|
||||
#include <ghoul/io/socket/tcpsocketserver.h>
|
||||
#include <modules/softwareintegration/interruptibleconcurrentqueue.h>
|
||||
|
||||
namespace openspace {
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
class NetworkEngine {
|
||||
namespace openspace::softwareintegration::network {
|
||||
|
||||
class SoftwareConnectionLostError : public ghoul::RuntimeError {
|
||||
public:
|
||||
NetworkEngine(const int port = 4700);
|
||||
~NetworkEngine();
|
||||
|
||||
struct IncomingMessage {
|
||||
size_t connection_id;
|
||||
SoftwareConnection::Message message{ softwareintegration::simp::MessageType::Unknown };
|
||||
};
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void postSync();
|
||||
|
||||
private:
|
||||
void handleNewSoftwareConnections();
|
||||
void handleIncomingMessage(IncomingMessage incomingMessage);
|
||||
void peerEventLoop(size_t connection_id);
|
||||
void eventLoop();
|
||||
|
||||
// The destuction of the object a shared_ptr is pointing to, occurs when the pointer no longer has any owners
|
||||
std::shared_ptr<SoftwareConnection> getSoftwareConnection(size_t id);
|
||||
|
||||
std::unordered_map<size_t, std::shared_ptr<SoftwareConnection>> _softwareConnections;
|
||||
std::mutex _softwareConnectionsMutex;
|
||||
|
||||
ghoul::io::TcpSocketServer _socketServer;
|
||||
std::thread _serverThread;
|
||||
std::atomic_bool _shouldStopServerThread = false;
|
||||
std::thread _eventLoopThread;
|
||||
std::atomic_bool _shouldStopEventThread = false;
|
||||
|
||||
|
||||
const int _port;
|
||||
|
||||
// Message handlers
|
||||
PointDataMessageHandler _pointDataMessageHandler;
|
||||
|
||||
InterruptibleConcurrentQueue<IncomingMessage> _incomingMessages;
|
||||
explicit SoftwareConnectionLostError(const std::string& msg);
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
struct IncomingMessage {
|
||||
std::weak_ptr<SoftwareConnection> connection;
|
||||
softwareintegration::simp::MessageType type{ softwareintegration::simp::MessageType::Unknown };
|
||||
std::vector<char> content{};
|
||||
std::string rawMessageType{""};
|
||||
};
|
||||
|
||||
struct NetworkState {
|
||||
ghoul::io::TcpSocketServer server;
|
||||
|
||||
std::thread serverThread;
|
||||
std::thread eventLoopThread;
|
||||
|
||||
std::unordered_map<size_t, std::shared_ptr<SoftwareConnection>> softwareConnections{};
|
||||
std::mutex softwareConnectionsMutex{};
|
||||
|
||||
std::atomic_bool shouldStopThreads{ false };
|
||||
|
||||
InterruptibleConcurrentQueue<IncomingMessage> incomingMessages{};
|
||||
};
|
||||
|
||||
std::shared_ptr<NetworkState> serve(const int port = 4700);
|
||||
|
||||
void stopServer(std::shared_ptr<NetworkState> networkState);
|
||||
|
||||
} // namespace openspace::softwareintegration::network
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SOFTWAREINTEGRATION___NETWORKENGINE___H__
|
||||
@@ -1,245 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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/softwareintegration/network/networkengine.h>
|
||||
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/globalscallbacks.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "NetworkEngine";
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
using namespace softwareintegration;
|
||||
|
||||
NetworkEngine::NetworkEngine(const int port)
|
||||
: _port{port}
|
||||
{}
|
||||
|
||||
NetworkEngine::~NetworkEngine() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void NetworkEngine::start() {
|
||||
_socketServer.listen(_port);
|
||||
|
||||
_serverThread = std::thread([this]() { handleNewSoftwareConnections(); });
|
||||
_eventLoopThread = std::thread([this]() { eventLoop(); });
|
||||
}
|
||||
|
||||
void NetworkEngine::stop() {
|
||||
_shouldStopServerThread = true;
|
||||
|
||||
{
|
||||
std::lock_guard guardSoftwareConnections(_softwareConnectionsMutex);
|
||||
for (auto& [id, connectionPtr] : _softwareConnections) {
|
||||
SoftwareConnection::NetworkEngineFriends::stopThread(connectionPtr);
|
||||
}
|
||||
}
|
||||
|
||||
_incomingMessages.interrupt();
|
||||
|
||||
_shouldStopEventThread = true;
|
||||
_socketServer.close();
|
||||
_softwareConnections.clear();
|
||||
|
||||
if (_serverThread.joinable()) {
|
||||
_serverThread.join();
|
||||
}
|
||||
if (_eventLoopThread.joinable()) {
|
||||
_eventLoopThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkEngine::postSync() {
|
||||
_pointDataMessageHandler.postSync();
|
||||
}
|
||||
|
||||
void NetworkEngine::handleNewSoftwareConnections() {
|
||||
while (!_shouldStopServerThread) {
|
||||
std::unique_ptr<ghoul::io::TcpSocket> socket = _socketServer.awaitPendingTcpSocket();
|
||||
|
||||
if (!socket) return;
|
||||
|
||||
socket->startStreams();
|
||||
|
||||
auto p = std::make_shared<SoftwareConnection>(std::move(socket));
|
||||
std::lock_guard guard(_softwareConnectionsMutex);
|
||||
auto [it, peerInserted] = _softwareConnections.emplace(p->id(), p);
|
||||
|
||||
if (peerInserted) {
|
||||
auto& connectionPtr = it->second;
|
||||
auto thread = std::thread{
|
||||
[this, &connectionPtr] {
|
||||
peerEventLoop(connectionPtr->id());
|
||||
}
|
||||
};
|
||||
connectionPtr->setThread(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkEngine::peerEventLoop(size_t connection_id) {
|
||||
using namespace std::literals::chrono_literals;
|
||||
auto connectionPtr = getSoftwareConnection(connection_id);
|
||||
|
||||
while (!connectionPtr->shouldStopThread()) {
|
||||
try {
|
||||
SoftwareConnection::Message m = connectionPtr->receiveMessageFromSoftware();
|
||||
_incomingMessages.push({ connection_id, m });
|
||||
}
|
||||
catch (const SoftwareConnection::SoftwareConnectionLostError& err) {
|
||||
if (connectionPtr->shouldStopThread()) break;
|
||||
|
||||
if (connectionPtr && (!connectionPtr->shouldStopThread() || !connectionPtr->isConnectedOrConnecting())) {
|
||||
LDEBUG(fmt::format("Connection lost to {}: {}", connection_id, err.message));
|
||||
_incomingMessages.push({
|
||||
connection_id,
|
||||
SoftwareConnection::Message{ simp::MessageType::InternalDisconnection }
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkEngine::eventLoop() {
|
||||
while (!_shouldStopEventThread) {
|
||||
// The call to "pop" below will block execution
|
||||
// on this thread until interrupt is called
|
||||
try {
|
||||
auto pm = _incomingMessages.pop();
|
||||
handleIncomingMessage(pm);
|
||||
}
|
||||
catch (const ghoul::RuntimeError&) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<SoftwareConnection> NetworkEngine::getSoftwareConnection(size_t id) {
|
||||
std::lock_guard guard(_softwareConnectionsMutex);
|
||||
auto it = _softwareConnections.find(id);
|
||||
if (it == _softwareConnections.end()) return nullptr;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void NetworkEngine::handleIncomingMessage(IncomingMessage incomingMessage) {
|
||||
auto connectionPtr = getSoftwareConnection(incomingMessage.connection_id);
|
||||
|
||||
if(!connectionPtr) {
|
||||
LDEBUG(fmt::format("Trying to handle message from disconnected peer. Aborting."));
|
||||
return;
|
||||
}
|
||||
|
||||
const simp::MessageType messageType = incomingMessage.message.type;
|
||||
std::vector<char>& message = incomingMessage.message.content;
|
||||
|
||||
switch (messageType) {
|
||||
case simp::MessageType::Connection: {
|
||||
LDEBUG(fmt::format("Message recieved... Connection: {}", incomingMessage.connection_id));
|
||||
size_t offset = 0;
|
||||
const std::string software = simp::readString(message, offset);
|
||||
|
||||
// Send back message to software to complete handshake
|
||||
connectionPtr->sendMessage(simp::formatConnectionMessage(software));
|
||||
LINFO(fmt::format("OpenSpace has connected with {} through socket", software));
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::PointData: {
|
||||
LDEBUG("Message recieved... Point data");
|
||||
_pointDataMessageHandler.handlePointDataMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::VelocityData: {
|
||||
LDEBUG("Message recieved... Velocity data");
|
||||
_pointDataMessageHandler.handleVelocityDataMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::RemoveSceneGraphNode: {
|
||||
LDEBUG(fmt::format("Message recieved... Remove SGN"));
|
||||
_pointDataMessageHandler.handleRemoveSGNMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Color: {
|
||||
LDEBUG(fmt::format("Message recieved... Color"));
|
||||
_pointDataMessageHandler.handleFixedColorMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Colormap: {
|
||||
LDEBUG(fmt::format("Message recieved... Colormap"));
|
||||
_pointDataMessageHandler.handleColormapMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::AttributeData: {
|
||||
LDEBUG(fmt::format("Message recieved... Attribute data"));
|
||||
_pointDataMessageHandler.handleAttributeDataMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Opacity: {
|
||||
LDEBUG(fmt::format("Message recieved... Opacity"));
|
||||
_pointDataMessageHandler.handleOpacityMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::FixedSize: {
|
||||
LDEBUG(fmt::format("Message recieved... Size"));
|
||||
_pointDataMessageHandler.handleFixedPointSizeMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::LinearSize: {
|
||||
LDEBUG(fmt::format("Message recieved... Linear size"));
|
||||
_pointDataMessageHandler.handleLinearPointSizeMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::Visibility: {
|
||||
LDEBUG(fmt::format("Message recieved... Visibility"));
|
||||
_pointDataMessageHandler.handleVisibilityMessage(message, connectionPtr);
|
||||
break;
|
||||
}
|
||||
case simp::MessageType::InternalDisconnection: {
|
||||
LDEBUG(fmt::format("Message recieved... Disconnection from software connection: {}", incomingMessage.connection_id));
|
||||
std::lock_guard guard(_softwareConnectionsMutex);
|
||||
SoftwareConnection::NetworkEngineFriends::stopThread(connectionPtr);
|
||||
|
||||
if (_softwareConnections.count(incomingMessage.connection_id)) {
|
||||
_softwareConnections.erase(incomingMessage.connection_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LERROR(fmt::format(
|
||||
"Unsupported message type: {}", incomingMessage.message.rawMessageType
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <modules/softwareintegration/network/softwareconnection.h>
|
||||
|
||||
#include <modules/softwareintegration/network/network.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/syncengine.h>
|
||||
@@ -39,10 +40,6 @@ namespace openspace {
|
||||
|
||||
std::atomic_size_t SoftwareConnection::_nextConnectionId = 1;
|
||||
|
||||
SoftwareConnection::SoftwareConnectionLostError::SoftwareConnectionLostError(const std::string& msg)
|
||||
: ghoul::RuntimeError(fmt::format("{}{}", "Software connection lost", msg), "SoftwareConnection")
|
||||
{}
|
||||
|
||||
SoftwareConnection::SoftwareConnection(std::unique_ptr<ghoul::io::TcpSocket> socket)
|
||||
: _id{ _nextConnectionId++ }, _socket{ std::move(socket) }, _sceneGraphNodes{},
|
||||
_thread{}, _shouldStopThread{ false }
|
||||
@@ -52,7 +49,7 @@ SoftwareConnection::SoftwareConnection(std::unique_ptr<ghoul::io::TcpSocket> soc
|
||||
|
||||
SoftwareConnection::SoftwareConnection(SoftwareConnection&& sc)
|
||||
: _id{ std::move(sc._id) }, _socket{ std::move(sc._socket) },
|
||||
_isConnected{ sc._isConnected }, _sceneGraphNodes{ std::move(sc._sceneGraphNodes) },
|
||||
_sceneGraphNodes{ std::move(sc._sceneGraphNodes) },
|
||||
_thread{}, _shouldStopThread{ false }
|
||||
{}
|
||||
|
||||
@@ -61,16 +58,13 @@ SoftwareConnection::~SoftwareConnection() {
|
||||
// destructor is called when disconnecting external
|
||||
// since NetworkEngine and MessageHandler has
|
||||
// shared_ptrs to SoftwareConnection, which can cause
|
||||
// bugs if not handled properly.
|
||||
// bugs if not handled properly.
|
||||
// Tips: use weak_ptr instead of shared_ptr in callbacks.
|
||||
LDEBUG(fmt::format("Removing software connection {}", _id));
|
||||
|
||||
if (!_isConnected) return;
|
||||
_isConnected = false;
|
||||
|
||||
if (_socket) {
|
||||
_socket->disconnect();
|
||||
}
|
||||
_shouldStopThread = true;
|
||||
_thread.detach();
|
||||
disconnect();
|
||||
}
|
||||
|
||||
void SoftwareConnection::addPropertySubscription(
|
||||
@@ -199,8 +193,7 @@ void SoftwareConnection::removePropertySubscriptions(const std::string& identifi
|
||||
_subscribedProperties.erase(propertySubscriptions);
|
||||
}
|
||||
|
||||
void SoftwareConnection::PointDataMessageHandlerFriends::removePropertySubscription(
|
||||
std::shared_ptr<SoftwareConnection> connectionPtr,
|
||||
void SoftwareConnection::removePropertySubscription(
|
||||
const std::string& propertyName,
|
||||
const std::string& identifier
|
||||
) {
|
||||
@@ -224,8 +217,8 @@ void SoftwareConnection::PointDataMessageHandlerFriends::removePropertySubscript
|
||||
|
||||
auto property = r->property(propertyName);
|
||||
|
||||
auto propertySubscriptions = connectionPtr->_subscribedProperties.find(identifier);
|
||||
if (propertySubscriptions != connectionPtr->_subscribedProperties.end()) {
|
||||
auto propertySubscriptions = _subscribedProperties.find(identifier);
|
||||
if (propertySubscriptions != _subscribedProperties.end()) {
|
||||
// At least one property have been subscribed to on this SGN
|
||||
auto propertySubscription = propertySubscriptions->second.find(propertyName);
|
||||
if (propertySubscription != propertySubscriptions->second.end()) {
|
||||
@@ -246,11 +239,11 @@ void SoftwareConnection::disconnect() {
|
||||
}
|
||||
|
||||
bool SoftwareConnection::isConnected() const {
|
||||
return _isConnected && _socket && _socket->isConnected();
|
||||
return _socket && _socket->isConnected();
|
||||
}
|
||||
|
||||
bool SoftwareConnection::isConnectedOrConnecting() const {
|
||||
return _isConnected && _socket && (_socket->isConnected() || _socket->isConnecting());
|
||||
return _socket && (_socket->isConnected() || _socket->isConnecting());
|
||||
}
|
||||
|
||||
bool SoftwareConnection::sendMessage(const std::string& message) {
|
||||
@@ -285,7 +278,51 @@ void SoftwareConnection::removeSceneGraphNode(const std::string& identifier) {
|
||||
}
|
||||
}
|
||||
|
||||
SoftwareConnection::Message SoftwareConnection::receiveMessageFromSoftware() {
|
||||
size_t SoftwareConnection::id() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
void SoftwareConnection::setThread(std::thread& t) {
|
||||
_thread = std::move(t);
|
||||
}
|
||||
|
||||
ghoul::io::TcpSocket* SoftwareConnection::socket() {
|
||||
return _socket.get();
|
||||
}
|
||||
|
||||
namespace softwareintegration::network::connection {
|
||||
|
||||
void eventLoop(
|
||||
std::weak_ptr<SoftwareConnection> connectionWeakPtr,
|
||||
std::weak_ptr<NetworkState> networkStateWeakPtr
|
||||
) {
|
||||
while (!connectionWeakPtr.expired()) {
|
||||
auto connectionPtr = connectionWeakPtr.lock();
|
||||
if (connectionPtr->_shouldStopThread) break;
|
||||
|
||||
try {
|
||||
IncomingMessage m = receiveMessageFromSoftware(connectionPtr);
|
||||
if (networkStateWeakPtr.expired()) break;
|
||||
networkStateWeakPtr.lock()->incomingMessages.push(m);
|
||||
}
|
||||
catch (const SoftwareConnectionLostError& err) {
|
||||
if (!networkStateWeakPtr.expired()
|
||||
& (!connectionPtr->_shouldStopThread || !connectionPtr->isConnectedOrConnecting())
|
||||
) {
|
||||
LDEBUG(fmt::format("Connection lost to {}: {}", connectionPtr->id(), err.message));
|
||||
auto networkState = networkStateWeakPtr.lock();
|
||||
if (networkState->softwareConnections.count(connectionPtr->id())) {
|
||||
networkState->softwareConnections.erase(connectionPtr->id());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IncomingMessage receiveMessageFromSoftware(
|
||||
std::shared_ptr<SoftwareConnection> connectionPtr
|
||||
) {
|
||||
// Header consists of version (3 char), message type (4 char) & subject size (15 char)
|
||||
size_t headerSize = 22 * sizeof(char);
|
||||
|
||||
@@ -294,7 +331,7 @@ SoftwareConnection::Message SoftwareConnection::receiveMessageFromSoftware() {
|
||||
std::vector<char> subjectBuffer;
|
||||
|
||||
// Receive the header data
|
||||
if (!_socket->get(headerBuffer.data(), headerSize)) {
|
||||
if (!connectionPtr->socket()->get(headerBuffer.data(), headerSize)) {
|
||||
throw SoftwareConnectionLostError("Failed to read header from socket. Disconnecting.");
|
||||
}
|
||||
|
||||
@@ -329,34 +366,16 @@ SoftwareConnection::Message SoftwareConnection::receiveMessageFromSoftware() {
|
||||
auto typeEnum = softwareintegration::simp::getMessageType(type);
|
||||
|
||||
// Receive the message data
|
||||
if (typeEnum != softwareintegration::simp::MessageType::InternalDisconnection && typeEnum != softwareintegration::simp::MessageType::Unknown) {
|
||||
if (typeEnum != softwareintegration::simp::MessageType::Unknown) {
|
||||
subjectBuffer.resize(subjectSize);
|
||||
if (!_socket->get(subjectBuffer.data(), subjectSize)) {
|
||||
if (!connectionPtr->socket()->get(subjectBuffer.data(), subjectSize)) {
|
||||
throw SoftwareConnectionLostError("Failed to read message from socket. Disconnecting.");
|
||||
}
|
||||
}
|
||||
|
||||
return Message{ typeEnum, subjectBuffer, type };
|
||||
return { connectionPtr, typeEnum, subjectBuffer, type };
|
||||
}
|
||||
|
||||
bool SoftwareConnection::shouldStopThread() {
|
||||
return _shouldStopThread;
|
||||
}
|
||||
|
||||
size_t SoftwareConnection::id() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
void SoftwareConnection::setThread(std::thread& t) {
|
||||
_thread = std::move(t);
|
||||
}
|
||||
|
||||
void SoftwareConnection::NetworkEngineFriends::stopThread(std::shared_ptr<SoftwareConnection> connectionPtr) {
|
||||
connectionPtr->_shouldStopThread = true;
|
||||
connectionPtr->disconnect();
|
||||
if (connectionPtr->_thread.joinable()) {
|
||||
connectionPtr->_thread.join();
|
||||
}
|
||||
}
|
||||
} // namespace softwareintegration::network::connection
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -25,8 +25,8 @@
|
||||
#ifndef __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SOFTWARECONNECTION___H__
|
||||
#define __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SOFTWARECONNECTION___H__
|
||||
|
||||
#include <openspace/network/messagestructures.h>
|
||||
#include <modules/softwareintegration/utils.h>
|
||||
#include <openspace/network/messagestructures.h>
|
||||
#include <ghoul/io/socket/tcpsocket.h>
|
||||
#include <openspace/properties/property.h>
|
||||
|
||||
@@ -36,8 +36,30 @@
|
||||
namespace openspace {
|
||||
|
||||
class Renderable;
|
||||
class SoftwareConnection;
|
||||
|
||||
namespace softwareintegration::network {
|
||||
|
||||
struct NetworkState;
|
||||
struct IncomingMessage;
|
||||
|
||||
namespace connection {
|
||||
void eventLoop(
|
||||
std::weak_ptr<SoftwareConnection> connectionWeakPtr,
|
||||
std::weak_ptr<softwareintegration::network::NetworkState> networkStateWeakPtr
|
||||
);
|
||||
|
||||
IncomingMessage receiveMessageFromSoftware(
|
||||
std::shared_ptr<SoftwareConnection> connectionPtr
|
||||
);
|
||||
} // namespace connection
|
||||
|
||||
} // namespace softwareintegration::network
|
||||
|
||||
using namespace softwareintegration::network;
|
||||
|
||||
class SoftwareConnection {
|
||||
|
||||
public:
|
||||
using OnChangeHandle = properties::Property::OnChangeHandle;
|
||||
struct PropertySubscription {
|
||||
@@ -49,14 +71,6 @@ public:
|
||||
using Identifier = std::string;
|
||||
using SubscribedProperties = std::unordered_map<Identifier, PropertySubscriptions>;
|
||||
|
||||
struct Message {
|
||||
softwareintegration::simp::MessageType type;
|
||||
std::vector<char> content{};
|
||||
std::string rawMessageType{""};
|
||||
};
|
||||
|
||||
class SoftwareConnectionLostError;
|
||||
|
||||
explicit SoftwareConnection(std::unique_ptr<ghoul::io::TcpSocket> socket);
|
||||
SoftwareConnection(SoftwareConnection&& p);
|
||||
~SoftwareConnection();
|
||||
@@ -76,40 +90,33 @@ public:
|
||||
// const std::string& identifier
|
||||
// );
|
||||
|
||||
SoftwareConnection::Message receiveMessageFromSoftware();
|
||||
|
||||
void addSceneGraphNode(const std::string& identifier);
|
||||
void removeSceneGraphNode(const std::string& identifier);
|
||||
|
||||
size_t id();
|
||||
size_t nConnections();
|
||||
void setThread(std::thread& t);
|
||||
bool shouldStopThread();
|
||||
|
||||
class NetworkEngineFriends {
|
||||
private:
|
||||
static void stopThread(std::shared_ptr<SoftwareConnection> connectionPtr);
|
||||
friend class NetworkEngine;
|
||||
};
|
||||
friend void connection::eventLoop(
|
||||
std::weak_ptr<SoftwareConnection> connectionWeakPtr,
|
||||
std::weak_ptr<NetworkState> networkStateWeakPtr
|
||||
);
|
||||
|
||||
class PointDataMessageHandlerFriends {
|
||||
private:
|
||||
static void removePropertySubscription(
|
||||
std::shared_ptr<SoftwareConnection> connectionPtr,
|
||||
const std::string& propertyName,
|
||||
const std::string& identifier
|
||||
);
|
||||
friend class PointDataMessageHandler;
|
||||
};
|
||||
friend IncomingMessage connection::receiveMessageFromSoftware(
|
||||
std::shared_ptr<SoftwareConnection> connectionPtr
|
||||
);
|
||||
|
||||
void removePropertySubscription(const std::string& propertyName, const std::string& identifier);
|
||||
|
||||
void removePropertySubscriptions(const std::string& identifier);
|
||||
|
||||
ghoul::io::TcpSocket* socket();
|
||||
|
||||
private:
|
||||
void removePropertySubscriptions(const std::string& identifier);
|
||||
|
||||
SubscribedProperties _subscribedProperties;
|
||||
|
||||
std::unordered_set<std::string> _sceneGraphNodes;
|
||||
|
||||
bool _isConnected = true;
|
||||
std::unique_ptr<ghoul::io::TcpSocket> _socket;
|
||||
|
||||
size_t _id;
|
||||
@@ -119,11 +126,6 @@ private:
|
||||
static std::atomic_size_t _nextConnectionId;
|
||||
};
|
||||
|
||||
class SoftwareConnection::SoftwareConnectionLostError : public ghoul::RuntimeError {
|
||||
public:
|
||||
explicit SoftwareConnectionLostError(const std::string& msg);
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SOFTWAREINTEGRATIONMODULE___H__
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_SOFTWAREINTEGRATION___POINTDATAMESSAGEHANDLER___H__
|
||||
#define __OPENSPACE_MODULE_SOFTWAREINTEGRATION___POINTDATAMESSAGEHANDLER___H__
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
|
||||
#include <modules/softwareintegration/network/softwareconnection.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class Renderable;
|
||||
|
||||
class PointDataMessageHandler {
|
||||
struct Callback {
|
||||
std::function<void()> function;
|
||||
std::vector<softwareintegration::storage::Key> waitForData = {};
|
||||
std::string description = "???"; // To help debugging. Maybe remove?
|
||||
};
|
||||
using CallbackList = std::vector<Callback>;
|
||||
using CallbackMap = std::unordered_map<std::string, CallbackList>;
|
||||
|
||||
public:
|
||||
void handlePointDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleVelocityDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleFixedColorMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleColormapMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleAttributeDataMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleOpacityMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleFixedPointSizeMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleLinearPointSizeMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleVisibilityMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
void handleRemoveSGNMessage(const std::vector<char>& message, std::shared_ptr<SoftwareConnection> connection);
|
||||
|
||||
void postSync();
|
||||
|
||||
private:
|
||||
const Renderable* getRenderable(const std::string& identifier);
|
||||
void checkRenderable(
|
||||
const std::vector<char>& message, size_t& messageOffset,
|
||||
std::shared_ptr<SoftwareConnection> connection, std::string& identifier
|
||||
);
|
||||
|
||||
void addCallback(
|
||||
const std::string& identifier,
|
||||
const Callback& newCallback
|
||||
);
|
||||
|
||||
CallbackMap _onceNodeExistsCallbacks;
|
||||
std::mutex _onceNodeExistsCallbacksMutex;
|
||||
size_t _onceNodeExistsCallbacksRetries{0};
|
||||
|
||||
void onFixedColorChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
);
|
||||
void onOpacityChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
);
|
||||
void onFixedPointSizeChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
);
|
||||
void onVisibilityChange(
|
||||
properties::Property* property,
|
||||
const std::string& identifier,
|
||||
std::shared_ptr<SoftwareConnection> connection
|
||||
);
|
||||
|
||||
void convertToMeterPerSecond(
|
||||
softwareintegration::simp::LengthUnit currLengthUnit,
|
||||
std::vector<float>& data
|
||||
);
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SOFTWAREINTEGRATION___POINTDATAMESSAGEHANDLER___H__
|
||||
@@ -24,13 +24,13 @@
|
||||
|
||||
#include <modules/softwareintegration/softwareintegrationmodule.h>
|
||||
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <modules/softwareintegration/rendering/renderablepointscloud.h>
|
||||
#include <modules/softwareintegration/messagehandler.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/syncengine.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <modules/softwareintegration/rendering/renderablepointscloud.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/engine/globalscallbacks.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
@@ -48,14 +48,9 @@ constexpr const char* _loggerCat = "SoftwareIntegrationModule";
|
||||
|
||||
namespace openspace {
|
||||
|
||||
SoftwareIntegrationModule::SoftwareIntegrationModule() : OpenSpaceModule(Name) {
|
||||
if (global::windowDelegate->isMaster()) {
|
||||
// The Master node will handle all communication with the external software
|
||||
// and forward it to the Client nodes
|
||||
// 4700, is the defualt port where the tcp socket will be opened to the ext. software
|
||||
_networkEngine = std::make_unique<NetworkEngine>();
|
||||
}
|
||||
}
|
||||
SoftwareIntegrationModule::SoftwareIntegrationModule()
|
||||
: OpenSpaceModule(Name)
|
||||
{}
|
||||
|
||||
SoftwareIntegrationModule::~SoftwareIntegrationModule() {
|
||||
internalDeinitialize();
|
||||
@@ -89,17 +84,21 @@ void SoftwareIntegrationModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
fRenderable->registerClass<RenderablePointsCloud>("RenderablePointsCloud");
|
||||
|
||||
if (global::windowDelegate->isMaster()) {
|
||||
_networkEngine->start();
|
||||
// The Master node will handle all communication with the external software
|
||||
// and forward it to the Client nodes
|
||||
_networkState = softwareintegration::network::serve();
|
||||
|
||||
global::callback::postSyncPreDraw->emplace_back([this]() {
|
||||
if (!_networkEngine) return;
|
||||
_networkEngine->postSync();
|
||||
softwareintegration::network::postSyncCallbacks();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareIntegrationModule::internalDeinitialize() {
|
||||
global::syncEngine->removeSyncables(getSyncables());
|
||||
if (_networkState) {
|
||||
softwareintegration::network::stopServer(_networkState);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<documentation::Documentation> SoftwareIntegrationModule::documentations() const {
|
||||
|
||||
@@ -26,10 +26,9 @@
|
||||
#define __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SOFTWAREINTEGRATIONMODULE___H__
|
||||
|
||||
#include <openspace/util/openspacemodule.h>
|
||||
|
||||
#include <modules/softwareintegration/network/networkengine.h>
|
||||
#include <modules/softwareintegration/syncablefloatdatastorage.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <modules/softwareintegration/network/network.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
@@ -64,8 +63,7 @@ private:
|
||||
// Centralized storage for datasets
|
||||
SyncableFloatDataStorage _syncableFloatDataStorage;
|
||||
|
||||
// Network engine
|
||||
std::unique_ptr<NetworkEngine> _networkEngine;
|
||||
std::shared_ptr<softwareintegration::network::NetworkState> _networkState;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -38,6 +38,20 @@ namespace openspace {
|
||||
namespace softwareintegration {
|
||||
|
||||
namespace storage {
|
||||
|
||||
// Anonymous namespace
|
||||
namespace {
|
||||
|
||||
const std::unordered_map<std::string, Key> _keyStringFromKey{
|
||||
{ "DataPoints", Key::DataPoints },
|
||||
{ "VelocityData", Key::VelocityData },
|
||||
{ "Colormap", Key::Colormap },
|
||||
{ "ColormapAttributeData", Key::ColormapAttrData },
|
||||
{ "LinearSizeAttributeData", Key::LinearSizeAttrData },
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool hasStorageKey(const std::string& key) {
|
||||
return _keyStringFromKey.count(key) > 0;
|
||||
}
|
||||
@@ -72,6 +86,7 @@ namespace {
|
||||
const std::unordered_map<std::string, MessageType> _messageTypeFromSIMPType{
|
||||
{ "CONN", MessageType::Connection },
|
||||
{ "PDAT", MessageType::PointData },
|
||||
{ "VDAT", MessageType::VelocityData },
|
||||
{ "RSGN", MessageType::RemoveSceneGraphNode },
|
||||
{ "FCOL", MessageType::Color },
|
||||
{ "LCOL", MessageType::Colormap },
|
||||
|
||||
@@ -40,13 +40,6 @@ enum class Key : uint8_t {
|
||||
Unknown
|
||||
};
|
||||
|
||||
const std::unordered_map<std::string, Key> _keyStringFromKey{
|
||||
{ "DataPoints", Key::DataPoints },
|
||||
{ "Colormap", Key::Colormap },
|
||||
{ "ColormapAttributeData", Key::ColormapAttrData },
|
||||
{ "LinearSizeAttributeData", Key::LinearSizeAttrData },
|
||||
};
|
||||
|
||||
Key getStorageKey(const std::string& key);
|
||||
|
||||
std::string getStorageKeyString(const Key key);
|
||||
@@ -73,7 +66,6 @@ enum class MessageType : uint32_t {
|
||||
FixedSize,
|
||||
LinearSize,
|
||||
Visibility,
|
||||
InternalDisconnection,
|
||||
Unknown
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user