/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2021 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include #include namespace { constexpr const char* _loggerCat = "SoftwareConnection"; } // namespace namespace openspace { const std::string SoftwareConnection::ProtocolVersion = "1.0"; std::map SoftwareConnection::mapSIMPTypeToMessageType { {"CONN", MessageType::Connection}, {"PDAT", MessageType::ReadPointData}, {"RSGN", MessageType::RemoveSceneGraphNode}, {"UPCO", MessageType::Color}, {"UPOP", MessageType::Opacity}, {"UPSI", MessageType::Size}, {"TOVI", MessageType::Visibility}, {"DISC", MessageType::Disconnection}, }; SoftwareConnection::Message::Message(SoftwareConnection::MessageType type, std::vector content) : type{ type }, content{ std::move(content) } {} SoftwareConnection::SoftwareConnectionLostError::SoftwareConnectionLostError() : ghoul::RuntimeError("Software connection lost", "SoftwareConnection") {} SoftwareConnection::SoftwareConnection(std::unique_ptr socket) : _socket(std::move(socket)) {} bool SoftwareConnection::isConnected() const { return _socket->isConnected(); } bool SoftwareConnection::isConnectedOrConnecting() const { return _socket->isConnected() || _socket->isConnecting(); } bool SoftwareConnection::sendMessage(std::string message) { LDEBUG(fmt::format("In SoftwareConnection::sendMessage()", 0)); if (_isListening) { if (!_socket->put(message.data(), message.size())) { return false; } LDEBUG(fmt::format("Message sent: {}", message)); } else { _isListening = true; return false; } return true; } void SoftwareConnection::disconnect() { if (_socket) { _socket->disconnect(); } } ghoul::io::TcpSocket* SoftwareConnection::socket() { return _socket.get(); } /** * @brief This function is only called on the server node, i.e. the node connected to the external software * * @return SoftwareConnection::Message */ SoftwareConnection::Message SoftwareConnection::receiveMessageFromSoftware() { // LDEBUG(fmt::format("t{}: receiveMessageFromSoftware()", std::this_thread::get_id())); // Header consists of version (3 char), message type (4 char) & subject size (9 char) size_t headerSize = 16 * sizeof(char); // Create basic buffer for receiving first part of message std::vector headerBuffer(headerSize); std::vector subjectBuffer; // TODO: Thread stops here and wait LDEBUG(fmt::format("t{}: Trying to receive header", std::this_thread::get_id())); // Receive the header data // bool headerSuccess = _socket->get(headerBuffer.data(), headerSize); // LDEBUG(fmt::format("t{}: Right after '_socket->get(headerBuffer.data(), headerSize)'", std::this_thread::get_id())); // LDEBUG(fmt::format("t{}: receiveMessageFromSoftware() - headerSuccess: {}", std::this_thread::get_id(), headerSuccess)); if (!_socket->get(headerBuffer.data(), headerSize)) { LERROR(fmt::format("t{}: Failed to read header from socket. Disconnecting.", std::this_thread::get_id())); throw SoftwareConnectionLostError(); } // TODO: REMOVE ==== std::string headerBufferStr; for (int i = 0; i < 16; i++) { headerBufferStr.push_back(headerBuffer[i]); } LDEBUG(fmt::format("t{}: headerBufferStr: {}", std::this_thread::get_id(), headerBufferStr)); // ========= // Read and convert version number: Byte 0-2 std::string version; for (int i = 0; i < 3; i++) { version.push_back(headerBuffer[i]); } const std::string protocolVersionIn = 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 3-6 std::string type; for (int i = 3; i < 7; i++) { type.push_back(headerBuffer[i]); } // Read and convert message size: Byte 7-15 std::string subjectSizeIn; for (int i = 7; i < 16; i++) { subjectSizeIn.push_back(headerBuffer[i]); } const size_t subjectSize = std::stoi(subjectSizeIn); // Receive the message data if (mapSIMPTypeToMessageType[type] != MessageType::Disconnection) { subjectBuffer.resize(subjectSize); bool subjectSuccess = _socket->get(subjectBuffer.data(), subjectSize); if (!subjectSuccess) { LERROR("Failed to read message from socket. Disconnecting."); throw SoftwareConnectionLostError(); } } // Delegate decoding depending on message type if (mapSIMPTypeToMessageType.count(type) != 0) { if (mapSIMPTypeToMessageType[type] == MessageType::Color || mapSIMPTypeToMessageType[type] == MessageType::Opacity || mapSIMPTypeToMessageType[type] == MessageType::Size || mapSIMPTypeToMessageType[type] == MessageType::Visibility ) { _isListening = false; } return Message(mapSIMPTypeToMessageType[type], subjectBuffer); } else { LERROR(fmt::format("Unsupported message type: {}. Disconnecting...", type)); return Message(MessageType::Disconnection, subjectBuffer); } } } // namespace openspace