Merge branch 'thesis/2022/software-integration_refactor-simp' into thesis/2022/software-integration

This commit is contained in:
Victor Lindquist
2022-06-22 15:52:06 -06:00
26 changed files with 2377 additions and 1908 deletions
+6 -2
View File
@@ -26,12 +26,16 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/softwareintegrationmodule.h
${CMAKE_CURRENT_SOURCE_DIR}/softwareintegrationmodule.inl
${CMAKE_CURRENT_SOURCE_DIR}/messagehandler.h
${CMAKE_CURRENT_SOURCE_DIR}/messagehandler.inl
${CMAKE_CURRENT_SOURCE_DIR}/network/network.h
${CMAKE_CURRENT_SOURCE_DIR}/network/softwareconnection.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepointscloud.h
${CMAKE_CURRENT_SOURCE_DIR}/utils.h
${CMAKE_CURRENT_SOURCE_DIR}/syncablefloatdatastorage.h
${CMAKE_CURRENT_SOURCE_DIR}/utils.inl
${CMAKE_CURRENT_SOURCE_DIR}/syncablestorage.h
${CMAKE_CURRENT_SOURCE_DIR}/syncablestorage.inl
${CMAKE_CURRENT_SOURCE_DIR}/interruptibleconcurrentqueue.h
${CMAKE_CURRENT_SOURCE_DIR}/interruptibleconcurrentqueue.inl
${CMAKE_CURRENT_SOURCE_DIR}/assethelper.h
@@ -46,7 +50,7 @@ set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/network/softwareconnection.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablepointscloud.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/syncablefloatdatastorage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/syncablestorage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/assethelper.cpp
)
source_group("Source Files" FILES ${SOURCE_FILES})
+21 -5
View File
@@ -25,8 +25,9 @@
#include <modules/softwareintegration/assethelper.h>
#include <modules/softwareintegration/softwareintegrationmodule.h>
#include <modules/softwareintegration/syncablefloatdatastorage.h>
#include <modules/softwareintegration/syncablestorage.h>
#include <openspace/engine/globals.h>
#include <openspace/scripting/scriptengine.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/rendering/renderable.h>
#include <openspace/query/query.h>
@@ -87,7 +88,7 @@ bool readFile(std::filesystem::path path, std::vector<std::byte>& buffer, std::s
}
}
bool saveSessionData(SyncableFloatDataStorage& storage,
bool saveSessionData(SyncableStorage& storage,
const std::filesystem::path& filePath,
std::string& errorMessage)
{
@@ -132,7 +133,7 @@ bool AssetHelper::loadSessionData(SoftwareIntegrationModule* module,
}
try {
module->_syncableFloatDataStorage.store(byteStream);
module->_syncableStorage.store(byteStream);
}
catch (const std::exception& e) {
errorMessage = fmt::format("Couldn't store loaded data in Software Integration storage", e.what());
@@ -140,6 +141,21 @@ bool AssetHelper::loadSessionData(SoftwareIntegrationModule* module,
return false;
}
// Set large time steps for the GUI (so you for example
// can see the movement of stars at 5000 years/second)
// Values set in seconds: Real time, 5k years,
// 10k year, 50k year, 100k year, 500k year, 1M year
std::string largeTimeSteps = "{ 1.0, 157680000000.0, 315360000000.0,"
" 1576800000000.0, 3153600000000.0,"
" 15768000000000.0, 3153600000000.0 }";
global::scriptEngine->queueScript(
fmt::format(
"openspace.time.setDeltaTimeSteps({});",
largeTimeSteps
),
scripting::ScriptEngine::RemoteScripting::Yes
);
return true;
}
@@ -164,7 +180,7 @@ bool AssetHelper::saveSession(const std::string& wantedFileName, std::string& er
auto sessionDataFilePath = dirPath / std::filesystem::path{ dirPath.filename().string() + ".dat" };
if (
!saveSessionData(
softwareIntegrationModule->_syncableFloatDataStorage,
softwareIntegrationModule->_syncableStorage,
sessionDataFilePath,
errorMessage
)
@@ -179,7 +195,7 @@ bool AssetHelper::saveSession(const std::string& wantedFileName, std::string& er
assetFile << "local nodes = {\n";
auto identifiers = softwareIntegrationModule->_syncableFloatDataStorage.getAllIdentifiers();
auto identifiers = softwareIntegrationModule->_syncableStorage.getAllIdentifiers();
bool isFirstSgn = true;
for (auto& identifier : identifiers) {
auto r = renderable(identifier);
File diff suppressed because it is too large Load Diff
+12 -1
View File
@@ -31,7 +31,7 @@
#include <modules/softwareintegration/network/softwareconnection.h>
namespace openspace::softwareintegration::network {
namespace openspace::softwareintegration::messagehandler {
struct Callback {
std::function<void()> function;
@@ -45,6 +45,17 @@ void postSyncCallbacks();
void handleMessage(IncomingMessage& incomingMessage);
template<typename T>
bool handleEnumValue(
const std::vector<std::byte>& message,
size_t& offset,
const simp::DataKey& dataKey,
const std::string& identifier,
const std::string& propertyName
);
} // namespace openspace::softwareintegration::messagehandler
#include "messagehandler.inl"
#endif // __OPENSPACE_MODULE_SOFTWAREINTEGRATION___MESSAGEHANDLER___H__
@@ -0,0 +1,96 @@
/*****************************************************************************************
* *
* 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. *
****************************************************************************************/
namespace openspace::softwareintegration::messagehandler {
namespace {
template<typename T>
bool handleEnumValue(
const std::vector<std::byte>& message,
size_t& offset,
const std::string& identifier,
const simp::DataKey& dataKey,
const std::string& propertyName
) {
int32_t newValue;
try {
simp::readValue(message, offset, newValue);
}
catch (const simp::SimpError& err) {
LERROR(fmt::format(
"Error when parsing int32_t in DATA.{} message: {}",
simp::getStringFromDataKey(dataKey), err.message
));
return false;
}
try {
static_cast<T>(newValue);
}
catch (const std::exception& err) {
LERROR(fmt::format(
"Error when casting {} to {} in DATA.{} message: {}",
newValue, typeid(T).name(), simp::getStringFromDataKey(dataKey), err.what()
));
return false;
}
auto setEnumCallback = [identifier, propertyName, newValue] {
// Get renderable
auto r = getRenderable(identifier);
if (!r) return;
// Get property
auto property = r->property(propertyName);
if (!property) return;
// Update bool property of renderable
auto currentValue = std::any_cast<int>(property->get());
if (newValue != currentValue) {
global::scriptEngine->queueScript(
fmt::format(
"openspace.setPropertyValueSingle('Scene.{}.Renderable.{}', {});",
identifier, propertyName, ghoul::to_string(newValue)
),
scripting::ScriptEngine::RemoteScripting::Yes
);
}
};
addCallback(
identifier,
{
setEnumCallback,
{},
fmt::format("Callback on property {}", propertyName),
}
);
return true;
}
} // namespace
} // openspace::softwareintegration::messagehandler
@@ -51,7 +51,7 @@ void eventLoop(std::weak_ptr<NetworkState> networkStateWeakPtr) {
// on this thread until interrupt is called
try {
auto pm = networkState->incomingMessages.pop();
handleMessage(pm);
messagehandler::handleMessage(pm);
}
catch (const ghoul::RuntimeError&) {
break;
@@ -43,7 +43,7 @@ public:
struct IncomingMessage {
std::weak_ptr<SoftwareConnection> connection;
softwareintegration::simp::MessageType type{ softwareintegration::simp::MessageType::Unknown };
std::vector<char> content{};
std::vector<std::byte> content{};
std::string rawMessageType{""};
};
@@ -25,6 +25,7 @@
#include <modules/softwareintegration/network/softwareconnection.h>
#include <modules/softwareintegration/network/network.h>
#include <modules/softwareintegration/utils.h>
#include <ghoul/logging/logmanager.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/syncengine.h>
@@ -62,6 +63,12 @@ SoftwareConnection::~SoftwareConnection() {
// Tips: use weak_ptr instead of shared_ptr in callbacks.
LDEBUG(fmt::format("Removing software connection {}", _id));
_shouldStopOutgoingMessagesThread = true;
_outgoingMessagesNotifier.notify_all();
if (_outgoingMessagesThread && _outgoingMessagesThread->joinable()) {
_outgoingMessagesThread->join();
}
_shouldStopThread = true;
_thread.detach();
disconnect();
@@ -82,7 +89,8 @@ void SoftwareConnection::addPropertySubscription(
return;
}
if (!r->hasProperty(propertyName)) {
auto property = r->property(propertyName);
if (!property) {
LWARNING(fmt::format(
"Couldn't add property subscription. Property \"{}\" doesn't exist on \"{}\"",
propertyName, identifier
@@ -90,72 +98,49 @@ void SoftwareConnection::addPropertySubscription(
return;
}
auto property = r->property(propertyName);
// Set new onChange handler
OnChangeHandle onChangeHandle = property->onChange(newHandler);
SceneGraphNodeInfo::OnChangeHandle onChangeHandle = property->onChange(newHandler);
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()) {
// Property subscription already exists
// Remove old handle
property->removeOnChange(propertySubscription->second);
auto propertySubscriptions = _sceneGraphNodes.find(identifier);
if (propertySubscriptions == _sceneGraphNodes.end()) {
LERROR(fmt::format("Couldn't add property subscription. No SceneGraphNode with identifier {} exists.", identifier));
return;
}
// Save new handle
propertySubscription->second = onChangeHandle;
}
else {
// Property subscription doesn't exist
propertySubscriptions->second.emplace(propertyName, onChangeHandle);
}
auto propertySubscription = propertySubscriptions->second.propertySubscriptions.find(propertyName);
if (propertySubscription != propertySubscriptions->second.propertySubscriptions.end()) {
// Property subscription already exists
// Remove old onChange handler
property->removeOnChange(propertySubscription->second.onChangehandle);
// Save new onChange handler
propertySubscription->second.onChangehandle = onChangeHandle;
}
else {
// No properties have been subscribed to on this SGN
PropertySubscriptions newPropertySubscriptionMap{ { propertyName, onChangeHandle } };
_subscribedProperties.emplace(identifier, newPropertySubscriptionMap);
// Property subscription doesn't exist
SceneGraphNodeInfo::PropertySubscription newPropertySubscription{ onChangeHandle };
propertySubscriptions->second.propertySubscriptions.emplace(propertyName, newPropertySubscription);
}
// if (_subscribedProperties.count(identifier)) {
// // At least one property have been subscribed to on this SGN
// auto propertySubscription = _subscribedProperties.at(identifier);
// if (propertySubscription.count(propertyName)) {
// // Property subscription already exists
// removeExistingPropertySubscription(identifier, property, propertySubscription.at(propertyName));
// propertySubscription.at(propertyName)->onChangeHandle = onChangeHandle;
// }
// else {
// // Property subscription doesn't exist
// PropertySubscription newPropertySub{ onChangeHandle };
// _subscribedProperties.at(identifier).emplace(
// propertyName,
// std::make_shared<PropertySubscription>(std::move(newPropertySub))
// );
// }
// }
// else {
// // No properties have been subscribed to on this SGN
// PropertySubscription newPropertySub{ onChangeHandle };
// PropertySubscriptions newPropertySubs{{
// propertyName,
// std::make_shared<PropertySubscription>(std::move(newPropertySub))
// }};
// _subscribedProperties.emplace(identifier, std::move(newPropertySubs));
// }
}
// std::shared_ptr<SoftwareConnection::PropertySubscription> SoftwareConnection::getPropertySubscription(
// const std::string& propertyName,
// const std::string& identifier
// ) {
// if (!_subscribedProperties.count(identifier)) return nullptr;
// if (!_subscribedProperties.at(identifier).count(propertyName)) return nullptr;
bool SoftwareConnection::hasPropertySubscription(
const std::string& identifier,
const std::string& propertyName
) {
// Get renderable
auto r = renderable(identifier);
if (!r) {
LWARNING(fmt::format(
"Couldn't check for property subscriptions, renderable {} doesn't exist",
identifier
));
return false;
}
// return _subscribedProperties.at(identifier).at(propertyName);
// }
if (!_sceneGraphNodes.count(identifier)) return false;
return _sceneGraphNodes.at(identifier).propertySubscriptions.count(propertyName);
}
void SoftwareConnection::removePropertySubscriptions(const std::string& identifier) {
// Get renderable
@@ -168,34 +153,32 @@ void SoftwareConnection::removePropertySubscriptions(const std::string& identifi
return;
}
auto propertySubscriptions = _subscribedProperties.find(identifier);
auto propertySubscriptions = _sceneGraphNodes.find(identifier);
if (propertySubscriptions == _subscribedProperties.end()) return;
if (propertySubscriptions == _sceneGraphNodes.end()) return;
auto propertySubscriptionIt = std::begin(propertySubscriptions->second);
while (propertySubscriptionIt != std::end(propertySubscriptions->second)) {
auto& [propertyName, onChangeHandle] = *propertySubscriptionIt;
auto propertySubscriptionIt = propertySubscriptions->second.propertySubscriptions.begin();
while (propertySubscriptionIt != propertySubscriptions->second.propertySubscriptions.end()) {
auto propertySubscriptionCopy = *propertySubscriptionIt;
propertySubscriptionIt = propertySubscriptions->second.propertySubscriptions.erase(propertySubscriptionIt);
if (!r->hasProperty(propertyName)) {
auto property = r->property(propertySubscriptionCopy.first);
if (!property) {
LWARNING(fmt::format(
"Couldn't remove property subscription. Property \"{}\" doesn't exist on \"{}\"",
propertyName, identifier
propertySubscriptionCopy.first, identifier
));
++propertySubscriptionIt;
continue;
}
auto property = r->property(propertyName);
property->removeOnChange(onChangeHandle);
propertySubscriptionIt = propertySubscriptions->second.erase(propertySubscriptionIt);
property->removeOnChange(propertySubscriptionCopy.second.onChangehandle);
}
_subscribedProperties.erase(propertySubscriptions);
}
void SoftwareConnection::removePropertySubscription(
const std::string& propertyName,
const std::string& identifier
const std::string& identifier,
const std::string& propertyName
) {
// Get renderable
auto r = renderable(identifier);
@@ -217,22 +200,64 @@ void SoftwareConnection::removePropertySubscription(
auto property = r->property(propertyName);
auto propertySubscriptions = _subscribedProperties.find(identifier);
if (propertySubscriptions != _subscribedProperties.end()) {
auto propertySubscriptions = _sceneGraphNodes.find(identifier);
if (propertySubscriptions != _sceneGraphNodes.end()) {
// At least one property have been subscribed to on this SGN
auto propertySubscription = propertySubscriptions->second.find(propertyName);
if (propertySubscription != propertySubscriptions->second.end()) {
auto propertySubscription = propertySubscriptions->second.propertySubscriptions.find(propertyName);
if (propertySubscription != propertySubscriptions->second.propertySubscriptions.end()) {
// Property subscription already exists
// Remove onChange handle
property->removeOnChange(propertySubscription->second);
property->removeOnChange(propertySubscription->second.onChangehandle);
// Remove property subscription
propertySubscriptions->second.erase(propertySubscription);
propertySubscriptions->second.propertySubscriptions.erase(propertySubscription);
}
}
}
bool SoftwareConnection::shouldSendData(const std::string& identifier, const std::string& propertyName) {
auto sgn = _sceneGraphNodes.find(identifier);
if (sgn == _sceneGraphNodes.end()) {
return false;
}
auto propertySubscription = sgn->second.propertySubscriptions.find(propertyName);
if (propertySubscription == sgn->second.propertySubscriptions.end()) {
return false;
}
if (propertySubscription->second.shouldSendMessage) {
return true;
}
else {
propertySubscription->second.shouldSendMessage = true;
return false;
}
}
void SoftwareConnection::setShouldNotSendData(const std::string& identifier, const std::string& propertyName) {
auto sgn = _sceneGraphNodes.find(identifier);
if (sgn == _sceneGraphNodes.end()) {
LERROR(fmt::format(
"Couldn't set shouldNotSendData on property {} on SceneGraphNode {}. SceneGraphNode doesn't exist.",
propertyName, identifier
));
return;
}
auto propertySubscription = sgn->second.propertySubscriptions.find(propertyName);
if (propertySubscription == sgn->second.propertySubscriptions.end()) {
LERROR(fmt::format(
"Couldn't set shouldNotSendData on property {} on SceneGraphNode {}. No subscription on property.",
propertyName, identifier
));
return;
}
propertySubscription->second.shouldSendMessage = false;
}
void SoftwareConnection::disconnect() {
_socket->disconnect();
LINFO(fmt::format("OpenSpace has disconnected with external software through socket"));
@@ -246,36 +271,56 @@ bool SoftwareConnection::isConnectedOrConnecting() const {
return _socket && (_socket->isConnected() || _socket->isConnecting());
}
bool SoftwareConnection::sendMessage(const std::string& message) {
bool SoftwareConnection::sendMessage(
const softwareintegration::simp::MessageType& messageType,
const std::vector<std::byte>& subjectBuffer
) {
using namespace softwareintegration;
try {
if (!_socket || !isConnected()) {
throw SoftwareConnectionLostError("Connection lost...");
}
if (_socket->put<char>(message.data(), message.size())) {
LDEBUG(fmt::format("Message sent: {}", message));
return true;
LDEBUG(fmt::format(
"sendMessage: messageType={}, subjectBuffer.size()={}",
simp::getStringFromMessageType(messageType),
subjectBuffer.size()
));
std::vector<std::byte> message{};
std::string header = fmt::format(
"{}{}{}",
simp::protocolVersion,
simp::getStringFromMessageType(messageType),
simp::formatLengthOfSubject(subjectBuffer.size())
);
simp::toByteBuffer(message, 0, header);
simp::toByteBuffer(message, message.size(), subjectBuffer);
if (!_socket->put(message.data(), message.size())) {
return false;
}
}
catch (const SoftwareConnectionLostError& err) {
LERROR(fmt::format("Couldn't send message: \"{}\", due to: {}", message, err.message));
LERROR(fmt::format("Couldn't send message with type \"{}\", due to: {}", simp::getStringFromMessageType(messageType), err.message));
}
catch (const std::exception& err) {
LERROR(fmt::format("Couldn't send message: \"{}\", due to: {}", message, err.what()));
LERROR(fmt::format("Couldn't send message with type \"{}\", due to: {}", simp::getStringFromMessageType(messageType), err.what()));
}
return false;
LDEBUG(fmt::format("Sent message with type {}", simp::getStringFromMessageType(messageType)));
return true;
}
void SoftwareConnection::addSceneGraphNode(const std::string& identifier) {
_sceneGraphNodes.insert(identifier);
if (_sceneGraphNodes.count(identifier)) return;
_sceneGraphNodes.insert({ identifier, {} });
}
void SoftwareConnection::removeSceneGraphNode(const std::string& identifier) {
size_t amountRemoved = _sceneGraphNodes.erase(identifier);
if (amountRemoved > 0) {
removePropertySubscriptions(identifier);
}
if (!_sceneGraphNodes.count(identifier)) return;
removeMessageQueue(identifier);
removePropertySubscriptions(identifier);
_sceneGraphNodes.erase(identifier);
}
size_t SoftwareConnection::id() {
@@ -290,12 +335,153 @@ ghoul::io::TcpSocket* SoftwareConnection::socket() {
return _socket.get();
}
std::mutex& SoftwareConnection::outgoingMessagesMutex() {
return _outgoingMessagesMutex;
}
/**
* DANGER! You need to lock outgoing message queue mutex
* (outgoingMessagesMutex) before calling this function
*
* @param entry holds the value in bytes and its length
*/
void SoftwareConnection::addToMessageQueue(
const std::string& identifier,
softwareintegration::simp::DataKey dataKey,
const std::tuple<std::vector<std::byte>, int32_t>& entry
) {
auto sgn = _sceneGraphNodes.find(identifier);
if (sgn == _sceneGraphNodes.end()) {
LERROR(fmt::format(
"Couldn't add {} to message queue. No SceneGraphNode with identifier {} exists.",
softwareintegration::simp::getStringFromDataKey(dataKey), identifier
));
return;
}
auto outgoingMessages = sgn->second.outgoingMessages.find(dataKey);
if (outgoingMessages != sgn->second.outgoingMessages.end()) {
outgoingMessages->second = entry;
}
else {
sgn->second.outgoingMessages.emplace(dataKey, entry);
}
}
void SoftwareConnection::removeMessageQueue(const std::string& identifier) {
auto sgn = _sceneGraphNodes.find(identifier);
if (sgn == _sceneGraphNodes.end()) {
LERROR(fmt::format(
"Couldn't remove message queue. No SceneGraphNode with identifier {} exists.",
identifier
));
return;
}
sgn->second.outgoingMessages.clear();
}
void SoftwareConnection::notifyMessageQueueHandler() {
_outgoingMessagesNotifier.notify_one();
}
void SoftwareConnection::handleOutgoingMessages() {
using namespace softwareintegration;
_outgoingMessagesThread = std::make_unique<std::thread>([this] {
std::vector<std::byte> subjectBuffer{};
while (!_shouldStopOutgoingMessagesThread) {
auto outgoingMessagesAreReady = [this] {
bool dataMessagesEmpty = true;
for (auto& s : _sceneGraphNodes) {
if (!s.second.outgoingMessages.empty()) {
dataMessagesEmpty = false;
break;
}
}
return _shouldStopOutgoingMessagesThread || !dataMessagesEmpty;
// return _shouldStopOutgoingMessagesThread || (!dataMessagesEmpty && _outgoingMessagesMutex.try_lock());
};
// Block excecution on this thread for as long as queue mutex is locked or queue is empty
if (!outgoingMessagesAreReady()) {
std::unique_lock lock(_outgoingMessagesNotifierMutex);
_outgoingMessagesNotifier.wait(lock, outgoingMessagesAreReady);
}
// Check again since we're blocking excecution above
if (_shouldStopOutgoingMessagesThread) break;
// Lock queue mutex so that other threads cannot mutate the list while gathering all data to be sent
std::lock_guard guard{ _outgoingMessagesMutex };
// TODO: Breakout to function?
// Add all data to outgoing message subject
for (auto [identifier, sceneGraphNodeInfo] : _sceneGraphNodes) {
if (sceneGraphNodeInfo.outgoingMessages.empty()) continue;
auto r = renderable(identifier);
if (!r) continue;
auto guiNameProp = r->property("Name");
if (!guiNameProp) continue;
size_t subjectBufferOffset = 0;
subjectBuffer.clear();
std::string subjectPrefixString = fmt::format(
"{}{}{}{}", identifier, simp::DELIM, guiNameProp->getStringValue(), simp::DELIM
);
simp::toByteBuffer(subjectBuffer, subjectBufferOffset, subjectPrefixString);
// TODO: Breakout to function
// Add all data for SGN to outgoing message subject
for (auto& [dataKey, data] : sceneGraphNodeInfo.outgoingMessages) {
auto& [dataBuffer, nValues] = data;
std::string dataKeyString = fmt::format(
"{}{}", simp::getStringFromDataKey(dataKey), simp::DELIM
);
std::string nValuesString = nValues > 1 ? std::to_string(nValues) : "";
LDEBUG(fmt::format("Sending {} {} to OpenSpace", nValuesString, simp::getStringFromDataKey(dataKey)));
simp::toByteBuffer(subjectBuffer, subjectBufferOffset, dataKeyString);
if (nValues > 1) {
// Add length to subject if data has multiple values
simp::toByteBuffer(subjectBuffer, subjectBufferOffset, nValues);
}
simp::toByteBuffer(subjectBuffer, subjectBufferOffset, dataBuffer);
}
sceneGraphNodeInfo.outgoingMessages.clear();
if (!subjectBuffer.empty()) {
sendMessage(simp::MessageType::Data, subjectBuffer);
}
}
}
});
}
bool SoftwareConnection::handshakeHasBeenMade() {
return _handshakeHasBeenMade;
}
void SoftwareConnection::setHandshakeHasBeenMade() {
_handshakeHasBeenMade = true;
}
namespace softwareintegration::network::connection {
void eventLoop(
std::weak_ptr<SoftwareConnection> connectionWeakPtr,
std::weak_ptr<NetworkState> networkStateWeakPtr
) {
if (connectionWeakPtr.expired()) return;
connectionWeakPtr.lock()->handleOutgoingMessages();
while (!connectionWeakPtr.expired()) {
auto connectionPtr = connectionWeakPtr.lock();
if (connectionPtr->_shouldStopThread) break;
@@ -320,52 +506,39 @@ void eventLoop(
}
}
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);
IncomingMessage receiveMessageFromSoftware(std::shared_ptr<SoftwareConnection> connectionPtr) {
// Header consists of version (5 chars), message type (4 chars) & subject size (15 chars)
size_t headerSize = 24 * sizeof(char);
// Create basic buffer for receiving first part of message
std::vector<char> headerBuffer(headerSize);
std::vector<char> subjectBuffer;
std::vector<std::byte> subjectBuffer;
// Receive the header data
if (!connectionPtr->socket()->get(headerBuffer.data(), headerSize)) {
throw SoftwareConnectionLostError("Failed to read header from socket. Disconnecting.");
}
// Read the protocol version number: Byte 0-2
std::string protocolVersionIn;
for (int i = 0; i < 3; i++) {
protocolVersionIn.push_back(headerBuffer[i]);
}
// // Read the protocol version number: Byte 0-4
std::string protocolVersionIn{ headerBuffer.begin(), headerBuffer.begin() + 5 };
// Make sure that header matches the protocol version
if (protocolVersionIn != softwareintegration::simp::ProtocolVersion) {
if (protocolVersionIn != softwareintegration::simp::protocolVersion) {
throw SoftwareConnectionLostError(fmt::format(
"Protocol versions do not match. Remote version: {}, Local version: {}",
protocolVersionIn,
softwareintegration::simp::ProtocolVersion
softwareintegration::simp::protocolVersion
));
}
// Read message type: Byte 3-6
std::string type;
for (int i = 3; i < 7; i++) {
type.push_back(headerBuffer[i]);
}
// Read and convert message size: Byte 7-22
std::string subjectSizeIn;
for (int i = 7; i < 22; i++) {
subjectSizeIn.push_back(headerBuffer[i]);
}
const size_t subjectSize = std::stoi(subjectSizeIn);
// Read message type: Byte 5-8
std::string type{ headerBuffer.begin() + 5, headerBuffer.begin() + 9 };
auto typeEnum = softwareintegration::simp::getMessageType(type);
// Receive the message data
// Read and convert message size: Byte 9-24
const size_t subjectSize = std::stoll(std::string{ headerBuffer.begin() + 9, headerBuffer.begin() + 25 });
// Receive the message subject
if (typeEnum != softwareintegration::simp::MessageType::Unknown) {
subjectBuffer.resize(subjectSize);
if (!connectionPtr->socket()->get(subjectBuffer.data(), subjectSize)) {
@@ -30,7 +30,6 @@
#include <ghoul/io/socket/tcpsocket.h>
#include <openspace/properties/property.h>
#include <unordered_set>
#include <unordered_map>
namespace openspace {
@@ -44,6 +43,7 @@ struct NetworkState;
struct IncomingMessage;
namespace connection {
void eventLoop(
std::weak_ptr<SoftwareConnection> connectionWeakPtr,
std::weak_ptr<softwareintegration::network::NetworkState> networkStateWeakPtr
@@ -52,6 +52,7 @@ namespace connection {
IncomingMessage receiveMessageFromSoftware(
std::shared_ptr<SoftwareConnection> connectionPtr
);
} // namespace connection
} // namespace softwareintegration::network
@@ -59,17 +60,8 @@ namespace connection {
using namespace softwareintegration::network;
class SoftwareConnection {
public:
using OnChangeHandle = properties::Property::OnChangeHandle;
struct PropertySubscription {
OnChangeHandle onChangeHandle;
bool shouldSendMessage{true};
};
using PropertyName = std::string;
using PropertySubscriptions = std::unordered_map<PropertyName, OnChangeHandle>;
using Identifier = std::string;
using SubscribedProperties = std::unordered_map<Identifier, PropertySubscriptions>;
struct SceneGraphNodeInfo;
explicit SoftwareConnection(std::unique_ptr<ghoul::io::TcpSocket> socket);
SoftwareConnection(SoftwareConnection&& p);
@@ -78,17 +70,21 @@ public:
void disconnect();
bool isConnected() const;
bool isConnectedOrConnecting() const;
bool sendMessage(const std::string& message);
bool sendMessage(
const softwareintegration::simp::MessageType& message_type,
const std::vector<std::byte>& subjectBuffer
);
bool hasPropertySubscription(
const std::string& identifier,
const std::string& propertyName
);
void addPropertySubscription(
const std::string& propertyName,
const std::string& identifier,
std::function<void()> newHandler
);
// std::shared_ptr<PropertySubscription> getPropertySubscription(
// const std::string& propertyName,
// const std::string& identifier
// );
void addSceneGraphNode(const std::string& identifier);
void removeSceneGraphNode(const std::string& identifier);
@@ -101,29 +97,82 @@ public:
std::weak_ptr<NetworkState> networkStateWeakPtr
);
void removePropertySubscriptions(const std::string& identifier);
void removePropertySubscription(const std::string& identifier, const std::string& propertyName);
/**
* @brief Called from onChange handlers to see if we should send back to
* external software. Will also set its state to "true" when called
*
*/
bool shouldSendData(const std::string& identifier, const std::string& propertyName);
/**
* @brief Called from message handlers to not send back data in onChange handlers
* when properties are updated
*
*/
void setShouldNotSendData(const std::string& identifier, const std::string& propertyName);
void addToMessageQueue(
const std::string& identifier,
softwareintegration::simp::DataKey dataKey,
const std::tuple<std::vector<std::byte>, int32_t>& entry
);
void notifyMessageQueueHandler();
void handleOutgoingMessages();
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);
bool handshakeHasBeenMade();
void setHandshakeHasBeenMade();
ghoul::io::TcpSocket* socket();
std::mutex& outgoingMessagesMutex();
private:
SubscribedProperties _subscribedProperties;
std::unordered_set<std::string> _sceneGraphNodes;
void removeMessageQueue(const std::string& identifier);
std::unique_ptr<ghoul::io::TcpSocket> _socket;
std::unordered_map<std::string, SceneGraphNodeInfo> _sceneGraphNodes;
std::mutex _outgoingMessagesMutex;
std::atomic_bool _shouldStopOutgoingMessagesThread;
std::unique_ptr<std::thread> _outgoingMessagesThread;
std::mutex _outgoingMessagesNotifierMutex;
std::condition_variable _outgoingMessagesNotifier;
size_t _id;
std::thread _thread;
std::atomic_bool _shouldStopThread;
static std::atomic_size_t _nextConnectionId;
bool _handshakeHasBeenMade = false;
};
struct SoftwareConnection::SceneGraphNodeInfo {
using OnChangeHandle = properties::Property::OnChangeHandle;
struct PropertySubscription {
OnChangeHandle onChangehandle;
bool shouldSendMessage{ false };
};
using PropertySubscriptions = std::unordered_map<std::string, PropertySubscription>;
// /\
// |
// propertyName
using OutgoingMessages = std::unordered_map<
softwareintegration::simp::DataKey,
std::tuple<std::vector<std::byte>, int32_t>
>;
OutgoingMessages outgoingMessages{};
PropertySubscriptions propertySubscriptions{};
};
} // namespace openspace
@@ -24,8 +24,8 @@
#include <modules/softwareintegration/rendering/renderablepointscloud.h>
#include <modules/softwareintegration/utils.h>
#include <modules/softwareintegration/softwareintegrationmodule.h>
#include <modules/softwareintegration/utils.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
@@ -46,12 +46,12 @@
namespace {
constexpr const char* _loggerCat = "PointsCloud";
constexpr const std::array<const char*, 21> UniformNames = {
"color", "opacity", "size", "modelMatrix", "cameraUp", "screenSize",
constexpr const std::array<const char*, 20> UniformNames = {
"color", "size", "modelMatrix", "cameraUp", "screenSize",
"cameraViewProjectionMatrix", "eyePosition", "sizeOption",
"colormapTexture", "colormapMin", "colormapMax", "colormapNaNMode",
"colormapNaNColor", "colormapEnabled", "linearSizeMin", "linearSizeMax",
"linearSizeEnabled","velocityNaNMode", "motionEnabled", "time"
"colormapTexture", "colormapMin", "colormapMax", "colormapNanMode",
"colormapNanColor", "colormapEnabled", "linearSizeMin", "linearSizeMax",
"linearSizeEnabled","velocityNanMode", "motionEnabled", "time"
};
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
@@ -66,17 +66,17 @@ namespace {
"The size of the points."
};
constexpr openspace::properties::Property::PropertyInfo DataInfo = {
"Data",
"Data",
"Data to use for the positions of the points, given in Parsec."
};
constexpr openspace::properties::Property::PropertyInfo IdentifierInfo = {
"Identifier",
"Identifier",
"Identifier used as part of key to access data in centralized central storage."
};
constexpr openspace::properties::Property::PropertyInfo PointUnitInfo = {
"PointUnit",
"Point Unit",
"The distance unit of the point data."
};
constexpr openspace::properties::Property::PropertyInfo SizeOptionInfo = {
"SizeOption",
@@ -96,14 +96,14 @@ namespace {
"Maximum value to sample from colormap."
};
constexpr openspace::properties::Property::PropertyInfo ColormapNaNModeInfo = {
"ColormapNaNMode",
constexpr openspace::properties::Property::PropertyInfo ColormapNanModeInfo = {
"ColormapNanMode",
"Colormap NaN Mode",
"How points with NaN value in colormap attribute should be represented."
};
constexpr openspace::properties::Property::PropertyInfo ColormapNaNColorInfo = {
"ColormapNaNColor",
constexpr openspace::properties::Property::PropertyInfo ColormapNanColorInfo = {
"ColormapNanColor",
"Colormap NaN Color",
"The color of the points where the colormap scalar is NaN."
};
@@ -137,15 +137,15 @@ namespace {
"Velocity Distance Unit",
"The distance unit of the velocity data."
};
constexpr openspace::properties::Property::PropertyInfo VelocityTimeUnitInfo = {
"VelocityTimeUnit",
"Velocity Time Unit",
"The time unit of the velocity data."
};
constexpr openspace::properties::Property::PropertyInfo VelocityNaNModeInfo = {
"VelocityNaNMode",
constexpr openspace::properties::Property::PropertyInfo VelocityNanModeInfo = {
"VelocityNanMode",
"Velocity NaN Mode",
"How points with NaN value in colormap attribute should be represented."
};
@@ -169,24 +169,23 @@ namespace {
// [[codegen::verbatim(SizeInfo.description)]]
std::optional<float> size;
// TODO: This can be removed? Not in use anymore?
// [[codegen::verbatim(DataInfo.description)]]
std::optional<std::vector<glm::vec3>> data;
// [[codegen::verbatim(IdentifierInfo.description)]]
std::optional<std::string> identifier;
// [[codegen::verbatim(PointUnitInfo.description)]]
std::optional<std::string> pointUnit;
// [[codegen::verbatim(ColormapMinInfo.description)]]
std::optional<float> colormapMin;
// [[codegen::verbatim(ColormapMaxInfo.description)]]
std::optional<float> colormapMax;
// [[codegen::verbatim(ColormapNaNModeInfo.description)]]
std::optional<int> colormapNaNMode;
// [[codegen::verbatim(ColormapNanModeInfo.description)]]
std::optional<int> colormapNanMode;
// [[codegen::verbatim(ColormapNaNColorInfo.description)]]
std::optional<glm::vec4> colormapNaNColor;
// [[codegen::verbatim(ColormapNanColorInfo.description)]]
std::optional<glm::vec4> colormapNanColor;
// [[codegen::verbatim(ColormapEnabledInfo.description)]]
std::optional<bool> colormapEnabled;
@@ -206,8 +205,8 @@ namespace {
// [[codegen::verbatim(VelocityTimeUnitInfo.description)]]
std::optional<std::string> velocityTimeUnit;
// [[codegen::verbatim(VelocityNaNModeInfo.description)]]
std::optional<int> velocityNaNMode;
// [[codegen::verbatim(VelocityNanModeInfo.description)]]
std::optional<int> velocityNanMode;
// [[codegen::verbatim(MotionEnabledInfo.description)]]
std::optional<bool> motionEnabled;
@@ -215,8 +214,9 @@ namespace {
// [[codegen::verbatim(NameInfo.description)]]
std::optional<std::string> name;
enum class SizeOption : uint32_t {
Uniform,
// VOLATILE: Keep in sync with SizeOption in `./renderablepointscloud.h`
enum class SizeOption {
Uniform = 0,
NonUniform
};
// [[codegen::verbatim(SizeOptionInfo.description)]]
@@ -237,18 +237,19 @@ RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary
: Renderable(dictionary)
, _color(ColorInfo, glm::vec4(glm::vec3(0.5f), 1.f), glm::vec4(0.f), glm::vec4(1.f), glm::vec4(.01f))
, _size(SizeInfo, 1.f, 0.f, 500.f, .1f)
, _pointUnit(PointUnitInfo, "<no unit set>")
, _sizeOption(SizeOptionInfo, properties::OptionProperty::DisplayType::Dropdown)
, _colormapMin(ColormapMinInfo)
, _colormapMax(ColormapMaxInfo)
, _colormapNaNMode(ColormapNaNModeInfo)
, _colormapNaNColor(ColormapNaNColorInfo, glm::vec4(glm::vec3(0.5f), 1.f), glm::vec4(1.0f), glm::vec4(0.f), glm::vec4(0.f))
, _colormapNanMode(ColormapNanModeInfo)
, _colormapNanColor(ColormapNanColorInfo, glm::vec4(glm::vec3(0.5f), 1.f), glm::vec4(1.0f), glm::vec4(0.f), glm::vec4(0.f))
, _colormapEnabled(ColormapEnabledInfo, false)
, _linearSizeMax(LinearSizeMinInfo)
, _linearSizeMin(LinearSizeMaxInfo)
, _linearSizeEnabled(LinearSizeEnabledInfo, false)
, _velocityDistanceUnit(VelocityDistanceUnitInfo, "<no unit set>")
, _velocityTimeUnit(VelocityTimeUnitInfo, "<no unit set>")
, _velocityNaNMode(VelocityNaNModeInfo)
, _velocityNanMode(VelocityNanModeInfo)
, _name(NameInfo)
, _motionEnabled(MotionEnabledInfo, false)
{
@@ -283,7 +284,10 @@ RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary
}
addProperty(_sizeOption);
addProperty(_opacity);
_pointUnit = p.pointUnit.value_or(_pointUnit);
_pointUnit.setVisibility(properties::Property::Visibility::Hidden);
_pointUnit.onChange([this] { _pointUnitIsDirty = true; });
addProperty(_pointUnit);
// =============== Colormap ===============
_colormapMin = p.colormapMin.value_or(_colormapMin);
@@ -296,13 +300,13 @@ RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary
_colormapMax.onChange([this] { checkColormapMinMax(); });
addProperty(_colormapMax);
_colormapNaNMode = p.colormapNaNMode.value_or(_colormapNaNMode);
_colormapNaNMode.setVisibility(properties::Property::Visibility::Hidden);
addProperty(_colormapNaNMode);
_colormapNanMode = p.colormapNanMode.value_or(_colormapNanMode);
_colormapNanMode.setVisibility(properties::Property::Visibility::Hidden);
addProperty(_colormapNanMode);
_colormapNaNColor = p.colormapNaNColor.value_or(_colormapNaNColor);
_colormapNaNColor.setVisibility(properties::Property::Visibility::Hidden);
addProperty(_colormapNaNColor);
_colormapNanColor = p.colormapNanColor.value_or(_colormapNanColor);
_colormapNanColor.setVisibility(properties::Property::Visibility::Hidden);
addProperty(_colormapNanColor);
_colormapEnabled = p.colormapEnabled.value_or(_colormapEnabled);
_colormapEnabled.onChange([this] { checkIfColormapCanBeEnabled(); });
@@ -341,9 +345,9 @@ RenderablePointsCloud::RenderablePointsCloud(const ghoul::Dictionary& dictionary
_velocityTimeUnit.onChange([this] { _velocityUnitsAreDirty = true; });
addProperty(_velocityTimeUnit);
_velocityNaNMode = p.velocityNaNMode.value_or(_velocityNaNMode);
_velocityNaNMode.setVisibility(properties::Property::Visibility::Hidden);
addProperty(_velocityNaNMode);
_velocityNanMode = p.velocityNanMode.value_or(_velocityNanMode);
_velocityNanMode.setVisibility(properties::Property::Visibility::Hidden);
addProperty(_velocityNanMode);
_motionEnabled = p.motionEnabled.value_or(_motionEnabled);
_motionEnabled.onChange([this] { checkIfMotionCanBeEnabled(); });
@@ -416,11 +420,6 @@ void RenderablePointsCloud::render(const RenderData& data, RendererTasks&) {
ghoul::opengl::TextureUnit colorUnit;
if (_colormapTexture) {
// _colormapAttributeData
// TODO: Set _colormapTextre in shader. A trasnfer function similar to
// 'bv2rgb' in C:\OpenSpace\SoftwareIntegration\modules\space\shaders\star_fs.glsl
// should probably be used.
colorUnit.activate();
_colormapTexture->bind();
_shaderProgram->setUniform(_uniformCache.colormapTexture, colorUnit);
@@ -432,15 +431,15 @@ void RenderablePointsCloud::render(const RenderData& data, RendererTasks&) {
_shaderProgram->setUniform(_uniformCache.colormapMin, _colormapMin);
_shaderProgram->setUniform(_uniformCache.colormapMax, _colormapMax);
_shaderProgram->setUniform(_uniformCache.colormapNaNMode, _colormapNaNMode);
_shaderProgram->setUniform(_uniformCache.colormapNaNColor, _colormapNaNColor);
_shaderProgram->setUniform(_uniformCache.colormapNanMode, _colormapNanMode);
_shaderProgram->setUniform(_uniformCache.colormapNanColor, _colormapNanColor);
_shaderProgram->setUniform(_uniformCache.colormapEnabled, _colormapEnabled);
_shaderProgram->setUniform(_uniformCache.linearSizeMin, _linearSizeMin);
_shaderProgram->setUniform(_uniformCache.linearSizeMax, _linearSizeMax);
_shaderProgram->setUniform(_uniformCache.linearSizeEnabled, _linearSizeEnabled);
_shaderProgram->setUniform(_uniformCache.velocityNaNMode, _velocityNaNMode);
_shaderProgram->setUniform(_uniformCache.velocityNanMode, _velocityNanMode);
_shaderProgram->setUniform(_uniformCache.motionEnabled, _motionEnabled);
_shaderProgram->setUniform(
_uniformCache.time,
@@ -449,7 +448,6 @@ void RenderablePointsCloud::render(const RenderData& data, RendererTasks&) {
_shaderProgram->setUniform(_uniformCache.color, _color);
_shaderProgram->setUniform(_uniformCache.opacity, _opacity);
_shaderProgram->setUniform(_uniformCache.size, _size);
_shaderProgram->setUniform(_uniformCache.sizeOption, _sizeOption);
@@ -512,14 +510,14 @@ void RenderablePointsCloud::update(const UpdateData&) {
bufferData.push_back(colormapAttrDataSlice->at(i));
}
else {
bufferData.push_back(0.0); // TODO: We might not want to put 0.0 here. How is this rendered?
bufferData.push_back(std::nanf("0"));
}
if (linearSizeAttrDataSlice->size() > i) {
bufferData.push_back(linearSizeAttrDataSlice->at(i));
}
else {
bufferData.push_back(0.0); // TODO: We might not want to put 0.0 here. How is this rendered?
bufferData.push_back(std::nanf("0"));
}
if (velocityDataSlice->size() > (j + 2)) {
@@ -528,12 +526,9 @@ void RenderablePointsCloud::update(const UpdateData&) {
bufferData.push_back(velocityDataSlice->at(j + 2));
}
else {
bufferData.push_back(std::nanf("0")); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
bufferData.push_back(std::nanf("0")); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
bufferData.push_back(std::nanf("0")); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
// bufferData.push_back(0.0); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
// bufferData.push_back(0.0); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
// bufferData.push_back(0.0); // TODO: We might not want to put 0.0 here. How is this rendered? Maybe push NAN?
bufferData.push_back(std::nanf("0"));
bufferData.push_back(std::nanf("0"));
bufferData.push_back(std::nanf("0"));
}
}
@@ -612,30 +607,27 @@ bool RenderablePointsCloud::checkDataStorage() {
bool updatedDataSlices = false;
auto softwareIntegrationModule = global::moduleEngine->module<SoftwareIntegrationModule>();
if (softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::DataPoints)) {
loadData(softwareIntegrationModule);
if (shouldLoadPointData(softwareIntegrationModule)) {
loadPointData(softwareIntegrationModule);
updatedDataSlices = true;
_pointUnitIsDirty = false;
}
if (softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::Colormap)) {
if (shouldLoadColormap(softwareIntegrationModule)) {
loadColormap(softwareIntegrationModule);
}
if (softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::ColormapAttrData)) {
if (shouldLoadColormapAttrData(softwareIntegrationModule)) {
loadColormapAttributeData(softwareIntegrationModule);
updatedDataSlices = true;
}
if (softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::LinearSizeAttrData)) {
if (shouldLoadLinearSizeAttrData(softwareIntegrationModule)) {
loadLinearSizeAttributeData(softwareIntegrationModule);
updatedDataSlices = true;
}
if (shouldLoadVelocityData(
softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::VelocityData)
)
)
{
if (shouldLoadVelocityData(softwareIntegrationModule)) {
loadVelocityData(softwareIntegrationModule);
updatedDataSlices = true;
_velocityUnitsAreDirty = false;
@@ -644,46 +636,131 @@ bool RenderablePointsCloud::checkDataStorage() {
return updatedDataSlices;
}
void RenderablePointsCloud::loadData(SoftwareIntegrationModule* softwareIntegrationModule) {
// Fetch data from module's centralized storage
auto fullPointData = softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::DataPoints);
if (fullPointData.empty()) {
LWARNING("There was an issue trying to fetch the point data from the centralized storage.");
void RenderablePointsCloud::loadPointData(SoftwareIntegrationModule* softwareIntegrationModule) {
// Fetch point data from module's centralized storage
std::vector<float> pointData;
if (!softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::DataPoints, pointData)) {
LERROR("There was an issue trying to fetch the point data from the centralized storage.");
return;
}
// Parse unit
DistanceUnit pointUnit;
try {
pointUnit = distanceUnitFromString(_pointUnit.value().c_str());
}
catch (const ghoul::MissingCaseException& ) {
LERROR(fmt::format(
"Error when parsing point unit."
"OpenSpace doesn't support the distance unit '{}'.",
_pointUnit
));
return;
}
// Convert to meters if needed
if (pointUnit != DistanceUnit::Meter) {
float toMeters = static_cast<float>(toMeter(pointUnit));
for (auto& point : pointData) {
point *= toMeters;
}
}
// Assign point data to point data slice
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
pointDataSlice->clear();
pointDataSlice->reserve(fullPointData.size());
pointDataSlice->assign(pointData.begin(), pointData.end());
// Create data slice
auto addPosition = [&](const glm::vec4& pos) {
for (glm::vec4::length_type j = 0; j < glm::vec4::length() - 1; ++j) {
pointDataSlice->push_back(pos[j]);
}
};
for (size_t i = 0; i < fullPointData.size(); i += 3) {
glm::dvec4 transformedPos = {
fullPointData[i + 0],
fullPointData[i + 1],
fullPointData[i + 2],
1.0
};
// W-normalization
// transformedPos /= transformedPos.w; // TODO: Unnecessary. transformedPos.w == 1
transformedPos *= distanceconstants::Parsec; // Convert parsec => meter
addPosition(transformedPos);
}
softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::DataPoints);
LDEBUG("New point data has loaded");
}
void RenderablePointsCloud::loadVelocityData(SoftwareIntegrationModule* softwareIntegrationModule) {
// Fetch velocity data from module's centralized storage
std::vector<float> velocityData;
if (!softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::VelocityData, velocityData)) {
LWARNING("There was an issue trying to fetch the velocity data from the centralized storage.");
return;
}
// Check that velocity data is same length as point data
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
// if (pointDataSlice->size() != velocityData.size()) {
// LWARNING(fmt::format(
// "There is a mismatch in the amount of velocity data ({}) and the amount of points ({})",
// velocityData.size() / 3, pointDataSlice->size() / 3
// ));
// _motionEnabled = false;
// return;
// }
// Parse units
DistanceUnit velocityDistanceUnit;
TimeUnit velocityTimeUnit;
try {
velocityDistanceUnit = distanceUnitFromString(_velocityDistanceUnit.value().c_str());
velocityTimeUnit = timeUnitFromString(_velocityTimeUnit.value().c_str());
}
catch (const ghoul::MissingCaseException& ) {
LERROR(fmt::format(
"Error when parsing velocity units."
"OpenSpace doesn't support the velocity unit '{}/{}'.",
_velocityDistanceUnit,
_velocityTimeUnit
));
return;
}
// Units conversion
bool conversionNeeded = false;
// Meters multiplier
float toMeters = 1.0;
if (velocityDistanceUnit != DistanceUnit::Meter) {
toMeters = static_cast<float>(toMeter(velocityDistanceUnit));
conversionNeeded = true;
}
// Seconds divider
float toSeconds = 1.0;
if (velocityTimeUnit != TimeUnit::Second) {
toSeconds = static_cast<float>(convertTime(1.0, velocityTimeUnit, TimeUnit::Second));
conversionNeeded = true;
}
// Check for NaN values and convert to m/s if needed
int nNans = 0;
for (auto& v : velocityData) {
if (isnan(v)) nNans++;
if (conversionNeeded) {
v *= toMeters / toSeconds;
}
}
// Assign velocity data to velocity data slice
auto velocityDataSlice = getDataSlice(DataSliceKey::Velocity);
velocityDataSlice->clear();
velocityDataSlice->assign(velocityData.begin(), velocityData.end());
LINFO(
fmt::format(
"Viewing {} points with velocity ({} points in total). "
"{} points have NaN-value velocity and are hidden or static.",
velocityData.size()/3 - nNans/3,
pointDataSlice->size()/3,
nNans/3
)
);
_hasLoadedVelocityData = true;
softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::VelocityData);
LDEBUG("New velocity data has loaded");
}
void RenderablePointsCloud::loadColormap(SoftwareIntegrationModule* softwareIntegrationModule) {
auto colormap = softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::Colormap);
if (colormap.empty()) {
// Fetch colormap data from module's centralized storage
std::vector<float> colormap;
if (!softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::Colormap, colormap)) {
LWARNING("There was an issue trying to fetch the colormap data from the centralized storage.");
return;
}
@@ -705,171 +782,64 @@ void RenderablePointsCloud::loadColormap(SoftwareIntegrationModule* softwareInte
_colormapTexture->uploadTexture();
_hasLoadedColormap = true;
LDEBUG("New colormap has loaded");
softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::Colormap);
}
void RenderablePointsCloud::loadColormapAttributeData(SoftwareIntegrationModule* softwareIntegrationModule) {
auto colormapAttributeData = softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::ColormapAttrData);
if (colormapAttributeData.empty()) {
LWARNING("There was an issue trying to fetch the colormap data from the centralized storage.");
// Fetch colormap attribute data from module's centralized storage
std::vector<float> colormapAttributeData;
if (!softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::ColormapAttrData, colormapAttributeData)) {
LWARNING("There was an issue trying to fetch the colormap attribute data from the centralized storage.");
return;
}
// Check that colormap attribute data is same length as point data
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
if (pointDataSlice->size() / 3 != colormapAttributeData.size()) {
LWARNING(fmt::format(
"There is a mismatch in the amount of colormap attribute scalars ({}) and the amount of points ({})",
colormapAttributeData.size(), pointDataSlice->size() / 3
));
_colormapEnabled = false;
return;
}
// if (pointDataSlice->size() / 3 != colormapAttributeData.size()) {
// LWARNING(fmt::format(
// "There is a mismatch in the amount of colormap attribute scalars ({}) and the amount of points ({})",
// colormapAttributeData.size(), pointDataSlice->size() / 3
// ));
// _colormapEnabled = false;
// return;
// }
auto colormapAttributeDataSlice = getDataSlice(DataSliceKey::ColormapAttributes);
colormapAttributeDataSlice->clear();
colormapAttributeDataSlice->reserve(colormapAttributeData.size());
for (size_t i = 0; i < (pointDataSlice->size() / 3); ++i) {
colormapAttributeDataSlice->push_back(colormapAttributeData[i]);
}
colormapAttributeDataSlice->assign(colormapAttributeData.begin(), colormapAttributeData.end());
_hasLoadedColormapAttributeData = true;
LDEBUG("Rerendering with colormap attribute data");
softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::ColormapAttrData);
LDEBUG("New colormap attribute data has loaded");
}
void RenderablePointsCloud::loadLinearSizeAttributeData(SoftwareIntegrationModule* softwareIntegrationModule) {
auto linearSizeAttributeData = softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::LinearSizeAttrData);
if (linearSizeAttributeData.empty()) {
// Fetch linear size attribute data from module's centralized storage
std::vector<float> linearSizeAttributeData;
if (softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::LinearSizeAttrData, linearSizeAttributeData)) {
LWARNING("There was an issue trying to fetch the linear size attribute data from the centralized storage.");
return;
}
// Check that linear size attribute data is same length as point data
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
if (pointDataSlice->size() / 3 != linearSizeAttributeData.size()) {
LWARNING(fmt::format(
"There is a mismatch in the amount of linear size attribute scalars ({}) and the amount of points ({})",
linearSizeAttributeData.size(), pointDataSlice->size() / 3
));
_linearSizeEnabled = false;
return;
}
// if (pointDataSlice->size() / 3 != linearSizeAttributeData.size()) {
// LWARNING(fmt::format(
// "There is a mismatch in the amount of linear size attribute scalars ({}) and the amount of points ({})",
// linearSizeAttributeData.size(), pointDataSlice->size() / 3
// ));
// _linearSizeEnabled = false;
// return;
// }
auto linearSizeAttributeDataSlice = getDataSlice(DataSliceKey::LinearSizeAttributes);
linearSizeAttributeDataSlice->clear();
linearSizeAttributeDataSlice->reserve(linearSizeAttributeData.size());
for (size_t i = 0; i < (pointDataSlice->size() / 3); ++i) {
linearSizeAttributeDataSlice->push_back(linearSizeAttributeData[i]);
}
linearSizeAttributeDataSlice->assign(linearSizeAttributeData.begin(), linearSizeAttributeData.end());
_hasLoadedLinearSizeAttributeData = true;
LDEBUG("Rerendering with linear size attribute data");
softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::LinearSizeAttrData);
}
void RenderablePointsCloud::loadVelocityData(SoftwareIntegrationModule* softwareIntegrationModule) {
// Fetch data from module's centralized storage
auto velocityData = softwareIntegrationModule->fetchData(_identifier.value(), storage::Key::VelocityData);
if (velocityData.empty()) {
LWARNING("There was an issue trying to fetch the velocity data from the centralized storage.");
return;
}
auto pointDataSlice = getDataSlice(DataSliceKey::Points);
if (pointDataSlice->size() != velocityData.size()) {
LWARNING(fmt::format(
"There is a mismatch in the amount of velocity data ({}) and the amount of points ({})",
velocityData.size() / 3, pointDataSlice->size() / 3
));
_motionEnabled = false;
return;
}
auto velocityDataSlice = getDataSlice(DataSliceKey::Velocity);
velocityDataSlice->clear();
velocityDataSlice->reserve(velocityData.size());
// =========================================
int nNans = 0;
int prevNNans = 0;
int nPointsContainingNans = 0;
// Create velocity data slice
auto addPosition = [&](const glm::vec4& pos) {
for (glm::vec4::length_type j = 0; j < glm::vec4::length() - 1; ++j) {
if (isnan(pos[j])) {
nNans++;
}
velocityDataSlice->push_back(pos[j]);
}
if (prevNNans < nNans) {
nPointsContainingNans++;
prevNNans = nNans;
}
};
// Parse units
DistanceUnit velocityDistanceUnit;
TimeUnit velocityTimeUnit;
try {
velocityDistanceUnit = distanceUnitFromString(_velocityDistanceUnit.value().c_str());
velocityTimeUnit = timeUnitFromString(_velocityTimeUnit.value().c_str());
}
catch (const ghoul::MissingCaseException& ) {
LERROR(fmt::format(
"Error when parsing velocity units."
"OpenSpace doesn't support the unit '{}/{}'.",
_velocityDistanceUnit,
_velocityTimeUnit
));
return;
}
// Meters multiplier
float toMeters = 1.0;
if (velocityDistanceUnit != DistanceUnit::Meter) {
toMeters = static_cast<float>(toMeter(velocityDistanceUnit));
}
// Seconds divider
float toSeconds = 1.0;
if (velocityTimeUnit != TimeUnit::Second) {
toSeconds = static_cast<float>(convertTime(1.0, velocityTimeUnit, TimeUnit::Second));
}
for (size_t i = 0; i < velocityData.size(); i += 3) {
glm::dvec4 transformedPos = {
velocityData[i + 0],
velocityData[i + 1],
velocityData[i + 2],
1.0
};
// Convert to m/s if needed
transformedPos *= toMeters / toSeconds;
addPosition(transformedPos);
}
LINFO(
fmt::format(
"Viewing {} points with velocity ({} points in total). "
"{} points have NaN-value velocity and are hidden or static.",
velocityData.size()/3 - nPointsContainingNans,
pointDataSlice->size()/3,
nPointsContainingNans
)
);
_hasLoadedVelocityData = true;
softwareIntegrationModule->setDataLoaded(_identifier.value(), storage::Key::VelocityData);
LDEBUG("Rerendering with motion based on velocity data");
LDEBUG("New colormap attribute data has loaded");
}
std::shared_ptr<RenderablePointsCloud::DataSlice> RenderablePointsCloud::getDataSlice(DataSliceKey key) {
@@ -949,12 +919,38 @@ void RenderablePointsCloud::checkColormapMinMax() {
}
}
bool RenderablePointsCloud::shouldLoadVelocityData(bool isVelocityDataDirty) {
bool RenderablePointsCloud::shouldLoadPointData(SoftwareIntegrationModule* softwareIntegrationModule) {
return (
(isVelocityDataDirty || _velocityUnitsAreDirty)
(
_pointUnitIsDirty
|| softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::DataPoints)
)
&& _pointUnit.value() != "<no unit set>"
);
}
bool RenderablePointsCloud::shouldLoadVelocityData(SoftwareIntegrationModule* softwareIntegrationModule) {
return (
(
_velocityUnitsAreDirty
|| softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::VelocityData)
)
&& _velocityDistanceUnit.value() != "<no unit set>"
&& _velocityTimeUnit.value() != "<no unit set>"
);
}
bool RenderablePointsCloud::shouldLoadColormap(SoftwareIntegrationModule* softwareIntegrationModule) {
return softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::Colormap);
}
bool RenderablePointsCloud::shouldLoadColormapAttrData(SoftwareIntegrationModule* softwareIntegrationModule) {
return softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::ColormapAttrData);
}
bool RenderablePointsCloud::shouldLoadLinearSizeAttrData(SoftwareIntegrationModule* softwareIntegrationModule) {
return softwareIntegrationModule->isDataDirty(_identifier.value(), storage::Key::LinearSizeAttrData);
}
} // namespace openspace
@@ -63,7 +63,7 @@ public:
static documentation::Documentation Documentation();
private:
void loadData(SoftwareIntegrationModule* softwareIntegrationModule);
void loadPointData(SoftwareIntegrationModule* softwareIntegrationModule);
void loadColormap(SoftwareIntegrationModule* softwareIntegrationModule);
void loadColormapAttributeData(SoftwareIntegrationModule* softwareIntegrationModule);
void loadLinearSizeAttributeData(SoftwareIntegrationModule* softwareIntegrationModule);
@@ -76,20 +76,26 @@ private:
void checkIfLinearSizeCanBeEnabled();
void checkIfMotionCanBeEnabled();
bool shouldLoadVelocityData(bool isVelocityDataDirty);
bool shouldLoadPointData(SoftwareIntegrationModule* softwareIntegrationModule);
bool shouldLoadVelocityData(SoftwareIntegrationModule* softwareIntegrationModule);
bool shouldLoadColormap(SoftwareIntegrationModule* softwareIntegrationModule);
bool shouldLoadColormapAttrData(SoftwareIntegrationModule* softwareIntegrationModule);
bool shouldLoadLinearSizeAttrData(SoftwareIntegrationModule* softwareIntegrationModule);
std::unique_ptr<ghoul::opengl::ProgramObject> _shaderProgram = nullptr;
UniformCache(
color, opacity, size, modelMatrix, cameraUp, screenSize,
color, size, modelMatrix, cameraUp, screenSize,
cameraViewProjectionMatrix, eyePosition, sizeOption,
colormapTexture, colormapMin, colormapMax, colormapNaNMode,
colormapNaNColor, colormapEnabled, linearSizeMin, linearSizeMax,
linearSizeEnabled, velocityNaNMode, motionEnabled, time
colormapTexture, colormapMin, colormapMax, colormapNanMode,
colormapNanColor, colormapEnabled, linearSizeMin, linearSizeMax,
linearSizeEnabled, velocityNanMode, motionEnabled, time
) _uniformCache;
properties::StringProperty _name;
properties::FloatProperty _size;
properties::Vec4Property _color;
properties::StringProperty _pointUnit;
properties::OptionProperty _sizeOption;
properties::BoolProperty _linearSizeEnabled;
properties::FloatProperty _linearSizeMin;
@@ -99,17 +105,19 @@ private:
properties::BoolProperty _colormapEnabled;
properties::FloatProperty _colormapMin;
properties::FloatProperty _colormapMax;
properties::IntProperty _colormapNaNMode;
properties::Vec4Property _colormapNaNColor;
properties::IntProperty _colormapNanMode;
properties::Vec4Property _colormapNanColor;
properties::BoolProperty _motionEnabled;
properties::StringProperty _velocityDistanceUnit;
properties::StringProperty _velocityTimeUnit;
properties::IntProperty _velocityNaNMode;
properties::IntProperty _velocityNanMode;
std::optional<std::string> _identifier = std::nullopt;
bool _pointUnitIsDirty = false;
bool _hasLoadedColormapAttributeData = false;
bool _hasLoadedColormap = false;
@@ -130,10 +138,9 @@ private:
using DataSlice = std::vector<float>;
std::unordered_map<DataSliceKey, std::shared_ptr<DataSlice>> _dataSlices;
std::shared_ptr<DataSlice> getDataSlice(DataSliceKey key);
enum SizeOption {
Uniform = 0,
NonUniform = 1
NonUniform
};
struct VBOLayout {
@@ -40,17 +40,16 @@ in float ta;
in vec3 ge_velocity;
uniform vec4 color;
uniform float opacity;
uniform float colormapMin;
uniform float colormapMax;
uniform int colormapNaNMode;
uniform vec4 colormapNaNColor;
uniform int colormapNanMode;
uniform vec4 colormapNanColor;
uniform bool colormapEnabled;
uniform sampler1D colormapTexture;
uniform bool motionEnabled;
uniform int velocityNaNMode;
uniform int velocityNanMode;
vec4 attributeScalarToRgb(float attributeData) {
float t = (attributeData - colormapMin) / (colormapMax - colormapMin);
@@ -64,29 +63,29 @@ vec4 attributeScalarToRgb(float attributeData) {
}
Fragment getFragment() {
if (ta == 0.001 || opacity == 0.00001 || color.a == 0.00001) {
if (ta == 0.001 || color.a == 0.00001) {
discard;
}
// Don't show points with no value for that
// attribute, if ColormapNaNRenderMode is Hidden
if (colormapEnabled
// attribute, if ColormapNanRenderMode is Hidden
if (
colormapEnabled
&& isnan(ge_colormapAttributeScalar)
&& colormapNaNMode == COLORMAPNANMODE_HIDDEN)
{
&& colormapNanMode == COLORMAPNANMODE_HIDDEN
) {
discard;
}
// ========== Velocity NaN mode ==========
// Don't show points with no value for
// velocity, if VelocityNaNRenderMode is Hidden
// vec3 vel = ge_velocity;
bool velocityIsNaN = (isnan(ge_velocity[0]) ||
// velocity, if VelocityNanRenderMode is Hidden
bool velocityIsNan = (isnan(ge_velocity[0]) ||
isnan(ge_velocity[1]) ||
isnan(ge_velocity[2]));
if (motionEnabled &&
velocityIsNaN &&
velocityNaNMode == VELOCITYNANMODE_HIDDEN)
velocityIsNan &&
velocityNanMode == VELOCITYNANMODE_HIDDEN)
{
discard;
} // else the point is left static
@@ -98,15 +97,15 @@ Fragment getFragment() {
// calculate distance from the origin point
float circle = smoothstep(radius, radius - (radius * 0.2), distance);
vec4 outputColor = vec4(color.rgb, color.a * opacity);
vec4 outputColor = color;
if (colormapEnabled) {
// Set colormapNaNColor if point doesn't have a value for the attribute
if (isnan(ge_colormapAttributeScalar) && colormapNaNMode == COLORMAPNANMODE_COLOR) {
outputColor = vec4(colormapNaNColor.rgb, colormapNaNColor.a * opacity);
// Set colormapNanColor if point doesn't have a value for the attribute
if (isnan(ge_colormapAttributeScalar) && colormapNanMode == COLORMAPNANMODE_COLOR) {
outputColor = vec4(colormapNanColor.rgb, colormapNanColor.a);
}
else {
vec4 colorFromColormap = attributeScalarToRgb(ge_colormapAttributeScalar);
outputColor = vec4(colorFromColormap.rgb, colorFromColormap.a * color.a * opacity);
outputColor = vec4(colorFromColormap.rgb, colorFromColormap.a * color.a);
}
}
@@ -118,4 +117,4 @@ Fragment getFragment() {
frag.disableLDR2HDR = false;
return frag;
}
}
@@ -111,17 +111,16 @@ void main() {
if (linearSizeEnabled) {
float interpolatedSizeAtt = 1.0;
// TODO: "colormapAttributeScalar" should be "linearSizeAttributeScalar"???
float colormapAttributeScalar = vs_linearSizeAttributeScalar[0];
if (colormapAttributeScalar < linearSizeMin) {
float linearSizeAttributeScalar = vs_linearSizeAttributeScalar[0];
if (linearSizeAttributeScalar < linearSizeMin) {
interpolatedSizeAtt = 0.0;
}
else if (colormapAttributeScalar > linearSizeMax) {
else if (linearSizeAttributeScalar > linearSizeMax) {
interpolatedSizeAtt = 1.0;
}
else {
// Linear interpolation
interpolatedSizeAtt = (colormapAttributeScalar - linearSizeMin) / (linearSizeMax - linearSizeMin);
interpolatedSizeAtt = (linearSizeAttributeScalar - linearSizeMin) / (linearSizeMax - linearSizeMin);
}
interpolatedSizeAtt = mix(1.0, 5.0, interpolatedSizeAtt);
@@ -48,8 +48,8 @@ void main() {
// Add velocity if applicable
// Velocity (UVW) is already in m/s
vs_velocity = in_velocity;
bool velocityIsNaN = (isnan(in_velocity[0]) || isnan(in_velocity[1]) || isnan(in_velocity[2]));
if (motionEnabled && !velocityIsNaN) {
bool velocityIsNan = (isnan(in_velocity[0]) || isnan(in_velocity[1]) || isnan(in_velocity[2]));
if (motionEnabled && !velocityIsNan) {
// TODO: Need to subtract with t = 0 (when the position was measured)
objectPosition.xyz += time * in_velocity;
}
@@ -57,38 +57,32 @@ SoftwareIntegrationModule::~SoftwareIntegrationModule() {
}
void SoftwareIntegrationModule::storeData(
const SyncableFloatDataStorage::Identifier& identifier,
const storage::Key key, const std::vector<float>& data
const SyncableStorage::Identifier& identifier,
const simp::DataKey key,
const std::vector<std::byte>& data
) {
_syncableFloatDataStorage.store(identifier, key, data);
}
const std::vector<float>& SoftwareIntegrationModule::fetchData(
const SyncableFloatDataStorage::Identifier& identifier,
const storage::Key key
) {
return _syncableFloatDataStorage.fetch(identifier, key);
_syncableStorage.store(identifier, key, data);
}
bool SoftwareIntegrationModule::isDataDirty(
const SyncableFloatDataStorage::Identifier& identifier,
const SyncableStorage::Identifier& identifier,
const storage::Key key
) {
return _syncableFloatDataStorage.isDirty(identifier, key);
return _syncableStorage.isDirty(identifier, key);
}
void SoftwareIntegrationModule::setDataLoaded(
const SyncableFloatDataStorage::Identifier& identifier,
const SyncableStorage::Identifier& identifier,
const storage::Key key
) {
_syncableFloatDataStorage.setLoaded(identifier, key);
_syncableStorage.setLoaded(identifier, key);
}
bool SoftwareIntegrationModule::dataLoaded(
const SyncableFloatDataStorage::Identifier& identifier,
const SyncableStorage::Identifier& identifier,
const storage::Key key
) {
return _syncableFloatDataStorage.hasLoaded(identifier, key);
return _syncableStorage.hasLoaded(identifier, key);
}
void SoftwareIntegrationModule::internalInitialize(const ghoul::Dictionary&) {
@@ -104,7 +98,12 @@ void SoftwareIntegrationModule::internalInitialize(const ghoul::Dictionary&) {
_networkState = softwareintegration::network::serve();
global::callback::postSyncPreDraw->emplace_back([this]() {
softwareintegration::network::postSyncCallbacks();
if (_networkState) {
for (auto& connections : _networkState->softwareConnections) {
connections.second->notifyMessageQueueHandler();
}
}
softwareintegration::messagehandler::postSyncCallbacks();
});
}
}
@@ -125,12 +124,7 @@ SoftwareIntegrationModule::documentations() const
}
std::vector<Syncable*> SoftwareIntegrationModule::getSyncables() {
return { &_syncableFloatDataStorage };
}
// Helper function for debugging
std::string SoftwareIntegrationModule::getStringOfAllKeysInStorage() {
return _syncableFloatDataStorage.getStringOfAllKeysInStorage();
return { &_syncableStorage };
}
scripting::LuaLibrary SoftwareIntegrationModule::luaLibrary() const {
@@ -26,7 +26,7 @@
#define __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SOFTWAREINTEGRATIONMODULE___H__
#include <openspace/util/openspacemodule.h>
#include <modules/softwareintegration/syncablefloatdatastorage.h>
#include <modules/softwareintegration/syncablestorage.h>
#include <openspace/documentation/documentation.h>
#include <modules/softwareintegration/network/network.h>
@@ -44,27 +44,28 @@ public:
~SoftwareIntegrationModule();
void storeData(
const SyncableFloatDataStorage::Identifier& identifier,
const storage::Key key,
const std::vector<float>& data
const SyncableStorage::Identifier& identifier,
const simp::DataKey key,
const std::vector<std::byte>& data
);
const std::vector<float>& fetchData(
const SyncableFloatDataStorage::Identifier& identifier,
const storage::Key key
template <typename T>
bool fetchData(
const SyncableStorage::Identifier& identifier,
const storage::Key key,
T& resultingData
);
bool isDataDirty(
const SyncableFloatDataStorage::Identifier& identifier,
const SyncableStorage::Identifier& identifier,
const storage::Key key
);
void setDataLoaded(
const SyncableFloatDataStorage::Identifier& identifier,
const SyncableStorage::Identifier& identifier,
const storage::Key key
);
bool dataLoaded(
const SyncableFloatDataStorage::Identifier& identifier,
const SyncableStorage::Identifier& identifier,
const storage::Key key
);
std::string getStringOfAllKeysInStorage();
std::vector<documentation::Documentation> documentations() const override;
@@ -77,11 +78,13 @@ private:
std::vector<Syncable*> getSyncables();
// Centralized storage for datasets
SyncableFloatDataStorage _syncableFloatDataStorage;
SyncableStorage _syncableStorage;
std::shared_ptr<softwareintegration::network::NetworkState> _networkState;
};
} // namespace openspace
#include "softwareintegrationmodule.inl"
#endif // __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SOFTWAREINTEGRATIONMODULE___H__
@@ -0,0 +1,36 @@
/*****************************************************************************************
* *
* 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. *
****************************************************************************************/
namespace openspace {
template <typename T>
bool SoftwareIntegrationModule::fetchData(
const SyncableStorage::Identifier& identifier,
const storage::Key key,
T& resultingData
) {
return _syncableStorage.fetch(identifier, key, resultingData);
}
} // namespace openspace
@@ -1,271 +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/syncablefloatdatastorage.h>
#include <modules/softwareintegration/utils.h>
#include <openspace/util/syncbuffer.h>
#include <ghoul/misc/profiling.h>
#include <ghoul/logging/logmanager.h>
namespace {
constexpr const char* _loggerCat = "SyncableFloatDataStorage";
} // namespace
namespace openspace {
using namespace softwareintegration;
/* ============== SyncEngine functions ============== */
void SyncableFloatDataStorage::encode(SyncBuffer* syncBuffer) {
ZoneScopedN("SyncableFloatDataStorage::encode");
encodeStorage(syncBuffer);
}
void SyncableFloatDataStorage::decode(SyncBuffer* syncBuffer) {
ZoneScopedN("SyncableFloatDataStorage::decode");
decodeStorage(syncBuffer);
}
void SyncableFloatDataStorage::postSync(bool isMaster) {
if (isMaster) {
std::lock_guard guard(_mutex);
for (auto& sgnStorage : _storage) {
for (auto& storageEntry : sgnStorage.second) {
if (storageEntry.second.syncDirty && storageEntry.second.hasEncoded) {
storageEntry.second.syncDirty = false;
storageEntry.second.hasEncoded = false;
}
}
}
}
}
/* ================================================== */
const SyncableFloatDataStorage::ValueData& SyncableFloatDataStorage::fetch(
const Identifier& identifier,
const storage::Key key
) {
LDEBUG(fmt::format("Loading data from float data storage: {}-{}", identifier, storage::getStorageKeyString(key)));
std::lock_guard guard(_mutex);
if (!count(identifier)) {
LERROR(fmt::format(
"Could not find any data for SceneGraphNode '{}' in the centralized data storage",
identifier
));
return ValueData{};
}
if (!count(identifier, key)) {
LERROR(fmt::format(
"SceneGraphNode {} has no data with key '{}' in the centralized data storage", identifier,
storage::getStorageKeyString(key)
));
return ValueData{};
}
auto& value = _storage.find(identifier)->second.find(key)->second;
value.dirty = false;
return value.data;
}
bool SyncableFloatDataStorage::isDirty(const Identifier& identifier, const storage::Key key) {
if (!count(identifier, key)) {
return false;
}
return _storage.find(identifier)->second.find(key)->second.dirty;
}
void SyncableFloatDataStorage::setLoaded(const Identifier& identifier, const storage::Key key) {
if (!count(identifier, key)) {
LERROR(fmt::format(
"SceneGraphNode {} has no data with key '{}' in the centralized data storage",
identifier,
storage::getStorageKeyString(key)
));
return;
}
_storage.find(identifier)->second.find(key)->second.hasLoaded = true;
}
bool SyncableFloatDataStorage::hasLoaded(const Identifier& identifier, const storage::Key key) {
if (!count(identifier, key)) {
return false;
}
return _storage.find(identifier)->second.find(key)->second.hasLoaded;
}
void SyncableFloatDataStorage::store(
const Identifier& identifier,
const storage::Key key,
const ValueData& data
) {
LDEBUG(fmt::format("Storing data in float data storage: {}-{}", identifier, storage::getStorageKeyString(key)));
std::lock_guard guard(_mutex);
insertAssign(identifier, key, { data });
}
void SyncableFloatDataStorage::encodeStorage(SyncBuffer* syncBuffer, bool skipNonSynced) {
std::lock_guard guard(_mutex);
syncBuffer->encode(static_cast<uint16_t>(_storage.size()));
for (auto& [identifier, sgnStorage] : _storage) {
syncBuffer->encode(identifier);
syncBuffer->encode(static_cast<uint16_t>(sgnStorage.size()));
for (auto& [key, storageEntry] : sgnStorage) {
if (skipNonSynced) {
bool isSyncDirty = storageEntry.syncDirty;
syncBuffer->encode(isSyncDirty);
if (!isSyncDirty) continue;
}
syncBuffer->encode(static_cast<uint8_t>(key));
syncBuffer->encode(storageEntry.data);
// TODO: Maybe combine solution with syncDirty?
storageEntry.hasEncoded = true;
}
}
}
void SyncableFloatDataStorage::decodeStorage(SyncBuffer* syncBuffer, bool skipNonSynced) {
std::lock_guard guard(_mutex);
uint16_t nSGNs;
syncBuffer->decode(nSGNs);
for (uint16_t i = 0; i < nSGNs; ++i) {
std::string identifier;
syncBuffer->decode(identifier);
uint16_t nStorageEntries;
syncBuffer->decode(nStorageEntries);
for (uint16_t j = 0; j < nStorageEntries; ++j) {
if (skipNonSynced) {
bool isSyncDirty;
syncBuffer->decode(isSyncDirty);
if (!isSyncDirty) continue;
}
uint8_t keyRaw;
syncBuffer->decode(keyRaw);
auto key = static_cast<storage::Key>(keyRaw);
std::vector<float> dataEntry{};
syncBuffer->decode(dataEntry);
insertAssign(identifier, key, Value{ dataEntry });
}
}
}
void SyncableFloatDataStorage::store(const std::vector<std::byte>& storageDump) {
ZoneScopedN("SyncableFloatDataStorage::store");
auto syncBuffer = new SyncBuffer{ 0 };
syncBuffer->setData(storageDump);
decodeStorage(syncBuffer, false);
}
void SyncableFloatDataStorage::dump(std::vector<std::byte>& storageDump) {
ZoneScopedN("SyncableFloatDataStorage::dump");
auto syncBuffer = new SyncBuffer{ 0 };
encodeStorage(syncBuffer, false);
storageDump = syncBuffer->data();
}
/* =============== Utility functions ================ */
bool SyncableFloatDataStorage::erase(const Identifier& identifier, const storage::Key key) {
if (count(identifier, key) == 0) return false;
auto nErased = _storage.find(identifier)->second.erase(key);
return nErased > 0;
}
void SyncableFloatDataStorage::insertAssign(const Identifier& identifier, const storage::Key key, const Value& value) {
if (count(identifier)) {
if (count(identifier, key)) {
_storage.find(identifier)->second.find(key)->second = value;
}
else {
_storage.find(identifier)->second.emplace(key, value);
}
}
else {
SceneStorage newSceneStorage{ { key, value } };
_storage.emplace(identifier, std::move(newSceneStorage));
}
}
size_t SyncableFloatDataStorage::count(const Identifier& identifier) {
return _storage.count(identifier);
}
size_t SyncableFloatDataStorage::count(const Identifier& identifier, const storage::Key key) {
auto sceneIt = _storage.find(identifier);
if (sceneIt == _storage.end()) return 0;
return sceneIt->second.count(key);
}
std::vector<SyncableFloatDataStorage::Identifier> SyncableFloatDataStorage::getAllIdentifiers() {
std::vector<Identifier> identifiers;
identifiers.reserve(_storage.size());
for (auto [identifier, sceneStorage] : _storage) {
identifiers.push_back(identifier);
}
return std::move(identifiers);
}
// Helper function for debugging
std::string SyncableFloatDataStorage::getStringOfAllKeysInStorage() {
std::string keysString;
for (auto [id, sceneStorage] : _storage) {
keysString += '(' + id + ')' + ": ";
for (auto [key, val] : sceneStorage) {
keysString += storage::getStorageKeyString(key) + " ";
}
keysString += '\n';
}
return keysString;
}
/* ================================================== */
} // namespace openspace
@@ -0,0 +1,359 @@
/*****************************************************************************************
* *
* 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/syncablestorage.h>
#include <modules/softwareintegration/utils.h>
#include <openspace/util/syncbuffer.h>
#include <ghoul/misc/profiling.h>
#include <ghoul/logging/logmanager.h>
namespace {
constexpr const char* _loggerCat = "SyncableStorage";
} // namespace
namespace openspace {
using namespace softwareintegration;
/* ============== SyncEngine functions ============== */
void SyncableStorage::encode(SyncBuffer* syncBuffer) {
ZoneScopedN("SyncableStorage::encode");
encodeStorage(syncBuffer);
}
void SyncableStorage::decode(SyncBuffer* syncBuffer) {
ZoneScopedN("SyncableStorage::decode");
decodeStorage(syncBuffer);
}
/* ================================================== */
bool SyncableStorage::isDirty(const Identifier& identifier, const storage::Key storageKey) {
auto simpDataKeys = simpDataKeysFromStorageKey(storageKey);
for (auto key : simpDataKeys) {
if (!count(identifier, key)) {
return false;
}
if (_storage.find(identifier)->second.find(key)->second.dirty) {
return true;
}
}
return false;
}
void SyncableStorage::setLoaded(const Identifier& identifier, const storage::Key storageKey) {
auto simpDataKeys = simpDataKeysFromStorageKey(storageKey);
for (auto key : simpDataKeys) {
if (!count(identifier, key)) {
LERROR(fmt::format(
"SceneGraphNode {} has no data with key '{}' in the centralized data storage",
identifier,
simp::getStringFromDataKey(key)
));
return;
}
_storage.find(identifier)->second.find(key)->second.hasLoaded = true;
}
}
bool SyncableStorage::hasLoaded(const Identifier& identifier, const storage::Key storageKey) {
auto simpDataKeys = simpDataKeysFromStorageKey(storageKey);
for (auto key : simpDataKeys) {
if (!count(identifier, key)) {
return false;
}
if (!_storage.find(identifier)->second.find(key)->second.hasLoaded) {
return false;
}
}
return true;
}
void SyncableStorage::store(
const Identifier& identifier,
const simp::DataKey key,
const std::vector<std::byte>& data
) {
LDEBUG(fmt::format("Storing data in float data storage: {}-{}", identifier, simp::getStringFromDataKey(key)));
std::lock_guard guard(_mutex);
insertAssign(identifier, key, { data });
}
void SyncableStorage::encodeStorage(SyncBuffer* syncBuffer, bool skipNonSynced) {
std::lock_guard guard(_mutex);
syncBuffer->encode(static_cast<uint16_t>(_storage.size()));
for (auto& [identifier, sgnStorage] : _storage) {
syncBuffer->encode(identifier);
syncBuffer->encode(static_cast<uint16_t>(sgnStorage.size()));
for (auto& [key, storageEntry] : sgnStorage) {
if (skipNonSynced) {
bool& isSyncDirty = storageEntry.syncDirty;
syncBuffer->encode(isSyncDirty);
if (!isSyncDirty) continue;
isSyncDirty = false;
}
syncBuffer->encode(static_cast<uint8_t>(key));
syncBuffer->encode(storageEntry.data);
}
}
}
void SyncableStorage::decodeStorage(SyncBuffer* syncBuffer, bool skipNonSynced) {
std::lock_guard guard(_mutex);
uint16_t nSGNs;
syncBuffer->decode(nSGNs);
for (uint16_t i = 0; i < nSGNs; ++i) {
std::string identifier;
syncBuffer->decode(identifier);
uint16_t nStorageEntries;
syncBuffer->decode(nStorageEntries);
for (uint16_t j = 0; j < nStorageEntries; ++j) {
if (skipNonSynced) {
bool isSyncDirty;
syncBuffer->decode(isSyncDirty);
if (!isSyncDirty) continue;
}
uint8_t keyRaw;
syncBuffer->decode(keyRaw);
auto key = static_cast<simp::DataKey>(keyRaw);
std::vector<std::byte> dataEntry{};
syncBuffer->decode(dataEntry);
insertAssign(identifier, key, Value{ dataEntry });
}
}
}
void SyncableStorage::store(const std::vector<std::byte>& storageDump) {
ZoneScopedN("SyncableStorage::store");
auto syncBuffer = new SyncBuffer{ 0 };
syncBuffer->setData(storageDump);
decodeStorage(syncBuffer, false);
}
void SyncableStorage::dump(std::vector<std::byte>& storageDump) {
ZoneScopedN("SyncableStorage::dump");
auto syncBuffer = new SyncBuffer{ 0 };
encodeStorage(syncBuffer, false);
storageDump = syncBuffer->data();
}
std::vector<SyncableStorage::Identifier> SyncableStorage::getAllIdentifiers() {
std::vector<Identifier> identifiers;
identifiers.reserve(_storage.size());
for (auto [identifier, sceneStorage] : _storage) {
identifiers.push_back(identifier);
}
return std::move(identifiers);
}
/* =============== Utility functions ================ */
void SyncableStorage::insertAssign(const Identifier& identifier, const simp::DataKey key, const Value& value) {
if (count(identifier)) {
if (count(identifier, key)) {
_storage.find(identifier)->second.find(key)->second = value;
}
else {
_storage.find(identifier)->second.emplace(key, value);
}
}
else {
SceneStorage newSceneStorage{ { key, value } };
_storage.emplace(identifier, std::move(newSceneStorage));
}
}
size_t SyncableStorage::count(const Identifier& identifier) {
return _storage.count(identifier);
}
size_t SyncableStorage::count(const Identifier& identifier, const simp::DataKey key) {
auto sceneIt = _storage.find(identifier);
if (sceneIt == _storage.end()) return 0;
return sceneIt->second.count(key);
}
std::vector<simp::DataKey> SyncableStorage::simpDataKeysFromStorageKey(const storage::Key key) {
switch (key) {
case storage::Key::DataPoints: {
return { simp::DataKey::X, simp::DataKey::Y, simp::DataKey::Z };
}
case storage::Key::VelocityData:{
return { simp::DataKey::U, simp::DataKey::V, simp::DataKey::W };
}
case storage::Key::Colormap:{
return {
simp::DataKey::ColormapReds,
simp::DataKey::ColormapGreens,
simp::DataKey::ColormapBlues,
simp::DataKey::ColormapAlphas
};
}
case storage::Key::ColormapAttrData:{
return { simp::DataKey::ColormapAttributeData };
}
case storage::Key::LinearSizeAttrData:{
return { simp::DataKey::LinearSizeAttributeData };
}
default: { // Unknown
LERROR(fmt::format(
"There's no storage key '{}'",
storage::getStorageKeyString(key)
));
return {};
}
}
}
/**
* Fetches float data for 1 or more dimensions of data
*
*/
bool SyncableStorage::fetchDimFloatData(
const Identifier& identifier,
const std::vector<simp::DataKey> dimDataKeys,
std::vector<float>& resultingData
) {
// Fetch all values from storage
size_t nBytesPerDim = 0;
size_t nDimensions = dimDataKeys.size();
size_t nValues = dimDataKeys.size();
if (!count(identifier)) {
LERROR(fmt::format(
"SceneGraphNode {} is missing from the centralized data storage",
identifier
));
return false;
}
auto& sceneStorage = _storage.at(identifier);
for (size_t i = 0; i < nDimensions; i++) {
if (!count(identifier, dimDataKeys[i])) {
LERROR(fmt::format(
"SceneGraphNode {} is missing {} from the centralized data storage",
identifier, simp::getStringFromDataKey(dimDataKeys[i])
));
return false;
}
auto dimValues = sceneStorage.at(dimDataKeys[i]).data;
// All dimensions must have same length
if (i != 0 && nBytesPerDim != dimValues.size()) {
LERROR(fmt::format(
"Error while trying to fetch float data."
"The dimensions of values does not have the same length."
));
return false;
}
nBytesPerDim = dimValues.size();
nValues = (nBytesPerDim / 4);
}
resultingData.resize(nValues * nDimensions);
size_t nAddedValues = 0;
for (size_t i = 0; i < nDimensions; ++i) {
auto dataOnDim = sceneStorage.at(dimDataKeys[i]).data;
size_t offset = 0;
size_t index = i;
while (offset < nBytesPerDim) {
float value;
try {
// We can use readValue() (which converts to big endian)
// because we haven't changed from big (network) endian
// to little endian yet for the data in syncable storage
simp::readValue(
dataOnDim,
offset,
value
);
}
catch (const simp::SimpError& err) {
LERROR(fmt::format(
"Couldn't parse value on offset {} from storage for {}: {}",
offset, simp::getStringFromDataKey(dimDataKeys[i]), err.message
));
resultingData.clear();
return false;
}
resultingData[index] = value;
++nAddedValues;
// Advance nDimensions (nDimension represent stride)
index += nDimensions;
}
}
if (nAddedValues != resultingData.size()) {
std::string dataKeysString = "(";
for (size_t i = 0; i < dimDataKeys.size(); ++i) {
std::string prefix = (i != 0 ? ", " : "");
dataKeysString += prefix + simp::getStringFromDataKey(dimDataKeys[i]);
}
dataKeysString += ")";
LERROR(fmt::format(
"Mismatch in number of values in syncable storage ({}) and loaded values ({}), when loading {}.",
resultingData.size(), nAddedValues, dataKeysString
));
resultingData.clear();
return false;
}
// Set all values to not dirty
for (auto dimDataKey : dimDataKeys) {
sceneStorage.at(dimDataKey).dirty = false;
}
return true;
}
/* ================================================== */
} // namespace openspace
@@ -22,8 +22,8 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SYNCABLEFLOATDATASTORAGE___H__
#define __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SYNCABLEFLOATDATASTORAGE___H__
#ifndef __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SYNCABLESTORAGE___H__
#define __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SYNCABLESTORAGE___H__
#include <openspace/util/syncable.h>
@@ -36,19 +36,17 @@ namespace openspace {
using namespace softwareintegration;
class SyncableFloatDataStorage : public Syncable {
class SyncableStorage : public Syncable {
public:
/* ====================== Types ===================== */
struct Value {
// a dataset stored like x1, y1, z1, x2, y2 ....
std::vector<float> data;
bool hasEncoded = false;
bool syncDirty = true;
std::vector<std::byte> data;
bool syncDirty = true; // Only used on master node
bool hasLoaded = false;
bool dirty = true;
};
using ValueData = decltype(Value::data);
using SceneStorage = std::unordered_map<storage::Key, Value>;
using SceneStorage = std::unordered_map<simp::DataKey, Value>;
using Identifier = std::string;
using Storage = std::unordered_map<Identifier, SceneStorage>;
using Iterator = Storage::iterator;
@@ -58,15 +56,18 @@ public:
/* ============== SyncEngine functions ============== */
virtual void encode(SyncBuffer* syncBuffer) override;
virtual void decode(SyncBuffer* syncBuffer) override;
virtual void postSync(bool isMaster) override;
/* ================================================== */
const ValueData& fetch(const Identifier& identifier, const storage::Key key);
void setLoaded(const Identifier& identifier, const storage::Key key);
bool hasLoaded(const Identifier& identifier, const storage::Key key);
bool isDirty(const Identifier& identifier, const storage::Key key);
bool isSyncDirty(const Identifier& identifier, const storage::Key key);
void store(const Identifier& identifier, const storage::Key key, const ValueData& data);
template<typename T>
bool fetch(
const Identifier& identifier,
const storage::Key storageKey,
T& resultingData
);
void setLoaded(const Identifier& identifier, const storage::Key storageKey);
bool hasLoaded(const Identifier& identifier, const storage::Key storageKey);
bool isDirty(const Identifier& identifier, const storage::Key storageKey);
void store(const Identifier& identifier, const simp::DataKey key, const std::vector<std::byte>& data);
void encodeStorage(SyncBuffer* syncBuffer, bool skipNonSynced = true);
void decodeStorage(SyncBuffer* syncBuffer, bool skipNonSynced = true);
@@ -74,16 +75,17 @@ public:
void store(const std::vector<std::byte>& storageDump);
std::vector<Identifier> getAllIdentifiers();
std::string getStringOfAllKeysInStorage();
private:
/* =============== Utility functions ================ */
bool erase(const Identifier& identifier, const storage::Key key);
void insertAssign(const Identifier& identifier, const storage::Key key, const Value& value);
size_t count(const Identifier& identifier, const storage::Key key);
void insertAssign(const Identifier& identifier, const simp::DataKey key, const Value& value);
size_t count(const Identifier& identifier);
size_t count(const Identifier& identifier, const simp::DataKey key);
std::vector<simp::DataKey> simpDataKeysFromStorageKey(const storage::Key key);
bool fetchDimFloatData(
const Identifier& identifier,
const std::vector<simp::DataKey> dimDataKeys,
std::vector<float>& resultingData
);
/* ================================================== */
std::mutex _mutex;
@@ -92,4 +94,6 @@ private:
} // namespace openspace
#endif // __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SYNCABLEFLOATDATASTORAGE___H__
#include "syncablestorage.inl"
#endif // __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SYNCABLESTORAGE___H__
@@ -0,0 +1,71 @@
/*****************************************************************************************
* *
* 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. *
****************************************************************************************/
namespace openspace {
template<typename T>
bool SyncableStorage::fetch(
const Identifier& identifier,
const storage::Key storageKey,
T& resultingData
) {
LDEBUG(fmt::format("Loading data from float data storage: {}-{}", identifier, storage::getStorageKeyString(storageKey)));
std::lock_guard guard(_mutex);
if (!count(identifier)) {
LERROR(fmt::format(
"Could not find any data for SceneGraphNode '{}' in the centralized data storage",
identifier
));
return false;
}
switch (storageKey) {
case storage::Key::DataPoints:
case storage::Key::Colormap:
case storage::Key::ColormapAttrData:
case storage::Key::LinearSizeAttrData:
case storage::Key::VelocityData: {
if (!std::is_same<T, std::vector<float>>::value) {
LERROR(fmt::format(
"Can't put {} into a {}.",
storage::getStorageKeyString(storageKey), typeid(T).name()
));
return false;
}
return fetchDimFloatData(identifier, simpDataKeysFromStorageKey(storageKey), resultingData);
}
default: {
LERROR(fmt::format(
"Could not find data in storage for the key {}",
storage::getStorageKeyString(storageKey)
));
break;
}
}
return false;
}
} // namespace openspace
+276 -219
View File
@@ -83,70 +83,189 @@ namespace simp {
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},
{"ATDA", MessageType::AttributeData},
{"FOPA", MessageType::Opacity},
{"FPSI", MessageType::FixedSize},
{"LPSI", MessageType::LinearSize},
{"TOVI", MessageType::Visibility},
{ "CONN", MessageType::Connection },
{ "DATA", MessageType::Data },
{ "RSGN", MessageType::RemoveSceneGraphNode },
};
const std::unordered_map<std::string, ColormapNaNRenderMode> _colormapNaNRenderModeFromString {
{"Hide", ColormapNaNRenderMode::Hide},
{"FixedColor", ColormapNaNRenderMode::FixedColor}
const std::unordered_map<std::string, DataKey> _dataTypeFromString{
{ "pos.x", DataKey::X },
{ "pos.y", DataKey::Y },
{ "pos.z", DataKey::Z },
{ "pos.unit", DataKey::PointUnit },
{ "vel.u", DataKey::U },
{ "vel.v", DataKey::V },
{ "vel.w", DataKey::W },
{ "vel.unit.dist", DataKey::VelocityDistanceUnit },
{ "vel.unit.time", DataKey::VelocityTimeUnit },
{ "vel.nan.mode", DataKey::VelocityNanMode },
{ "vel.enable", DataKey::VelocityEnabled },
{ "col.r", DataKey::Red },
{ "col.g", DataKey::Green },
{ "col.b", DataKey::Blue },
{ "col.a", DataKey::Alpha },
{ "cmap.enable", DataKey::ColormapEnabled },
{ "cmap.r", DataKey::ColormapReds },
{ "cmap.g", DataKey::ColormapGreens },
{ "cmap.b", DataKey::ColormapBlues },
{ "cmap.a", DataKey::ColormapAlphas },
{ "cmap.min", DataKey::ColormapMin },
{ "cmap.max", DataKey::ColormapMax },
{ "cmap.nan.r", DataKey::ColormapNanR },
{ "cmap.nan.g", DataKey::ColormapNanG },
{ "cmap.nan.b", DataKey::ColormapNanB },
{ "cmap.nan.a", DataKey::ColormapNanA },
{ "cmap.nan.mode", DataKey::ColormapNanMode },
{ "cmap.attr", DataKey::ColormapAttributeData },
{ "size.val", DataKey::FixedSize },
{ "lsize.enabled", DataKey::LinearSizeEnabled },
{ "lsize.min", DataKey::LinearSizeMin },
{ "lsize.max", DataKey::LinearSizeMax },
{ "lsize.attr", DataKey::LinearSizeAttributeData },
{ "vis.val", DataKey::Visibility },
};
const std::unordered_map<std::string, VelocityNaNRenderMode> _velocityNaNRenderModeFromString {
{"Hide", VelocityNaNRenderMode::Hide},
{"Static", VelocityNaNRenderMode::Static}
const std::unordered_map<std::string, ColormapNanRenderMode> _colormapNanRenderModeFromString {
{ "Hide", ColormapNanRenderMode::Hide },
{ "FixedColor", ColormapNanRenderMode::FixedColor },
};
glm::vec4 readSingleColor(const std::vector<char>& message, size_t& offset) {
if (message[offset] != '[') {
throw SimpError(
tools::ErrorCode::Generic,
fmt::format("Expected to read '[', got {} in 'readColor'", message[offset])
);
}
++offset;
const std::unordered_map<std::string, VelocityNanRenderMode> _velocityNanRenderModeFromString {
{ "Hide", VelocityNanRenderMode::Hide },
{ "Static", VelocityNanRenderMode::Static },
};
float r = readFloatValue(message, offset);
float g = readFloatValue(message, offset);
float b = readFloatValue(message, offset);
float a = readFloatValue(message, offset);
// glm::vec4 readSingleColor(const std::vector<std::byte>& message, size_t& offset) {
// if (message[offset] != '[') {
// throw SimpError(
// tools::ErrorCode::Generic,
// fmt::format("Expected to read '[', got {} in 'readColor'", message[offset])
// );
// }
// ++offset;
if (message[offset] != ']') {
throw SimpError(
tools::ErrorCode::Generic,
fmt::format("Expected to read ']', got {} in 'readColor'", message[offset])
);
}
++offset;
// float r = readFloat32Value(message, offset);
// float g = readFloat32Value(message, offset);
// float b = readFloat32Value(message, offset);
// float a = readFloat32Value(message, offset);
return { r, g, b, a };
}
// if (message[offset] != ']') {
// throw SimpError(
// tools::ErrorCode::Generic,
// fmt::format("Expected to read ']', got {} in 'readColor'", message[offset])
// );
// }
// ++offset;
bool isEndOfCurrentValue(const std::vector<char>& message, size_t offset) {
// return { r, g, b, a };
// }
void checkOffset(const std::vector<std::byte>& message, size_t offset) {
if (offset >= message.size()) {
throw SimpError(
tools::ErrorCode::OffsetLargerThanMessageSize,
"Unexpectedly reached the end of the message..."
"Offset is larger than length of message..."
);
}
}
void checkOffset(const std::vector<std::byte>& message, std::vector<size_t>& offsets) {
for (size_t offset : offsets) {
checkOffset(message, offset);
}
}
int32_t readInt32Value(const std::vector<std::byte>& message, size_t& offset) {
std::vector<size_t> offsetsToCheck{ offset, offset + 3 };
checkOffset(message, offsetsToCheck);
int32_t value;
try {
// Read 4 bytes
std::memcpy(&value, message.data() + offset, 4);
}
catch(std::exception &err) {
throw SimpError(
fmt::format("Error when trying to parse an integer at offset {}", err.what())
);
}
if (message.size() > 0 && offset == message.size() - 1 && message[offset] != SEP) {
offset += 4;
return networkToHostEndian(value);
}
bool readBoolValue(const std::vector<std::byte>& message, size_t& offset) {
checkOffset(message, offset);
bool value;
try {
// Read 1 byte
std::memcpy(&value, message.data() + offset, 1);
}
catch(std::exception &err) {
throw SimpError(
fmt::format("Error when trying to parse a bool at offset {}", err.what())
);
}
offset += 1;
return value;
}
/**
* Assumption: float is 4 bytes
* Maybe add a check in beginning?
*/
float readFloat32Value(const std::vector<std::byte>& message, size_t& offset) {
std::vector<size_t> offsetsToCheck{ offset, offset + 3 };
checkOffset(message, offsetsToCheck);
float value;
try {
// Read 4 bytes
std::memcpy(&value, message.data() + offset, 4);
}
catch(std::exception &err) {
throw SimpError(
fmt::format("Error when trying to parse a float at offset = {}", err.what())
);
}
offset += 4;
return networkToHostEndian(value);
}
size_t findEndOfString(const std::vector<std::byte>& message, size_t offset) {
checkOffset(message, offset);
auto delimIt = std::find(message.begin() + offset, message.end(), DELIM_BYTES);
auto delimOffset = std::distance(message.begin(), delimIt);
while (message[delimOffset - 1] == std::byte{ '\\' }) {
delimIt = std::find(message.begin() + delimOffset + 1, message.end(), DELIM_BYTES);
delimOffset = std::distance(message.begin(), delimIt);
}
if (delimIt == message.end()) {
throw SimpError(
tools::ErrorCode::ReachedEndBeforeSeparator,
"Reached end of message before reading separator character..."
"Message reached end before delimiter character"
);
}
return offset != 0 && message[offset] == SEP && message[offset - 1] != '\\';
checkOffset(message, delimOffset); // Sanity check
return delimOffset;
}
std::string readString(const std::vector<std::byte>& message, size_t& offset) {
checkOffset(message, offset);
size_t offsetAtEndOfString = findEndOfString(message, offset);
std::vector<std::byte> stringByteBuffer{ message.begin() + offset, message.begin() + offsetAtEndOfString };
std::string value{ reinterpret_cast<const char *>(stringByteBuffer.data()), stringByteBuffer.size() };
offset = offsetAtEndOfString + 1; // +1 because we need to skip delimiter char in next value read
return value;
}
} // namespace
@@ -164,7 +283,7 @@ MessageType getMessageType(const std::string& type) {
return _messageTypeFromSIMPType.at(type);
}
std::string getSIMPType(const MessageType& type) {
std::string getStringFromMessageType(const MessageType& type) {
auto it = std::find_if(
_messageTypeFromSIMPType.begin(),
_messageTypeFromSIMPType.end(),
@@ -176,14 +295,37 @@ std::string getSIMPType(const MessageType& type) {
return it->first;
}
ColormapNaNRenderMode getColormapNaNRenderMode(const std::string& type) {
if (_colormapNaNRenderModeFromString.count(type) == 0) return ColormapNaNRenderMode::Unknown;
return _colormapNaNRenderModeFromString.at(type);
bool hasDataKey(const std::string& key) {
return _dataTypeFromString.count(key) > 0;
}
VelocityNaNRenderMode getVelocityNaNRenderMode(const std::string& type) {
if (_velocityNaNRenderModeFromString.count(type) == 0) return VelocityNaNRenderMode::Unknown;
return _velocityNaNRenderModeFromString.at(type);
DataKey getDataKey(const std::string& key) {
if (hasDataKey(key)) {
return _dataTypeFromString.at(key);
}
return DataKey::Unknown;
}
std::string getStringFromDataKey(const DataKey& key) {
auto it = std::find_if(
_dataTypeFromString.begin(),
_dataTypeFromString.end(),
[key](const std::pair<const std::string, DataKey>& p) {
return key == p.second;
}
);
if (it == _dataTypeFromString.end()) return "";
return it->first;
}
ColormapNanRenderMode getColormapNanRenderMode(const std::string& type) {
if (_colormapNanRenderModeFromString.count(type) == 0) return ColormapNanRenderMode::Unknown;
return _colormapNanRenderModeFromString.at(type);
}
VelocityNanRenderMode getVelocityNanRenderMode(const std::string& type) {
if (_velocityNanRenderModeFromString.count(type) == 0) return VelocityNanRenderMode::Unknown;
return _velocityNanRenderModeFromString.at(type);
}
std::string formatLengthOfSubject(size_t lengthOfSubject) {
@@ -193,181 +335,96 @@ std::string formatLengthOfSubject(size_t lengthOfSubject) {
return os.str();
}
std::string formatUpdateMessage(MessageType messageType,
std::string_view identifier,
std::string_view value)
{
std::string subject = fmt::format(
"{}{}{}{}", identifier, SEP, value, SEP
);
const std::string lengthOfSubject = formatLengthOfSubject(subject.length());
return fmt::format("{}{}{}{}", ProtocolVersion, getSIMPType(messageType), lengthOfSubject, subject);
}
std::string formatConnectionMessage(std::string_view software) {
std::string subject = fmt::format(
"{}{}", software, SEP
);
const std::string lengthOfSubject = formatLengthOfSubject(subject.length());
return fmt::format("{}{}{}{}", ProtocolVersion, getSIMPType(MessageType::Connection), lengthOfSubject, subject);
}
std::string formatColorMessage(std::string_view identifier, glm::vec4 color) {
std::ostringstream value_stream;
value_stream << "[" << floatToHex(color.r) << SEP << floatToHex(color.g) << SEP
<< floatToHex(color.b) << SEP << floatToHex(color.a) << SEP << "]";
return formatUpdateMessage(MessageType::Color, identifier, value_stream.str());
}
std::string formatPointDataCallbackMessage(std::string_view identifier) {
std::string subject = fmt::format(
"{}{}", identifier, SEP
);
const std::string lengthOfSubject = formatLengthOfSubject(subject.length());
return fmt::format("{}{}{}{}", ProtocolVersion, getSIMPType(MessageType::PointData), lengthOfSubject, subject);
}
std::string floatToHex(const float f) {
const uint32_t *int_representation = reinterpret_cast<const uint32_t *>(&f);
std::ostringstream stream;
stream << "0x" << std::setfill ('0') << std::setw(sizeof(float)*2) << std::hex << *int_representation;
return stream.str();
}
float hexToFloat(const std::string& f) {
uint32_t int_value = static_cast<uint32_t>(std::stoul(f, nullptr, 16));
std::ostringstream stream;
stream << std::dec << int_value;
return *reinterpret_cast<float*>(&int_value);
}
int readIntValue(const std::vector<char>& message, size_t& offset) {
std::string string_value;
int value;
bool isHex = false;
while (!isEndOfCurrentValue(message, offset)) {
char c = message[offset];
if (c == 'x' || c == 'X') isHex = true;
string_value.push_back(c);
offset++;
}
try {
value = std::stoi(string_value, nullptr, isHex ? 16 : 10);
}
catch(std::exception &err) {
throw SimpError(
tools::ErrorCode::Generic,
fmt::format("Error when trying to parse the integer {}: {}", string_value, err.what())
);
}
++offset;
return value;
}
float readFloatValue(const std::vector<char>& message, size_t& offset) {
std::string string_value;
float value;
while (!isEndOfCurrentValue(message, offset)) {
string_value.push_back(message[offset]);
offset++;
}
try {
value = hexToFloat(string_value);
}
catch(std::exception &err) {
throw SimpError(
tools::ErrorCode::Generic,
fmt::format("Error when trying to parse the float {}: {}", string_value, err.what())
);
}
++offset;
return value;
}
void readColormap(
const std::vector<char>& message, size_t& offset, size_t nColors, std::vector<float>& colorMap
) {
colorMap.reserve(nColors * 4);
while (message[offset] != SEP) {
glm::vec4 color = readSingleColor(message, offset);
// Colormap should be stored in a sequential vector
// of floats for syncing between nodes and when
// loaded to as a texture in the shader.
colorMap.push_back(color[0]);
colorMap.push_back(color[1]);
colorMap.push_back(color[2]);
colorMap.push_back(color[3]);
}
offset++;
}
glm::vec4 readColor(const std::vector<char>& message, size_t& offset) {
glm::vec4 color = readSingleColor(message, offset);
++offset;
return color;
}
std::string readString(const std::vector<char>& message, size_t& offset) {
std::string value;
while (!isEndOfCurrentValue(message, offset)) {
value.push_back(message[offset]);
++offset;
}
++offset;
return value;
}
void readPointData(
const std::vector<char>& message,
bool readColorChannel(
const std::vector<std::byte>& message,
size_t& offset,
size_t nPoints,
size_t dimensionality,
std::vector<float>& pointData
const DataKey& dataKey,
glm::vec4& color,
const glm::vec4::length_type& channel
) {
pointData.reserve(nPoints * dimensionality);
while (!isEndOfCurrentValue(message, offset)) {
if (message[offset] != '[') {
throw SimpError(
tools::ErrorCode::Generic,
fmt::format("Expected to read '[', got {} in 'readPointData'", message[offset])
);
}
++offset;
for (int i = 0; i < dimensionality; ++i) {
pointData.push_back(readFloatValue(message, offset));
}
if (message[offset] != ']') {
throw SimpError(
tools::ErrorCode::Generic,
fmt::format("Expected to read ']', got {} in 'readPointData'", message[offset])
);
}
++offset;
float newChannelValue;
try {
simp::readValue(message, offset, newChannelValue);
}
catch (const simp::SimpError& err) {
LERROR(fmt::format(
"Error when parsing float in {} message: {}",
simp::getStringFromDataKey(dataKey), err.message
));
return false;
}
color[channel] = newChannelValue;
++offset;
return true;
}
void readValue(const std::vector<std::byte>& message, size_t& offset, float& value) {
value = readFloat32Value(message, offset);
}
void readValue(const std::vector<std::byte>& message, size_t& offset, int32_t& value) {
value = readInt32Value(message, offset);
}
void readValue(const std::vector<std::byte>& message, size_t& offset, std::string& value) {
value = readString(message, offset);
}
void readValue(const std::vector<std::byte>& message, size_t& offset, bool& value) {
// Bool is only one byte in SIMP => no need to convert endianness
value = readBoolValue(message, offset);
}
void toByteBuffer(std::vector<std::byte>& byteBuffer, const size_t& offset, float value) {
byteBuffer.resize(byteBuffer.size() + 4);
auto valueInNetworkByteorder = hostToNetworkEndian(value);
std::memcpy(byteBuffer.data() + offset, &valueInNetworkByteorder, 4);
}
void toByteBuffer(std::vector<std::byte>& byteBuffer, size_t& offset, float value) {
toByteBuffer(byteBuffer, static_cast<const size_t&>(offset), value);
offset += 4;
}
void toByteBuffer(std::vector<std::byte>& byteBuffer, const size_t& offset, int32_t value) {
byteBuffer.resize(byteBuffer.size() + 4);
auto valueInNetworkByteorder = hostToNetworkEndian(value);
std::memcpy(byteBuffer.data() + offset, &valueInNetworkByteorder, 4);
}
void toByteBuffer(std::vector<std::byte>& byteBuffer, size_t& offset, int32_t value) {
toByteBuffer(byteBuffer, static_cast<const size_t&>(offset), value);
offset += 4;
}
void toByteBuffer(std::vector<std::byte>& byteBuffer, const size_t& offset, bool value) {
byteBuffer.resize(byteBuffer.size() + 1);
// Bool is only one byte in SIMP => no need to convert endianness (byteorder)
std::memcpy(byteBuffer.data() + offset, &value, 1);
}
void toByteBuffer(std::vector<std::byte>& byteBuffer, size_t& offset, bool value) {
toByteBuffer(byteBuffer, static_cast<const size_t&>(offset), value);
offset += 1;
}
void toByteBuffer(std::vector<std::byte>& byteBuffer, const size_t& offset, const std::string& value) {
byteBuffer.resize(byteBuffer.size() + (value.size() * sizeof(char)));
// Don't want to convert byteorder of strings, since we want to
std::memcpy(byteBuffer.data() + offset, value.data(), value.size() * sizeof(char));
}
void toByteBuffer(std::vector<std::byte>& byteBuffer, size_t& offset, const std::string& value) {
toByteBuffer(byteBuffer, static_cast<const size_t&>(offset), value);
offset += value.size() * sizeof(char);
}
void toByteBuffer(std::vector<std::byte>& byteBuffer, const size_t& offset, const std::vector<std::byte>& value) {
byteBuffer.resize(byteBuffer.size() + value.size());
std::memcpy(byteBuffer.data() + offset, value.data(), value.size());
}
void toByteBuffer(std::vector<std::byte>& byteBuffer, size_t& offset, const std::vector<std::byte>& value) {
toByteBuffer(byteBuffer, static_cast<const size_t&>(offset), value);
offset += value.size();
}
} // namespace simp
} // namespace openspace::softwareintegration
+93 -46
View File
@@ -50,32 +50,78 @@ bool hasStorageKey(const std::string& key);
namespace simp {
const std::string ProtocolVersion = "1.9";
namespace {
const char SEP = ';';
const uint8_t _version_major = 1;
const uint8_t _version_minor = 9;
const uint8_t _version_patch = 1;
enum class MessageType : uint32_t {
} // namespace
const std::string protocolVersion = fmt::format("{}.{}.{}", _version_major, _version_minor, _version_patch);
const char DELIM = ';';
const std::byte DELIM_BYTES{ DELIM };
enum class MessageType : uint8_t {
Connection = 0,
PointData,
VelocityData,
Data,
RemoveSceneGraphNode,
Color,
Colormap,
AttributeData,
Opacity,
Unknown
};
enum class DataKey : uint16_t {
// Point
X = 0,
Y,
Z,
PointUnit,
// Velocity
U,
V,
W,
VelocityDistanceUnit,
VelocityTimeUnit,
VelocityNanMode,
VelocityEnabled,
// Color
Red,
Green,
Blue,
Alpha,
ColormapEnabled,
// Colormap
ColormapReds,
ColormapGreens,
ColormapBlues,
ColormapAlphas,
ColormapMin,
ColormapMax,
ColormapNanR,
ColormapNanG,
ColormapNanB,
ColormapNanA,
ColormapNanMode,
ColormapAttributeData,
// Fixed size
FixedSize,
LinearSize,
LinearSizeEnabled,
// Linear size
LinearSizeMin,
LinearSizeMax,
LinearSizeAttributeData,
// Visibility
Visibility,
Unknown
};
enum class ColormapNaNRenderMode : uint8_t {
enum class ColormapNanRenderMode {
Hide = 0,
FixedColor,
Unknown
};
enum class VelocityNaNRenderMode : uint8_t {
enum class VelocityNanRenderMode {
Hide = 0,
Static,
Unknown
@@ -101,51 +147,52 @@ public:
MessageType getMessageType(const std::string& type);
std::string getSIMPType(const MessageType& type);
std::string getStringFromMessageType(const MessageType& type);
ColormapNaNRenderMode getColormapNaNRenderMode(const std::string& type);
DataKey getDataKey(const std::string& type);
VelocityNaNRenderMode getVelocityNaNRenderMode(const std::string& type);
std::string getStringFromDataKey(const DataKey& type);
ColormapNanRenderMode getColormapNanRenderMode(const std::string& type);
VelocityNanRenderMode getVelocityNanRenderMode(const std::string& type);
std::string formatLengthOfSubject(size_t lengthOfSubject);
std::string formatUpdateMessage(MessageType messageType, std::string_view identifier, std::string_view value);
std::string formatConnectionMessage(std::string_view value);
std::string formatColorMessage(std::string_view identifier, glm::vec4 color);
std::string formatPointDataCallbackMessage(std::string_view identifier);
std::string floatToHex(const float f);
float hexToFloat(const std::string& f);
float readFloatValue(const std::vector<char>& message, size_t& offset);
int readIntValue(const std::vector<char>& message, size_t& offset);
std::string readString(const std::vector<char>& message, size_t& offset);
glm::vec4 readColor(const std::vector<char>& message, size_t& offset);
void readPointData(
const std::vector<char>& message,
bool readColorChannel(
const std::vector<std::byte>& message,
size_t& offset,
size_t nPoints,
size_t dimensionality,
std::vector<float>& pointData
const DataKey& dataKey,
glm::vec4& color,
const glm::vec4::length_type& channel
);
void readColormap(
const std::vector<char>& message,
size_t& offset,
size_t nColors,
std::vector<float>& colorMap
);
template <typename T>
T networkToHostEndian(T value);
template <typename T>
T hostToNetworkEndian(T value);
void readValue(const std::vector<std::byte>& message, size_t& offset, float& value);
void readValue(const std::vector<std::byte>& message, size_t& offset, int32_t& value);
void readValue(const std::vector<std::byte>& message, size_t& offset, bool& value);
void readValue(const std::vector<std::byte>& message, size_t& offset, std::string& value);
void toByteBuffer(std::vector<std::byte>& byteBuffer, const size_t& offset, float value);
void toByteBuffer(std::vector<std::byte>& byteBuffer, size_t& offset, float value);
void toByteBuffer(std::vector<std::byte>& byteBuffer, const size_t& offset, int32_t value);
void toByteBuffer(std::vector<std::byte>& byteBuffer, size_t& offset, int32_t value);
void toByteBuffer(std::vector<std::byte>& byteBuffer, const size_t& offset, bool value);
void toByteBuffer(std::vector<std::byte>& byteBuffer, size_t& offset, bool value);
void toByteBuffer(std::vector<std::byte>& byteBuffer, const size_t& offset, const std::string& value);
void toByteBuffer(std::vector<std::byte>& byteBuffer, size_t& offset, const std::string& value);
void toByteBuffer(std::vector<std::byte>& byteBuffer, const size_t& offset, const std::vector<std::byte>& value);
void toByteBuffer(std::vector<std::byte>& byteBuffer, size_t& offset, const std::vector<std::byte>& value);
} // namespace simp
} // namespace openspace::softwareintegration
#include "utils.inl"
#endif // __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SIMP___H__
+77
View File
@@ -0,0 +1,77 @@
/*****************************************************************************************
* *
* 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 <climits>
namespace openspace::softwareintegration::simp {
namespace {
bool hostIsBigEndian() {
const int i = 1;
// For big endian the most significant byte
// is placed at the byte with the lowest address
// Example: 4 byte int with with value 1 is as bytes
// is 0001 for big endian and 1000 for little endian
return *(char*)&i == 0;
}
template <typename T>
T swapEndian(T value) {
union {
T u;
unsigned char u8[sizeof(T)];
} source, dest;
source.u = value;
for (size_t k = 0; k < sizeof(T); k++) {
dest.u8[k] = source.u8[sizeof(T) - k - 1];
}
return *reinterpret_cast<T*>(dest.u8);
}
} // namespace
template <typename T>
T networkToHostEndian(T value) {
static_assert (CHAR_BIT == 8, "CHAR_BIT != 8");
if (hostIsBigEndian()) return value;
return swapEndian(value);
}
template <typename T>
T hostToNetworkEndian(T value) {
static_assert (CHAR_BIT == 8, "CHAR_BIT != 8");
if (!hostIsBigEndian()) return value;
return swapEndian(value);
}
} // namespace openspace::softwareintegration::simp
+1 -1
View File
@@ -241,7 +241,7 @@ PrintEvents = false
ShutdownCountdown = 3
ScreenshotUseDate = true
BypassLauncher = true
BypassLauncher = false
-- OnScreenTextScaling = "framebuffer"
-- PerProfileCache = true