/** * Handler for the HMI language * $Id$ * $HeadURL$ * $Author$ */ #include "object_danger_area.h" #include "special_area_provider.h" #include "Utils/polygons.h" #include #include "handler_bccontamination.h" #include #include namespace AreaEffectSimulation { ObjectDangerArea::ObjectDangerArea(MainHandler *mainHandler, QObject *parent) : ObjectBaseTmpl(mainHandler, QStringLiteral("DangerArea"), parent) , m_zone(-1) { connect(m_mainHandler->bcContaminationHandler().get(), &HandlerBase::newAiv, this, &ObjectDangerArea::onNewAiv); connect(m_mainHandler->bcContaminationHandler().get(), &HandlerBase::removeAiv, this, &ObjectDangerArea::onRemoveAiv); } ObjectDangerArea::~ObjectDangerArea() { removeTopics(); } Core::FunctionReplyObject ObjectDangerArea::createObject(const Database::AesDangerArea &data, const std::vector &segmentsDds, const QVector &segmentsDb) { auto getRightId = [](int id, bool originOc) -> int { return (originOc)?(id*-1):(id); }; bool originOc = false; m_data = data; // New Data! if (m_data.id <= 0) { DBTransaction trans(m_mainHandler->db()->database()); m_data.effectStateId = AesEffectState::EsUnknown; if (segmentsDds.size() == 0) return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Could not create danger area without segments.")); if (m_data.id < 0) // is the id given as negative? make positive again -> used by OC-Laptop { m_data.id *= -1; m_data.priv_entityId = m_data.id; // OC is using id from Tetra and these are unique originOc = true; } else m_data.id = 0; if (!m_mainHandler->db()->insert(m_data)) return Core::FunctionReplyObject(getRightId(m_data.id, originOc), tr_lts("dds", "Error inserting AesDangerArea-data into database.")); m_segments.reserve(segmentsDds.size()); for (const Core::GeoPosition2D &segmentDds : segmentsDds) { Database::AesDangerSegment segment; segment.id = 0; segment.dangerAreaId = m_data.privDangerAreaId; // db segment.position = QPointF(segmentDds.lon(), segmentDds.lat()); // will update segment.id if (!m_mainHandler->db()->insert(segment)) return Core::FunctionReplyObject(getRightId(m_data.id, originOc), tr_lts("dds", "Error inserting AesDangerSegment-data into database.")); m_segments.append(std::move(segment)); } trans.commit(); } else { m_segments = segmentsDb; } m_publishDataTimer->start(); checkEnterNewState(); return Core::FunctionReplyObject(getRightId(m_data.id, originOc)); } Core::FunctionReplyObject ObjectDangerArea::updateObject(const Database::AesDangerArea &data, const std::vector &segments) { if (segments.size() == 0) return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Could not modify danger area without segments.")); DBTransaction trans(m_mainHandler->db()->database()); auto tmpData = m_data; tmpData.id = std::abs(data.id); // Todo: Why only name and effect Start? tmpData.name = data.name; tmpData.effectStart = data.effectStart; tmpData.properties = data.properties; tmpData.effectStateId = data.effectStateId; // overwrite for oc auto tmpSegments = m_segments; const int newSegmentSize = segments.size(); // segment count increased for (int i = m_segments.size(); i < newSegmentSize; ++i) { Database::AesDangerSegment segment; segment.dangerAreaId = m_data.privDangerAreaId; // db // will update segment.id if (!m_mainHandler->db()->insert(segment)) return Core::FunctionReplyObject(data.id, tr_lts("dds", "Error inserting AesMineAreaSegment-data into database.")); tmpSegments.append(segment); } // segment count decreased for (int i = newSegmentSize; i < m_segments.size(); ++i) { if (!m_mainHandler->db()->removeAesDangerSegment(m_segments.at(i).id)) return Core::FunctionReplyObject(data.id, tr_lts("dds", "Error removing AesMineAreaSegment-data into database.")); } tmpSegments.resize(newSegmentSize); // if count is decreased for (size_t i = 0; i < segments.size(); ++i) tmpSegments[i].position = QPointF(segments[i].lon(), segments[i].lat()); for (const Database::AesDangerSegment &segment : qAsConst(tmpSegments)) { if (!m_mainHandler->db()->update(segment)) return Core::FunctionReplyObject(data.id, tr_lts("dds", "Error updating AesDangerSegment-data in database.")); } if (!m_mainHandler->db()->update(tmpData)) return Core::FunctionReplyObject(data.id, tr_lts("dds", "Error updating danger area with id %1 in database.")).arg(m_data.id); trans.commit(); m_data = tmpData; m_segments = tmpSegments; m_publishDataTimer->start(); checkEnterNewState(); return Core::FunctionReplyObject(data.id); } void ObjectDangerArea::onPublishData() { ObjectBaseTmpl::onPublishData(); if (!m_mainHandler->db()->update(m_data)) ERROR_LOG(m_logger) << "Error updating AesDangeArea-data in database"; DangerArea ddsData(m_data.id, m_data.name.toStdString(), effectState() == AesEffectState::EsRemoved, false, m_mainHandler->dds()->currentPartitionTimeMSec(), {m_data.chargeTypeId,0,0}, m_data.effectDuration, toOptional(toMSec(m_data.effectStart)), effectState(), m_data.effectSourceId, toOptional(m_data.partyId), toOptional(m_data.participantId), m_data.effectMarkingInTa, m_data.externalId, m_data.isCentral, m_data.properties.toStdString(), {}, toOptional(m_data.associatedAesId)); dbSegment2DdsSegment(m_segments, ddsData.segments()); m_mainHandler->dds()->dts()->publish(ddsData); publishAiv(); } void ObjectDangerArea::publishAiv() { // Todo: calculate unique id. // Better than aiv.id(1000 * aesItemId + AesType::AtExplosionArea); // Todo: Use Polygons::dangerArea(); QPolygonF poly(m_segments.size()); for(int i = 0; i < m_segments.size(); ++i) poly[i] = m_segments.at(i).position; AesItemView aiv(m_aiv); aiv.aesItemId(m_data.id); aiv.chargeStruct().chargeTypeId(m_data.chargeTypeId); aiv.effectStart(toMSec(m_data.effectStart)); ddsVecFromPoly(poly, aiv.form()); const QRectF bounds(poly.boundingRect()); aiv.boundingBoxBottomLeft(Core::GeoPosition2D(bounds.bottomLeft().x(), bounds.bottomLeft().y())); aiv.boundingBoxTopRight(Core::GeoPosition2D(bounds.topRight().x(), bounds.topRight().y())); aiv.aesTypeId(AesType::AtDangerArea); aiv.effectStateId(effectState()); aiv.id(1000 * m_data.id + AesType::AtDangerArea); aiv.partyId(0); checkIsCentralAndPublish(aiv.form(), m_aiv.form(), m_data.isCentral); if (aiv != m_aiv) { aiv.sampleTime(partitionTimeMSec()); m_aiv = aiv; m_mainHandler->dds()->dts()->publish(aiv); emit newAiv(aiv); } m_zone = LtsCoordTrans::UtmCoordinate::fromGeographic(poly[0]).zone; m_utmPoly = utmPolyFromGeoVec(aiv.form(), m_zone); onUpdateCorrelation(); } QPolygonF ObjectDangerArea::utmPolyFromGeoVec(const std::vector &geoPos, int zone) { if (geoPos.size() == 0) return QPolygonF(); int actZone = LtsCoordTrans::UtmCoordinate::fromGeographic(geoPos.at(0).lon(),geoPos.at(0).lat(), zone).zone; QPolygonF retVal(geoPos.size()); int i=0; for(const auto &pos : geoPos) { LtsCoordTrans::UtmCoordinate utm = LtsCoordTrans::UtmCoordinate::fromGeographic(QPointF(pos.lon(), pos.lat()), actZone); retVal[i++] = QPointF(utm.x, utm.y); } return retVal; } void ObjectDangerArea::removeTopics() { auto dts = m_mainHandler->dds()->dts(); dts->removeDangerAreaByKey(m_data.id); dts->removeAesItemViewByKey(1000 * m_data.id + AesType::inner_enum::AtDangerArea); for (int idx : qAsConst(m_correlatedAesItems)) dts->removeDangerAreaCorrelatedAesItemByKey(idForCorrelatedItem(idx)); m_correlatedAesItems.clear(); emit removeAiv(m_data.id); } void ObjectDangerArea::onUpdateCorrelation() { // Method Called, when Danger Area changes... // If Danger Area is not active, remove correlations and return if (effectState() != AesEffectState::EsActivated) { for(int oldItem : qAsConst(m_correlatedAesItems)) m_mainHandler->dds()->dts()->removeDangerAreaCorrelatedAesItemByKey(idForCorrelatedItem(oldItem)); m_correlatedAesItems.clear(); return; } QSet vec; // BC Contaminations: const auto bcConts = m_mainHandler->bcContaminationHandler()->forms(); QHashIterator > it(bcConts); while (it.hasNext()) { it.next(); QPolygonF utmPolyBc = utmPolyFromGeoVec(it.value(), m_zone); if(intersects(m_utmPoly, utmPolyBc)) { DEBUG_LOG(m_logger) << QStringLiteral("B/C Area %1 cuts Danger Area %2").arg(it.key()).arg(m_data.id); vec << it.key(); } } // Other AesItems go here: // ... // Conclude: const QSet newItems = vec - m_correlatedAesItems; const QSet oldItems = m_correlatedAesItems - vec; for(int newItem : newItems) { DangerAreaCorrelatedAesItem item; item.id(idForCorrelatedItem(newItem)); item.aesItemId(newItem); item.dangerAreaId(m_data.id); // on dds - correct m_mainHandler->dds()->dts()->publish(item); } for(int oldItem : oldItems) m_mainHandler->dds()->dts()->removeDangerAreaCorrelatedAesItemByKey(idForCorrelatedItem(oldItem)); m_correlatedAesItems = m_correlatedAesItems + newItems - oldItems; } void ObjectDangerArea::onNewAiv(const AesItemView &itemView) { // Method called, when (other) Aes Items change // Warning: TypeId not in Key, so don't process multiple AIV per Type! // Todo: Change here if more than BC is needed if (itemView.aesTypeId() != AesType_def::AtBCContamination) return; if (effectState() != AesEffectState_def::EsActivated || itemView.effectStateId() != AesEffectState_def::EsActivated) { // If ES of this DA or the Contamination changed from(!) Activated to Whatever, kill the correlation m_mainHandler->dds()->dts()->removeDangerAreaCorrelatedAesItemByKey(idForCorrelatedItem(itemView.aesItemId())); return; } DEBUG_LOG(m_logger) << "New Aiv iof Type BC Contamination: " << itemView.aesItemId(); QPolygonF utmPolyBc = utmPolyFromGeoVec(itemView.form(), m_zone); if (!intersects(m_utmPoly, utmPolyBc)) return; DEBUG_LOG(m_logger) << "intersects with Danger Area:" << m_data.id; DangerAreaCorrelatedAesItem item; item.id(idForCorrelatedItem(itemView.aesItemId())); item.aesItemId(itemView.aesItemId()); item.dangerAreaId(m_data.id); m_mainHandler->dds()->dts()->publish(item); m_correlatedAesItems += itemView.aesItemId(); } void ObjectDangerArea::onRemoveAiv(int32_t aesItemId) { // Don't worry, if aesItemId is not on bus or in List... m_mainHandler->dds()->dts()->removeDangerAreaCorrelatedAesItemByKey(idForCorrelatedItem(aesItemId)); m_correlatedAesItems -= aesItemId; } QPolygonF ObjectDangerArea::generateForm() { QPolygonF poly(m_segments.size()); for(int i = 0; i < m_segments.size(); ++i) poly[i] = m_segments.at(i).position; return poly; } } // Namespace AreaEffectSimulation