/** * @RDE_HEADER@ * ISM Custom functions (extends ism_db_impl.cpp/.h) * $Id$ * $HeadURL$ * $Author$ * Created by Iuliana Ojog, SLSE, 2016-11-26 */ #include "ism_dtc_impl_custom.h" #include "ism_dtc_impl_custom_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------------------------------------------------------------------------- namespace InfrastructureManagement { namespace Interface { //------------------------------------------------------------------------------ // // [GUEZE-3258] Performance-Untersuchung der OpApp // //------------------------------------------------------------------------------ // 10.01.2025, Norbert Thoden // Das Starten dieses Plugins ist teuer, da viele (ca. 500) MainStructureElement // mit noch mehr (~25000) StructureElements // Es wurde // - auf relevante Gebäude gefiltert // - ForeignInterfaceImpl::init() mit QtConcurrent beschleunigt. // - 2025: getStructureElementById ist es wert, beschleunigt zu werden. // Daher wurde ein Cache erstellt: structureElementCache // - diese Cache wird via QSignal mainStructureElementChanged aktualisert. // - Es muss dabei aber sichergestellt werden, dass dies // aktualisert, _BEVOR_ andere slots von mainStructureElementChanged // aufgerufen werden und möglicherweise getStructureElementById nutzen!!! // Also muss das connect im ctor von DtcImplCustom durchgeführt werden. // - Bzgl. ForeignInterfaceImpl::init() ist es also wichtig, das structureElementCache // auf Stand ist. // 2025 zeigt sich, das ein fremdes Plugin (CDS) als erstes das mainStructureElement // enabled. Darauf möchte man sich aber ja nicht verlassen. // Szenario: direct das ResourcenManagement starten: // ./bin/OperatorApplication -b guez -l de_DE -u Norbert -p xxx -a 1 // In getStructureElementById() ist es allerdings auch nicht so einfach, da // es auch verschiedenen Threads aufgerufen werden kann und dann Abstürze drohen... // Daher wird reloadStructureElementCacheIfEmpty inkl. Mutext eingeführt. // - Den structureElementCache mache wir nicht static, da anderen Übungen // möglicherweise andere Gebäude o.a. haben. //------------------------------------------------------------------------------ // Classes anmd files: // source Lts/Plugins/InfrastructureManagement/Data/ism_data_plugin.cpp // Lts/Plugins/InfrastructureManagement/Data/ism_foreign_data_plugin.cpp // Lts/Services/InfrastructureManagement/ism_dtc_impl_custom.cpp // build Lts/Services/InfrastructureManagement/ism_dtc_impl.h // Dtc // ^ // | // DtcImpl // ^ // | // DtcImplCustom // // DataPlugin has member m_dtc and m_foreignInterface (ForeignInterfaceImpl) // -> ForeignInterfaceImpl can just call methods of the Interface 'Dtc'. // Calling methods of DtcImpl or DtcImplCustom is difficult. //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // class Interface::Private //------------------------------------------------------------------------------ class DtcImplCustom::Private { public: Private() = default; ~Private() {} MainFootPrintCache mainFootPrintCacheGroundLevel; QHash structureElementCache; }; DtcImplCustom::DtcImplCustom(const std::shared_ptr &ddsConnector) : DtcImpl(ddsConnector) , d(new Private) { // 1. d->mainFootPrintCacheGroundLevel.init(this); // 2. // [GUEZE-3258] Performance-Untersuchung der OpApp // Norbert Thoden, Patric Karwat, Gerlinde Rudolphus 09.01.2025 // We should be the very first connecting the that signal to guarantee, // the slot being called _before_ all other slots are called!!!!! // Since the DtcImpl is created within this ctor, that should be true connect(this, &DtcImpl::mainStructureElementChanged, this, &DtcImplCustom::updateStructureElementCache, Qt::DirectConnection); // // reloadStructureElementCache(); // reloadStructureElementCache an dieser Stelle führt zu einem Crash in svcAudioVideoManagment - grr } DtcImplCustom::~DtcImplCustom() { delete d; } void DtcImplCustom::reloadStructureElementCacheIfEmpty() { static QMutex s_mutex; QMutexLocker locker(&s_mutex); if (!d->structureElementCache.isEmpty()) return; reloadStructureElementCache(); } long DtcImplCustom::reloadStructureElementCache() { DEBUG_LOG(m_logger) << "ISM::StructureElementCache: reloadStructureElementCache; START, structureElementCacheSize = " << structureElementCacheSize(); // BENCHMARK_SETUP_AND_START(); // struggle on link of QElapesedTimer - grrr waitForHistoricalDataMainStructureElement(1000); d->structureElementCache.clear(); //const auto &data = m_dtcMainStructureElement->readAlive(); for (const auto &sample : m_dtcMainStructureElement->readAlive()) updateStructureElementCache(sample, DataModification::eAdded); DEBUG_LOG(m_logger) << "ISM::StructureElementCache: reloadStructureElementCache; END, structureElementCacheSize = " << structureElementCacheSize(); return d->structureElementCache.size(); } void DtcImplCustom::updateStructureElementCache(const MainStructureElement &mse, DataModification modType) { static bool bIsVeryFirstCall = true; if (bIsVeryFirstCall) { DEBUG_LOG(m_logger) << "ISM::StructureElementCache: updateStructureElementCache starts"; bIsVeryFirstCall = false; } const std::vector& ses = mse.elements(); for (const auto &se : ses) { if (modType==DataModification::eRemoved) d->structureElementCache.remove(se.id()); else d->structureElementCache[se.id()] = se; } // for DEBUG_LOG(m_logger) << "ISM::StructureElementCache updateStructureElementCache, new size = " << d->structureElementCache.size(); } long DtcImplCustom::structureElementCacheSize() const { return d->structureElementCache.size(); } //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::isElementAssignedToExercise(const MainStructureElement &mse, int exerciseId) const { const QJsonObject obj(LtsUtils::JsonObject::fromJson(mse.properties())); auto it = obj.find(QStringLiteral("available_in_exercise")); if (it == obj.end()) { INFO_LOG(m_logger) << "Main structure element id " << mse.id() << ": Attribute \"available_in_exercise\" not found. Ignoring..."; return false; } const QJsonArray arr = (*it).toArray(); if (std::any_of(arr.begin(), arr.end(), [exerciseId](const QJsonValue & val) { return val.isDouble() && exerciseId == val.toInt(); })) return true; INFO_LOG(m_logger) << "Main structure element id " << mse.id() << ": Not assigned (\"available_in_exercise\") to current exercise which has id " << exerciseId << ". Ignoring..."; return false; } // tested in ISM Module Tests //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::getMountingPointAssetId(int structureElementId, int assetId, int &mountingPointAssetId) const { // Returns valid MpaId, if one or both input Values are set. for (const auto &sample : m_dtcMountingPointAsset->readAlive()) { DEBUG_LOG(m_logger) << "getMountingPointAssetId: " << " sample.data().structureElementId()=" << sample.data().structureElementId() << " structureElementId=" << structureElementId << " sample.data().assetId()=" << sample.data().assetId() << " assetId" << assetId; if ((sample.data().structureElementId() == structureElementId || structureElementId <= 0) && (sample.data().assetId() == assetId || assetId <= 0)) { mountingPointAssetId = sample.data().id(); return true; } } mountingPointAssetId = 0; return false; } //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::getAssetIdFromMountingPoint(int structureElementId, int mountingPointAssetId, int &assetId) const { // Returns valid MpaId, if one or both input Values are set. for (const auto &sample : m_dtcMountingPointAsset->readAlive()) { TRACE_LOG(m_logger) << "getAssetIdFromMountingPoint:" << " structureElementId=" << structureElementId << " mountingPointAssetId=" << mountingPointAssetId << " sample.data().id()=" << sample.data().id() << " sample.data().structureElementId()=" << sample.data().structureElementId() << " sample.data().assetId()=" << sample.data().assetId(); if (sample.data().structureElementId() == structureElementId) { assetId = sample.data().assetId(); return true; } } assetId = 0; return false; } //--------------------------------------------------------------------------------------------------------------------------------------------- int DtcImplCustom::getOpsCodeFromJSon(const std::string &s) const { QJsonParseError jError; QJsonDocument doc(QJsonDocument::fromJson(s.c_str(), &jError)); if (jError.error != QJsonParseError::NoError || !doc.isObject()) { ERROR_LOG(m_logger) << "Can not read json data from IPS asset - properties are not parsable: " << jError.errorString() << " - '" << s << "'"; return 0; } QJsonObject obj(doc.object()); return obj[QStringLiteral("opticalCode")].toInt(); } //--------------------------------------------------------------------------------------------------------------------------------------------- int DtcImplCustom::getOpticalCodeFromAssetId(int assetId) const { DEBUG_LOG(m_logger) << "getOpticalCodeFromAsset: assetId=" << assetId; // Returns valid MpaId, if one or both input Values are set. for (const InfrastructureManagement::InstrumentationAsset &instrumentationAsset : m_dtcInstrumentationAsset->readAlive()) { DEBUG_LOG(m_logger) << "getAssetIdFromOpticalCode:" << instrumentationAsset.properties(); if ((instrumentationAsset.mountingPointTypeId() == InfrastructureManagement::MountingPointType_def::IndoorIPS) || (instrumentationAsset.mountingPointTypeId() == InfrastructureManagement::MountingPointType_def::OutdoorIPS)) { DEBUG_LOG(m_logger) << "getOpticalCodeFromAsset:" << " assetId=" << instrumentationAsset.id() << " opticalCode=" << getOpsCodeFromJSon(instrumentationAsset.properties()); if (assetId == instrumentationAsset.id()) return getOpsCodeFromJSon(instrumentationAsset.properties()); } } return assetId; } int DtcImplCustom::getAssetIdFromOpticalCode(int opticalCode) const { DEBUG_LOG(m_logger) << "getAssetIdFromOpticalCode: opticalCode=" << opticalCode; int assetId = -1; // Returns valid MpaId, if one or both input Values are set. for (const InfrastructureManagement::InstrumentationAsset &instrumentationAsset : m_dtcInstrumentationAsset->readAlive()) { DEBUG_LOG(m_logger) << "getAssetIdFromOpticalCode:" << instrumentationAsset.properties(); if ((instrumentationAsset.mountingPointTypeId() == InfrastructureManagement::MountingPointType_def::IndoorIPS) || (instrumentationAsset.mountingPointTypeId() == InfrastructureManagement::MountingPointType_def::OutdoorIPS)) { DEBUG_LOG(m_logger) << "getAssetIdFromOpticalCode:" << instrumentationAsset.properties(); int opticalCodeAsset = getOpsCodeFromJSon(instrumentationAsset.properties()); if (opticalCodeAsset == opticalCode) return instrumentationAsset.id(); } } return assetId; } int DtcImplCustom::getAssetIdFromMountingPointId(int mainStructureElementId, int mountingPointId) const { DEBUG_LOG(m_logger) << "getAssetIdFromMountingPointId mainStructureElementId=" << mainStructureElementId << " mountingPointId=" << mountingPointId; for (const auto &sample : m_dtcMainStructureElement->readAlive()) { if (sample.data().id() != mainStructureElementId) continue; for (const StructureElement &elem : sample.data().elements()) { DEBUG_LOG(m_logger) << "elem.id=" << elem.id() << " elem.mountingPointId=" << elem.mountingPointId(); if (elem.mountingPointId() == mountingPointId) { return (getMountedAssetId(elem.id())); } } } return -1; } //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::getOpsDataFromMseIdAndSeId(int mainStructureElementId, int structureElementId, int &roomId, Core::GeoPosition &geoPosition) const { DEBUG_LOG(m_logger) << "getOpsDataFromMseIdAndSeId" << " mainStructureElementId=" << mainStructureElementId << " structureElementId=" << structureElementId; for (const auto &sample : m_dtcMainStructureElement->readAlive()) { if (sample.data().id() != mainStructureElementId) continue; for (const StructureElement &elem : sample.data().elements()) { DEBUG_LOG(m_logger) << "elem.id=" << elem.id() << " elem.mountingPointId=" << elem.mountingPointId() << " elem.path().size=" << elem.path().size(); if (elem.id() == structureElementId) { if (elem.path().size() > 1) roomId = elem.path().at(elem.path().size() - 2); geoPosition = elem.geoPosition(); return true; } } } return true; } // tested in ISM Module Tests //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::getStructureElementById(int structureElementId, StructureElement &structureElement) const { // initial code: (but slow on 500 bldgs, ~25000 structureElements) // const auto &data = m_dtcMainStructureElement->readAlive(); // for (const auto &sample : data) // { // for (const StructureElement &elem : sample.data().elements()) // { // if (elem.id() == structureElementId) // { // structureElement = elem; // return true; // } // } // } // return false; // not reliable (since threading) // if (d->structureElementCache.isEmpty()) // { // DtcImplCustom *THIS = const_cast(this); // cast const away // THIS->reloadStructureElementCache(); // } // Attention: this method is called from different threads. { DtcImplCustom *NON_CONST_THIS = const_cast(this); // cast const away NON_CONST_THIS->reloadStructureElementCacheIfEmpty(); } // use cache: const auto& it = d->structureElementCache.find(structureElementId); if (it != d->structureElementCache.end()) { structureElement = *it; return true; } DEBUG_LOG(m_logger) << "ISM::StructureElementCache old approach is used"; // fallback: old approach for (const auto &sample : m_dtcMainStructureElement->readAlive()) { for (const StructureElement &elem : sample.data().elements()) { if (elem.id() == structureElementId) { structureElement = elem; return true; } } } return false; } //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::getStructureElementInMntPointAssetByAssetId(int assetId, StructureElement &structureElement) const { int structureElementId = 0; for (const auto &sample : m_dtcMountingPointAsset->readAlive()) { if (sample.data().assetId() == assetId) { structureElementId = sample.data().structureElementId(); break; } } if (structureElementId == 0) return false; for (const auto &sample : m_dtcMainStructureElement->readAlive()) { for (const StructureElement &elem : sample.data().elements()) { if (elem.id() == structureElementId) { structureElement = elem; return true; } } } return false; } //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::getStructureElementByBuildingIdAndMountingPointId(int mainStructureElementId, int mountingPointId, StructureElement &structureElement) { for (const auto &sample : m_dtcMainStructureElement->readAlive()) { if (sample.data().id() != mainStructureElementId) continue; for (const StructureElement &elem : sample.data().elements()) { if (elem.mountingPointId() == mountingPointId) { structureElement = elem; return true; } } } return false; } //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::getAllAssetsPerRoom(const std::function &predicate, QMap > &allAssets) { using DATA_A = InfrastructureManagement::InstrumentationAsset; using LOANED_DATA_A = rti::sub::LoanedSample; using DATA_MPA = InfrastructureManagement::MountingPointAsset; using LOANED_DATA_MPA = rti::sub::LoanedSample; using DATA_MSE = InfrastructureManagement::MainStructureElement; using LOANED_DATA_MSE = rti::sub::LoanedSample; dds::sub::LoanedSamples samples_a = m_dtcInstrumentationAsset->readAlive(); std::unordered_map assetMap; for (const LOANED_DATA_A &asset : samples_a) assetMap.insert(std::make_pair(asset.data().id(), asset)); dds::sub::LoanedSamples samples_mse = m_dtcMainStructureElement->readAlive(); std::unordered_map structElementMap; for (const LOANED_DATA_MSE &mse : samples_mse) for (const auto &se : mse.data().elements()) structElementMap.insert(std::make_pair(se.id(), se)); dds::sub::LoanedSamples samples_mpa = m_dtcMountingPointAsset->readAlive(); for (const LOANED_DATA_MPA &mpa : samples_mpa) { const int assetId = mpa.data().assetId(); const auto assetIt = assetMap.find(assetId); if (assetIt == assetMap.end()) continue; if (!predicate(assetIt->second)) continue; const int structureElementId = mpa.data().structureElementId(); const auto structureElementIt = structElementMap.find(structureElementId); if (structureElementIt == structElementMap.end()) continue; const InfrastructureManagement::StructureElement &se = structureElementIt->second; if (se.structureElementTypeId() != InfrastructureManagement::StructureElementType::MountingPoint) continue; int roomId = 0; if (se.path().size() > 1) roomId = se.path().at(se.path().size() - 2); allAssets[roomId].push_back(assetIt->second); } return true; } //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::getAllAssetsPerBuilding(const std::function &predicate, QMap > &allAssets) const { using DATA_A = InfrastructureManagement::InstrumentationAsset; using LOANED_DATA_A = rti::sub::LoanedSample; using DATA_MPA = InfrastructureManagement::MountingPointAsset; using LOANED_DATA_MPA = rti::sub::LoanedSample; dds::sub::LoanedSamples samples_a = m_dtcInstrumentationAsset->readAlive(); std::unordered_map assetMap; for (const LOANED_DATA_A &asset : samples_a) assetMap.insert(std::make_pair(asset.data().id(), asset)); dds::sub::LoanedSamples samples_mpa = m_dtcMountingPointAsset->readAlive(); for (const LOANED_DATA_MPA &mpa : samples_mpa) { const int assetId = mpa.data().assetId(); DEBUG_LOG(m_logger) << "Found assetId = " << assetId; const auto assetIt = assetMap.find(assetId); if (assetIt == assetMap.end()) continue; if (!predicate(assetIt->second)) continue; const int structureElementId = mpa.data().structureElementId(); int buildingId = 0; if (getBuildingId(structureElementId, buildingId)) { DEBUG_LOG(m_logger) << "Found buildingId = " << buildingId << ", assetId = " << assetId; allAssets[buildingId].push_back(assetIt->second); } } return true; } //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::getBuildingId(int structureElementId, int &buildingId) const { for (const auto &sample : m_dtcMainStructureElement->readAlive()) { for (const auto &se : sample.data().elements()) { if (se.id() == structureElementId) { buildingId = sample.data().id(); return true; } } } buildingId = 0; return false; } //--------------------------------------------------------------------------------------------------------------------------------------------- QList DtcImplCustom::getSections() { QList sections; for (const auto &sample : m_dtcMainStructureElement->readAlive()) { if (!sections.contains(sample.data().groupId())) sections.append(sample.data().groupId()); } return sections; } //--------------------------------------------------------------------------------------------------------------------------------------------- QString DtcImplCustom::getBuildingDisplayName(int buildingId) { return getBuildingDisplayName(mainStructureElement(buildingId)); } //--------------------------------------------------------------------------------------------------------------------------------------------- QString DtcImplCustom::getBuildingDisplayName(const MainStructureElement &mse) const { for (const StructureElement &elem : mse.elements()) { if ((elem.structureElementTypeId() == InfrastructureManagement::StructureElementType::Building) || (elem.structureElementTypeId() == InfrastructureManagement::StructureElementType::Bridge)) { if (elem.displayName().size() > 0) return QString::fromStdString(elem.displayName()); } } return QString::fromStdString(mse.name()); } MountingPointPlan DtcImplCustom::getMountingPointPlan(int scheduleId, int structureElementId) const { for (const auto &sample : m_dtcMainSchedule->readAlive()) { if (sample.data().id() == scheduleId) { for (auto &mpp : sample.data().elements()) { if (mpp.structureElementId() == structureElementId) return mpp; } } } return MountingPointPlan(); } //--------------------------------------------------------------------------------------------------------------------------------------------- int DtcImplCustom::getBuildingRoomsCount(int buildingId) { int roomCount = 0; const MainStructureElement mse = mainStructureElement(buildingId); if (mse.id() > 0) { for (const StructureElement &elem : mse.elements()) { if (elem.structureElementTypeId() == InfrastructureManagement::StructureElementType::Room) roomCount++; } } return roomCount; } //--------------------------------------------------------------------------------------------------------------------------------------------- BridgeStatus DtcImplCustom::getBridgeStatus(int buildingId) { BridgeStatus ret = BridgeStatus::Intact; const MainStructureElement mse = mainStructureElement(buildingId); if (mse.id() > 0 && mse.structureElementTypeId() == StructureElementType::Bridge) { const QJsonObject obj(LtsUtils::JsonObject::fromJson(mse.properties())); auto it = obj.find(QStringLiteral("bridgeStatus")); if (it == obj.end()) return ret; int st = (*it).toInt(); if (st >= 1) ret = BridgeStatus::Broken; } return ret; } // this is just a quicker method to determine if a plan is complete or not //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::checkSchedule(int scheduleId) { const MainSchedule schedule = mainSchedule(scheduleId); for (const MountingPointPlan &mpp : schedule.elements()) // { if (mpp.mountingPointState() == MountingPointState::Idle) // whatever mounted or unmounted should be ok ? continue; int assetId = getMountedAssetId(mpp.structureElementId()); if (mpp.mountingPointState() == MountingPointState::Empty && assetId != -1) //mounted, but it should be empty return false; else if (mpp.mountingPointState() == MountingPointState::Equipped && assetId == -1) // not mounted, but it should be return false; } return true; } // this gives us info about differences too //--------------------------------------------------------------------------------------------------------------------------------------------- bool DtcImplCustom::getScheduleDifferences(int scheduleId, QList &diffList) { const MainSchedule schedule = mainSchedule(scheduleId); for (const MountingPointPlan &mpp : schedule.elements()) // { if (mpp.mountingPointState() == MountingPointState::Idle) // whatever mounted or unmounted should be ok ? continue; int assetId = getMountedAssetId(mpp.structureElementId()); if (mpp.mountingPointState() == MountingPointState::Empty && assetId != -1) //mounted, but it should be empty diffList.append(mpp); else if (mpp.mountingPointState() == MountingPointState::Equipped && assetId == -1) // not mounted, but it should be diffList.append(mpp); } return diffList.isEmpty(); } //--------------------------------------------------------------------------------------------------------- int DtcImplCustom::getMountedAssetId(int structureElementId) const { // Norbert Thoden, 12.12.2022: // implementation could be slow // consider using prepared ISM::MountingPoints, prepared at loading of plugion: // InfrastructureManagement::ForeignDataInterface m_dataInterface; // m_dataInterface->mountingPoint(mountingPointId).associatedAssetId(); using DATA_MPA = InfrastructureManagement::MountingPointAsset; using LOANED_DATA_MPA = rti::sub::LoanedSample; dds::sub::LoanedSamples samples_mpa = m_dtcMountingPointAsset->readAlive(); for (const LOANED_DATA_MPA &mpa : samples_mpa) { if (mpa.data().structureElementId() == structureElementId) return mpa.data().assetId(); } return -1; } //--------------------------------------------------------------------------------------------------------- int DtcImplCustom::getStructueElementIdFromAssetId(int assetId) const { using DATA_MPA = InfrastructureManagement::MountingPointAsset; using LOANED_DATA_MPA = rti::sub::LoanedSample; dds::sub::LoanedSamples samples_mpa = m_dtcMountingPointAsset->readAlive(); for (const LOANED_DATA_MPA &mpa : samples_mpa) { DEBUG_LOG(m_logger) << "mpa.data().assetId()=" << mpa.data().assetId() << " assetId=" << assetId << " mpa.data().structureElementId()=" << mpa.data().structureElementId(); if (mpa.data().assetId() == assetId) return mpa.data().structureElementId(); } return -1; } QMap DtcImplCustom::getFootprintsByExerciseId(int exerciseId) { QMap footprints; const auto &buildings = mainStructureElements(); for (const auto &building : buildings) { int mseId = building.id(); if (isElementAssignedToExercise(building, exerciseId)) footprints.insert(mseId, mainFootprint(mseId)); } return footprints; } QMap DtcImplCustom::getUtmFootprintsByExerciseId(int exerciseId, int &zone, bool isGroundLevel) { QMap footprints; DEBUG_LOG(m_logger) << "getUtmFootprintsByExerciseId exerciseId=" << exerciseId; const auto &buildings = mainStructureElements(); for (const auto &building : buildings) { const int mseId = building.id(); const MainStructureElement &mse = mainStructureElement(mseId); DEBUG_LOG(m_logger) << "mseId: " << mseId; if (isElementAssignedToExercise(building, exerciseId)) { // skip Bridges: const std::vector &elementsRef = mse.elements(); // per definition, frontElement contains StructureElementType: if (elementsRef.empty() || (elementsRef.front().structureElementTypeId() != InfrastructureManagement::StructureElementType_def::Building) ) { DEBUG_LOG(m_logger) << "Footprint: " << mseId << ", skip due to StructureElementType != Building"; continue; } // END skip Bridges: const QPolygonF &footprintUtm = getUtmFootprintById(mseId, zone, isGroundLevel); if (footprintUtm.isEmpty()) { INFO_LOG(m_logger) << "getUtmFootprintsByExerciseId, mseId " << mseId << " skip since footprintUtm is empty"; continue; } footprints.insert(mseId, footprintUtm); DEBUG_LOG(m_logger) << "getUtmFootprintsByExerciseId footprints.insert mseId=" << mseId; } } DEBUG_LOG(m_logger) << "getUtmFootprintsByExerciseId footprints.size=" << footprints.size(); return footprints; } // returns absolut utm positions of the footprint QPolygonF DtcImplCustom::getUtmFootprintById(int mseId, int &zoneOut, bool isGroundLevel) { // attention: zone is just _out_ variable, not _in_ variable!!! return d->mainFootPrintCacheGroundLevel.getUtmFootprintById(mseId, isGroundLevel, zoneOut); //--------------- // const MainStructureElement &mse = mainStructureElement(mseId); // if (mse.id() != mseId) // return {}; // const MainFootprint &mf = mainFootprint(mseId); // if (mf.id() != mseId) // return {}; // const std::vector &mfElements = mf.elements(); // // insert all Points to poly // QPolygonF poly; // poly.reserve(mfElements.size()); // DEBUG_LOG(m_logger) << "mseId: " << mseId << " isGroundLevel: " << isGroundLevel; // for (const InfrastructureManagement::FootprintElement &element : qAsConst(mfElements)) // { // // maybe this is easier? // // // if different, continue // // if (isGroundLevel != element.isGroundLevel()) // // continue; // if (isGroundLevel && !element.isGroundLevel()) // continue; // else if (!isGroundLevel && element.isGroundLevel()) // continue; // const auto &relPos = element.relPosition(); // DEBUG_LOG(m_logger) << "relPos: " << relPos.offsetX() << ", " << relPos.offsetY() << ", " << relPos.offsetZ(); // // maybe this is easier? DEBUG_LOG(m_logger) << "relPos: " << relPos; // poly.push_back(QPointF(relPos.offsetX(), relPos.offsetY())); // } // // Get other footprint if groundLevel footprint is not available // if (poly.isEmpty() && isGroundLevel) // { // for (const InfrastructureManagement::FootprintElement &element : qAsConst(mfElements)) // { // const auto &relPos = element.relPosition(); // poly.push_back(QPointF(relPos.offsetX(), relPos.offsetY())); // } // } // // rotate poly // QTransform trans; // trans.rotate(mse.angle()); // poly = trans.map(poly); // const auto utm = LtsCoordTrans::UtmCoordinate::fromGeographic(QPointF(mse.geoPosition().lon(), mse.geoPosition().lat()), zone); // zone = utm.zone; // const QPointF utmFpPosition(utm.x, utm.y); // QPolygonF retPolyUtm; // retPolyUtm.reserve(poly.size()); // for (const QPointF &point : qAsConst(poly)) // { // const QPointF finalUtmPoint(utmFpPosition + point); // retPolyUtm.push_back(finalUtmPoint); // DEBUG_LOG(m_logger) << "utmPoint x: " << finalUtmPoint.x() << " utmPoint.y : " << finalUtmPoint.y(); // } // return retPolyUtm; } // returns all building IDs that have an intersection with the area under the polygon and are assigned to the specified exercise. // Parameter: completeEnclosed true = Only buildings that are completely enclosed by the polygon are taken into account. // false = All buildings that are completely enclosed by the polygon and that have an overlap with the polygon are taken into account. QVector DtcImplCustom::getBuildingByPolygon(int exerciseId, const QPolygonF &polygon, bool completeEnclosed, bool isGroundLevel) { DEBUG_LOG(m_logger) << "getBuildingByPolygon execiseId=" << exerciseId << " polygon.size=" << polygon.size() << " completeEnclosed=" << completeEnclosed; QVector result; QPolygonF polygonUtm; polygonUtm.reserve(polygon.size()); // Convert Form to UTM int zone = -1; for (const QPointF &point : qAsConst(polygon)) { auto utm = LtsCoordTrans::UtmCoordinate::fromGeographic(point, zone); DEBUG_LOG(m_logger) << "geo=" << point.x() << "/" << point.y() << " utm=" << utm.x << "/" << utm.y << " zone=" << zone; polygonUtm.append(QPointF(utm.x, utm.y)); } const QMap &buildingPoygons = getUtmFootprintsByExerciseId(exerciseId, zone, isGroundLevel); DEBUG_LOG(m_logger) << "buildingPoygons.size=" << buildingPoygons.size(); DEBUG_LOG(m_logger) << "zone: " << zone; for (auto footprintPolygon = buildingPoygons.cbegin(), end = buildingPoygons.cend(); footprintPolygon != end; ++footprintPolygon) { DEBUG_LOG(m_logger) << "key=" << footprintPolygon.key(); #if QT_VERSION < QT_VERSION_CHECK(5,10,0) if (polygonUtm.intersected(footprintPolygon.value()).isClosed()) #else if (polygonUtm.intersects(footprintPolygon.value())) #endif { bool appendToResult = true; DEBUG_LOG(m_logger) << "getBuildingByPolygon Append Building id=" << footprintPolygon.key(); if (completeEnclosed) { const QPolygonF &footprintPolygonRef = footprintPolygon.value(); for (const QPointF &fpPoint : qAsConst(footprintPolygonRef)) { if (!polygonUtm.containsPoint(fpPoint, Qt::OddEvenFill)) { DEBUG_LOG(m_logger) << "!polygonUtm.containsPoint" << " point=" << fpPoint.x() << "/" << fpPoint.y(); appendToResult = false; break; } } } DEBUG_LOG(m_logger) << " appendToResult=" << appendToResult; if (appendToResult) { DEBUG_LOG(m_logger) << " appendToResult=" << appendToResult << " key=" << footprintPolygon.key(); result.append(footprintPolygon.key()); } } } return result; } // returns building ID surrounds the polygon and is assigned to the specified exercise. // returns -1 if no building found int DtcImplCustom::getSurroundingBuildingByPolygon(int exerciseId, const QPolygonF &polygon, bool isGroundLevel) { bool completeEnclosed = true; DEBUG_LOG(m_logger) << "getSurroundingBuildingByPolygon execiseId=" << exerciseId << " polygon.size=" << polygon.size() << " completeEnclosed=" << completeEnclosed; QPolygonF polygonUtm; polygonUtm.reserve(polygon.size()); // Convert Form to UTM int zone = -1; for (const QPointF &point : qAsConst(polygon)) { auto utm = LtsCoordTrans::UtmCoordinate::fromGeographic(point, zone); DEBUG_LOG(m_logger) << "geo=" << point.x() << "/" << point.y() << " utm=" << utm.x << "/" << utm.y; polygonUtm.append(QPointF(utm.x, utm.y)); } const QMap &buildingPoygons = getUtmFootprintsByExerciseId(exerciseId, zone, isGroundLevel); DEBUG_LOG(m_logger) << "buildingPoygons.size=" << buildingPoygons.size(); DEBUG_LOG(m_logger) << "zone: " << zone; // loop over all buildings for (auto footprintPolygon = buildingPoygons.cbegin(), end = buildingPoygons.cend(); footprintPolygon != end; ++footprintPolygon) { DEBUG_LOG(m_logger) << "key=" << footprintPolygon.key(); for (int i = 0; i < footprintPolygon.value().size(); i++) DEBUG_LOG(m_logger) << " value=" << footprintPolygon.value()[i].x() << "/" << footprintPolygon.value()[i].y(); #if QT_VERSION < QT_VERSION_CHECK(5,10,0) if (polygonUtm.intersected(footprintPolygon.value()).isClosed()) #else if (polygonUtm.intersects(footprintPolygon.value())) #endif { bool appendToResult = true; DEBUG_LOG(m_logger) << "getBuildingByPolygon Append Building id=" << footprintPolygon.key(); if (completeEnclosed) { for (const QPointF &fpPoint : qAsConst(polygonUtm)) { if (!footprintPolygon.value().containsPoint(fpPoint, Qt::OddEvenFill)) { DEBUG_LOG(m_logger) << "!footprintPolygonRef.containsPoint" << " point=" << fpPoint.x() << "/" << fpPoint.y(); appendToResult = false; break; } } } DEBUG_LOG(m_logger) << " appendToResult=" << appendToResult; if (appendToResult) { DEBUG_LOG(m_logger) << " appendToResult=" << appendToResult << " key=" << footprintPolygon.key(); return footprintPolygon.key(); } } } return -1; } /*! * \brief DtcImplCustom::getBuildingIdAtPosition * \param exerciseId * \param utmPos * \param isGroundLevel * \return */ int DtcImplCustom::getBuildingIdAtPosition(int exerciseId, const QPointF &utmPos, bool isGroundLevel) { int zone = -1; int bldId = -1; const QMap &buildingPoygons = getUtmFootprintsByExerciseId(exerciseId, zone, isGroundLevel); for (auto bldFootPrint = buildingPoygons.cbegin(), end = buildingPoygons.cend(); bldFootPrint != end; ++bldFootPrint) { const QPolygonF &bldUtmPolygon = bldFootPrint.value(); if (bldUtmPolygon.containsPoint(utmPos, Qt::OddEvenFill)) { bldId = bldFootPrint.key(); break; } } return bldId; } // The floors list is sorted according to height. QVector DtcImplCustom::getFloorIdsFromBuilding(int buildingId) { QVector floorIds; QList floors = getFloorsFromBuilding(buildingId); floorIds.reserve(floors.size()); for (int i = 0; i < floors.size(); i++) floorIds.append(floors.at(i).id()); return floorIds; } bool structureElementHeightComparator(const StructureElement &a, const StructureElement &b) { return a.relPosition().offsetZ() < b.relPosition().offsetZ(); } QList DtcImplCustom::getFloorsFromBuilding(int buildingId) { QList floors; const MainStructureElement mse = mainStructureElement(buildingId); // Determine all floor heights if (mse.id() > 0) { for (const StructureElement &elem : mse.elements()) { if (elem.structureElementTypeId() == InfrastructureManagement::StructureElementType::Floor) { DEBUG_LOG(m_logger) << "getFloorsFromBuilding append " << " id=" << elem.id() << " Z=" << elem.relPosition().offsetZ(); floors.append(elem); } } } // put the floor heights in the correct order std::sort(floors.begin(), floors.end(), structureElementHeightComparator); return floors; } double DtcImplCustom::getHalfFloorHeight(int buildingId, int floorId) { QList floorHeights = getFloorsFromBuilding(buildingId); DEBUG_LOG(m_logger) << "getHalfFloorHeight" << " buildingId=" << buildingId << " floorId=" << floorId << " floorHeights.size=" << floorHeights.size(); // Determine the height of each floor. The specified z-value is the height of the floor for (int i = 0; i < floorHeights.size(); i++) { DEBUG_LOG(m_logger) << "i=" << i << " id= " << floorHeights[i].id() << " offsetZ()=" << floorHeights[i].relPosition().offsetZ(); if (floorHeights[i].id() == floorId) { if (floorHeights.size() > i + 1) return (floorHeights[i].relPosition().offsetZ() + floorHeights[i + 1].relPosition().offsetZ()) / 2.0; else return floorHeights[i].relPosition().offsetZ() + 2.0; } } return 0.0; } QVector DtcImplCustom::getRoomIdsFromFloorId(int buildingId, int floorId) { DEBUG_LOG(m_logger) << "getRoomIdsFromFloorId " << " buildingId=" << buildingId << " floorId=" << floorId; QVector rooms; const MainStructureElement mse = mainStructureElement(buildingId); rooms.reserve(mse.elements().size()); // Determine all floor heights if (mse.id() > 0) { for (const StructureElement &elem : mse.elements()) { std::vector< int32_t > path = elem.path(); if (std::find(path.begin(), path.end(), floorId) != path.end()) if (elem.structureElementTypeId() == InfrastructureManagement::StructureElementType::Room) { DEBUG_LOG(m_logger) << " rooms.append=" << elem.id(); rooms.append(elem.id()); } } } return rooms; } QVector DtcImplCustom::getBuildingIdsByExercise ( int exerciseId) { QVector result; const auto &buildings = mainStructureElements(); for (const auto &building : buildings) { if (isElementAssignedToExercise(building, exerciseId)) result.append(building.id()); } return result; } QByteArray DtcImplCustom::getVulnerabilityModelData( int buildingId) { const MainStructureElement &mse = mainStructureElement(buildingId); if (mse.id() != buildingId) { WARNING_LOG(m_logger) << "mainStructureElement not found, id/buildingId = " << buildingId; return {}; } static const QString optPath = QStringLiteral("/opt/BuildingModels/StaticMeshes/"); const QString &vulnerabilityModelFilename = optPath + QStringLiteral("%1.flt.VWM_Building.xml").arg(QString::fromStdString(mse.name())); QFile f(vulnerabilityModelFilename); if (!f.open(QIODevice::ReadOnly)) { WARNING_LOG(m_logger) << "Could not open file '" << vulnerabilityModelFilename << "' for reading: " << f.errorString(); } QByteArray dataBa = f.readAll(); return dataBa; } } // namespace Interface } // namespace InfrastructureManagement