Files
qt5-legatus-legacy/Lts/Services/InfrastructureManagement/ism_dtc_impl_custom.cpp
T
2026-08-13 18:33:10 +02:00

1160 lines
42 KiB
C++
Executable File

/**
* @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 <DDS/DTC/generic_dtc.h>
#include <DDS/Generic/generic_data_reader.h>
#include <LtsUtilsCore/json_object.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <QSet>
#include <QVector>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QtGui/QPolygonF>
#include <QtGui/QTransform>
#include <unordered_map>
#include <algorithm>
#include <QFile>
#include <QMutex>
#include <QMutexLocker>
//---------------------------------------------------------------------------------------------------------------------------------------------
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<int, StructureElement> structureElementCache;
};
DtcImplCustom::DtcImplCustom(const std::shared_ptr<DDSConnector::DdsConnector> &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<InfrastructureManagement::StructureElement>& 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<DtcImplCustom *>(this); // cast const away
// THIS->reloadStructureElementCache();
// }
// Attention: this method is called from different threads.
{
DtcImplCustom *NON_CONST_THIS = const_cast<DtcImplCustom *>(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<bool(const InstrumentationAsset &)> &predicate,
QMap<int, QVector<InstrumentationAsset> > &allAssets)
{
using DATA_A = InfrastructureManagement::InstrumentationAsset;
using LOANED_DATA_A = rti::sub::LoanedSample<DATA_A>;
using DATA_MPA = InfrastructureManagement::MountingPointAsset;
using LOANED_DATA_MPA = rti::sub::LoanedSample<DATA_MPA>;
using DATA_MSE = InfrastructureManagement::MainStructureElement;
using LOANED_DATA_MSE = rti::sub::LoanedSample<DATA_MSE>;
dds::sub::LoanedSamples<DATA_A> samples_a = m_dtcInstrumentationAsset->readAlive();
std::unordered_map<int, LOANED_DATA_A> assetMap;
for (const LOANED_DATA_A &asset : samples_a)
assetMap.insert(std::make_pair(asset.data().id(), asset));
dds::sub::LoanedSamples<DATA_MSE> samples_mse = m_dtcMainStructureElement->readAlive();
std::unordered_map<int, InfrastructureManagement::StructureElement> 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<DATA_MPA> 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<bool(const InstrumentationAsset &)> &predicate,
QMap<int, QVector<InstrumentationAsset> > &allAssets) const
{
using DATA_A = InfrastructureManagement::InstrumentationAsset;
using LOANED_DATA_A = rti::sub::LoanedSample<DATA_A>;
using DATA_MPA = InfrastructureManagement::MountingPointAsset;
using LOANED_DATA_MPA = rti::sub::LoanedSample<DATA_MPA>;
dds::sub::LoanedSamples<DATA_A> samples_a = m_dtcInstrumentationAsset->readAlive();
std::unordered_map<int, LOANED_DATA_A> assetMap;
for (const LOANED_DATA_A &asset : samples_a)
assetMap.insert(std::make_pair(asset.data().id(), asset));
dds::sub::LoanedSamples<DATA_MPA> 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<int> DtcImplCustom::getSections()
{
QList<int> 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<MountingPointPlan> &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<DATA_MPA>;
dds::sub::LoanedSamples<DATA_MPA> 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<DATA_MPA>;
dds::sub::LoanedSamples<DATA_MPA> 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<int, MainFootprint> DtcImplCustom::getFootprintsByExerciseId(int exerciseId)
{
QMap<int, MainFootprint> 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<int, QPolygonF> DtcImplCustom::getUtmFootprintsByExerciseId(int exerciseId, int &zone, bool isGroundLevel)
{
QMap<int, QPolygonF> 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<InfrastructureManagement::StructureElement> &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<InfrastructureManagement::FootprintElement> &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 <int> 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 <int> 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<int, QPolygonF> &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<int, QPolygonF> &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<int, QPolygonF> &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 <int> DtcImplCustom::getFloorIdsFromBuilding(int buildingId)
{
QVector <int> floorIds;
QList <StructureElement> 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 <StructureElement> DtcImplCustom::getFloorsFromBuilding(int buildingId)
{
QList <StructureElement> 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 <StructureElement> 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 <int> DtcImplCustom::getRoomIdsFromFloorId(int buildingId, int floorId)
{
DEBUG_LOG(m_logger) << "getRoomIdsFromFloorId "
<< " buildingId=" << buildingId
<< " floorId=" << floorId;
QVector <int> 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 <int> DtcImplCustom::getBuildingIdsByExercise ( int exerciseId)
{
QVector <int> 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