mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-05-03 01:09:34 -05:00
Softwareintegrationmodule and Softwareconnection are two separte files.
This commit is contained in:
@@ -22,46 +22,22 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/softwareintegration/softwareintegrationmodule.h>
|
||||
#include <modules/softwareintegration/network/softwareconnection.h>
|
||||
|
||||
#include <modules/softwareintegration/rendering/renderablepointscloud.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <openspace/rendering/renderable.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <openspace/scripting/lualibrary.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <openspace/util/factorymanager.h>
|
||||
#include <openspace/query/query.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/io/socket/tcpsocket.h>
|
||||
#include <ghoul/io/socket/tcpsocketserver.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
#include <ghoul/misc/templatefactory.h>
|
||||
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "SoftwareIntegrationModule";
|
||||
constexpr const char* _loggerCat = "SoftwareConnection";
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
const unsigned int SoftwareConnection::ProtocolVersion = 1;
|
||||
|
||||
SoftwareIntegrationModule::SoftwareIntegrationModule() : OpenSpaceModule(Name) {}
|
||||
|
||||
SoftwareConnection::Message::Message(MessageType type, std::vector<char> content)
|
||||
: type(type)
|
||||
, content(std::move(content))
|
||||
@@ -75,37 +51,20 @@ namespace openspace {
|
||||
: _socket(std::move(socket))
|
||||
{}
|
||||
|
||||
void SoftwareIntegrationModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
auto fRenderable = FactoryManager::ref().factory<Renderable>();
|
||||
ghoul_assert(fRenderable, "No renderable factory existed");
|
||||
|
||||
fRenderable->registerClass<RenderablePointsCloud>("RenderablePointsCloud");
|
||||
|
||||
start(4700);
|
||||
}
|
||||
|
||||
void SoftwareIntegrationModule::internalDeinitializeGL() {
|
||||
|
||||
}
|
||||
|
||||
// Connection
|
||||
bool SoftwareConnection::isConnectedOrConnecting() const {
|
||||
return _socket->isConnected() || _socket->isConnecting();
|
||||
}
|
||||
|
||||
// Connection
|
||||
void SoftwareConnection::disconnect() {
|
||||
if (_socket) {
|
||||
_socket->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Connection
|
||||
ghoul::io::TcpSocket* SoftwareConnection::socket() {
|
||||
return _socket.get();
|
||||
}
|
||||
|
||||
// Connection
|
||||
SoftwareConnection::Message SoftwareConnection::receiveMessage() {
|
||||
// Header consists of version (1 char), message type (4 char) & message size (4 char)
|
||||
size_t HeaderSize = 9 * sizeof(char);
|
||||
@@ -174,7 +133,6 @@ namespace openspace {
|
||||
}
|
||||
}
|
||||
|
||||
//Connection
|
||||
bool SoftwareConnection::sendMessage(std::string message) {
|
||||
if (!_socket->put<char>(message.data(), message.size())) {
|
||||
return false;
|
||||
@@ -185,16 +143,13 @@ namespace openspace {
|
||||
|
||||
void SoftwareConnection::handleProperties(std::string identifier) {
|
||||
|
||||
/*std::string message;
|
||||
|
||||
const Renderable* myRenderable = renderable(identifier);
|
||||
properties::Property* colorProperty = myRenderable->property("Color");
|
||||
properties::Property* opacityProperty = myRenderable->property("Opacity");
|
||||
properties::Property* sizeProperty = myRenderable->property("Size");
|
||||
|
||||
// Update color of renderable
|
||||
//auto onChange =
|
||||
colorProperty->onChange([&colorProperty, identifier, &message]() {
|
||||
auto updateColor = [colorProperty, identifier]() {
|
||||
std::string lengthOfIdentifier = std::to_string(identifier.length());
|
||||
std::string propertyValue = colorProperty->getStringValue();
|
||||
std::string lengthOfValue = std::to_string(propertyValue.length());
|
||||
@@ -206,394 +161,57 @@ namespace openspace {
|
||||
os << std::setfill('0') << std::setw(4) << subject.length();
|
||||
std::string lengthOfSubject = os.str();
|
||||
|
||||
message = messageType + lengthOfSubject + subject;
|
||||
});
|
||||
|
||||
sendMessage(message);
|
||||
LERROR(fmt::format("Meddelandet som skickas {}", message));
|
||||
std::string message = messageType + lengthOfSubject + subject;
|
||||
SoftwareConnection send;
|
||||
send.sendMessage(message);
|
||||
LERROR(fmt::format("Meddelandet som skickas {}", message));
|
||||
};
|
||||
colorProperty->onChange(updateColor);
|
||||
|
||||
/*
|
||||
|
||||
// Update opacity of renderable
|
||||
opacityProperty->onChange([&]() {
|
||||
propertyValue = opacityProperty->getStringValue();
|
||||
lengthOfValue = std::to_string(propertyValue.length());
|
||||
messageType = "UPOP";
|
||||
subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue;
|
||||
auto updateOpacity = [opacityProperty, identifier]() {
|
||||
std::string lengthOfIdentifier = std::to_string(identifier.length());
|
||||
std::string propertyValue = opacityProperty->getStringValue();
|
||||
std::string lengthOfValue = std::to_string(propertyValue.length());
|
||||
std::string messageType = "UPOP";
|
||||
std::string subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue;
|
||||
|
||||
// Format length of subject to always be 4 digits
|
||||
std::ostringstream os;
|
||||
os << std::setfill('0') << std::setw(4) << subject.length();
|
||||
lengthOfSubject = os.str();
|
||||
std::string lengthOfSubject = os.str();
|
||||
|
||||
message = messageType + lengthOfSubject + subject;
|
||||
|
||||
sendMessage(message);
|
||||
std::string message = messageType + lengthOfSubject + subject;
|
||||
SoftwareConnection send;
|
||||
send.sendMessage(message);
|
||||
LERROR(fmt::format("Meddelandet som skickas {}", message));
|
||||
});
|
||||
};
|
||||
opacityProperty->onChange(updateOpacity);
|
||||
|
||||
// Update size of renderable
|
||||
sizeProperty->onChange([&]() {
|
||||
propertyValue = sizeProperty->getStringValue();
|
||||
lengthOfValue = std::to_string(propertyValue.length());
|
||||
messageType = "UPSI";
|
||||
subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue;
|
||||
auto updateSize = [sizeProperty, identifier]() {
|
||||
std::string lengthOfIdentifier = std::to_string(identifier.length());
|
||||
std::string propertyValue = sizeProperty->getStringValue();
|
||||
std::string lengthOfValue = std::to_string(propertyValue.length());
|
||||
std::string messageType = "UPSI";
|
||||
std::string subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue;
|
||||
|
||||
// Format length of subject to always be 4 digits
|
||||
std::ostringstream os;
|
||||
os << std::setfill('0') << std::setw(4) << subject.length();
|
||||
lengthOfSubject = os.str();
|
||||
std::string lengthOfSubject = os.str();
|
||||
|
||||
message = messageType + lengthOfSubject + subject;
|
||||
|
||||
sendMessage(message);
|
||||
std::string message = messageType + lengthOfSubject + subject;
|
||||
SoftwareConnection send;
|
||||
send.sendMessage(message);
|
||||
LERROR(fmt::format("Meddelandet som skickas {}", message));
|
||||
});*/
|
||||
}
|
||||
|
||||
// Server
|
||||
void SoftwareIntegrationModule::start(int port)
|
||||
{
|
||||
_socketServer.listen(port);
|
||||
|
||||
_serverThread = std::thread([this]() { handleNewPeers(); });
|
||||
_eventLoopThread = std::thread([this]() { eventLoop(); });
|
||||
}
|
||||
|
||||
// Server
|
||||
void SoftwareIntegrationModule::stop() {
|
||||
_shouldStop = true;
|
||||
_socketServer.close();
|
||||
}
|
||||
|
||||
// Server
|
||||
void SoftwareIntegrationModule::handleNewPeers() {
|
||||
while (!_shouldStop) {
|
||||
std::unique_ptr<ghoul::io::TcpSocket> socket =
|
||||
_socketServer.awaitPendingTcpSocket();
|
||||
|
||||
socket->startStreams();
|
||||
|
||||
const size_t id = _nextConnectionId++;
|
||||
std::shared_ptr<Peer> p = std::make_shared<Peer>(Peer{
|
||||
id,
|
||||
"",
|
||||
SoftwareConnection(std::move(socket)),
|
||||
SoftwareConnection::Status::Connecting,
|
||||
std::thread()
|
||||
});
|
||||
auto it = _peers.emplace(p->id, p);
|
||||
it.first->second->thread = std::thread([this, id]() {
|
||||
handlePeer(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Server
|
||||
std::shared_ptr<SoftwareIntegrationModule::Peer> SoftwareIntegrationModule::peer(size_t id) {
|
||||
std::lock_guard<std::mutex> lock(_peerListMutex);
|
||||
auto it = _peers.find(id);
|
||||
if (it == _peers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void SoftwareIntegrationModule::handlePeer(size_t id) {
|
||||
while (!_shouldStop) {
|
||||
std::shared_ptr<Peer> p = peer(id);
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!p->connection.isConnectedOrConnecting()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
SoftwareConnection::Message m = p->connection.receiveMessage();
|
||||
_incomingMessages.push({ id, m });
|
||||
}
|
||||
catch (const SoftwareConnection::SoftwareConnectionLostError&) {
|
||||
LERROR(fmt::format("Connection lost to {}", p->id));
|
||||
_incomingMessages.push({
|
||||
id,
|
||||
SoftwareConnection::Message(
|
||||
SoftwareConnection::MessageType::Disconnection, std::vector<char>()
|
||||
)
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareIntegrationModule::eventLoop() {
|
||||
while (!_shouldStop) {
|
||||
PeerMessage pm = _incomingMessages.pop();
|
||||
handlePeerMessage(std::move(pm));
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareIntegrationModule::handlePeerMessage(PeerMessage peerMessage) {
|
||||
const size_t peerId = peerMessage.peerId;
|
||||
auto it = _peers.find(peerId);
|
||||
if (it == _peers.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<Peer>& peer = it->second;
|
||||
|
||||
const SoftwareConnection::MessageType messageType = peerMessage.message.type;
|
||||
std::vector<char>& message = peerMessage.message.content;
|
||||
switch (messageType) {
|
||||
case SoftwareConnection::MessageType::Connection: {
|
||||
std::string software(message.begin(), message.end());
|
||||
LINFO(fmt::format("OpenSpace has connected with {} through socket.", software));
|
||||
break;
|
||||
}
|
||||
case SoftwareConnection::MessageType::AddSceneGraphNode: {
|
||||
std::string identifier = readIdentifier(message);
|
||||
glm::vec3 color = readColor(message);
|
||||
std::string file = readString(message);
|
||||
float opacity = readFloatValue(message);
|
||||
float size = readFloatValue(message);
|
||||
std::string guiName = readString(message);
|
||||
|
||||
ghoul::Dictionary renderable = {
|
||||
{ "Type", "RenderablePointsCloud"s },
|
||||
{ "Color", static_cast<glm::dvec3>(color)},
|
||||
{ "File", file },
|
||||
{ "Opacity", static_cast<double>(opacity) },
|
||||
{ "Size", static_cast<double>(size)}
|
||||
};
|
||||
|
||||
ghoul::Dictionary gui = {
|
||||
{ "Name", guiName },
|
||||
{ "Path", "/Examples"s }
|
||||
};
|
||||
|
||||
ghoul::Dictionary node = {
|
||||
{ "Identifier", identifier },
|
||||
{ "Renderable", renderable },
|
||||
{ "GUI", gui }
|
||||
};
|
||||
|
||||
try {
|
||||
SceneGraphNode* sgn = global::renderEngine.scene()->loadNode(node);
|
||||
if (!sgn) {
|
||||
LERROR("Scene", "Could not load scene graph node");
|
||||
}
|
||||
global::renderEngine.scene()->initializeNode(sgn);
|
||||
}
|
||||
catch (const documentation::SpecificationError& e) {
|
||||
return LERROR(fmt::format("Documentation SpecificationError: Error loading scene graph node {}",
|
||||
e.what())
|
||||
);
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
return LERROR(fmt::format("RuntimeError: Error loading scene graph node {}",
|
||||
e.what())
|
||||
);
|
||||
}
|
||||
|
||||
//SoftwareConnection prop;
|
||||
//prop.handleProperties(identifier);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
case SoftwareConnection::MessageType::RemoveSceneGraphNode: {
|
||||
std::string identifier(message.begin(), message.end());
|
||||
LERROR(fmt::format("Identifier: {}", identifier));
|
||||
|
||||
openspace::global::scriptEngine.queueScript(
|
||||
"openspace.removeSceneGraphNode('" + identifier + "');",
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
case SoftwareConnection::MessageType::Color: {
|
||||
std::string identifier = readIdentifier(message);
|
||||
glm::vec3 color = readColor(message);
|
||||
|
||||
// Update color of renderable
|
||||
const Renderable* myrenderable = renderable(identifier);
|
||||
properties::Property* colorProperty = myrenderable->property("Color");
|
||||
colorProperty->set(color);
|
||||
break;
|
||||
}
|
||||
case SoftwareConnection::MessageType::Opacity: {
|
||||
std::string identifier = readIdentifier(message);
|
||||
float opacity = readFloatValue(message);
|
||||
|
||||
// Update opacity of renderable
|
||||
const Renderable* myrenderable = renderable(identifier);
|
||||
properties::Property* opacityProperty = myrenderable->property("Opacity");
|
||||
opacityProperty->set(opacity);
|
||||
break;
|
||||
}
|
||||
case SoftwareConnection::MessageType::Size: {
|
||||
std::string identifier = readIdentifier(message);
|
||||
float size = readFloatValue(message);
|
||||
|
||||
// Update size of renderable
|
||||
const Renderable* myrenderable = renderable(identifier);
|
||||
properties::Property* sizeProperty = myrenderable->property("Size");
|
||||
sizeProperty->set(size);
|
||||
break;
|
||||
}
|
||||
case SoftwareConnection::MessageType::Disconnection: {
|
||||
disconnect(*peer);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LERROR(fmt::format(
|
||||
"Unsupported message type: {}", static_cast<int>(messageType)
|
||||
));
|
||||
break;
|
||||
}
|
||||
sizeProperty->onChange(updateSize);
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
std::string SoftwareIntegrationModule::readIdentifier(std::vector<char>& message) {
|
||||
|
||||
std::string length;
|
||||
length.push_back(message[0]);
|
||||
length.push_back(message[1]);
|
||||
|
||||
int lengthOfIdentifier = stoi(length);
|
||||
int counter = 0;
|
||||
messageOffset = 2;
|
||||
|
||||
std::string identifier;
|
||||
while (counter != lengthOfIdentifier)
|
||||
{
|
||||
identifier.push_back(message[messageOffset]);
|
||||
messageOffset++;
|
||||
counter++;
|
||||
}
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
// Read size value or opacity value
|
||||
float SoftwareIntegrationModule::readFloatValue(std::vector<char>& message) {
|
||||
|
||||
std::string length;
|
||||
length.push_back(message[messageOffset]);
|
||||
messageOffset += 1;
|
||||
|
||||
int lengthOfValue = stoi(length);
|
||||
std::string value;
|
||||
int counter = 0;
|
||||
while (counter != lengthOfValue)
|
||||
{
|
||||
value.push_back(message[messageOffset]);
|
||||
messageOffset++;
|
||||
counter++;
|
||||
}
|
||||
float floatValue = std::stof(value);
|
||||
|
||||
return floatValue;
|
||||
}
|
||||
|
||||
glm::vec3 SoftwareIntegrationModule::readColor(std::vector<char>& message) {
|
||||
|
||||
std::string lengthOfColor; // Not used for now, but sent in message
|
||||
lengthOfColor.push_back(message[messageOffset]);
|
||||
lengthOfColor.push_back(message[messageOffset + 1]);
|
||||
messageOffset += 2;
|
||||
|
||||
// Red
|
||||
std::string red;
|
||||
while (message[messageOffset] != ',')
|
||||
{
|
||||
if (message[messageOffset] == '(')
|
||||
messageOffset++;
|
||||
else {
|
||||
red.push_back(message[messageOffset]);
|
||||
messageOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
// Green
|
||||
std::string green;
|
||||
messageOffset++;
|
||||
while (message[messageOffset] != ',')
|
||||
{
|
||||
green.push_back(message[messageOffset]);
|
||||
messageOffset++;
|
||||
}
|
||||
|
||||
// Blue
|
||||
std::string blue;
|
||||
messageOffset++;
|
||||
while (message[messageOffset] != ')')
|
||||
{
|
||||
blue.push_back(message[messageOffset]);
|
||||
messageOffset++;
|
||||
}
|
||||
messageOffset++;
|
||||
|
||||
// Convert rgb string to floats
|
||||
float r = std::stof(red);
|
||||
float g = std::stof(green);
|
||||
float b = std::stof(blue);
|
||||
|
||||
glm::vec3 color(r, g, b);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
// Read File path or GUI Name
|
||||
std::string SoftwareIntegrationModule::readString(std::vector<char>& message) {
|
||||
|
||||
std::string length;
|
||||
length.push_back(message[messageOffset]);
|
||||
length.push_back(message[messageOffset + 1]);
|
||||
messageOffset += 2;
|
||||
|
||||
int lengthOfString = stoi(length);
|
||||
std::string name;
|
||||
int counter = 0;
|
||||
while (counter != lengthOfString)
|
||||
{
|
||||
name.push_back(message[messageOffset]);
|
||||
messageOffset++;
|
||||
counter++;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
// Server
|
||||
bool SoftwareIntegrationModule::isConnected(const Peer& peer) const {
|
||||
return peer.status != SoftwareConnection::Status::Connecting &&
|
||||
peer.status != SoftwareConnection::Status::Disconnected;
|
||||
}
|
||||
|
||||
// Server
|
||||
void SoftwareIntegrationModule::disconnect(Peer& peer) {
|
||||
if (isConnected(peer)) {
|
||||
_nConnections = nConnections() - 1;
|
||||
}
|
||||
|
||||
peer.connection.disconnect();
|
||||
peer.thread.join();
|
||||
_peers.erase(peer.id);
|
||||
}
|
||||
|
||||
size_t SoftwareIntegrationModule::nConnections() const {
|
||||
return _nConnections;
|
||||
}
|
||||
|
||||
std::vector<documentation::Documentation> SoftwareIntegrationModule::documentations() const {
|
||||
return {
|
||||
RenderablePointsCloud::Documentation(),
|
||||
};
|
||||
}
|
||||
|
||||
scripting::LuaLibrary SoftwareIntegrationModule::luaLibrary() const {
|
||||
scripting::LuaLibrary res;
|
||||
res.name = "softwareintegration";
|
||||
res.scripts = {
|
||||
absPath("${MODULE_SOFTWAREINTEGRATION}/scripts/network.lua")
|
||||
};
|
||||
return res;
|
||||
}
|
||||
} // namespace openspace
|
||||
|
||||
|
||||
@@ -25,24 +25,14 @@
|
||||
#ifndef __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SOFTWAREINTEGRATIONMODULE___H__
|
||||
#define __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SOFTWAREINTEGRATIONMODULE___H__
|
||||
|
||||
#include <openspace/util/openspacemodule.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/network/messagestructures.h>
|
||||
#include <openspace/util/concurrentqueue.h>
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/io/socket/tcpsocket.h>
|
||||
#include <ghoul/io/socket/tcpsocketserver.h>
|
||||
#include <ghoul/misc/exception.h>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class SoftwareConnection {
|
||||
public:
|
||||
// Connection
|
||||
enum class Status : uint32_t {
|
||||
Disconnected = 0,
|
||||
Connecting
|
||||
@@ -74,12 +64,10 @@ public:
|
||||
SoftwareConnection() = default;
|
||||
SoftwareConnection(std::unique_ptr<ghoul::io::TcpSocket> socket);
|
||||
|
||||
// Connection
|
||||
bool isConnectedOrConnecting() const;
|
||||
void disconnect();
|
||||
ghoul::io::TcpSocket* socket();
|
||||
bool sendMessage(std::string message);
|
||||
//bool sendMessage(const SoftwareConnection::Message& message);
|
||||
|
||||
void handleProperties(std::string identifier);
|
||||
|
||||
@@ -88,68 +76,7 @@ public:
|
||||
static const unsigned int ProtocolVersion;
|
||||
|
||||
private:
|
||||
// Connection
|
||||
std::unique_ptr<ghoul::io::TcpSocket> _socket;
|
||||
|
||||
};
|
||||
|
||||
class SoftwareIntegrationModule : public OpenSpaceModule {
|
||||
public:
|
||||
constexpr static const char* Name = "SoftwareIntegration";
|
||||
|
||||
SoftwareIntegrationModule();
|
||||
virtual ~SoftwareIntegrationModule() = default;
|
||||
|
||||
// Server
|
||||
void start(int port);
|
||||
void stop();
|
||||
size_t nConnections() const;
|
||||
|
||||
size_t messageOffset = 0;
|
||||
|
||||
std::vector<documentation::Documentation> documentations() const override;
|
||||
scripting::LuaLibrary luaLibrary() const override;
|
||||
|
||||
private:
|
||||
// Server
|
||||
struct Peer {
|
||||
size_t id;
|
||||
std::string name;
|
||||
SoftwareConnection connection;
|
||||
SoftwareConnection::Status status;
|
||||
std::thread thread;
|
||||
};
|
||||
|
||||
struct PeerMessage {
|
||||
size_t peerId;
|
||||
SoftwareConnection::Message message;
|
||||
};
|
||||
|
||||
// Server
|
||||
bool isConnected(const Peer& peer) const;
|
||||
void disconnect(Peer& peer);
|
||||
void handleNewPeers();
|
||||
void eventLoop();
|
||||
std::shared_ptr<Peer> peer(size_t id);
|
||||
void handlePeer(size_t id);
|
||||
void handlePeerMessage(PeerMessage peerMessage);
|
||||
std::unordered_map<size_t, std::shared_ptr<Peer>> _peers;
|
||||
mutable std::mutex _peerListMutex;
|
||||
std::thread _serverThread;
|
||||
std::thread _eventLoopThread;
|
||||
ghoul::io::TcpSocketServer _socketServer;
|
||||
size_t _nextConnectionId = 1;
|
||||
std::atomic_bool _shouldStop = false;
|
||||
std::atomic_size_t _nConnections = 0;
|
||||
ConcurrentQueue<PeerMessage> _incomingMessages;
|
||||
|
||||
std::string readIdentifier(std::vector<char>& message);
|
||||
float readFloatValue(std::vector<char>& message);
|
||||
glm::vec3 readColor(std::vector<char>& message);
|
||||
std::string readString(std::vector<char>& message);
|
||||
|
||||
void internalInitialize(const ghoul::Dictionary&) override;
|
||||
void internalDeinitializeGL() override;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -58,23 +58,8 @@ namespace {
|
||||
|
||||
namespace openspace {
|
||||
|
||||
const unsigned int SoftwareConnection::ProtocolVersion = 1;
|
||||
|
||||
SoftwareIntegrationModule::SoftwareIntegrationModule() : OpenSpaceModule(Name) {}
|
||||
|
||||
SoftwareConnection::Message::Message(MessageType type, std::vector<char> content)
|
||||
: type(type)
|
||||
, content(std::move(content))
|
||||
{}
|
||||
|
||||
SoftwareConnection::SoftwareConnectionLostError::SoftwareConnectionLostError()
|
||||
: ghoul::RuntimeError("Connection lost", "Connection")
|
||||
{}
|
||||
|
||||
SoftwareConnection::SoftwareConnection(std::unique_ptr<ghoul::io::TcpSocket> socket)
|
||||
: _socket(std::move(socket))
|
||||
{}
|
||||
|
||||
void SoftwareIntegrationModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
auto fRenderable = FactoryManager::ref().factory<Renderable>();
|
||||
ghoul_assert(fRenderable, "No renderable factory existed");
|
||||
@@ -88,165 +73,6 @@ namespace openspace {
|
||||
|
||||
}
|
||||
|
||||
// Connection
|
||||
bool SoftwareConnection::isConnectedOrConnecting() const {
|
||||
return _socket->isConnected() || _socket->isConnecting();
|
||||
}
|
||||
|
||||
// Connection
|
||||
void SoftwareConnection::disconnect() {
|
||||
if (_socket) {
|
||||
_socket->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Connection
|
||||
ghoul::io::TcpSocket* SoftwareConnection::socket() {
|
||||
return _socket.get();
|
||||
}
|
||||
|
||||
// Connection
|
||||
SoftwareConnection::Message SoftwareConnection::receiveMessage() {
|
||||
// Header consists of version (1 char), message type (4 char) & message size (4 char)
|
||||
size_t HeaderSize = 9 * sizeof(char);
|
||||
|
||||
// Create basic buffer for receiving first part of message
|
||||
std::vector<char> headerBuffer(HeaderSize);
|
||||
std::vector<char> messageBuffer;
|
||||
|
||||
// Receive the header data
|
||||
if (!_socket->get(headerBuffer.data(), HeaderSize)) {
|
||||
LERROR("Failed to read header from socket. Disconnecting.");
|
||||
throw SoftwareConnectionLostError();
|
||||
}
|
||||
|
||||
// Read and convert version number: Byte 0
|
||||
std::string version;
|
||||
version.push_back(headerBuffer[0]);
|
||||
const uint32_t protocolVersionIn = std::stoi(version);
|
||||
|
||||
// Make sure that header matches the protocol version
|
||||
if (!(protocolVersionIn == ProtocolVersion)) {
|
||||
LERROR(fmt::format(
|
||||
"Protocol versions do not match. Remote version: {}, Local version: {}",
|
||||
protocolVersionIn,
|
||||
ProtocolVersion
|
||||
));
|
||||
throw SoftwareConnectionLostError();
|
||||
}
|
||||
|
||||
// Read message type: Byte 1-4
|
||||
std::string type;
|
||||
for(int i = 1; i < 5; i++)
|
||||
type.push_back(headerBuffer[i]);
|
||||
|
||||
// Read and convert message size: Byte 5-8
|
||||
std::string messageSizeIn;
|
||||
for (int i = 5; i < 9; i++)
|
||||
messageSizeIn.push_back(headerBuffer[i]);
|
||||
const size_t messageSize = stoi(messageSizeIn);
|
||||
|
||||
// Receive the message data
|
||||
messageBuffer.resize(messageSize);
|
||||
if (!_socket->get(messageBuffer.data(), messageSize)) {
|
||||
LERROR("Failed to read message from socket. Disconnecting.");
|
||||
throw SoftwareConnectionLostError();
|
||||
}
|
||||
|
||||
// And delegate decoding depending on message type
|
||||
if (type == "CONN")
|
||||
return Message(MessageType::Connection, messageBuffer);
|
||||
else if( type == "ASGN")
|
||||
return Message(MessageType::AddSceneGraphNode, messageBuffer);
|
||||
else if (type == "RSGN")
|
||||
return Message(MessageType::RemoveSceneGraphNode, messageBuffer);
|
||||
else if (type == "UPCO")
|
||||
return Message(MessageType::Color, messageBuffer);
|
||||
else if (type == "UPOP")
|
||||
return Message(MessageType::Opacity, messageBuffer);
|
||||
else if( type == "UPSI")
|
||||
return Message(MessageType::Size, messageBuffer);
|
||||
else if (type == "DISC")
|
||||
return Message(MessageType::Disconnection, messageBuffer);
|
||||
else {
|
||||
LERROR(fmt::format("Unsupported message type: {}. Disconnecting...", type));
|
||||
return Message(MessageType::Disconnection, messageBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
//Connection
|
||||
bool SoftwareConnection::sendMessage(std::string message) {
|
||||
if (!_socket->put<char>(message.data(), message.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoftwareConnection::handleProperties(std::string identifier) {
|
||||
|
||||
/*std::string message;
|
||||
|
||||
const Renderable* myRenderable = renderable(identifier);
|
||||
properties::Property* colorProperty = myRenderable->property("Color");
|
||||
properties::Property* opacityProperty = myRenderable->property("Opacity");
|
||||
properties::Property* sizeProperty = myRenderable->property("Size");
|
||||
|
||||
// Update color of renderable
|
||||
//auto onChange =
|
||||
colorProperty->onChange([&colorProperty, identifier, &message]() {
|
||||
std::string lengthOfIdentifier = std::to_string(identifier.length());
|
||||
std::string propertyValue = colorProperty->getStringValue();
|
||||
std::string lengthOfValue = std::to_string(propertyValue.length());
|
||||
std::string messageType = "UPCO";
|
||||
std::string subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue;
|
||||
|
||||
// Format length of subject to always be 4 digits
|
||||
std::ostringstream os;
|
||||
os << std::setfill('0') << std::setw(4) << subject.length();
|
||||
std::string lengthOfSubject = os.str();
|
||||
|
||||
message = messageType + lengthOfSubject + subject;
|
||||
});
|
||||
|
||||
sendMessage(message);
|
||||
LERROR(fmt::format("Meddelandet som skickas {}", message));
|
||||
// Update opacity of renderable
|
||||
opacityProperty->onChange([&]() {
|
||||
propertyValue = opacityProperty->getStringValue();
|
||||
lengthOfValue = std::to_string(propertyValue.length());
|
||||
messageType = "UPOP";
|
||||
subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue;
|
||||
|
||||
std::ostringstream os;
|
||||
os << std::setfill('0') << std::setw(4) << subject.length();
|
||||
lengthOfSubject = os.str();
|
||||
|
||||
message = messageType + lengthOfSubject + subject;
|
||||
|
||||
sendMessage(message);
|
||||
LERROR(fmt::format("Meddelandet som skickas {}", message));
|
||||
});
|
||||
|
||||
// Update size of renderable
|
||||
sizeProperty->onChange([&]() {
|
||||
propertyValue = sizeProperty->getStringValue();
|
||||
lengthOfValue = std::to_string(propertyValue.length());
|
||||
messageType = "UPSI";
|
||||
subject = lengthOfIdentifier + identifier + lengthOfValue + propertyValue;
|
||||
|
||||
std::ostringstream os;
|
||||
os << std::setfill('0') << std::setw(4) << subject.length();
|
||||
lengthOfSubject = os.str();
|
||||
|
||||
message = messageType + lengthOfSubject + subject;
|
||||
|
||||
sendMessage(message);
|
||||
LERROR(fmt::format("Meddelandet som skickas {}", message));
|
||||
});*/
|
||||
}
|
||||
|
||||
// Server
|
||||
void SoftwareIntegrationModule::start(int port)
|
||||
{
|
||||
_socketServer.listen(port);
|
||||
@@ -255,13 +81,11 @@ namespace openspace {
|
||||
_eventLoopThread = std::thread([this]() { eventLoop(); });
|
||||
}
|
||||
|
||||
// Server
|
||||
void SoftwareIntegrationModule::stop() {
|
||||
_shouldStop = true;
|
||||
_socketServer.close();
|
||||
}
|
||||
|
||||
// Server
|
||||
void SoftwareIntegrationModule::handleNewPeers() {
|
||||
while (!_shouldStop) {
|
||||
std::unique_ptr<ghoul::io::TcpSocket> socket =
|
||||
@@ -284,7 +108,6 @@ namespace openspace {
|
||||
}
|
||||
}
|
||||
|
||||
// Server
|
||||
std::shared_ptr<SoftwareIntegrationModule::Peer> SoftwareIntegrationModule::peer(size_t id) {
|
||||
std::lock_guard<std::mutex> lock(_peerListMutex);
|
||||
auto it = _peers.find(id);
|
||||
@@ -390,9 +213,8 @@ namespace openspace {
|
||||
);
|
||||
}
|
||||
|
||||
//SoftwareConnection prop;
|
||||
//prop.handleProperties(identifier);
|
||||
|
||||
SoftwareConnection callback;
|
||||
callback.handleProperties(identifier);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -560,13 +382,11 @@ namespace openspace {
|
||||
return name;
|
||||
}
|
||||
|
||||
// Server
|
||||
bool SoftwareIntegrationModule::isConnected(const Peer& peer) const {
|
||||
return peer.status != SoftwareConnection::Status::Connecting &&
|
||||
peer.status != SoftwareConnection::Status::Disconnected;
|
||||
}
|
||||
|
||||
// Server
|
||||
void SoftwareIntegrationModule::disconnect(Peer& peer) {
|
||||
if (isConnected(peer)) {
|
||||
_nConnections = nConnections() - 1;
|
||||
|
||||
@@ -25,74 +25,19 @@
|
||||
#ifndef __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SOFTWAREINTEGRATIONMODULE___H__
|
||||
#define __OPENSPACE_MODULE_SOFTWAREINTEGRATION___SOFTWAREINTEGRATIONMODULE___H__
|
||||
|
||||
#include <modules/softwareintegration/network/softwareconnection.h>
|
||||
|
||||
#include <openspace/util/openspacemodule.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/network/messagestructures.h>
|
||||
#include <openspace/util/concurrentqueue.h>
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/io/socket/tcpsocket.h>
|
||||
#include <ghoul/io/socket/tcpsocketserver.h>
|
||||
#include <ghoul/misc/exception.h>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class SoftwareConnection {
|
||||
public:
|
||||
// Connection
|
||||
enum class Status : uint32_t {
|
||||
Disconnected = 0,
|
||||
Connecting
|
||||
};
|
||||
|
||||
enum class MessageType : uint32_t {
|
||||
Connection = 0,
|
||||
AddSceneGraphNode,
|
||||
RemoveSceneGraphNode,
|
||||
Color,
|
||||
Opacity,
|
||||
Size,
|
||||
Disconnection
|
||||
};
|
||||
|
||||
struct Message {
|
||||
Message() = default;
|
||||
Message(MessageType type, std::vector<char> content);
|
||||
|
||||
MessageType type;
|
||||
std::vector<char> content;
|
||||
};
|
||||
|
||||
class SoftwareConnectionLostError : public ghoul::RuntimeError {
|
||||
public:
|
||||
explicit SoftwareConnectionLostError();
|
||||
};
|
||||
|
||||
SoftwareConnection() = default;
|
||||
SoftwareConnection(std::unique_ptr<ghoul::io::TcpSocket> socket);
|
||||
|
||||
// Connection
|
||||
bool isConnectedOrConnecting() const;
|
||||
void disconnect();
|
||||
ghoul::io::TcpSocket* socket();
|
||||
bool sendMessage(std::string message);
|
||||
//bool sendMessage(const SoftwareConnection::Message& message);
|
||||
|
||||
void handleProperties(std::string identifier);
|
||||
|
||||
SoftwareConnection::Message receiveMessage();
|
||||
|
||||
static const unsigned int ProtocolVersion;
|
||||
|
||||
private:
|
||||
// Connection
|
||||
std::unique_ptr<ghoul::io::TcpSocket> _socket;
|
||||
|
||||
};
|
||||
|
||||
class SoftwareIntegrationModule : public OpenSpaceModule {
|
||||
public:
|
||||
constexpr static const char* Name = "SoftwareIntegration";
|
||||
@@ -100,7 +45,6 @@ public:
|
||||
SoftwareIntegrationModule();
|
||||
virtual ~SoftwareIntegrationModule() = default;
|
||||
|
||||
// Server
|
||||
void start(int port);
|
||||
void stop();
|
||||
size_t nConnections() const;
|
||||
@@ -111,7 +55,6 @@ public:
|
||||
scripting::LuaLibrary luaLibrary() const override;
|
||||
|
||||
private:
|
||||
// Server
|
||||
struct Peer {
|
||||
size_t id;
|
||||
std::string name;
|
||||
@@ -125,7 +68,6 @@ private:
|
||||
SoftwareConnection::Message message;
|
||||
};
|
||||
|
||||
// Server
|
||||
bool isConnected(const Peer& peer) const;
|
||||
void disconnect(Peer& peer);
|
||||
void handleNewPeers();
|
||||
|
||||
Reference in New Issue
Block a user