/** * Retrieve data from InfrastructeManagement * $Id$ * $HeadURL$ * $Author$ * Created by Christian Ehrlicher, SLSE, 2016-04-14 */ #include "ism_handler.h" #include "main_handler.h" #include "settings.h" #include #include #include namespace TrainingAreaBase { IsmHandler::IsmHandler(MainHandler *mainHandler) : m_mainHandler(mainHandler) , m_dtcIsm(nullptr) , m_distanceIpsToGps(Settings::distanceIpsToGps()) , m_distanceOpsToGps(Settings::distanceOpsToGps()) , m_logger(getLogger("IsmHandler")) {} bool IsmHandler::init() { if (m_dtcIsm) return true; m_dtcIsm = InfrastructureManagement::Interface::Dtc::create(m_mainHandler->ddsHandler()->ddsConnector()); connect(m_dtcIsm.get(), &InfrastructureManagement::Interface::Dtc::instrumentationAssetChanged, this, &IsmHandler::onInstrumentationAssetChanged); connect(m_dtcIsm.get(), &InfrastructureManagement::Interface::Dtc::mountingPointAssetChanged, this, &IsmHandler::onMountingPointAssetChanged); connect(m_dtcIsm.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged, this, &IsmHandler::onMainStructureElementChanged); connect(m_dtcIsm.get(), &InfrastructureManagement::Interface::Dtc::mainFootprintChanged, this, &IsmHandler::onMainFootprintChanged); if (!m_dtcIsm->enableInstrumentationAsset() || !m_dtcIsm->enableMainStructureElement() || !m_dtcIsm->enableMountingPointAsset() || !m_dtcIsm->enableMainFootprint()) { ERROR_LOG(m_logger) << "Can not read data from ISM"; return false; } // int exerciseId = m_mainHandler->ddsHandler()->getExerciseId(); // m_mainFootprints = m_dtcIsm->getFootprintsByExerciseId(exerciseId); // // Get 'static' data for all MainFootprints // { // auto cb = [this](const InfrastructureManagement::MainFootprint & mfp) // { // onMainFootprintChanged(mfp, DataModification::eAdded); // }; // if (!m_dtcIsm->getFootprintsByExerciseId(exerciseId)) // return false; // } return true; } IsmHandler::OpsData IsmHandler::dataFromOpsCode(int ipsCode) { if ( m_opsData.contains(ipsCode)) { DEBUG_LOG(m_logger) << "dataFromOpsCode contains ipsCode:" << ipsCode << " assetId=" << m_opsData.value(ipsCode).assetId << " roomId=" << m_opsData.value(ipsCode).roomId << " mainStructureElementId=" << m_opsData.value(ipsCode).mainStructureElementId << " structureElementId=" << m_opsData.value(ipsCode).structureElementId << " mountingPointTypeId" << m_opsData.value(ipsCode).mountingPointTypeId; if (m_opsData.value(ipsCode).mainStructureElementId > 0 || m_opsData.value(ipsCode).structureElementId > 0) return m_opsData.value(ipsCode); } int assetId = m_dtcIsm->getAssetIdFromOpticalCode(ipsCode); OpsData opsData; // auto it = m_opsCode2AssetId.find(ipsCode); // if (it == m_opsCode2AssetId.end()) if ( assetId < 0) { WARNING_LOG(m_logger) << "No asset for ops code=" << ipsCode << " found"; return {}; } opsData.opsCode = ipsCode; opsData.assetId = assetId; // if (opsData.structureElementId < 0 ) // { // WARNING_LOG(m_logger) << "Asset assetId=)" << assetId << " not mounted"; // return {}; // } DEBUG_LOG(m_logger) << "OpsData.structureElementId=" << opsData.structureElementId << " assetId=" << opsData.assetId << " mountingPointTypeId=" << opsData.mountingPointTypeId << " opsCode=" << opsData.opsCode << " mainStructureElementId=" << opsData.mainStructureElementId << " roomId=" << opsData.roomId; { // [GUEZE-3496] Teilnehmer im Gebaeudeviewer nicht sichtbar // Wenn optische Codes kommen, die nicht in der Verwaltung vorhanden sind, wird sie aktualisiert. DEBUG_LOG(m_logger) <<"---------------- UPDATE OPS-Data ------------------"; updateOpsData(opsData, true); opsData= m_opsData[opsData.opsCode] ; } // return opsData; } void IsmHandler::onInstrumentationAssetChanged(const InfrastructureManagement::InstrumentationAsset &data, DataModification modType) { if (data.mountingPointTypeId() != InfrastructureManagement::MountingPointType::IndoorIPS && data.mountingPointTypeId() != InfrastructureManagement::MountingPointType::OutdoorIPS) return; switch (modType) { case DataModification::eAdded: case DataModification::eModified: { int opsCode = 0; QHashIterator i(m_opsData); while (i.hasNext()) { i.next(); if ( i.value().assetId == data.id()) { opsCode = i.value().opsCode; DEBUG_LOG(m_logger) << "onInstrumentationAssetChanged assetId=" << data.id(); break; } } OpsData &d = m_opsData[opsCode]; d.assetId = data.id(); d.opsCode = getOpsCodeFromJSon(data.properties()); d.opsTunnel = getIsOpsTunnel(data.properties()); d.mountingPointTypeId = data.mountingPointTypeId().underlying(); updateOpsData(d); break; } case DataModification::eRemoved: { int opsCode = 0; QHashIterator i(m_opsData); while (i.hasNext()) { i.next(); if ( i.value().assetId == data.id()) { opsCode = i.value().opsCode; DEBUG_LOG(m_logger) << "onInstrumentationAssetChanged assetId=" << data.id(); break; } } m_opsData.remove(opsCode); break; } } } void IsmHandler::onMountingPointAssetChanged(const InfrastructureManagement::MountingPointAsset &data, DataModification modType) { switch (modType) { case DataModification::eAdded: case DataModification::eModified: { OpsData opsData; opsData.opsCode = m_dtcIsm->getOpticalCodeFromAssetId(data.assetId()); opsData.assetId = data.assetId(); updateOpsData(opsData, true); break; } case DataModification::eRemoved: DEBUG_LOG(m_logger) << "onMountingPointAssetChanged Unmount id=" << data.id() << " assetId=" << data.assetId(); removeOpsAsset(data.assetId()); break; } } void IsmHandler::rotateBuildingFootprint(int mseId) { // InfrastructureManagement::MainStructureElement mse = m_mainStructureElements.value(mseId); if ( m_relBuildingFootprintsRotated.contains(mseId)) { DEBUG_LOG(m_logger) << " mainStructureElements.Rotate already done id=" << mseId; return; } InfrastructureManagement::MainStructureElement mse = m_dtcIsm->mainStructureElement(mseId); TetraIcd::RelativePosition relBldgPosition = m_mainHandler->posHandler()->geographicToRelative(mse.geoPosition()); DEBUG_LOG(m_logger) << " mainStructureElements.Rotate id=" << mseId; QMatrix matrix; QPolygonF relBuildingFootprintMgrs; qreal angle = mse.angle(); matrix.rotate(angle); const QPolygonF relBuildingFootprint = m_relBuildingFootprints.value(mseId); const QPolygonF relBuildingFootprintRotated = matrix.map(relBuildingFootprint); m_relBuildingFootprintsRotated.insert(mseId, relBuildingFootprintRotated); relBuildingFootprintMgrs.reserve(m_relBuildingFootprintsRotated.size()); for (const QPointF &p : relBuildingFootprintRotated) { QPointF relBuildingPoint(p.x() + relBldgPosition.offsetX, p.y() + relBldgPosition.offsetY); relBuildingFootprintMgrs.append(relBuildingPoint); DEBUG_LOG(m_logger) << " relBuildingFootprintRotated = " << p.x() << "/" << p.y() << " relBuildingPointMgrs=" << relBuildingPoint.x() << "/" << relBuildingPoint.y() << " angle=" << angle; } m_relBuildingFootprintsRotatedMgrs.insert(mseId, relBuildingFootprintMgrs); m_buildingBoundingBoxesMgrs.insert(mseId, relBuildingFootprintMgrs.boundingRect()); DEBUG_LOG(m_logger) << "BoundingBox bottomLeft=" << relBuildingFootprintMgrs.boundingRect().bottomLeft().x() << "/" << relBuildingFootprintMgrs.boundingRect().bottomLeft().x() << " topRight=" << relBuildingFootprintMgrs.boundingRect().topRight().x() << "/" << relBuildingFootprintMgrs.boundingRect().topRight().x(); } void IsmHandler::onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &data, DataModification modType) { //TetraIcd::RelativePosition relBldgPosition = m_mainHandler->posHandler()->geographicToRelative(data.geoPosition()); switch (modType) { case DataModification::eAdded: case DataModification::eModified: { int exerciseId = m_mainHandler->ddsHandler()->getExerciseId(); if ( !m_dtcIsm->isElementAssignedToExercise(data, exerciseId)) { INFO_LOG(m_logger) << "HandlerBuildingData::onMainStructureElementChanged() id=" << data.id() << " remove"; //m_mainStructureElements.remove(data.id()); //m_mainFootprints.remove(data.id()); m_buildingBoundingBoxes.remove(data.id()); m_relBuildingFootprints.remove(data.id()); m_relBuildingFootprintsRotated.remove((data.id())); m_buildingBoundingBoxesMgrs.remove(data.id()); m_relBuildingFootprintsRotatedMgrs.remove(data.id()); for (const auto &se : data.elements()) { if (se.mountingPointTypeId() != InfrastructureManagement::MountingPointType::IndoorIPS && se.mountingPointTypeId() != InfrastructureManagement::MountingPointType::OutdoorIPS) continue; removeOpsDataByStructureElementId(se.id()); } // Use mutable iterator to remove element(s): QMutableHashIterator iter(m_opsData); while (iter.hasNext()) { iter.next(); if (iter.value().mainStructureElementId == data.id()) { DEBUG_LOG(m_logger) << "remove from m_opsData, key=" << iter.key(); iter.remove(); break; } } // while return; } //m_mainStructureElements.insert(data.id(), data); //DEBUG_LOG(m_logger) << " m_mainStructureElements.insert key=" << data.id() // << " angle=" << data.angle(); for (const InfrastructureManagement::StructureElement &se : data.elements()) { if (se.mountingPointTypeId() != InfrastructureManagement::MountingPointType::IndoorIPS && se.mountingPointTypeId() != InfrastructureManagement::MountingPointType::OutdoorIPS) continue; auto it = std::find_if(m_opsData.begin(), m_opsData.end(), [se](const OpsData & d) { return d.structureElementId == se.id(); }); if (it != m_opsData.end()) updateOpsData(it.value()); } DEBUG_LOG(m_logger) << " mainStructureElements Vor Rotate id=" << data.id(); QMap mainFootprints = m_dtcIsm->getFootprintsByExerciseId(exerciseId); // Rotate Building-Footprint if (mainFootprints.contains(data.id())) { rotateBuildingFootprint(data.id()); } break; } case DataModification::eRemoved: //m_mainStructureElements.remove(data.id()); for (const auto &se : data.elements()) { if (se.mountingPointTypeId() != InfrastructureManagement::MountingPointType::IndoorIPS && se.mountingPointTypeId() != InfrastructureManagement::MountingPointType::OutdoorIPS) continue; removeOpsDataByStructureElementId(se.id()); } break; } } void IsmHandler::onMainFootprintChanged(const InfrastructureManagement::MainFootprint &data, DataModification modType) { DEBUG_LOG(m_logger) << "onMainFootprintChanged id=" << data.id() << " size=" << data.elements().size(); switch (modType) { case DataModification::eAdded: case DataModification::eModified: { //int exerciseId = m_mainHandler->ddsHandler()->getExerciseId(); //QMap mainFootprints = m_dtcIsm->getFootprintsByExerciseId(exerciseId); DEBUG_LOG(m_logger) << "mainStructureElements onMainFootprintChanged id=" << data.id(); double minX = 0; double maxX = 0; double minY = 0; double maxY = 0; QPolygonF relBuildingFootprint; relBuildingFootprint.reserve(data.elements().size()); for (const InfrastructureManagement::FootprintElement &fpElem : data.elements()) { const Core::RelativePosition &relPos = fpElem.relPosition(); minX = std::min(minX, relPos.offsetX()); maxX = std::max(maxX, relPos.offsetX()); minY = std::min(minY, relPos.offsetY()); maxY = std::min(maxY, relPos.offsetY()); QPointF point(relPos.offsetX(), relPos.offsetY()); relBuildingFootprint.append(point); DEBUG_LOG(m_logger) << "relBuildingFootprint.append() " << point.x() << "/" << point.y() << ")"; } // do not use boundingRect() because the top and bottom are swapped DEBUG_LOG(m_logger) << "minX=" << minX << " maxX=" << maxX << " minY=" << minY << " maxY=" << maxY << " m_distanceIpsToGps=" << m_distanceIpsToGps << " settings distanceIpsToGps=" << Settings::distanceIpsToGps(); QPointF boundingBoxTopLeft(minX - m_distanceIpsToGps, maxY + m_distanceIpsToGps); QPointF boundingBoxBottomRight(maxX + m_distanceIpsToGps, minY - m_distanceIpsToGps); QRectF BoundingBox(boundingBoxTopLeft, boundingBoxBottomRight); m_buildingBoundingBoxes.insert(data.id(), BoundingBox); m_relBuildingFootprints.insert(data.id(), relBuildingFootprint); for (const QPointF &p : qAsConst(relBuildingFootprint)) DEBUG_LOG(m_logger) << "relBuildingFootprint = " << p.x() << "/" << p.y(); rotateBuildingFootprint(data.id()); break; } case DataModification::eRemoved: { //m_mainFootprints.remove(data.id()); break; } } } void IsmHandler::removeOpsDataByStructureElementId(int structureElementId) { auto copy = m_opsData; for (const OpsData &pd : qAsConst(copy)) { if (pd.structureElementId == structureElementId) { removeOpsAsset(pd.assetId); break; } } } void IsmHandler::removeOpsAsset(int assetId) { int opsCode = 0; for (const OpsData &pd : qAsConst(m_opsData)) { if (pd.assetId == assetId) { m_structureElementId2AssetId.remove(pd.structureElementId); opsCode = pd.opsCode; break; } } m_opsData.remove(opsCode); } int IsmHandler::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(); } bool IsmHandler::getIsOpsTunnel(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 false; } QJsonObject obj(doc.object()); if ( obj.contains(QStringLiteral("opsTunnel"))) return obj[QStringLiteral("opsTunnel")].toBool(); else return false; } void IsmHandler::updateOpsData(OpsData &data, bool force) { DEBUG_LOG(m_logger) << "updateOpsData" << " opsCode=" << data.opsCode << " assetId=" << data.assetId << " force=" << force; int exerciseId = m_mainHandler->ddsHandler()->getExerciseId(); DEBUG_LOG(m_logger) << "updateOpsData for asset " << data.assetId << " with ops id " << data.opsCode; const int seId = m_dtcIsm->getStructueElementIdFromAssetId(data.assetId); data.structureElementId = 0; // in case MainStructureElement is not yet here data.mainStructureElementId = 0; data.roomId = 0; data.position = Core::GeoPosition(); const auto &mainStructureElements = m_dtcIsm->mainStructureElements(); // search for structure element in MainStructureElements and update data if found for (const auto &mse : qAsConst(mainStructureElements)) { //auto mse = m_mainStructureElements.value(data.mainStructureElementId); if (!force && !m_dtcIsm->isElementAssignedToExercise(mse, exerciseId)) continue; for (const InfrastructureManagement::StructureElement &se : mse.elements()) { if (se.id() == seId) { data.structureElementId = seId; data.mainStructureElementId = mse.id(); // last is own id if (se.path().size() > 1) data.roomId = se.path().at(se.path().size() - 2); data.position = se.geoPosition(); data.position.z(data.position.z() - mse.geoPosition().z()); // above msl -> above ground m_structure_ElementId2AssetTypeId[seId] = se.mountingPointTypeId().underlying(); DEBUG_LOG(m_logger) << "found position for asset " << data.assetId << " with se id " << seId << " " << data.position.lon() << "/" << data.position.lat() << "/" << data.position.z() << " name=" << se.name() << " type=" << se.mountingPointTypeId() << " m_structure_ElementId2AssetTypeId.size()=" << m_structure_ElementId2AssetTypeId.size(); data.mountingPointTypeId = se.mountingPointTypeId().underlying(); // m_opsAssets.insert(data.assetId , data ); m_opsData[data.opsCode] = data; return; } } } DEBUG_LOG(m_logger) << "found no position for asset " << data.assetId << " with se id " << seId; } bool IsmHandler::posInExtendedBuildingFootprint(int mainStructureElementId, const Core::GeoPosition &position, const int mountPointTypeId) const { int distance = 0; DEBUG_LOG(m_logger) << "IsmHandler::posInExtendedBuildingFootprint mainStructureElementId=" << mainStructureElementId << " mountPointTypeId=" << mountPointTypeId; if (m_distanceIpsToGps <= 0 && m_distanceOpsToGps <= 0) return false; switch (mountPointTypeId) { case InfrastructureManagement::MountingPointType_def::OutdoorIPS: DEBUG_LOG(m_logger) << "posInExtendedBuildingFootprint Distance = m_distanceOpsToGps"; distance = m_distanceOpsToGps; break; case InfrastructureManagement::MountingPointType_def::IndoorIPS: DEBUG_LOG(m_logger) << "posInExtendedBuildingFootprint Distance = m_distanceIpsToGps"; distance = m_distanceIpsToGps; break; default: return false; } ///////////////////////////////////////////////// // 1: Check, if Position is inside Bounding Box ///////////////////////////////////////////////// DEBUG_LOG(m_logger) << "mainStructureElementId=" << mainStructureElementId << " position=" << position; // if (!m_mainStructureElements.contains(mainStructureElementId)) // return false; int exerciseId = m_mainHandler->ddsHandler()->getExerciseId(); //auto mse = m_mainStructureElements.value(data.mainStructureElementId); InfrastructureManagement::MainStructureElement mse = m_dtcIsm->mainStructureElement(mainStructureElementId); if ( !m_dtcIsm->isElementAssignedToExercise(mse, exerciseId)) return false; if (!m_buildingBoundingBoxes.contains(mainStructureElementId)) return false; TetraIcd::RelativePosition relBldgPosition = m_mainHandler->posHandler()->geographicToRelative(mse.geoPosition()); TetraIcd::RelativePosition relPosition = m_mainHandler->posHandler()->geographicToRelative(position); DEBUG_LOG(m_logger) << "relBldgPosition=" << relBldgPosition.offsetX << "/" << relBldgPosition.offsetY << ", relPosition=" << relPosition.offsetX << "/" << relPosition.offsetY; QPointF relPositionMgrs(relPosition.offsetX, relPosition.offsetY); ///////////////////////////////////////////////// // 2: Check, if Position is inside Building-Footprint ///////////////////////////////////////////////// QPolygonF relBuildingFootprint = m_relBuildingFootprintsRotated[mainStructureElementId]; QPointF relPositionF(relPosition.offsetX - relBldgPosition.offsetX, relPosition.offsetY - relBldgPosition.offsetY); if (relBuildingFootprint.containsPoint(relPositionF, Qt::OddEvenFill)) { DEBUG_LOG(m_logger) << "Position is inside Building-Footprint"; return true; } ///////////////////////////////////////////////// // 3. Check whether the position is within an expanded segment ///////////////////////////////////////////////// DEBUG_LOG(m_logger) << "relBuildingFootprint.size=" << relBuildingFootprint.size(); for (int i = 0; i < relBuildingFootprint.size() ; i++) { int nextPoint = i + 1; if (i == relBuildingFootprint.size() - 1) nextPoint = 0; QPointF l1(relBuildingFootprint[i].x() + relBldgPosition.offsetX, relBuildingFootprint[i].y() + relBldgPosition.offsetY); QPointF l2(relBuildingFootprint[nextPoint].x() + relBldgPosition.offsetX, relBuildingFootprint[nextPoint].y() + relBldgPosition.offsetY); double distancePointLineValue = distancePointLine(relPositionMgrs, l1, l2); DEBUG_LOG(m_logger) << "distancePointLineValue=" << distancePointLineValue << " Point =" << relPositionMgrs.x() << "/" << relPositionMgrs.y() << " Line=" << l1.x() << "/" << l1.y() << " " << l2.x() << "/" << l2.y(); if (distancePointLineValue < distance) { DEBUG_LOG(m_logger) << "IsmHandler::posInExtendedBuildingFootprint return true"; return true; } } DEBUG_LOG(m_logger) << "IsmHandler::posInExtendedBuildingFootprint return false"; return false; } double IsmHandler::distancePointLine(const QPointF &p, const QPointF &l1, const QPointF &l2) const { const double u = (((p.x() - l1.x()) * (l2.x() - l1.x())) + ((p.y() - l1.y()) * (l2.y() - l1.y()))) / squareLen(l2, l1); if (u < 0.0f || u > 1.0f) { QLineF pToP1(p, l1); QLineF pToP2(p, l2); qreal length1 = pToP1.length(); qreal length2 = pToP2.length(); if (length1 < length2) { DEBUG_LOG(m_logger) << "a distancePointLine p=" << p.x() << "/" << p.y() << " line: p1=" << l1.x() << "/" << l1.y() << " p2=" << l2.x() << "/" << l2.y() << " distance=" << length1 << ", return=" << length1; return length1; } else { DEBUG_LOG(m_logger) << "b distancePointLine p=" << p.x() << "/" << p.y() << " line: p1=" << l1.x() << "/" << l1.y() << " p2=" << l2.x() << "/" << l2.y() << " distance=" << length2 << ", return=" << length2; return length2; } } QPointF intersectPnt(l1.x() + u * (l2.x() - l1.x()), l1.y() + u * (l2.y() - l1.y())); double distance = sqrt(squareLen(p, intersectPnt)); DEBUG_LOG(m_logger) << "c distancePointLine p=" << p.x() << "/" << p.y() << " line: p1=" << l1.x() << "/" << l1.y() << " p2=" << l2.x() << "/" << l2.y() << " distance=" << distance; return distance; } double IsmHandler::squareLen(const QPointF &p1, const QPointF &p2) { double vx = p2.x() - p1.x(); double vy = p2.y() - p1.y(); return vx * vx + vy * vy; } } // namespace TrainingAreaBase