/** * 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)) , m_logger(getLogger("HandlerOcData")) {} 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, 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)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 == static_cast(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 == static_cast(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