diff --git a/data/assets/examples/pointscloud.asset b/data/assets/examples/pointscloud.asset index 6780a2780d..c0397164b0 100644 --- a/data/assets/examples/pointscloud.asset +++ b/data/assets/examples/pointscloud.asset @@ -16,10 +16,10 @@ local RenderablePointsCloud = { Color = {1.0, 1.0, 0.5}, File = speck .. "/2MASS.speck", Opacity = 0.0, - Size = 0, + Size = 0 }, GUI = { - Name = "Points Cloud" + Name = "Points Cloud", Path = "/Examples" } } diff --git a/modules/softwareintegration/rendering/renderablepointscloud.cpp b/modules/softwareintegration/rendering/renderablepointscloud.cpp index f6209de6ac..8344acdd31 100644 --- a/modules/softwareintegration/rendering/renderablepointscloud.cpp +++ b/modules/softwareintegration/rendering/renderablepointscloud.cpp @@ -293,7 +293,7 @@ namespace openspace { return true; } if (!FileSys.fileExists(absPath(_speckFile))) { - LERROR(fmt::format("No path to speckFile found'", _speckFile)); + LERROR(fmt::format("No path to speckFile found {}", _speckFile)); return false; }; diff --git a/modules/softwareintegration/softwareintegrationmodule.cpp b/modules/softwareintegration/softwareintegrationmodule.cpp index b3c908d7fd..c5a40fceb7 100644 --- a/modules/softwareintegration/softwareintegrationmodule.cpp +++ b/modules/softwareintegration/softwareintegrationmodule.cpp @@ -26,32 +26,153 @@ #include #include +#include +#include #include #include #include #include #include +#include +#include +#include #include #include #include +#include +namespace { + constexpr const char* _loggerCat = "SoftwareIntegrationModule"; +} // namespace namespace openspace { - constexpr const char* _loggerCat = "SoftwareIntegrationModule"; + + const unsigned int SoftwareIntegrationModule::ProtocolVersion = 1; SoftwareIntegrationModule::SoftwareIntegrationModule() : OpenSpaceModule(Name) {} + Connection::Connection(std::unique_ptr socket) + : _socket(std::move(socket)) + {} + void SoftwareIntegrationModule::internalInitialize(const ghoul::Dictionary&) { auto fRenderable = FactoryManager::ref().factory(); ghoul_assert(fRenderable, "No renderable factory existed"); fRenderable->registerClass("RenderablePointsCloud"); + + start(4700); } void SoftwareIntegrationModule::internalDeinitializeGL() { } + // Connection + bool Connection::isConnectedOrConnecting() const { + return _socket->isConnected() || _socket->isConnecting(); + } + + // Connection + void Connection::disconnect() { + if (_socket) { + _socket->disconnect(); + } + } + + // Connection + ghoul::io::TcpSocket* Connection::socket() { + return _socket.get(); + } + + // Server + void SoftwareIntegrationModule::start(int port) + { + _socketServer.listen(port); + _socketServer.awaitPendingTcpSocket(); + //_serverThread = std::thread([this]() { handleNewPeers(); }); + } + + // Server + void SoftwareIntegrationModule::setDefaultHostAddress(std::string defaultHostAddress) { + std::lock_guard lock(_hostInfoMutex); + _defaultHostAddress = std::move(defaultHostAddress); + } + + // Server + std::string SoftwareIntegrationModule::defaultHostAddress() const { + std::lock_guard lock(_hostInfoMutex); + return _defaultHostAddress; + } + + // Server + void SoftwareIntegrationModule::stop() { + _shouldStop = true; + _socketServer.close(); + } + + // Server + void SoftwareIntegrationModule::handleNewPeers() { + while (!_shouldStop) { + std::unique_ptr socket = + _socketServer.awaitPendingTcpSocket(); + + socket->startStreams(); + + const size_t id = _nextConnectionId++; + std::shared_ptr p = std::make_shared(Peer{ + id, + "", + Connection(std::move(socket)), + Connection::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(size_t id) { + std::lock_guard lock(_peerListMutex); + auto it = _peers.find(id); + if (it == _peers.end()) { + return nullptr; + } + return it->second; + } + + // Server + bool SoftwareIntegrationModule::isConnected(const Peer& peer) const { + return peer.status != Connection::Status::Connecting && + peer.status != Connection::Status::Disconnected; + } + + // Server + void SoftwareIntegrationModule::disconnect(Peer& peer) { + if (isConnected(peer)) { + //nConnections() - 1; + } + + size_t hostPeerId = 0; + { + std::lock_guard lock(_hostInfoMutex); + hostPeerId = _hostPeerId; + } + + // Make sure any disconnecting host is first degraded to client, + // in order to notify other clients about host disconnection. + if (peer.id == hostPeerId) { + //setToClient(peer); + } + + peer.connection.disconnect(); + peer.thread.join(); + _peers.erase(peer.id); + } + std::vector SoftwareIntegrationModule::documentations() const { return { RenderablePointsCloud::Documentation(), diff --git a/modules/softwareintegration/softwareintegrationmodule.h b/modules/softwareintegration/softwareintegrationmodule.h index d6cffb0e55..f1662c502f 100644 --- a/modules/softwareintegration/softwareintegrationmodule.h +++ b/modules/softwareintegration/softwareintegrationmodule.h @@ -27,9 +27,39 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace openspace { +class Connection { +public: + // Connection + enum class Status : uint32_t { + Disconnected = 0, + Connecting + }; + + Connection(std::unique_ptr socket); + + // Connection + bool isConnectedOrConnecting() const; + void disconnect(); + ghoul::io::TcpSocket* socket(); + +private: + // Connection + std::unique_ptr _socket; + +}; + class SoftwareIntegrationModule : public OpenSpaceModule { public: constexpr static const char* Name = "SoftwareIntegration"; @@ -37,11 +67,48 @@ public: SoftwareIntegrationModule(); virtual ~SoftwareIntegrationModule() = default; + // Server + void start(int port); + void setDefaultHostAddress(std::string defaultHostAddress); + std::string defaultHostAddress() const; + void stop(); + //size_t nConnections() const; + std::vector documentations() const override; scripting::LuaLibrary luaLibrary() const override; - + static const unsigned int ProtocolVersion; private: + // Server + struct Peer { + size_t id; + std::string name; + Connection connection; + Connection::Status status; + std::thread thread; + }; + + // Server + bool isConnected(const Peer& peer) const; + void disconnect(Peer& peer); + //void setName(Peer& peer, std::string name); + //void assignHost(std::shared_ptr newHost); + //void handleDisconnection(std::shared_ptr peer); + void handleNewPeers(); + std::shared_ptr peer(size_t id); + //void handlePeer(size_t id); + std::unordered_map> _peers; + mutable std::mutex _peerListMutex; + std::thread _serverThread; + ghoul::io::TcpSocketServer _socketServer; + size_t _nextConnectionId = 1; + std::atomic_bool _shouldStop = false; + std::atomic_size_t _nConnections = 0; + std::atomic_size_t _hostPeerId = 0; + mutable std::mutex _hostInfoMutex; + std::string _hostName; + std::string _defaultHostAddress; + void internalInitialize(const ghoul::Dictionary&) override; void internalDeinitializeGL() override; };