/** * Websocket interface to the training area proxy * $Id$ * $HeadURL$ * $Author$ * Created by Christian Ehrlicher, SLSE, 2017-01-11 */ #include "web_socket_handler.h" #include "main_handler.h" #include "ngs_handler.h" #include "ppm_handler.h" #include #include #include namespace HighLevelArchitecture { WebSocketHandler::WebSocketHandler(int exerciseId, MainHandler *mainHandler, QObject *parent) : QObject(parent) , m_webSocketServer(new QWebSocketServer(QStringLiteral("WLS"), QWebSocketServer::NonSecureMode, this)) , m_mainHandler(mainHandler) , m_exerciseId(exerciseId) , m_logger(getLogger("WebSocketHandler")) {} WebSocketHandler::~WebSocketHandler() { for (const auto &s : qAsConst(m_webSockets)) s->disconnect(this); } bool WebSocketHandler::init(quint16 listenPort) { if (!m_webSocketServer->listen(QHostAddress::Any, listenPort)) { WARNING_LOG(m_logger) << "Can not listen on port " << listenPort << " - aborting"; return false; } connect(m_webSocketServer, &QWebSocketServer::newConnection, this, &WebSocketHandler::onNewConnection); return true; } bool WebSocketHandler::sendTelegramInternal(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *data, const QString &type, int exerciseId) { QJsonObject obj; obj[QStringLiteral("data")] = data->toJson(); obj[QStringLiteral("type")] = type; obj[QStringLiteral("typeId")] = data->id(); // if (data->id() != TrainingAreaBase::TetraIcd::TelegramId::BESTAETIGUNG) // obj[QStringLiteral("counter")] = m_sendCounter++; const QString jsonStr = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)); DEBUG_LOG(m_logger) << "jsonStr: " << jsonStr; bool bOk = false; for (const auto &wsw : qAsConst(m_webSockets)) { if (wsw->sendTextMessage(jsonStr, exerciseId)) bOk = true; } return bOk; } void WebSocketHandler::sendHlaConnectionState() { // send wls connection state (to all web sockets, should not hurt much) TrainingAreaBase::TetraIcd::ProxyConnectionState connState; connState.connectedToSystem = m_mainHandler->isConnectedToHla(); sendTelegram(&connState, m_exerciseId); DEBUG_LOG(m_logger) << "sendHlaConnectionState, isConnected: " << connState.connectedToSystem; } void WebSocketHandler::sendParticipantConnectionStates() { DEBUG_LOG(m_logger) << "sendParticipantConnectionStates"; TrainingAreaBase::TetraIcd::WlsParticipantsConnectionState partConnState; partConnState.equipmentNumber = -1; partConnState.dataCommunicationPossible = false; sendTelegram(&partConnState, m_exerciseId); if (!m_mainHandler->isConnectedToHla()) { INFO_LOG(m_logger) << "We are (not) yet connected to HLA - don't send participant connection states"; return; } const auto externalEquipmentNumbers = m_mainHandler->ngsHandler()->ppmHandler()->externalEquipmentNumbers(); for (int equipNo : externalEquipmentNumbers) { partConnState.equipmentNumber = equipNo; partConnState.dataCommunicationPossible = true; sendTelegram(&partConnState, m_exerciseId); } } void WebSocketHandler::onExternalEquipmentNumberAdded(int equipNo) { sendDataCommunicationState(equipNo, true); } void WebSocketHandler::onExternalEquipmentNumberRemoved(int equipNo) { sendDataCommunicationState(equipNo, false); } void WebSocketHandler::sendDataCommunicationState(int equipNo, bool bIsAvailable) { TrainingAreaBase::TetraIcd::WlsParticipantsConnectionState tele; tele.equipmentNumber = equipNo; tele.dataCommunicationPossible = bIsAvailable; sendTelegram(&tele, m_exerciseId); } void WebSocketHandler::onNewConnection() { QWebSocket *ws = m_webSocketServer->nextPendingConnection(); if (!ws) return; DEBUG_LOG(m_logger) << "New websocket connection from " << ws->peerAddress().toString() << ":" << ws->peerPort(); auto wsw = std::make_shared(ws); connect(wsw.get(), &WebSocketWrapper::textMessageReceived, this, &WebSocketHandler::onTextMessageReceived); connect(ws, &QWebSocket::disconnected, this, &WebSocketHandler::onWebSocketDisconnected); m_webSockets += wsw; } void WebSocketHandler::onTextMessageReceived(const QByteArray &baJson) { using namespace TrainingAreaBase::TetraIcd; TRACE_LOG(m_logger) << "WebSocketHandler::onTextMessageReceived: " << baJson; WebSocketWrapper *wsw = qobject_cast(sender()); if (!wsw) { ERROR_LOG(m_logger) << "Internal error - sender is no WebSocketWrapper"; return; } if (baJson.isEmpty()) return; const QJsonObject obj = QJsonDocument::fromJson(baJson).object(); // const QJsonValue sendCounter = obj.value(QStringLiteral("counter")); // if (!sendCounter.isUndefined()) // { // if (!sendCounter.isDouble()) // return false; // recvCounter = sendCounter.toInt(-1); // DEBUG_LOG(m_logger) << "Incoming telegram with Id " << obj.value(QStringLiteral("type")).toString(); // if (recvCounter < 0) // return false; // } const QJsonValue typeId = obj.value(QStringLiteral("typeId")); if (!typeId.isDouble()) { ERROR_LOG(m_logger) << "Can not read type id: " << baJson; return; } const QJsonObject data = obj.value(QStringLiteral("data")).toObject(); try { switch (typeId.toInt()) { case Constraints::id(): // 2008 wsw->setExerciseId(ExerciseStateData::fromJson(data).exerciseId); if (m_exerciseId == wsw->exerciseId()) { sendParticipantConnectionStates(); sendHlaConnectionState(); } break; case Constraints::id(): // 2012 // not interesed in atm but here to not create a warning in the logfile break; case Constraints::id(): // 1 emit initRequest(Init::fromJson(data)); break; case Constraints::id(): // 4 emit biteRequest(DemandBite::fromJson(data)); break; case Constraints::id(): // 54 emit vulnerabilityStateRequest(DemandVulnerabilityStateChange::fromJson(data)); break; default: WARNING_LOG(m_logger) << "Handler for telegram Id " << typeId.toInt() << " not yet implemented (" << baJson << ")"; return; } } catch (const std::exception &e) { WARNING_LOG(m_logger) << "Could not parse json: " << e.what() << " (" << baJson << ")"; return; } } void WebSocketHandler::onWebSocketDisconnected() { QWebSocket *ws = qobject_cast(sender()); if (!ws) return; for (auto it = m_webSockets.begin(); it != m_webSockets.end(); ++it) { auto wsw = *it; if (wsw->webSocket() == ws) { m_webSockets.erase(it); break; } } } // // WebSocketWrapper // WebSocketWrapper::WebSocketWrapper(QWebSocket *ws) : m_webSocket(ws) , m_exerciseId(-1) { connect(m_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketWrapper::onTextMessageReceived); } WebSocketWrapper::~WebSocketWrapper() { if (m_webSocket) { m_webSocket->disconnect(this); m_webSocket->close(); } delete m_webSocket; } bool WebSocketWrapper::sendTextMessage(const QString &msg, int exerciseId) { if (m_exerciseId != exerciseId && exerciseId != 0 && m_exerciseId != -1) return false; m_webSocket->sendTextMessage(msg); return true; } void WebSocketWrapper::setExerciseId(int exerciseId) { m_exerciseId = exerciseId; } void WebSocketWrapper::onTextMessageReceived(const QString &data) { emit textMessageReceived(data.toUtf8()); } } // namespace HighLevelArchitecture