/** * @RDE_HEADER@ * $Id$ * $HeadURL$ * $Author$ */ #include "pws_control_dialog.h" #include "ui_pws_control_dialog.h" #include "pws_vnc_viewer_dialog.h" #include #include #include #include #include #include #include #include #include namespace ProjectionWorkstation { ControlDialog::ControlDialog(const QString &menuGroup, QWidget *parent) : LtsWidgets::LtsDialog(parent) , m_ui(new Ui::ControlDialog) , m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("ControlDialog"))) , m_messageFactory(nullptr) , m_webSocketClient(nullptr) , m_session(nullptr) , m_menuGroup(menuGroup) , m_exerciseId(-1) , m_locationId(-1) , m_isConnected(false) { m_ui->setupUi(this); connect(m_ui->pbnConnect, &QPushButton::clicked, this, &ControlDialog::slotOnConnectClicked); connect(m_ui->pbnLogout, &QPushButton::clicked, this, &ControlDialog::slotOnLogoutClicked); connect(m_ui->chbInThisLocationOnly, &QCheckBox::toggled, this, &ControlDialog::fillCombobox); connect(m_ui->chbProjectionWSOnly, &QCheckBox::toggled, this, &ControlDialog::fillCombobox); connect(m_ui->cbxWorkstations, QOverload::of(&QComboBox::currentIndexChanged), this, &ControlDialog::slotCbxWorkstationsChanged); m_ui->pbnLogout->setEnabled(m_isConnected); m_ui->chbProjectionWSOnly->setVisible(false); #ifdef LTS_DEBUG m_ui->chbProjectionWSOnly->setVisible(true); #endif setOnlineHelpFilename(QStringLiteral("Situation-Projection_Control")); } ControlDialog::~ControlDialog() { if (m_webSocketClient) { // NLDEVCTRL-808: OpApp stürzt ab beim beenden (ProjWS) // at initWebSocketHandler the underlying QWebSocket is used for several // connections. But the disconnect was missing. Cleanup a little bit... // disconnect in reverse order: // 1) disconnect from underlying QWebSocket if (m_webSocketClient->webSocket()) m_webSocketClient->webSocket()->disconnect(); // to avoid our slots to be called // 2) disconnect from m_webSocketClient m_webSocketClient->disconnect(this); // add reply while destroying? const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId(); m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage()); // m_webSocketClient->deleteLater(); // ControlDialog is parent of WebSocketClient, therefore useless!?! } delete m_messageFactory; delete m_ui; } void ControlDialog::initWebSocketHandler() { if (!m_messageFactory) m_messageFactory = new LtsProjectionWorkstation::MessageFactory(this, m_logger); delete m_webSocketClient; m_webSocketClient = new WebSocketClient(m_messageFactory, this); auto s = m_webSocketClient->webSocket(); connect(s, &QWebSocket::connected, this, &ControlDialog::slotOnWebSocketConnected); connect(s, QOverload::of(&QWebSocket::error), this, &ControlDialog::slotOnWebSocketError); connect(s, QOverload&>::of(&QWebSocket::sslErrors), this, &ControlDialog::slotOnWebSocketSslError); connect(s, &QWebSocket::disconnected, this, &ControlDialog::slotOnWebSocketDisconnected); } bool ControlDialog::init(LtsSession::LtsSession *session, const std::shared_ptr &opmDtc, const std::shared_ptr &opmIfc, const std::shared_ptr &asmDtc, int exerciseId) { if (!session) return false; m_session = session; m_opmDtc = opmDtc; m_opmIfc = opmIfc; m_asmDtc = asmDtc; if (exerciseId < 0) return false; m_exerciseId = exerciseId; LtsSessionData::LtsSessionData *sessionData = LtsSessionData::LtsSessionData::getInstance(); auto getLocationIdFunc = [sessionData, this](const AssetManagement::Asset & data) -> bool { if (data.id() == sessionData->getWorkstationId()) { m_locationId = data.locationId(); return true; } return false; }; m_asmDtc->assets(std::function(getLocationIdFunc)); fillCombobox(); QString txt = m_ui->cbxWorkstations->currentText(); if (!txt.isEmpty()) { QHostInfo info = QHostInfo::fromName(txt); if (info.addresses().size() > 0) m_host = info.addresses().at(0); else ERROR_LOG(m_logger) << "Address for host " << m_ui->cbxWorkstations->currentText() << " not found!"; } return true; } void ControlDialog::aboutToClose() { for (VncViewerDialog *diag : qAsConst(m_viewers)) diag->closeRequest(); m_viewers.clear(); if (m_webSocketClient) { const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId(); m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage()); m_webSocketClient->webSocket()->disconnect(); } } void ControlDialog::slotOnConnectClicked() { TRACE_LOG(m_logger) << LOGFUNCTION << " START, this=" << this; if (!m_isConnected) { initWebSocketHandler(); int port = NGL::Connections::port(NGL::Connections::eConnection::PWS_WEBSOCKET); if (m_webSocketClient) m_webSocketClient->webSocket()->open(QUrl(QStringLiteral("ws://%1:%2/").arg(m_host.toString()).arg(port))); m_ui->pbnConnect->setEnabled(false); } else { if (m_webSocketClient) { const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId(); m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage()); m_webSocketClient->webSocket()->close(); } } DEBUG_LOG(m_logger) << LOGFUNCTION << " connect clicked, this=" << this; updateConnectButton(); TRACE_LOG(m_logger) << LOGFUNCTION << " END, this=" << this; } void ControlDialog::slotOnLogoutClicked() { if (m_webSocketClient) { m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::LogoutRequest(QUuid::createUuid().toString()))->toReplyMessage()); m_webSocketClient->webSocket()->close(); } DEBUG_LOG(m_logger) << LOGFUNCTION << " logout clicked, this=" << this; updateConnectButton(); } void ControlDialog::slotOnWebSocketConnected() { LtsSessionData::LtsSessionData *sessioData = LtsSessionData::LtsSessionData::getInstance(); int userId = sessioData->getOperatorId(); int exerciseId = m_exerciseId; OperatorManagement::User user = m_opmDtc->user(userId); if (user.id() > 0) { // NLDEVCTRL-445: Projektion Filter und Kartenausschnitt // very new app 'Lage', workspace isn`t saved yet and therefore no workspaceId. // But callWorkspaceSaveUnsaved needs an workspaceId for transfering the workspace with filter: QString workspaceIdToUse = m_session->workspaceId(); if (workspaceIdToUse.isEmpty()) { workspaceIdToUse = QUuid::createUuid().toString(); INFO_LOG(m_logger) << "workspaceIdToUse " << workspaceIdToUse; } m_opmIfc->callWorkspaceSaveUnsaved(workspaceIdToUse); LtsUtils::OperatorSettings* opSettings = LtsUtils::OperatorSettings::getInstance(); opSettings->setValue(QStringLiteral("PwsWorkspace"), QStringLiteral("CurrentId"), workspaceIdToUse); QByteArray opSettingsData = LtsUtils::OperatorSettings::getInstance()->exportSettings(); // This does not work since m_session->setWorkspaceId() triggers loading of filters on controlling WS :-( // if (m_session->workspaceId().isEmpty()) // { // const QString newId(QUuid::createUuid().toString()); // INFO_LOG(m_logger) << "new WsId" << newId; // m_session->setWorkspaceId(newId); // } // m_opmIfc->callWorkspaceSaveUnsaved(); // LtsUtils::OperatorSettings* opSettings = LtsUtils::OperatorSettings::getInstance(); // opSettings->setValue(QStringLiteral("PwsWorkspace"), QStringLiteral("CurrentId"), m_session->workspaceId()); // QByteArray opSettingsData = LtsUtils::OperatorSettings::getInstance()->exportSettings(); INFO_LOG(m_logger) << " connect userId=" << userId << ", with prio=" << user.projectionPriority() << ", PwsWorkspace/Id='" << workspaceIdToUse << "'"; LtsProjectionWorkstation::CTS::LoginRequest req(QUuid::createUuid().toString(), userId, user.projectionPriority(), exerciseId, opSettingsData); m_webSocketClient->addReply(req.toReplyMessage()); } } void ControlDialog::slotOnVncDisconnected() { INFO_LOG(m_logger) << " slotOnVncDisconnected"; if (m_webSocketClient) { const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId(); m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage()); } m_isConnected = false; if (!m_ui || !m_ui->pbnConnect) return; m_ui->pbnConnect->setEnabled(true); DEBUG_LOG(m_logger) << LOGFUNCTION << " VNC disconnected, this=" << this; updateConnectButton(); } void ControlDialog::slotOnWebSocketDisconnected() { for (VncViewerDialog *diag : qAsConst(m_viewers)) diag->closeRequest(); // will be deleted automatically m_viewers.clear(); m_isConnected = false; if (m_ui && m_ui->pbnConnect) m_ui->pbnConnect->setEnabled(true); DEBUG_LOG(m_logger) << LOGFUNCTION << " WEBSOCKET disconnected, this=" << this; updateConnectButton(); } void ControlDialog::slotOnWebSocketError() { ERROR_LOG(m_logger) << m_webSocketClient->webSocket()->errorString(); LtsWidgets::LtsMessageDialog::showMessage(this, m_webSocketClient->webSocket()->errorString(), tr("Connection to the presentation workstation failed!")); m_isConnected = false; m_ui->pbnConnect->setEnabled(true); DEBUG_LOG(m_logger) << LOGFUNCTION << " VWEBSOCKET ERROR, this=" << this; updateConnectButton(); } void ControlDialog::slotOnWebSocketSslError() { if (m_webSocketClient) m_webSocketClient->webSocket()->ignoreSslErrors(); } void ControlDialog::slotCbxWorkstationsChanged() { QString txt = m_ui->cbxWorkstations->currentText(); if (txt.isEmpty()) return; QHostInfo info = QHostInfo::fromName(txt); if (info.addresses().size() > 0) m_host = info.addresses().at(0); else ERROR_LOG(m_logger) << "Address for host " << m_ui->cbxWorkstations->currentText() << " not found!"; } void ControlDialog::fillCombobox() { m_ui->cbxWorkstations->clear(); m_asmDtc->assets([this](const AssetManagement::Asset & data) { if (m_ui->chbProjectionWSOnly->isChecked() && data.assetTypeId() != AssetManagement::AssetType_def::ProjectionWorkstation) return; if (m_ui->chbInThisLocationOnly->isChecked() && data.locationId() != m_locationId) return; m_ui->cbxWorkstations->addItem(QString::fromStdString(data.name())); }); m_ui->cbxWorkstations->model()->sort(0); } void ControlDialog::updateConnectButton() { TRACE_LOG(m_logger) << LOGFUNCTION << " START, this=" << this << ", m_isConnected=" << m_isConnected << ", pbnConnect->text()=" << m_ui->pbnConnect->text(); if (!m_ui) return; m_ui->pbnConnect->setText(m_isConnected ? tr("Disonnect") : tr("Connect")); m_ui->pbnLogout->setEnabled(m_isConnected); TRACE_LOG(m_logger) << LOGFUNCTION << " END, this=" << this << ", m_isConnected=" << m_isConnected << ", pbnConnect->text()=" << m_ui->pbnConnect->text(); } void ControlDialog::handleMessage(LtsWebSocket::AbstractClientMessage *message) { using namespace LtsProjectionWorkstation::STC; LoginRequestReply *login = dynamic_cast(message); if (login) { if (login->reason() == LoginRequestReply::LoginSuccessful) { for (const LtsProjectionWorkstation::VncScreen &screen : login->screens()) { VncViewerDialog *vnc = new VncViewerDialog(screen.name, this); if (vnc->init(m_host, screen.port, screen.geometry.size())) { m_viewers.append(vnc); connect(vnc, &VncViewerDialog::disconnected, this, &ControlDialog::slotOnVncDisconnected); QString name = screen.name; name.remove(QLatin1Char(' ')); addDialog(vnc, m_menuGroup, name, LtsWidgets::LayoutArea::eDialogTab, false, false); } } DEBUG_LOG(m_logger) << LOGFUNCTION << " LoginSuccessful YES, this=" << this; m_isConnected = true; m_ui->pbnConnect->setEnabled(true); updateConnectButton(); } else { QString errMsg; switch (login->reason()) { case LoginRequestReply::LoginSuccessful: break; case LoginRequestReply::OtherUser: errMsg = tr("The connection is blocked by user %1") .arg(QString::fromStdString(m_opmDtc->user(login->reasonUserId()).name())); break; } LtsWidgets::LtsMessageDialog::showMessage(this, errMsg, tr("Login failed!")); DEBUG_LOG(m_logger) << LOGFUNCTION << " LoginSuccessful NO, this=" << this; m_isConnected = false; m_ui->pbnConnect->setEnabled(true); updateConnectButton(); } return; } LtsProjectionWorkstation::STC::DedicatedDisconnect *disconnect = dynamic_cast(message); if (disconnect) { INFO_LOG(m_logger) << " DedicatedDisconnect.. " << ", getOperatorId=" << LtsSessionData::LtsSessionData::getInstance()->getOperatorId() << ", disconnect->userid()=" << disconnect->userid(); if ( (disconnect->userid() != LtsSessionData::LtsSessionData::getInstance()->getOperatorId()) && m_isConnected) { slotOnWebSocketDisconnected(); INFO_LOG(m_logger) << " DedicatedDisconnect.. done"; // NLDEVCTRL-445: Projektion Filter und Kartenausschnitt: // LtsWidgets::LtsMessageDialog::showMessage(this, tr("There is already a user with a higher priority connected. Please wait until the user has ended the session."), tr("Disconnected")); // LtsMessageDialog is blocking ; but we need unblocking: new LtsWidgets::LtsMessageDialogNew(this, tr("There is already a user with a higher priority connected. Please wait until the user has ended the session."), tr("Disconnected")); } } } void ControlDialog::retranslate() { translate(m_ui, tr("Control Projection")); } } // ProjectionWorkstation