/** * Handler for aes data to be send to the oc-laptop transparent * $Id$ * $HeadURL$ * $Author$ * Created by Philipp Dassow, NLST, 18-04-2018 */ #include "handler_oc_aes_data.h" #include #include #include #include #include "settings.h" namespace OcSituationServer { HandlerOcAesData::HandlerOcAesData(MainHandler *mainHandler, QObject *parent) : QObject(parent) , m_aesDtc(mainHandler->aesDtc()) , m_gimDtc(mainHandler->gimDtc()) , m_tabFtc(mainHandler->tabFtc()) , m_dbHandler(mainHandler->db()) , m_positionConverter(mainHandler->positionConverter()) , m_dangerAreaResetCnt(0) , m_iedResetCnt(0) , m_collectionTimer(QMap()) , m_demandingEquipments(QMap>()) , m_iedPositionsToBeSend(QMap()) , m_iedPositionCycleTimer(new QTimer(this)) , m_logger(getLogger("HandlerOcAesData")) {} HandlerOcAesData::~HandlerOcAesData() {} bool HandlerOcAesData::init() { // create collectionTimer m_collectionTimer[OcSituation::AesType::DangerArea] = new QTimer(this); m_collectionTimer[OcSituation::AesType::DangerArea]->setSingleShot(true); m_collectionTimer[OcSituation::AesType::DangerArea]->setInterval(Settings::aesDemandCollectionTime()); connect(m_collectionTimer[OcSituation::AesType::DangerArea], &QTimer::timeout, this, &HandlerOcAesData::onCollectionTimeOutDemandDangerAreas); m_collectionTimer[OcSituation::AesType::Ied] = new QTimer(this); m_collectionTimer[OcSituation::AesType::Ied]->setSingleShot(true); m_collectionTimer[OcSituation::AesType::Ied]->setInterval(Settings::aesDemandCollectionTime()); connect(m_collectionTimer[OcSituation::AesType::Ied], &QTimer::timeout, this, &HandlerOcAesData::onCollectionTimeOutDemandIeds); // set reset counter QVector dataVec; // read all Ieds from database if (!m_dbHandler->select(dataVec)) { ERROR_LOG(m_logger) << "Error reading reset counter from database!"; return false; } for (const auto &data : qAsConst(dataVec)) { switch (data.aesTypeId) { case OcSituation::AesType::DangerArea: { m_dangerAreaResetCnt = data.resetCounter; break; } case OcSituation::AesType::Ied: { m_iedResetCnt = data.resetCounter; break; } default: { WARNING_LOG(m_logger) << "Reset counter for aes type: " << data.aesTypeId << " read from db is not handled."; break; } } } // AES DTC enable if (!m_aesDtc->enableDangerArea()) { ERROR_LOG(m_logger) << "Enabling AES Data-Topics failed."; return false; } connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::dangerAreaChanged, this, &HandlerOcAesData::onDangerAreaChanged); // GIM DTC enable if (!m_gimDtc->enableIed() || !m_gimDtc->enableIedPosition()) { ERROR_LOG(m_logger) << "Enabling GIM Data-Topics failed."; return false; } connect(m_gimDtc.get(), &GuezIedManagement::Interface::Dtc::iedChanged, this, &HandlerOcAesData::onIedChanged); connect(m_gimDtc.get(), &GuezIedManagement::Interface::Dtc::iedPositionChanged, this, &HandlerOcAesData::onIedPositionChanged); connect(m_iedPositionCycleTimer, &QTimer::timeout, this, &HandlerOcAesData::sendIedPositions); m_iedPositionCycleTimer->setInterval(Settings::iedPositionCycleTime()); DEBUG_LOG(m_logger) << "Starting ied position timer with interval: " << m_iedPositionCycleTimer->interval(); m_iedPositionCycleTimer->start(); return true; } void HandlerOcAesData::demandAesItems(OcSituation::AesType aesType, int eqNumber) { switch (aesType) { case OcSituation::AesType::DangerArea: case OcSituation::AesType::Ied: { DEBUG_LOG(m_logger) << "Got demand for aes type: " << aesType << " from equipment: " << eqNumber; if (!m_collectionTimer[aesType]->isActive()) m_collectionTimer[aesType]->start(); m_demandingEquipments[aesType].insert(eqNumber); break; } default: { WARNING_LOG(m_logger) << "Aes type: " << aesType << " is currently not handled."; } } } void HandlerOcAesData::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 HandlerOcAesData::onDangerAreaChanged(const AreaEffectSimulation::DangerArea &data, DataModification mod) { Q_UNUSED(data); Q_UNUSED(mod); incrementResetCnt(OcSituation::AesType::DangerArea); } void HandlerOcAesData::onIedChanged(const GuezIedManagement::Ied &data, DataModification mod) { Q_UNUSED(data); Q_UNUSED(mod); incrementResetCnt(OcSituation::AesType::Ied); } void HandlerOcAesData::onIedPositionChanged(const GuezIedManagement::IedPosition &data, DataModification mod) { if (mod == DataModification::eRemoved) return; GuezIedManagement::Ied ied = m_gimDtc->ied(data.id()); if (ied.id() > 0) { if (ied.pairingState().underlying() != GuezIedManagement::IedPairingState::NotPaired) m_iedPositionsToBeSend.insert(data.id(), data); } else ERROR_LOG(m_logger) << "Got ied position for not existing ID: " << data.id(); } void HandlerOcAesData::onCollectionTimeOutDemandDangerAreas() { std::function f = std::bind(&HandlerOcAesData::sendDangerAreasToTab, this, std::placeholders::_1); collectionTimeout(OcSituation::AesType::DangerArea, f); } void HandlerOcAesData::onCollectionTimeOutDemandIeds() { std::function f = std::bind(&HandlerOcAesData::sendIedsToTab, this, std::placeholders::_1); collectionTimeout(OcSituation::AesType::Ied, f); } void HandlerOcAesData::sendIedPositions() { if (m_iedPositionsToBeSend.size() == 0) { DEBUG_LOG(m_logger) << "No new ied positions to be send."; return; } TrainingAreaBase::SendOcDataRequest req; req.equipmentNumber(0); // ied positions are always send broadcast QByteArray data, payload; int cnt = 0; // set "header" data LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload); LtsUtils::set1Byte(OcSituation::AesType::IedPositions, data); LtsUtils::set1Byte(cnt, data); for (const auto &pos : qAsConst(m_iedPositionsToBeSend)) { LtsUtils::set2Byte(m_gimDtc->ied(pos.id()).equipmentNumber(), data); const QPointF relCoord(m_positionConverter->relativeFromGeographic(pos.position())); LtsUtils::set3Byte(qRound(relCoord.x()), data); LtsUtils::set3Byte(qRound(relCoord.y()), data); data[1] = ++cnt; QByteArray ba = qCompress(data); DEBUG_LOG(m_logger) << "Added ied position " << pos << " data size: " << data.size() << " compressed size: " << ba.size(); if (ba.size() > Settings::maxNumOfBytePerAesTxCycle()) // one cycle is full -> send { DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' '); DEBUG_LOG(m_logger) << "Compressed Data: " << ba.toHex(' '); payload.append(ba); req.payload(LtsUtils::convert>(payload)); m_tabFtc->sendOcDataRequest(req, this); cnt = 0; data.clear(); payload.clear(); // set new "header" data LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload); LtsUtils::set1Byte(OcSituation::AesType::IedPositions, data); LtsUtils::set1Byte(cnt, data); } } if (cnt > 0) // is still data to be send? { DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' '); DEBUG_LOG(m_logger) << "Compressed Data: " << qCompress(data).toHex(' '); payload.append(qCompress(data)); req.payload(LtsUtils::convert>(payload)); m_tabFtc->sendOcDataRequest(req, this); } m_iedPositionsToBeSend.clear(); } void HandlerOcAesData::collectionTimeout(OcSituation::AesType type, const std::function &sendFunction) { DEBUG_LOG(m_logger) << "There are " << m_demandingEquipments[type].size() << " requests for aes type: " << type << " sending Items as " << ((m_demandingEquipments[type].size() > 5) ? QStringLiteral("broadcast") : QStringLiteral("direct")) << " telegram(s)"; if (m_demandingEquipments[type].size() > 5) sendFunction(0); // broadcast else for (int eqNumber : qAsConst(m_demandingEquipments[type])) sendFunction(eqNumber); //cleanup m_demandingEquipments[type].clear(); } void HandlerOcAesData::sendDangerAreasToTab(int eqNumber) { TrainingAreaBase::SendOcDataRequest req; req.equipmentNumber(eqNumber); const QMap dangerAreas = m_aesDtc->dangerAreas(); QByteArray data, payload; int cnt = 0; // set "header" data LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload); LtsUtils::set1Byte(OcSituation::AesType::DangerArea, data); LtsUtils::set1Byte(cnt, data); for (const auto &dangerArea : dangerAreas) { if (dangerArea.effectStateId() >= AreaEffectSimulation::AesEffectState::EsCreated) // do not transfer ended or deleted areas { LtsUtils::set2Byte(dangerArea.id(), data); QByteArray name(QByteArray::fromStdString(dangerArea.name())); name.resize(8); data.append(name); if (dangerArea.effectStart().is_set()) LtsUtils::set4Byte(dangerArea.effectStart().get() / 1000, data); else LtsUtils::set4Byte(0, data); LtsUtils::set1Byte(dangerArea.segments().size(), data); for (const auto &segment : dangerArea.segments()) { const QPointF relCoord(m_positionConverter->relativeFromGeographic(segment)); LtsUtils::set3Byte(qRound(relCoord.y()), data); LtsUtils::set3Byte(qRound(relCoord.x()), data); } data[1] = ++cnt; QByteArray ba = qCompress(data); DEBUG_LOG(m_logger) << "Added danger area " << dangerArea << " data size: " << data.size() << " compressed size: " << ba.size(); if (ba.size() > Settings::maxNumOfBytePerAesTxCycle()) // one cycle is full -> send { DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' '); DEBUG_LOG(m_logger) << "Compressed Data: " << ba.toHex(' '); payload.append(ba); req.payload(LtsUtils::convert>(payload)); m_tabFtc->sendOcDataRequest(req, this); cnt = 0; data.clear(); payload.clear(); // set new "header" data LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload); LtsUtils::set1Byte(OcSituation::AesType::DangerArea, data); LtsUtils::set1Byte(cnt, data); } } } if (cnt > 0) // is still data to be send? { DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' '); DEBUG_LOG(m_logger) << "Compressed Data: " << qCompress(data).toHex(' '); payload.append(qCompress(data)); req.payload(LtsUtils::convert>(payload)); m_tabFtc->sendOcDataRequest(req, this); } } void HandlerOcAesData::sendIedsToTab(int eqNumber) { TrainingAreaBase::SendOcDataRequest req; req.equipmentNumber(eqNumber); const QMap ieds = m_gimDtc->ieds(); QByteArray data, payload; int cnt = 0; // set "header" data LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload); LtsUtils::set1Byte(OcSituation::AesType::Ied, data); LtsUtils::set1Byte(cnt, data); for (const auto &ied : ieds) { LtsUtils::set2Byte(ied.equipmentNumber(), data); LtsUtils::set1Byte(ied.type().underlying(), data); uint8_t trigger = (uint8_t(ied.tautWireTrigger()) << 3) | (uint8_t(ied.lightBarrierTrigger()) << 2) | (uint8_t(ied.switchTrigger()) << 1) | (uint8_t(ied.remoteTrigger())); LtsUtils::set1Byte(trigger, data); LtsUtils::set1Byte(ied.explosiveForce(), data); LtsUtils::set1Byte(ied.partyId(), data); LtsUtils::set1Byte(ied.tacticalState().underlying(), data); // set an initial position, as only paired ieds are updated concurrently const auto pos = m_gimDtc->iedPosition(ied.id()); const QPointF relCoord(m_positionConverter->relativeFromGeographic(pos.position())); LtsUtils::set3Byte(qRound(relCoord.x()), data); LtsUtils::set3Byte(qRound(relCoord.y()), data); data[1] = ++cnt; QByteArray ba = qCompress(data); DEBUG_LOG(m_logger) << "Added ied " << ied << " data size: " << data.size() << " compressed size: " << ba.size(); if (ba.size() > Settings::maxNumOfBytePerAesTxCycle()) // one cycle is full -> send { DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' '); DEBUG_LOG(m_logger) << "Compressed Data: " << ba.toHex(' '); payload.append(ba); req.payload(LtsUtils::convert>(payload)); m_tabFtc->sendOcDataRequest(req, this); cnt = 0; data.clear(); payload.clear(); // set new "header" data LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload); LtsUtils::set1Byte(OcSituation::AesType::Ied, data); LtsUtils::set1Byte(cnt, data); } } if (cnt > 0) // is still data to be send? { DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' '); DEBUG_LOG(m_logger) << "Compressed Data: " << qCompress(data).toHex(' '); payload.append(qCompress(data)); req.payload(LtsUtils::convert>(payload)); m_tabFtc->sendOcDataRequest(req, this); } } void HandlerOcAesData::incrementResetCnt(OcSituation::AesType aesType) { switch (aesType) { case OcSituation::AesType::DangerArea: { setResetCnt(aesType, m_dangerAreaResetCnt + 1); break; } case OcSituation::AesType::Ied: { setResetCnt(aesType, m_iedResetCnt + 1); break; } default: { WARNING_LOG(m_logger) << "Reset counter for aes type: " << aesType << " is not handled in incrementResetCnt."; break; } } } void HandlerOcAesData::setResetCnt(OcSituation::AesType aesType, int resetCnt) { Database::OssResetCounter data; data.aesTypeId = aesType; data.resetCounter = resetCnt; switch (aesType) { case OcSituation::AesType::DangerArea: { m_dangerAreaResetCnt = resetCnt; break; } case OcSituation::AesType::Ied: { m_iedResetCnt = resetCnt; break; } default: { WARNING_LOG(m_logger) << "Reset counter for aes type: " << aesType << " is not handled in setResetCnt."; break; } } // update db if (!m_dbHandler->update(data)) ERROR_LOG(m_logger) << "Error updating reset counter in db."; } } // namespace OcSituationServer