/** * Handler for all oc data at the server * $Id$ * $HeadURL$ * $Author$ * Created by Philipp Dassow, NLST, 14-02-2018 */ #include "handler_oc_data.h" #include #include #include #include "main_handler.h" #include "oc_situation.h" #include "handler_oc_aes_data.h" #include "handler_oc_situation_data.h" #include "handler_oc_central_aes_data.h" #include "settings.h" #include "logger.h" namespace OcSituationServer { HandlerOcData::HandlerOcData(MainHandler *mainHandler, QObject *parent) : QObject(parent) , Interface::Fts::SendEquipmentMessageOcRequestHandler(mainHandler->dds()->fts()) , Interface::Fts::EquipmentErrorRequestHandler(mainHandler->dds()->fts()) , m_mainHandler(mainHandler) , m_handlerOcAesData(new HandlerOcAesData(mainHandler, this)) , m_dts(mainHandler->dds()->dts()) , m_fts(mainHandler->dds()->fts()) , m_evlDtc(mainHandler->evlDtc()) , m_evlFtc(mainHandler->evlFtc()) , m_msgFtc(mainHandler->msgFtc()) , m_ppmDtc(mainHandler->ppmDtc()) , m_ppmFtc(mainHandler->ppmFtc()) , m_tabDtc(mainHandler->tabDtc()) , m_tabFtc(mainHandler->tabFtc()) , m_currentActiveEvaluationSchemaId(0) , m_stateInformationTimer(new QTimer(this)) {} HandlerOcData::~HandlerOcData() {} bool HandlerOcData::init() { // EVL DTC enable if (!m_evlDtc->enableEvaluationSchema()) { ERROR_LOG(m_logger) << "Enabling EVL Data-Topics failed."; return false; } connect(m_evlDtc.get(), &Evaluations::Interface::Dtc::evaluationSchemaChanged, this, &HandlerOcData::onEvaluationSchemaChanged); auto cb = [this](const Evaluations::EvaluationSchema & schema) { onEvaluationSchemaChanged(schema, DataModification::eAdded); }; m_evlDtc->evaluationSchemas(cb); // EVL FTC enable if (!m_evlFtc->enableEvlCommentModify()) { ERROR_LOG(m_logger) << "Enabling EVL Function-Topics failed."; return false; } // Msg FTC enable if (!m_msgFtc->enableCreateOperatorMessage()) { ERROR_LOG(m_logger) << "Enabling MSG Function-Topics failed."; return false; } if (!m_ppmDtc->enableParticipant()) { ERROR_LOG(m_logger) << "Enabling PPM Data-Topics failed."; return false; } if (!m_ppmFtc->enableRegisterParticipant()) { ERROR_LOG(m_logger) << "Enabling PPM FTC-Topics failed."; return false; } // TAB DTC enable if (!m_tabDtc->enableOcData()) { ERROR_LOG(m_logger) << "Enabling TAB Data-Topics failed."; return false; } // TAB DTC enable if (!m_tabDtc->enableConnections()) { ERROR_LOG(m_logger) << "Enabling TAB Data-Topics failed."; return false; } connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::ocDataChanged, this, &HandlerOcData::onOcDataChanged); // TAB FTC enable if (!m_tabFtc->enableSendOcData()) { ERROR_LOG(m_logger) << "Enabling TAB Function-Topics failed."; return false; } // Handler aes data if (!m_handlerOcAesData->init()) { ERROR_LOG(m_logger) << "Could not initialise HandlerOcAesData."; return false; } connect(m_stateInformationTimer, &QTimer::timeout, this, &HandlerOcData::sendStateInformation); m_stateInformationTimer->setInterval(Settings::stateInformationCycleTime()); INFO_LOG(m_logger) << "Starting StateInformation timer with interval: " << m_stateInformationTimer->interval(); m_stateInformationTimer->start(); INFO_LOG(m_logger) << "Initialisation HandlerOcData succesful."; return true; } void HandlerOcData::onEvlCommentModifyReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Evaluation could not be transmitted to EVL: " << reply.translated(); else DEBUG_LOG(m_logger) << "Evaluation successfully transmitted to EVL!"; } void HandlerOcData::onCreateOperatorMessageReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Message could not be created by MSG: " << reply.translated(); else DEBUG_LOG(m_logger) << "Message created successfully by MSG!"; } void HandlerOcData::onSendOcDataReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Oc data could not be send: " << reply.translated(); else DEBUG_LOG(m_logger) << "Oc data send successfully to TAB!"; } void HandlerOcData::onRegisterParticipantReply(const ParticipantManagement::RegisterReply &reply) { if (reply.errorString().size()) ERROR_LOG(m_logger) << "Modify Participant data could not be send: " << reply.errorString(); else DEBUG_LOG(m_logger) << "Modify Participant data send successfully to ppm!"; } void HandlerOcData::onEvaluationSchemaChanged(const Evaluations::EvaluationSchema &data, DataModification modType) { if ((modType != DataModification::eAdded) && (modType != DataModification::eModified)) return; if (data.sendToTa()) m_currentActiveEvaluationSchemaId = data.id(); } void HandlerOcData::onOcDataChanged(const TrainingAreaBase::OcData &data, DataModification modType) { if ((modType != DataModification::eAdded) && (modType != DataModification::eModified)) return; if (data.sampleTime() < QDateTime::currentMSecsSinceEpoch() - 1000 * 60 * 10) // older than 10 mins -> ignore return; const QByteArray payload = LtsUtils::toQByteArray(data.payload()); unsigned int telegramId; LtsUtils::get1Byte(payload, telegramId); DEBUG_LOG(m_logger) << "New OcData telegramId: " << telegramId; switch (telegramId) { case OcSituation::TelegramId::Message: { handleMessage(payload); break; } case OcSituation::TelegramId::DemandEvaluationSchema: { handleDemandEvaluationSchema(payload, data.equipmentNumber()); break; } case OcSituation::TelegramId::Evaluation: { handleEvaluation(payload); break; } case OcSituation::TelegramId::DemandSituation: { handleDemandSituation(payload, data.equipmentNumber()); break; } case OcSituation::TelegramId::DemandAesItems: { OcSituation::AesType aesType; size_t pos = 1; LtsUtils::get1Byte(payload, pos, aesType); m_handlerOcAesData->demandAesItems(aesType, data.equipmentNumber()); break; } case OcSituation::TelegramId::EquipmentErrorRegistration: { handleEquipmentErrorRegistration(payload); break; } case OcSituation::TelegramId::EquipmentRegistration: { handleEquipmentRegistration(payload); break; } default: { INFO_LOG(m_logger) << "OC-data with telegram ID: " << telegramId << " is not by HandlerOcData OSS!"; break; } } } void HandlerOcData::onSendOcData(const TrainingAreaBase::SendOcDataRequest &req) { m_tabFtc->sendOcDataRequest(req, this); } void HandlerOcData::sendStateInformation() { QByteArray payload; LtsUtils::set1Byte(OcSituation::TelegramId::StateInformation, payload); LtsUtils::set2Byte(m_currentActiveEvaluationSchemaId, payload); LtsUtils::set2Byte(m_handlerOcAesData->dangerAreaResetCnt(), payload); LtsUtils::set2Byte(m_handlerOcAesData->iedResetCnt(), payload); TrainingAreaBase::SendOcDataRequest out; out.payload(LtsUtils::convert>(payload)); /*! * \details Only if Tetra is connected, the OcData telegram will be send * If we not check = A bunch of telegrams will be queued/stored until Tetra is connected, than Tetra is in Overload */ QMapIterator conIt(m_tabDtc->connectionss()); while (conIt.hasNext()) { conIt.next(); for (TrainingAreaBase::ConnectionState conState : conIt.value().connections()) { if (conState.name() == "TETRA" && conState.state() == false) return; } } onSendOcData(out); } void HandlerOcData::onSituationThreadExit() { ThreadOcSituationData *t = qobject_cast(sender()); INFO_LOG(m_logger) << "Exit O/C-Thread for O/C-Laptop-Id: " << m_situationThreads.key(t); QMutexLocker l(&m_accessMutex); m_situationThreads.remove(m_situationThreads.key(t)); t->deleteLater(); } void HandlerOcData::onCentralAesThreadExit() { ThreadOcCentralAesData *t = qobject_cast(sender()); INFO_LOG(m_logger) << "Exit CentralAesData-Thread for O/C-Laptop-Id: " << m_centralAesDataThreads.key(t); QMutexLocker l(&m_accessMutex); m_centralAesDataThreads.remove(m_centralAesDataThreads.key(t)); t->deleteLater(); } void HandlerOcData::onSendEquipmentMessageOcRequest(const rti::core::SampleIdentity &cookie, const SendEquipmentMessageRequest &request) { QByteArray preparedPayload; LtsUtils::set1Byte(OcSituation::TelegramId::Message, preparedPayload); LtsUtils::set2Byte(0, preparedPayload); // dummy receiverId is individually set for each eqId in list LtsUtils::set1Byte(Messaging::AddressType::ObserverController, preparedPayload); LtsUtils::set2Byte(request.sender(), preparedPayload); LtsUtils::set1Byte(request.senderType().underlying(), preparedPayload); LtsUtils::set2Byte(m_mainHandler->exerciseId(), preparedPayload); preparedPayload.append(QByteArray::fromStdString(request.subject())); preparedPayload.append('\n'); // separating subject from message preparedPayload.append(QByteArray::fromStdString(request.message())); for (const auto equipmentNumber : request.equipmentNumbers()) { TrainingAreaBase::SendOcDataRequest out; out.equipmentNumber(equipmentNumber); // replace 2 bytes with current receiver QByteArray receiverId; LtsUtils::set2Byte(equipmentNumber, receiverId); QByteArray payload = preparedPayload.replace(1, 2, receiverId); out.payload(LtsUtils::convert>(payload)); onSendOcData(out); } m_fts->sendEquipmentMessageOcReply(cookie, Core::FunctionReplyObject(request.requestId())); } void HandlerOcData::onEquipmentErrorRequest(const rti::core::SampleIdentity &cookie, const EquipmentErrorRequest &request) { ERROR_LOG(m_logger) << "onEquipmentErrorRequest" << request.equipmentNumber(); EquipmentErrorRegistration out; // out.id(request.requestId()); out.sampleTime(QDateTime::currentMSecsSinceEpoch()); out.participant(QString::number(request.equipmentNumber()).toStdString()); out.device(request.device()); out.text(request.text()); m_dts->publish(out); m_fts->equipmentErrorReply(cookie, Core::FunctionReplyObject(request.requestId())); } void HandlerOcData::handleDemandSituation(const QByteArray &data, int equipmentId) { OcSituation::SituationCenterType centerType; int centerEquipmentId = 0, radius = 0; Core::GeoPosition2D position; position.lon(std::nan("")); position.lat(std::nan("")); bool initial = false; size_t pos = 1; LtsUtils::get1Byte(data, pos, centerType); if (centerType == OcSituation::SituationCenterType::Equipment) { LtsUtils::get2Byte(data, pos, centerEquipmentId); LtsUtils::get2Byte(data, pos, radius); DEBUG_LOG(m_logger) << "Request from O/C-Laptop-Id: " << equipmentId << " centerEquipmentId: " << centerEquipmentId << " radius: " << radius; } else { double tmp; LtsUtils::get8Byte(data, pos, tmp); position.lon(tmp); LtsUtils::get8Byte(data, pos, tmp); position.lat(tmp); LtsUtils::get2Byte(data, pos, radius); DEBUG_LOG(m_logger) << "Request from O/C-Laptop-Id: " << equipmentId << " center: " << position << " radius: " << radius; } bool filterActive = false; LtsUtils::get1Byte(data, pos, initial); LtsUtils::get1Byte(data, pos, filterActive); QSet typeClassList{}; QSet partyList{}; if (filterActive) { // get partyTypes int pCount = 0; LtsUtils::get1Byte(data, pos, pCount); for (int i = 0; i < pCount; i++) { int partyId; LtsUtils::get2Byte(data, pos, partyId); partyList.insert(partyId); } //get types int typeCount = 0; LtsUtils::get1Byte(data, pos, typeCount); for (int i = 0; i < typeCount; i++) { int typClass; LtsUtils::get1Byte(data, pos, typClass); typeClassList.insert((ParticipantManagement::TypeClass::type)typClass); } } QMutexLocker l(&m_accessMutex); // Situation Thread ThreadOcSituationData *ocThread = m_situationThreads.value(equipmentId); bool bInitialSituation; if (!ocThread) { ocThread = new ThreadOcSituationData(m_mainHandler, equipmentId, centerEquipmentId, radius, filterActive, partyList, typeClassList); m_situationThreads.insert(equipmentId, ocThread); connect(ocThread, &QThread::finished, this, &HandlerOcData::onSituationThreadExit); connect(ocThread, &ThreadOcSituationData::sendOcData, this, &HandlerOcData::onSendOcData); ocThread->start(); bInitialSituation = true; } else bInitialSituation = initial; ocThread->requestOcSituationData(centerType, centerEquipmentId, position, radius, filterActive, partyList, typeClassList, bInitialSituation); // Central AES Data Thread bool bInitialAes; ThreadOcCentralAesData *centralAesDataThread = m_centralAesDataThreads.value(equipmentId); if (!centralAesDataThread) { centralAesDataThread = new ThreadOcCentralAesData(m_mainHandler, equipmentId, centerEquipmentId, radius); m_centralAesDataThreads.insert(equipmentId, centralAesDataThread); connect(centralAesDataThread, &QThread::finished, this, &HandlerOcData::onCentralAesThreadExit); connect(centralAesDataThread, &ThreadOcCentralAesData::sendOcData, this, &HandlerOcData::onSendOcData); centralAesDataThread->start(); bInitialAes = true; } else bInitialAes = initial; centralAesDataThread->requestOcCentralAesData(centerType, centerEquipmentId, position, radius, bInitialAes); } void HandlerOcData::handleDemandEvaluationSchema(const QByteArray &data, int equipmentNumber) { Q_UNUSED(data); // ignoring requested schema ID, as we only have one active schema to be send to TA Evaluations::EvaluationSchema schema = m_evlDtc->evaluationSchema(m_currentActiveEvaluationSchemaId); DEBUG_LOG(m_logger) << "Sending to equipmentnumber: " << equipmentNumber << " evaluation schmema: " << m_currentActiveEvaluationSchemaId; TrainingAreaBase::SendOcDataRequest out; out.equipmentNumber(equipmentNumber); QByteArray payload; LtsUtils::set1Byte(OcSituation::TelegramId::EvaluationSchema, payload); LtsUtils::set2Byte(schema.id(), payload); QByteArray compressed = qCompress(QByteArray::fromStdString(schema.schemadata())); payload.append(compressed); out.payload(LtsUtils::convert>(payload)); onSendOcData(out); } void HandlerOcData::handleEvaluation(const QByteArray &data) { Evaluations::EvlCommentModifyRequest out; size_t pos = 1; unsigned int tmp; // get schema id LtsUtils::get2Byte(data, pos, tmp); out.schemaId(tmp); // get sender operator id LtsUtils::get4Byte(data, pos, tmp); out.senderOperatorId(tmp); // get receiver operator id LtsUtils::get4Byte(data, pos, tmp); if (tmp > 0) out.receiverOperatorId(dds::core::optional(tmp)); // get timeStamp int64_t timeStamp; LtsUtils::get8Byte(data, pos, timeStamp); out.ts(timeStamp); // get hierarchy string unsigned int hierarchyLength; LtsUtils::get2Byte(data, pos, hierarchyLength); out.hierarchy(QByteArray(data.mid(pos, hierarchyLength)).toStdString()); pos += hierarchyLength; // get text string unsigned int textLength; LtsUtils::get2Byte(data, pos, textLength); out.text(QByteArray(data.mid(pos, textLength)).toStdString()); DEBUG_LOG(m_logger) << "Got new evl comment: " << out; m_evlFtc->evlCommentModifyRequest(out, this); } void HandlerOcData::handleMessage(const QByteArray &data) { Messaging::CreateOperatorMessageRequest out; size_t pos = 1; unsigned int tmp = 0; unsigned int addressType; // get receiver LtsUtils::get2Byte(data, pos, tmp); std::vector recipientIds; recipientIds.push_back(tmp); // get address type LtsUtils::get1Byte(data, pos, addressType); out.recipientType(static_cast(addressType)); if (addressType == Messaging::AddressType::ObserverController) { int participantId = m_ppmDtc->participantIdByEquipmentNumber(tmp); if (participantId > 0) recipientIds[0] = participantId; else { ERROR_LOG(m_logger) << "Recipient equipment number: " << tmp << " is unknown. Aborting creating message."; return; } } out.recipientIds(recipientIds); // get sender LtsUtils::get2Byte(data, pos, tmp); LtsUtils::get1Byte(data, pos, addressType); out.senderType(static_cast(addressType)); if (addressType == Messaging::AddressType::ObserverController) { int participantId = m_ppmDtc->participantIdByEquipmentNumber(tmp); if (participantId > 0) out.senderId(participantId); else { WARNING_LOG(m_logger) << "Sender equipment number: " << tmp << " is unknown. Setting senderId to 0."; out.senderId(0); out.senderType(Messaging::AddressType::ObserverController); } } else { // no need to check if operator still exists, there is no harm if not out.senderId(tmp); } // get exerciseId LtsUtils::get2Byte(data, pos, tmp); if (tmp == 0) WARNING_LOG(m_logger) << "Could'n set ExerciseId" << " " << tmp << " set to 0"; out.exerciseId(tmp); // get address type const QByteArray msg(data.right(data.size() - pos)); int idx = msg.indexOf('\n'); if (idx < 0) { out.subject((tr("Message from %1").arg(out.senderId())).toStdString()); out.message(msg.constData()); } else { out.subject(msg.left(idx).constData()); out.message(msg.mid(idx + 1).constData()); } DEBUG_LOG(m_logger) << "Got new message: " << out; m_msgFtc->createOperatorMessageRequest(out, this); } void HandlerOcData::handleEquipmentErrorRegistration(const QByteArray &data) { EquipmentErrorRegistration out; size_t pos = 1; unsigned int tmp; // equipmentNumber LtsUtils::get2Byte(data, pos, tmp); out.equipmentNumber(tmp); // participant out.participant(data.mid(pos, 36).toStdString()); pos += 36; // device out.device(data.mid(pos, 36).toStdString()); pos += 36; // length LtsUtils::get1Byte(data, pos, tmp); // text out.text(data.mid(pos, tmp).toStdString()); out.sampleTime(QDateTime::currentMSecsSinceEpoch()); DEBUG_LOG(m_logger) << "Got new error registration: " << out; m_dts->publish(out); } void HandlerOcData::handleEquipmentRegistration(const QByteArray &data) { size_t pos = 1; unsigned int tmp; // participantID LtsUtils::get2Byte(data, pos, tmp); int participantId = tmp; // equipmentNumber LtsUtils::get2Byte(data, pos, tmp); int equipmentNumber = tmp; ParticipantManagement::Participant part = m_ppmDtc->participant(participantId); if (part.equipmentNumber().is_set() && equipmentNumber > 0) { // we need a deregistration first! ParticipantManagement::RegisterParticipantRequest dereq; dereq.assignState(ParticipantManagement::ParticipantAssignState::NotAssigned); dereq.equipmentNumber(0); dereq.participantId(participantId); dereq.info(part.info()); dereq.identification(part.identification()); m_ppmFtc->registerParticipantRequest(dereq, this); } ParticipantManagement::RegisterParticipantRequest req; if (equipmentNumber > 0) req.assignState(ParticipantManagement::ParticipantAssignState::HandoutExcon); else req.assignState(ParticipantManagement::ParticipantAssignState::NotAssigned); req.equipmentNumber(equipmentNumber); req.participantId(participantId); req.info(part.info()); req.identification(part.identification()); DEBUG_LOG(m_logger) << "Try to register part with id: " << req.participantId() << " and eqNumeber " << req.equipmentNumber(); m_ppmFtc->registerParticipantRequest(req, this); } } // Namespace OcSituationServer