/** * MainHandler for TetraRadio * $Id$ * $HeadURL$ * $Author$ * Created by Christian Ehrlicher, SLSE, 2016-04-14 */ #include "main_handler.h" #include "settings.h" #include "logger.h" #include "WebSocketInterface/Messages/reply_message.h" #include "WebSocketInterface/json_keys.h" #include #include #include #include #include #include #include #include #include #include #include namespace TrainingAreaBase { MainHandler::MainHandler(QObject *parent) : QObject(parent) , m_ddsHandler(nullptr) , m_handlerWebSocket(nullptr) , m_logger(getLogger("MainHandler")) {} MainHandler::~MainHandler() { LtsPluginManager::LtsPluginLoader::getInstance()->unloadAllPlugins(); m_interfaces.clear(); m_ifcConnectionState.clear(); m_rxHandler.reset(); m_txHandler.reset(); m_ftsHandler.reset(); m_posHandler.reset(); m_ppmHandler.reset(); m_pdmHandler.reset(); m_exmHandler.reset(); m_ismHandler.reset(); m_aesHandler.reset(); m_ddsHandler.reset(); m_dbHandler.reset(); } bool MainHandler::init(int domainId, int exerciseId) { // initialize database m_dbHandler = std::make_shared(); if (!m_dbHandler->init(exerciseId, Settings::settings())) return false; // initialize DDS m_ddsHandler = std::make_shared(domainId, exerciseId); if (!m_ddsHandler->init()) return false; m_exmHandler = std::make_shared(this); connect(m_exmHandler.get(), &ExmHandler::exerciseChanged, this, &MainHandler::onExerciseChanged); connect(m_exmHandler.get(), &ExmHandler::exercise2RadioGroupChanged, this, &MainHandler::onExercise2RadioGroupChanged); m_exmHandler->init(); m_pdmHandler = std::make_shared(this); if (!m_pdmHandler->init()) return false; if (!m_pdmHandler->initializePdm()) return false; m_ppmHandler = std::make_shared(this); connect(m_ppmHandler.get(), &PpmHandler::position, this, &MainHandler::onPositionChanged); if (!m_ppmHandler->init()) return false; if (exerciseId > 0) { m_ismHandler = std::make_shared(this); if (!m_ismHandler->init()) return false; /* * Es gibt den Fall der Initialisierung, wenn der TAB Prozess neu gestartet wird. * Ist ein ABG zu diesem Zeitpunkt angemeldet und wird danach ein anderer Teilnehmer mit dem selben ABG angemeldet, * bleibt das Risiko bestehen, dass zwei Teilnehmer dasselbe ABG zum gleichen Zeitpunkt anzeigen. * * NSqlQuery q(databaseConnection()); TrainingAreaBase::RecordReplayPluginCustom *tabRecordReplay; tabRecordReplay = new TrainingAreaBase::RecordReplayPluginCustom; tabRecordReplay->initDtc(nullptr, exerciseId); TrainingAreaBase::RecordReplayPlugin recordReplay; recordReplay.initDtc(nullptr, exerciseId); if (!recordReplay.readParticipantSanitaryMaterialStockReportAt(q, QDateTime::currentMSecsSinceEpoch())) { ERROR_LOG(m_logger) << "Could not read ParticipantSanitaryMaterialStockReport"; // return false; } else { const auto &latestStock = tabRecordReplay->getParticipantSanitaryMaterialStockReportMap(); ERROR_LOG(m_logger) << "Starting to latest ParticipantSanitaryMaterialStock. Size: " << latestStock.size(); for (const auto &stock : latestStock) { ERROR_LOG(m_logger) << "Stock for equipment number " << stock.equipmentNumber(); if (stock.uniqueId() > 0 && !m_uniqueId2EquipmentNumber.contains(stock.uniqueId())) m_uniqueId2EquipmentNumber.insert(stock.uniqueId(), stock.equipmentNumber()); } } tabRecordReplay->deleteLater(); */ } m_posHandler = std::make_shared(this); if (!m_posHandler->init()) return false; // to TrainingArea m_txHandler = std::make_shared(); // from DDS m_ftsHandler = std::make_shared(this, m_ddsHandler->ftsTab(), m_txHandler); if (exerciseId > 0) { m_aesHandler = std::make_shared(this, m_txHandler); if (!m_aesHandler->init()) return false; } // to DDS m_rxHandler = std::make_shared(this); // to/from TrainingArea auto cb = [&](const LtsPluginManager::PluginInstance &pi) { return loadPlugin(pi); }; LtsPluginManager::LtsPluginLoader::getInstance()->loadPlugins(LtsPluginManager::PluginType::SPECIAL, cb, QStringList(), QStringLiteral("tab")); if (m_interfaces.empty()) { WARNING_LOG(m_logger) << "Could not load any training area plugin - can not send data to training area"; return false; } for (const std::shared_ptr &ifc : m_interfaces) m_txHandler->addTxHandler(ifc->sendToTab()); // WebSocketServer m_handlerWebSocket = std::make_shared(this); DEBUG_LOG(m_logger) << "Reset WebSocketInterfaceHandler successful."; if (!m_handlerWebSocket->init()) { ERROR_LOG(m_logger) << "Initialisation WebSocketInterfaceHandler failed."; return false; } m_ddsHandler->pacemaker()->addPort(m_handlerWebSocket->serverWebSocketPort(), Core::PortType::WebSocketServer, QStringLiteral("{\"id\":\"TAB\"}")); DEBUG_LOG(m_logger) << "Initialisation WebSocketInterfaceHandler successful."; m_ddsHandler->pacemaker()->setNewState(Core::RunningState::Running); return true; } bool MainHandler::useTetraScrambling() const { return Settings::useTetraScramling(); } ProjectDataManagement::RadioGroup MainHandler::exerciseRadioGroup() const { if (exerciseId() == 0) { // for exercise 0 we have to look for a base grp const QMap radioGrps = m_pdmHandler->radioGroups(); for (auto it = radioGrps.begin(); it != radioGrps.end(); ++it) if (it.value().base_group()) return it.value(); return ProjectDataManagement::RadioGroup(); } const auto ex = m_exmHandler->exercise(exerciseId()); if (ex.radioGroupId().is_set()) return m_pdmHandler->radioGroup(ex.radioGroupId().get()); return ProjectDataManagement::RadioGroup(); } bool MainHandler::loadPlugin(const LtsPluginManager::PluginInstance &pi) { INFO_LOG(m_logger) << "Trying to load plugin '" << pi.fullFileName << "'"; Plugin::Plugin *tabPlugin = qobject_cast(pi.plugin); if (!tabPlugin) { WARNING_LOG(m_logger) << "failed loading a training area plugin - not a TrainingAreaPlugin object!"; return false; } INFO_LOG(m_logger) << "... success"; std::shared_ptr ifc = tabPlugin->createInterface(this, m_rxHandler, m_txHandler, m_ddsHandler, Settings::settings()); if (!ifc || !ifc->init()) { WARNING_LOG(m_logger) << "Could not initialize training area plugin" << pi.pluginName << pi.fileName; return false; } connect(ifc.get(), &Plugin::Interface::connectionState, this, &MainHandler::onConnectionState); connect(ifc.get(), &Plugin::Interface::directMessage, this, &MainHandler::onDirectMessage); m_interfaces.push_back(ifc); m_ccsMap = m_pdmHandler->dtc()->canComponentStrings(); m_bctMap = m_pdmHandler->dtc()->biteComponentTexts(); return true; } TetraIcd::ExerciseEnvironmentData MainHandler::exerciseEnvironmentData() const { TetraIcd::ExerciseEnvironmentData exData; exData.exerciseId = exerciseId(); const auto &baseCoord = m_posHandler->baseCoordinate(); // 32UPD const QString zone = baseCoord.mgrs(0, false); if (zone.isEmpty()) return exData; exData.utmZone = baseCoord.zone; exData.grid = zone.mid(3, 2).toStdString(); exData.utmBand = zone.mid(2, 1).at(0).toLatin1(); exData.timeZone = m_exerciseData.defaultTimeZone(); return exData; } TetraIcd::ExerciseStateData MainHandler::exerciseStateData() const { TetraIcd::ExerciseStateData exData; if (m_exerciseData.id() != exerciseId()) return exData; exData.exerciseId = m_exerciseData.id(); exData.exerciseState = static_cast(m_exerciseData.state().underlying()); return exData; } TetraIcd::ExerciseEquipmentAssignmentComplete MainHandler::exerciseEquipmentAssignment() const { TetraIcd::ExerciseEquipmentAssignmentComplete eeac; eeac.exerciseEquipmentAssignments = m_ppmHandler->exerciseEquipmentAssignment(); const auto exercises = m_exmHandler->exercise2RadioGroup(); const QMap radioGroups = m_pdmHandler->radioGroups(); for (const auto &it : exercises) { const ProjectDataManagement::RadioGroup rg = radioGroups.value(it.second); if (rg.id() == 0) { WARNING_LOG(m_logger) << "Got a radio group id " << it.second << " for exercise " << it.first << " but could not find valid data for it - ignoring!"; continue; } TrainingAreaBase::TetraIcd::GroupIssiInformation gissi; gissi.exerciseId = it.first; gissi.serverIssi = rg.issi(); gissi.broadcastIssi1 = rg.group_issi1(); gissi.broadcastIssi2 = rg.group_issi2(); eeac.groupIssiInformation.push_back(std::move(gissi)); } return eeac; } QVector MainHandler::aesTelegrams() const { return m_aesHandler ? m_aesHandler->sentAesTelegrams() : QVector(); } QSqlDatabase MainHandler::databaseConnection() const { if (!m_dbHandler || !m_dbHandler->db()) return QSqlDatabase(); return m_dbHandler->db()->database(); } QString MainHandler::databaseSchemaName() const { if (!m_dbHandler || !m_dbHandler->db()) return QString(); return m_dbHandler->db()->schemaName(); } int MainHandler::domainId() const { if (!m_ddsHandler) return 0; if (!m_ddsHandler->ddsConnector()) return 0; return m_ddsHandler->ddsConnector()->getDomainId(); } void MainHandler::onExerciseChanged(const ExerciseManagement::Exercise &data, DataModification modType) { if (data.id() != exerciseId()) return; m_exerciseData = data; if (modType == DataModification::eModified) m_txHandler->txData(exerciseStateData()); // update Interfaces for (const std::shared_ptr &ifc : qAsConst(m_interfaces)) ifc->exerciseChanged(); } void MainHandler::onExercise2RadioGroupChanged() { m_txHandler->txData(exerciseEquipmentAssignment()); } void MainHandler::onConnectionState(const QString &name, bool connectionState) { auto itC = m_ifcConnectionState.find(name); if (itC != m_ifcConnectionState.end()) { if (itC.value() == connectionState) return; // already send to DDS itC.value() = connectionState; } else { m_ifcConnectionState.insert(name, connectionState); } DEBUG_LOG(m_logger) << "Publishing connection states: " << m_ifcConnectionState.size(); TrainingAreaBase::Connections connections; connections.id(1); for (auto it = m_ifcConnectionState.cbegin(); it != m_ifcConnectionState.cend(); ++it) connections.connections().push_back( TrainingAreaBase::ConnectionState(it.key().toStdString(), it.value())); m_ddsHandler->dtsTab()->publish(connections); } void MainHandler::onDirectMessage(const QByteArray &jsonMessage) { DEBUG_LOG(m_logger) << "Got direct message " << jsonMessage; WebSocketInterface::Messages::BiteDataMessage::BiteData biteData; QJsonObject obj = QJsonDocument::fromJson(jsonMessage).object(); biteData.equipNo = obj.value(WebSocketInterface::JsonKeys::EQUIP_NO()).toInt(); biteData.isLicensed = obj.value(QStringLiteral("doLicence")).toBool(); qint64 biteState = obj.value(QStringLiteral("biteState")).toInt(); qint64 configState = obj.value(QStringLiteral("configState")).toInt(); qint64 batteryState = obj.value(QStringLiteral("batteryState")).toInt(); qint64 configError = obj.value(QStringLiteral("configError")).toInt(); QJsonArray biteError = obj.value(QStringLiteral("biteError")).toArray(); if (biteData.equipNo <= 0) return; // configuration errors for (int it = 0 ; it < 32; it++) { quint32 bitValue = 1U << it; QString name; QString isOk; for (const ProjectDataManagement::CanComponentString &ccs : qAsConst(m_ccsMap)) { if (ccs.bitMask() == bitValue) { name = m_pdmHandler->dtc()->trCanComponentStringName(ccs.id()); break; } } if (((biteState & bitValue) == bitValue)) { if (((configState & bitValue) != bitValue)) { isOk = tr("Error"); } } else if (((configState & bitValue) == bitValue)) { isOk = tr("Communication error"); } if(!name.isEmpty() && !isOk.isEmpty()) { WebSocketInterface::Messages::BiteDataMessage::ConfigurationError error; error.componentName = name; error.text = isOk; biteData.configurationErrors.append(error); } } // basic errors if(configError != 0) { WebSocketInterface::Messages::BiteDataMessage::BasicError error; error.text = tr("Configcheck error"); biteData.basicErrors.append(error); } if (batteryState != 0) { WebSocketInterface::Messages::BiteDataMessage::BasicError error; error.text = tr("Battery Warning"); biteData.basicErrors.append(error); } // componentErrors for (const auto &ele : qAsConst(biteError)) { QString text; QJsonObject error = ele.toObject(); ParticipantManagement::BiteError bitError; bitError.canDeviceNumber(error.value(QStringLiteral("canDeviceNumber")).toInt()); bitError.errorNumber(error.value(QStringLiteral("errorNumber")).toInt()); bitError.subdeviceNumber(error.value(QStringLiteral("subdeviceNumber")).toInt()); bitError.uniqueId(error.value(QStringLiteral("uniqueId")).toInt()); if (bitError.subdeviceNumber() != 0) text = (tr("Error: %1 \nComponent: %2 \nSubdev: %3 \nUId: %4 ") .arg(getErrorString(bitError), getCanDeviceName(bitError.canDeviceNumber()), getSubDeviceName(bitError.subdeviceNumber())) .arg(QString::number( bitError.uniqueId(), 16 ).toUpper())); else if (bitError.canDeviceNumber() != 0) text = (tr("Error: %1 \nComponent: %2") .arg(getErrorString(bitError), getCanDeviceName(bitError.canDeviceNumber()))); WebSocketInterface::Messages::BiteDataMessage::BiteError biteErrorLocal; biteErrorLocal.deviceId = bitError.canDeviceNumber(); biteErrorLocal.subDevice = bitError.subdeviceNumber(); biteErrorLocal.errorNo = bitError.errorNumber(); biteErrorLocal.uniqueId = bitError.uniqueId(); WebSocketInterface::Messages::BiteDataMessage::ErrorText errorT; errorT.lang = QStringLiteral("de_DE"); errorT.text = text; biteErrorLocal.errorTexts.append(errorT); biteData.biteErrors.append(biteErrorLocal); } WebSocketInterface::Messages::BiteDataMessage *msg = new WebSocketInterface::Messages::BiteDataMessage(biteData, QStringLiteral("7777")); if (m_handlerWebSocket != nullptr) { m_handlerWebSocket->sendToAllClients(msg); } } QString MainHandler::getErrorString(const ParticipantManagement::BiteError &error) const { const bool subDevice = (error.subdeviceNumber() != 0); for (const ProjectDataManagement::BiteComponentText &bct : m_bctMap) { if (((subDevice && bct.subDeviceId() == error.subdeviceNumber()) || (bct.canDeviceId() == error.canDeviceNumber())) && (bct.errorNumber() == error.errorNumber())) return m_pdmHandler->dtc()->trBiteComponentTextName(bct.id()); } return tr("Unknown (%1)").arg(error.errorNumber()); } QString MainHandler::getCanDeviceName(int id) const { for (const ProjectDataManagement::CanComponentString &ccs : m_ccsMap) { if (ccs.canDeviceId() == id) return m_pdmHandler->dtc()->trCanComponentStringName(ccs.id()); } return tr("Unknown (%1)").arg(id); } QString MainHandler::getSubDeviceName(int id) const { for (const ProjectDataManagement::CanComponentString &ccs : m_ccsMap) { if (ccs.subDeviceId() == id) return m_pdmHandler->dtc()->trCanComponentStringName(ccs.id()); } return tr("Unknown (%1)").arg(id); } void MainHandler::onPositionChanged(int equipNo, const ParticipantManagement::ParticipantPosition &pos) { WebSocketInterface::Messages::PositionMessage::PositionData positionData; positionData.equipNo = equipNo; positionData.pos = pos.pos(); positionData.gpsTime = pos.sampleTime(); WebSocketInterface::Messages::PositionMessage *msg = new WebSocketInterface::Messages::PositionMessage(positionData, QUuid::createUuid().toString()); if (m_handlerWebSocket != nullptr) { m_handlerWebSocket->sendToAllClients(msg); } } } // namespace TrainingAreaBase