#include "building_process.h" #include #include "main_handler.h" #include "BuildingControlUnit/ControlEffects/xmlreader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Buildings { QStringList idsToStringList(const std::vector< int32_t >& ids) { QStringList sl; sl.reserve(ids.size()); for (const auto &id : std::as_const(ids)) sl.append(QString::number(id)); return sl; } BuildingProcess::BuildingProcess(MainHandler *mainHandler, const QString &procName, const BuildingInfo &info, const bool initBuilding, QObject *parent) : QObject(parent) , m_logger(getLogger("BuildingProcess")) , m_mainHandler(mainHandler) , m_buildingInfo(info) , m_wsSocket(nullptr) , m_wsReconnectTimer(new QTimer(this)) , m_hitLineInfoTimer(new QTimer(this)) , m_hitLineId(info.mseId << 16) , m_initBuilding(initBuilding) { m_proc.setProgram(procName); m_proc.setReadChannel(QProcess::StandardOutput); connect(m_wsReconnectTimer, &QTimer::timeout, this, &BuildingProcess::openWebSocket); connect(m_hitLineInfoTimer, &QTimer::timeout, this, &BuildingProcess::onHitLineInfoTimeout); m_hitLineInfoTimer->setSingleShot(false); m_hitLineInfoTimer->start(15000); } BuildingProcess::~BuildingProcess() { shutdown(); } void BuildingProcess::setBuildingInfo(const BuildingInfo &info) { m_buildingInfo = info; shutdown(); start(); } void BuildingProcess::onStructureElementUpdateEffectOverrideReply(const Core::FunctionReplyObject &replyObject) { DEBUG_LOG(m_logger) << "onSendCesHitReply for id " << replyObject.id() << " success= " << replyObject.error(); } bool BuildingProcess::start() { DEBUG_LOG(m_logger) << "BuildingProcess::start mseId = " << m_buildingInfo.mseId << " m_historyState.size() = " << m_historyState.stateHistory.size() << " m_initBuilding=" << m_initBuilding; if (m_buildingInfo.mseId <= 0) { WARNING_LOG(m_logger) << "No valid building information set - can not start building process"; return false; } if (m_proc.state() != QProcess::NotRunning) { WARNING_LOG(m_logger) << "Process for building " << m_buildingInfo.mseId << " is already running!"; return false; } const QString tmpPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1Char('/'); const QString optPath = QStringLiteral("/opt/BuildingModels/StaticMeshes/"); const QString vulnerabilityModelFilename = optPath + QStringLiteral("%1.flt.VWM_Building.xml").arg(m_buildingInfo.name); const QByteArray bldCfgData = generateBuildingConfigData(); const QString effectConfigFilename = NGL::findFile(NGL::FileType::ConfigFile, QStringLiteral("effect_config.xml")); WARNING_LOG(m_logger) << "(effectConfigFilename) = " << effectConfigFilename; const QString bldDataFilename = tmpPath + QStringLiteral("bldg_%1_building_config.xml").arg(m_buildingInfo.mseId); if (!writeFile(bldDataFilename, bldCfgData)) { WARNING_LOG(m_logger) << "(!writeFile(bldDataFilename, bldCfgData)"; return false; } const QByteArray chargesData = generateChargesData(); const QString chargesDataFilename = tmpPath + QStringLiteral("bldg_%1_charges.xml").arg(m_buildingInfo.mseId); if (!writeFile(chargesDataFilename, chargesData)) { WARNING_LOG(m_logger) << "(!writeFile(chargesDataFilename, chargesData)"; return false; } BuildingControlUnit::XmlReader *xmlReader = new BuildingControlUnit::XmlReader(); xmlReader->init(effectConfigFilename); const QVector &effectsPtrRef = xmlReader->contents().effects; for (auto effectPtr : effectsPtrRef) { if (!effectPtr) continue; DEBUG_LOG(m_logger) << "effect->id=" << effectPtr->id << " effect->category=" << effectPtr->category << " effect->effectType=" << effectPtr->effectType << " effect->override=" << static_cast(effectPtr->override); if (effectPtr->effectType == 1) { m_effectOverridesReconnect.insert(effectPtr->id, static_cast(effectPtr->override)); } } delete xmlReader; connect(&m_proc, QOverload::of(&QProcess::finished), this, &BuildingProcess::onProcessFinished, Qt::UniqueConnection); connect(&m_proc, &QProcess::readyRead, this, &BuildingProcess::onReadyRead, Qt::UniqueConnection); m_proc.setArguments( { QStringLiteral("--charges"), chargesDataFilename, QStringLiteral("--buildingConfig"), bldDataFilename, QStringLiteral("--effectConfig"), effectConfigFilename, QStringLiteral("--damage"), vulnerabilityModelFilename, QStringLiteral("--buildingId"), QString::number(m_buildingInfo.mseId), QStringLiteral("--percentDamageInit"), QString::number(m_initBuilding) }); m_proc.start(); return m_proc.waitForStarted(); } void BuildingProcess::shutdown() { m_proc.disconnect(this); if (m_proc.state() == QProcess::Running) { m_proc.terminate(); m_proc.waitForFinished(5000); if (m_proc.state() == QProcess::Running) m_proc.kill(); } closeWebSocket(); m_wsUrl.clear(); } void BuildingProcess::setHistoryState(const Buildings::HistoryState &historyState) { DEBUG_LOG(m_logger) << "BuildingProcess::setHistoryState size =" << historyState.stateHistory.size(); ERROR_LOG(m_logger) << "[BuildingProcess::setHistoryState]>>> history =" << historyState; m_historyState = historyState; m_historyState.sampleTime = QDateTime::currentMSecsSinceEpoch(); m_mainHandler->ddsHandler()->dts()->publish(m_historyState); } bool BuildingProcess::setVulnerabilityState(ProjectDataManagement::VulnerabilityState state, const std::vector &roomIds) { DEBUG_LOG(m_logger) << "BuildingProcess::setVulnerabilityState:" << state; if (!m_wsSocket || !m_wsSocket->isValid()) return false; BuildingControlUnit::Json::Telegrams::SetDamageState tele; tele.damageState = (state == ProjectDataManagement::VulnerabilityState::VulnerabilityState_CatastrophicKill ? BuildingControlUnit::DamageState::Destroyed : BuildingControlUnit::DamageState::NoDamage); tele.roomIds.reserve(roomIds.size()); for (int id : roomIds) tele.roomIds += m_buildingInfo.roomId2SubId.value(id); tele.originatorId = 0; DEBUG_LOG(m_logger) << "setVulnerabilityState websocket send: " << QString::fromUtf8(tele.toJson()); m_wsSocket->sendTextMessage(QString::fromUtf8(tele.toJson())); return true; } bool BuildingProcess::setContaminationState(const int &bldId, const ContaminationState &contaminationState) { DEBUG_LOG(m_logger) << "BuildingProcess::setContaminationState bldId: "<isValid()) return false; BuildingControlUnit::Json::Telegrams::SetContaminationState tele; BuildingControlUnit::ContaminationState contState; contState.aesId = contaminationState.aesId; if ( contaminationState.vulnerabilityState == ProjectDataManagement::VulnerabilityState::VulnerabilityState_CatastrophicKill ) contState.damagePercentNew = 100; else contState.damagePercentNew = 0; contState.rooms = contaminationState.rooms; tele.contaminationStates.push_back(contState); DEBUG_LOG(m_logger) << "SetContaminationState websocket send: " << QString::fromUtf8(tele.toJson()); m_wsSocket->sendTextMessage(QString::fromUtf8(tele.toJson())); return true; } bool BuildingProcess::resetContaminationState() { DEBUG_LOG(m_logger) << "BuildingProcess::resetContaminationState" ; if (!m_wsSocket || !m_wsSocket->isValid()) return false; BuildingControlUnit::Json::Telegrams::ResetContaminationState tele; DEBUG_LOG(m_logger) << "ResetContaminationState websocket send: " << QString::fromUtf8(tele.toJson()); m_wsSocket->sendTextMessage(QString::fromUtf8(tele.toJson())); return true; } bool BuildingProcess::setFortifyFactors(const std::vector &fortifyFactors) { if (!m_wsSocket || !m_wsSocket->isValid()) return false; BuildingControlUnit::Json::Telegrams::SetFortifyFactor tele; for (const auto &ff : fortifyFactors) { BuildingControlUnit::Json::Telegrams::SetFortifyFactor::FortifyInfo info; info.roomId = m_buildingInfo.roomId2SubId.value(ff.roomId); info.fortifyFactor = ff.fortifyFactor; tele.fortifyFactors.push_back(info); } DEBUG_LOG(m_logger) << "setFortifyFactors websocket send: " << QString::fromUtf8(tele.toJson()); m_wsSocket->sendTextMessage(QString::fromUtf8(tele.toJson())); return true; } void BuildingProcess::newRelayInfos(const QMap &map) { m_relayInfos = map; } bool BuildingProcess::setEffectStates( const int assetId, const QByteArray &properties) { BuildingControlUnit::Json::Telegrams::SetEffectStates tele; if (!m_wsSocket || !m_wsSocket->isValid()) return false; DEBUG_LOG(m_logger) << "setEffectStates" << " assetId=" << assetId << " properties=" << properties; if (assetId > 0) { for (const EgcuInfo &egcu : std::as_const(m_buildingInfo.egcus)) { DEBUG_LOG(m_logger) << "EGCU: " << egcu.assetName << " assetId=" << egcu.assetId << " egcu.assetId=" << egcu.assetId << " hostname=" << egcu.hostname; if ( assetId == egcu.assetId) { DEBUG_LOG(m_logger) << "segment=" << egcu.hostname; tele.segment = egcu.hostname; } } if (tele.segment.isEmpty()) { WARNING_LOG(m_logger) << "Asset with id " << assetId << " not found in this building"; return false; } if (tele.segment == QString()) { ERROR_LOG(m_logger) << "Asset with id=" << assetId << " has no Hostname!!!!"; return false; } } QJsonParseError parserError; const QJsonDocument doc(QJsonDocument::fromJson(properties, &parserError)); if (parserError.error != QJsonParseError::NoError) return false; #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) QSet allRelayIds; for (auto it = m_relayInfos.keyBegin(); it != m_relayInfos.keyEnd(); ++it) allRelayIds.insert(*it); #else QSet allRelayIds(m_relayInfos.keyBegin(), m_relayInfos.keyEnd()); #endif QJsonObject docObj = QJsonObject(doc.object()); QJsonObject overridesObj = docObj.value(QStringLiteral("Overrides")).toObject(); QJsonArray arrEgcus = overridesObj.value(QStringLiteral("relays")).toArray(); for (int i=0; i(value); tele.effectStates.append(bcuEffectState); } for (int relayId : std::as_const(allRelayIds)) { BuildingControlUnit::BcuEffectState bcuEffectState; bcuEffectState.id = relayId; bcuEffectState.type = BuildingControlUnit::BET_Relay; bcuEffectState.effectOverride = BuildingControlUnit::BcuEffectOverride::BEO_Auto; if (relayId == 99) { bcuEffectState.id = 1; bcuEffectState.type = BuildingControlUnit::BET_BackgroundSound; } tele.effectStates.append(bcuEffectState); } { BuildingControlUnit::BcuEffectState bcuEffectState; bcuEffectState.id = 1; bcuEffectState.type = BuildingControlUnit::BET_BsmFlash; int flashVal = -1; if (overridesObj.contains(QStringLiteral("flash"))) flashVal = overridesObj.value(QStringLiteral("flash")).toInt(); bcuEffectState.effectOverride = static_cast(flashVal); tele.effectStates.append(bcuEffectState); } QString valueStr = QString::fromUtf8(tele.toJson()); DEBUG_LOG(m_logger) << "setEffectStates websocket send: " << valueStr; qint64 sendBytesCount = m_wsSocket->sendTextMessage(valueStr); DEBUG_LOG(m_logger) << "sendBytesCount= " << sendBytesCount; m_effectStates.insert(assetId, valueStr); return true; } bool BuildingProcess::relayState(const std::vector &relayStates) { if (!m_wsSocket || !m_wsSocket->isValid()) return false; BuildingControlUnit::Json::Telegrams::RelayState tele; for (const auto &ff : relayStates) { BuildingControlUnit::Json::Telegrams::RelayState::RelayStateInfo info; info.id = ff.id; info.state = ff.state; tele.relayStates.push_back(info); } DEBUG_LOG(m_logger) << "relayState websocket send: " << QString::fromUtf8(tele.toJson()); m_wsSocket->sendTextMessage(QString::fromUtf8(tele.toJson())); return true; } bool BuildingProcess::sendCesHitOnBuilding(const qint64 effectTimeMSec, const int chargeTypeId, const int destructiveFactor, const int penetrationFactor, const int aesId, const Core::GeoPosition &impactPosition, const QString &properties) { if (!m_wsSocket || !m_wsSocket->isValid()) return false; const LtsCoordTrans::UtmCoordinate coord(LtsCoordTrans::UtmCoordinate::fromGeographic(m_buildingInfo.absPosition.lon, m_buildingInfo.absPosition.lat)); const LtsCoordTrans::UtmCoordinate impact(LtsCoordTrans::UtmCoordinate::fromGeographic(impactPosition.lon, impactPosition.lat, coord.zone)); Core::RelativePosition relPos(impact.x - coord.x, impact.y - coord.y, impactPosition.z - m_buildingInfo.absPosition.z); double xRot, yRot; QTransform m; m.rotate(-m_buildingInfo.angle); m.map(relPos.offsetX, relPos.offsetY, &xRot, &yRot); relPos.offsetX = xRot; relPos.offsetY = yRot; BuildingControlUnit::Json::Telegrams::CentralEffectSimulationHit tele; tele.effectTimeMSec = effectTimeMSec; tele.chargeTypeId = chargeTypeId; tele.destructiveFactor = destructiveFactor; tele.penetrationFactor = penetrationFactor; tele.aesId = aesId; tele.relPosition.x = relPos.offsetX; tele.relPosition.y = relPos.offsetY; tele.relPosition.z = relPos.offsetZ; tele.properties = properties; DEBUG_LOG(m_logger) << "sendCesHitOnBuilding websocket send: " << QString::fromUtf8(tele.toJson()); m_wsSocket->sendTextMessage(QString::fromUtf8(tele.toJson())); return true; } bool BuildingProcess::sendCesHit3DonBuilding(const qint64 effectTimeMSec, const int chargeTypeId, const int destructiveFactor, const int penetrationFactor, const int aesId, const Core::GeoPosition &impactPosition, const Core::GeoPosition &detonationLocation, double detonationDistance, const QString &properties) { if (!m_wsSocket || !m_wsSocket->isValid()) return false; const LtsCoordTrans::UtmCoordinate &coord(LtsCoordTrans::UtmCoordinate::fromGeographic(m_buildingInfo.absPosition.lon, m_buildingInfo.absPosition.lat)); Core::RelativePosition relImpactPos; { const LtsCoordTrans::UtmCoordinate &impactUtm(LtsCoordTrans::UtmCoordinate::fromGeographic(impactPosition.lon, impactPosition.lat, coord.zone)); relImpactPos = Core::RelativePosition(impactUtm.x - coord.x, impactUtm.y - coord.y, impactPosition.z - m_buildingInfo.absPosition.z); } Core::RelativePosition relDetoLoc; { const LtsCoordTrans::UtmCoordinate &detonationLocationUtm(LtsCoordTrans::UtmCoordinate::fromGeographic(detonationLocation.lon, detonationLocation.lat, coord.zone)); relDetoLoc = Core::RelativePosition(detonationLocationUtm.x - coord.x, detonationLocationUtm.y - coord.y, detonationLocation.z - m_buildingInfo.absPosition.z); } double xRot, yRot; QTransform m; m.rotate(-m_buildingInfo.angle); m.map(relImpactPos.offsetX, relImpactPos.offsetY, &xRot, &yRot); relImpactPos.offsetX = xRot; relImpactPos.offsetY = yRot; m.map(relDetoLoc.offsetX, relDetoLoc.offsetY, &xRot, &yRot); relDetoLoc.offsetX = xRot; relDetoLoc.offsetY = yRot; BuildingControlUnit::Json::Telegrams::CentralEffectSimulationHit3D tele; tele.effectTimeMSec = effectTimeMSec; tele.chargeTypeId = chargeTypeId; tele.destructiveFactor = destructiveFactor; tele.penetrationFactor = penetrationFactor; tele.aesId = aesId; tele.impactPosition.x = relImpactPos.offsetX; tele.impactPosition.y = relImpactPos.offsetY; tele.impactPosition.z = relImpactPos.offsetZ; tele.detonationLocation.x = relDetoLoc.offsetX; tele.detonationLocation.y = relDetoLoc.offsetY; tele.detonationLocation.z = relDetoLoc.offsetZ; tele.detonationDistance = detonationDistance; tele.properties = properties; DEBUG_LOG(m_logger) << "sendCesHit3DonBuilding websocket send: " << QString::fromUtf8(tele.toJson()); m_wsSocket->sendTextMessage(QString::fromUtf8(tele.toJson())); return true; } bool BuildingProcess::switchRelay(const SwitchRelayRequest &data) { BuildingControlUnit::Json::Telegrams::SwitchRelay tele; if (!m_wsSocket || !m_wsSocket->isValid()) return false; DEBUG_LOG(m_logger) << "switchRelay" << " buildingId=" << data.buildingId << " assetId=" << data.assetId << data.state; for (const EgcuInfo &egcu : std::as_const(m_buildingInfo.egcus)) { DEBUG_LOG(m_logger) << "EGCU: " << egcu.assetName << " assetId=" << egcu.assetId << " hostname=" << egcu.hostname; DEBUG_LOG(m_logger) << "data.assetId()=" << data.assetId << "egcu.assetId=" << egcu.assetId; if ( data.assetId != egcu.assetId) continue; tele.segment = egcu.hostname; tele.switchRelayValues.id = data.number; tele.switchRelayValues.bcuRelayState = static_cast (static_cast(data.state)); if ( data.state == StateSetRelay::Pulse) { tele.switchRelayValues.count = data.pulseAttributes.get().count; tele.switchRelayValues.delay = data.pulseAttributes.get().delay; tele.switchRelayValues.pulse = data.pulseAttributes.get().pulse; } DEBUG_LOG(m_logger) << "switchRelay websocket send: " << QString::fromUtf8(tele.toJson()); m_wsSocket->sendTextMessage(QString::fromUtf8(tele.toJson())); return true; } return false; } bool BuildingProcess::triggerFlashlight(const TriggerBsmFlashRequest data) { BuildingControlUnit::Json::Telegrams::BsmTriggerFlashlight tele; if (!m_wsSocket || !m_wsSocket->isValid()) return false; DEBUG_LOG(m_logger) << "triggerFlashlight" << " BuildingId=" << data.buildingId << " assetId=" << data.assetId << " m_buildingInfo.bsms.size()" << m_buildingInfo.bsms.size(); QMapIterator i(m_buildingInfo.bsms); while (i.hasNext()) { i.next(); DEBUG_LOG(m_logger) << "key=" << i.key() << " assetId=" << i.value().assetId << " bsmId=" << i.value().bsmId << " hostname=" << i.value().hostname << " port=" << i.value().port; } if ( m_buildingInfo.bsms.contains(data.assetId)) { BsmInfo bsmInfo = m_buildingInfo.bsms.value(data.assetId); DEBUG_LOG(m_logger) << " BuildingId=" << data.buildingId << " assetId=" << bsmInfo.assetId << " hostname=" << bsmInfo.hostname; tele.hostname = bsmInfo.hostname; tele.numberOfFlashes = 1; tele.pauseBetweenFlashesSec = 0; tele.sensorId = 0; DEBUG_LOG(m_logger) << "triggerFlashlight websocket send: " << QString::fromUtf8(tele.toJson()); m_wsSocket->sendTextMessage(QString::fromUtf8(tele.toJson())); return true; } return false; } void BuildingProcess::onReadyRead() { const QString data = QString::fromLocal8Bit(m_proc.readAll()); const QString strToSearch(QStringLiteral("web socket url:")); if (data.contains(strToSearch)) { m_wsUrl = data.split(strToSearch).at(1).split(QLatin1Char('\n')).at(0).trimmed(); INFO_LOG(m_logger) << "Using web socket url '" << m_wsUrl << "' for building " << m_buildingInfo.mseId; openWebSocket(); } } void BuildingProcess::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitCode == 0 && exitStatus == QProcess::NormalExit) return; const char *what = exitStatus == QProcess::NormalExit ? "stopped" : "crashed"; WARNING_LOG(m_logger) << "building control process for building " << m_buildingInfo.mseId << " " << what << " with exit code " << exitCode << " - restarting in 1 second"; if (exitCode != 0) { WARNING_LOG(m_logger) << "stdout: " << m_proc.readAllStandardOutput(); WARNING_LOG(m_logger) << "stderr: " << m_proc.readAllStandardError(); } closeWebSocket(); QTimer::singleShot(1000, this, &BuildingProcess::start); } void BuildingProcess::onTextMessageReceived(const QString &message) { TRACE_LOG(m_logger) << "Got message from building control process: " << message; BuildingControlUnit::Json::Telegrams::BaseTelegram *tel = BuildingControlUnit::Json::Telegrams::BaseTelegram::telegramFromJson(message.toUtf8()); if (!tel) { WARNING_LOG(m_logger) << "Can not convert '" << message << "' to a valid telegram!"; return; } if (tel->telegramId() == BuildingControlUnit::Json::Telegrams::BsmState::telegramId()) handleBsmState(static_cast(tel)); else if (tel->telegramId() == BuildingControlUnit::Json::Telegrams::HitResult::telegramId()) handleHitResult(static_cast(tel)); else if (tel->telegramId() == BuildingControlUnit::Json::Telegrams::RelayState::telegramId()) handleRelayState(static_cast(tel)); else if (tel->telegramId() == BuildingControlUnit::Json::Telegrams::EffectOverride::telegramId()) handleEffectOverride(static_cast(tel)); else WARNING_LOG(m_logger) << "Unhandled incoming telegram with id '" << tel->telegramId() << "'"; delete tel; } void BuildingProcess::handleBsmState(BuildingControlUnit::Json::Telegrams::BsmState *tel) { BuildingStateTechnical state; state.id = m_buildingInfo.mseId; state.assetId = m_buildingInfo.assetId; state.structureElementId = m_buildingInfo.structureElementId; state.sampleTime = QDateTime::currentMSecsSinceEpoch(); state.bsmStates.resize(tel->bsmStates.size()); BsmState s; for (int i = 0; i < tel->bsmStates.size(); ++i) { const BuildingControlUnit::Json::Telegrams::BsmState::BsmStateInfo &info = tel->bsmStates.at(i); s.id = info.id; s.connected = info.connected; s.hostport = info.port; s.hostname = info.hostname.toStdString(); s.versionInfo = info.versionInfo.toStdString(); state.bsmStates[i] = s; m_bsmStates.insert(info.id, s); } m_mainHandler->ddsHandler()->dts()->publish(state); } void BuildingProcess::historyState(const BuildingState &state) { if (state.vulnerabilityState == ProjectDataManagement::VulnerabilityState::VulnerabilityState_CombatReady && state.damagePercent == 0 && state.originatorParticipantId == 0 && state.contaminationStates.size() == 0) { DEBUG_LOG(m_logger) << "setVulnerabilityState->removeHistoryStateByKey:" << state.id; m_historyState.stateHistory.resize(0); } HistoryStateItem historyStateItem; historyStateItem.aesId = state.aesId; historyStateItem.chargeTypeId = state.chargeTypeId; historyStateItem.damagePercent = state.damagePercent; historyStateItem.damagePercentOld = state.damagePercentOld; historyStateItem.originatorParticipantId = state.originatorParticipantId; historyStateItem.originatorType = state.originatorType; historyStateItem.time = state.sampleTime; historyStateItem.vulnerabilityState = state.vulnerabilityState; historyStateItem.contaminationStates = state.contaminationStates; m_historyState.stateHistory.push_back(std::move(historyStateItem)); DEBUG_LOG(m_logger) << "BuildingProcess::historyState contaminationStates:" << state; m_historyState.mseId = state.id; DEBUG_LOG(m_logger) << "setVulnerabilityState->removeHistoryStateByKey:" << m_historyState.mseId; m_historyState.sampleTime = QDateTime::currentMSecsSinceEpoch(); m_mainHandler->ddsHandler()->dts()->publish(m_historyState); } void BuildingProcess::handleHitResult(BuildingControlUnit::Json::Telegrams::HitResult *tel) { const BuildingControlUnit::Json::Telegrams::HitResult::HitResultInfo &data = tel->hitResult; BuildingState state; DEBUG_LOG(m_logger) << Q_FUNC_INFO << " data.hitOnRoomId=" << data.hitOnRoomId; state.id = m_buildingInfo.mseId; int assetId = m_mainHandler->ddsHandler()->dtcIsm()->getAssetIdFromMountingPointId(m_buildingInfo.mseId, data.sensorId); DEBUG_LOG(m_logger) << "handleHitResult assetId=" << assetId; state.assetId = assetId; InfrastructureManagement::StructureElement se; m_mainHandler->ddsHandler()->dtcIsm()->getStructureElementInMntPointAssetByAssetId(assetId, se); DEBUG_LOG(m_logger) << "handleHitResult structureElementId=" << se.id; state.structureElementId = se.id; state.sampleTime = QDateTime::currentMSecsSinceEpoch(); if (data.originatorType == BuildingControlUnit::OriginatorType::DirectHit) { state.originatorParticipantId = m_mainHandler->ppmHandler()->equipmentNumberToParticipantId(data.originatorId); state.chargeTypeId = data.chargeTypeId; state.originatorType = ProjectDataManagement::StateChangeOriginator::StateChangeOriginator_Participant; } else if (data.originatorType == BuildingControlUnit::OriginatorType::CesHit) { state.aesId = data.originatorId; state.chargeTypeId = data.chargeTypeId; state.originatorType = ProjectDataManagement::StateChangeOriginator::StateChangeOriginator_AreaEffectSimulation; } else if (data.originatorType == BuildingControlUnit::OriginatorType::OC) { state.originatorParticipantId = 0; state.chargeTypeId = data.chargeTypeId; state.originatorType = ProjectDataManagement::StateChangeOriginator::StateChangeOriginator_ObserverController; } else { state.originatorParticipantId = 0; state.chargeTypeId = data.chargeTypeId; state.originatorType = ProjectDataManagement::StateChangeOriginator::StateChangeOriginator_ExerciseControl; } state.damagePercent = data.damagePercentNew; state.damagePercentOld = data.damagePercentOld; state.vulnerabilityState = data.damageState == BuildingControlUnit::DamageState::Destroyed ? ProjectDataManagement::VulnerabilityState::VulnerabilityState_CatastrophicKill : ProjectDataManagement::VulnerabilityState::VulnerabilityState_CombatReady; state.hitOnRoomId = data.hitOnRoomId; state.roomStates.reserve(data.rooms.size()); RoomState s; for (const auto &info : data.rooms) { s.id = m_buildingInfo.subId2RoomId.value(info.roomId); s.mountingPointId = info.roomId; s.damagePercent = info.damagePercent; s.remainingEffectPercent = info.remainingEffectPercent; s.fortifyFactor = info.fortifyFactor; state.roomStates.push_back(s); } ContaminationState cs; state.contaminationStates.reserve(data.contaminations.count()); for (const auto &csi : data.contaminations) { cs.aesId = csi.aesId; cs.rooms.assign(csi.rooms.begin(), csi.rooms.end()); state.contaminationStates.push_back(cs); } if (m_historyState.mseId != 0) historyState(state); else { m_historyState.mseId = m_buildingInfo.mseId; } m_mainHandler->ddsHandler()->dts()->publish(state); if (data.originatorType == BuildingControlUnit::OriginatorType::DirectHit) sendHitLine(state); } void BuildingProcess::handleReconnectEgcu(const QString &segment) { DEBUG_LOG(m_logger) << "handleReconnectEgcu segment =" << segment; for (const EgcuInfo &egcu : std::as_const(m_buildingInfo.egcus)) { if (QString::compare(egcu.hostname, segment, Qt::CaseInsensitive) == 0) { const auto keys = m_effectOverridesReconnect.keys(); for (const int &relayId : keys) { DEBUG_LOG(m_logger) << "RelayId=" << relayId << " Override=" << m_effectOverridesReconnect.value(relayId); if (m_effectOverridesReconnect.value(relayId) >= 0) { InfrastructureManagement::EffectOverride override = InfrastructureManagement::EffectOverride::PermanentOff; int structureElementIdMp = m_mainHandler->ddsHandler()->dtcIsm()->getStructueElementIdFromAssetId(egcu.assetId); DEBUG_LOG(m_logger) << "EGCU: " << egcu.assetName << " assetId=" << egcu.assetId << " hostname='" << egcu.hostname << "' buildingName=" << egcu.buildingName << " mountPointId=" << egcu.mountPointId << " structureElementIdMp=" << structureElementIdMp << " relayId=" << relayId; InfrastructureManagement::StructureElementUpdateEffectOverrideRequest request; request.structureElementId = structureElementIdMp; request.effectId = relayId; request.effectOverride = override; DEBUG_LOG(m_logger) << "Vor structureElementUpdateEffectOverrideRequest"; int result = m_mainHandler->ismHandler()->ftcIsm()->structureElementUpdateEffectOverrideRequest(request, this); DEBUG_LOG(m_logger) << "Nach structureElementUpdateEffectOverrideRequest result=" << result; } } break; } } } void BuildingProcess::handleRelayState(BuildingControlUnit::Json::Telegrams::RelayState *tel) { RelayState state; DEBUG_LOG(m_logger) << "handleRelayState EGCU:" << tel->segment << " m_buildingInfo.egcus.size()=" << m_buildingInfo.egcus.size() << " stateTime=" << tel->stateTime << " offlineTime=" << tel->offlineTime; if ( tel->offlineTime > 1000) { handleReconnectEgcu(tel->segment); } for (const EgcuInfo &egcu : std::as_const(m_buildingInfo.egcus)) { if (QString::compare(egcu.hostname,tel->segment, Qt::CaseInsensitive) == 0) { DEBUG_LOG(m_logger) << "EGCU: " << egcu.assetName << " assetId=" << egcu.assetId << " hostname='" << egcu.hostname << "' buildingName=" << egcu.buildingName; state.id = egcu.assetId; state.stateTime = tel->stateTime; state.offlineTime = tel->offlineTime; break; } } RelayStateItem item; for (const auto &info : std::as_const(tel->relayStates)) { item.id = info.id; item.state = info.state; state.relayStates.push_back(item); } m_mainHandler->ddsHandler()->dts()->publish(state); } void BuildingProcess::handleEffectOverride(BuildingControlUnit::Json::Telegrams::EffectOverride *tel) { DEBUG_LOG(m_logger) << "BuildingProcess::handleEffectOverride" << " tel->overrides.size=" << tel->overrides.size(); InfrastructureManagement::StructureElementUpdateEffectOverrideRequest request; for ( int i = 0; i < m_buildingInfo.egcus.size(); i++) { DEBUG_LOG(m_logger) << "BuildingProcess::handleEffectOverride i=" << i; if ( m_buildingInfo.egcus[i].hostname.toLower() == tel->segment) { int structureElementIdMp = m_mainHandler->ddsHandler()->dtcIsm()->getStructueElementIdFromAssetId( m_buildingInfo.egcus.at(i).assetId); DEBUG_LOG(m_logger) << "BuildingProcess::handleEffectOverride i=" << i << " mountPointId=" << m_buildingInfo.egcus.at(i).mountPointId << " assetId=" << m_buildingInfo.egcus.at(i).assetId << " structureElementId=" << m_buildingInfo.structureElementId << " structureElementIdMp=" << structureElementIdMp; request.structureElementId = structureElementIdMp; } for ( int j = 0; j < tel->overrides.size(); j++ ) { request.effectId = tel->overrides.at(j).id; switch (tel->overrides.at(j).effectOverride) { case -1: { request.effectOverride = InfrastructureManagement::EffectOverride::Automatic; break; } case 0: { request.effectOverride = InfrastructureManagement::EffectOverride::PermanentOff; break; } case 1: { request.effectOverride = InfrastructureManagement::EffectOverride::PermanentOn; break; } } DEBUG_LOG(m_logger) << "Send Override Request" << " structureElementId=" << request.structureElementId << " effectId=" << request.effectId << " effectOverride=" << request.effectOverride; if ( request.structureElementId > 0) m_mainHandler->ddsHandler()->ftcIsm()->structureElementUpdateEffectOverrideRequest(request, this); else ERROR_LOG(m_logger) << "request.structureElementId() =" << request.structureElementId << " NOT VALID!!!"; } } } bool BuildingProcess::isValidPos(const Core::GeoPosition &pos) { if (qFuzzyIsNull(pos.lat) && qFuzzyIsNull(pos.lon)) return false; return !std::isnan(pos.lat) && !std::isnan(pos.lon); } void BuildingProcess::sendHitLine(const BuildingState &state) { if (state.originatorParticipantId <= 0) return; const ParticipantManagement::ParticipantPosition participantPosition = m_mainHandler->ppmHandler()->participantPosition(state.originatorParticipantId); if (!isValidPos(participantPosition.pos)) return; BuildingHitLineInfo buildingHitLineInfo; InfrastructureManagement::MainStructureElement mainStructureElement = m_mainHandler->ismHandler()->mainStructureElement(state.id); buildingHitLineInfo.id = m_hitLineId; buildingHitLineInfo.buildingId = m_buildingInfo.mseId; buildingHitLineInfo.buildingPos = mainStructureElement.geoPosition; if (state.hitOnRoomId > 0) { for (const auto &se : mainStructureElement.elements) { if (se.structureElementTypeId == InfrastructureManagement::StructureElementType::Room && se.mountingPointId == state.hitOnRoomId) { buildingHitLineInfo.buildingPos = se.geoPosition; break; } } } buildingHitLineInfo.chargeTypeId = state.chargeTypeId; buildingHitLineInfo.originatorParticipantId = state.originatorParticipantId; buildingHitLineInfo.originatorPos = participantPosition.pos; buildingHitLineInfo.originatorPartyId = participantPosition.partyId; buildingHitLineInfo.sampleTime = state.sampleTime; buildingHitLineInfo.hitOnRoomId = state.hitOnRoomId; buildingHitLineInfo.contaminationStates = state.contaminationStates; m_mainHandler->ddsHandler()->dts()->publish(buildingHitLineInfo); m_hitLineInfoMap.insert(buildingHitLineInfo.id, buildingHitLineInfo); ++m_hitLineId; } void BuildingProcess::onHitLineInfoTimeout() { const qint64 tNow = QDateTime::currentMSecsSinceEpoch(); auto it = m_hitLineInfoMap.begin(); while (it != m_hitLineInfoMap.end()) { if (!LtsUtils::isPositionTimeOutdated(tNow, it.value().sampleTime)) break; m_mainHandler->ddsHandler()->dts()->removeBuildingHitLineInfoByKey(it.value().id); it = m_hitLineInfoMap.erase(it); } } bool BuildingProcess::writeFile(const QString &fn, const QByteArray &data) { QFile f(fn); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { WARNING_LOG(m_logger) << "Can not open file '" << fn << "' - error: " << f.errorString(); return false; } if (f.write(data) != data.size()) { WARNING_LOG(m_logger) << "Can not write data to file '" << fn << "' - error: " << f.errorString(); return false; } return true; } static void outputBsmData(QXmlStreamWriter &sw, const BsmInfo &bsm) { sw.writeStartElement(QStringLiteral("BSM")); { sw.writeAttribute(QStringLiteral("id"), QString::number(bsm.bsmId)); sw.writeAttribute(QStringLiteral("hostname"), bsm.hostname); sw.writeAttribute(QStringLiteral("port"), QString::number(bsm.port)); sw.writeStartElement(QStringLiteral("Position")); { sw.writeAttribute(QStringLiteral("x"), QString::number(bsm.relPosition.offsetX, 'f', 10)); sw.writeAttribute(QStringLiteral("y"), QString::number(bsm.relPosition.offsetY, 'f', 10)); sw.writeAttribute(QStringLiteral("z"), QString::number(bsm.relPosition.offsetZ, 'f', 10)); } sw.writeEndElement(); sw.writeStartElement(QStringLiteral("Direction")); { sw.writeAttribute(QStringLiteral("x"), QString::number(bsm.orientation.heave, 'f', 10)); sw.writeAttribute(QStringLiteral("y"), QString::number(bsm.orientation.pitch, 'f', 10)); sw.writeAttribute(QStringLiteral("z"), QString::number(bsm.orientation.roll, 'f', 10)); } sw.writeEndElement(); } sw.writeEndElement(); } static void outputEgcuData(QXmlStreamWriter &sw, const EgcuInfo &egcu) { sw.writeStartElement(QStringLiteral("EGCU")); { sw.writeAttribute(QStringLiteral("hostname"), egcu.hostname); sw.writeStartElement(QStringLiteral("rooms")); for (int roomId : egcu.roomIds) { sw.writeStartElement(QStringLiteral("room")); sw.writeAttribute(QStringLiteral("id"), QString::number(roomId)); sw.writeEndElement(); } sw.writeEndElement(); } sw.writeEndElement(); } QByteArray BuildingProcess::generateBuildingConfigData() const { QByteArray ret; QXmlStreamWriter sw(&ret); sw.setAutoFormatting(true); sw.setAutoFormattingIndent(2); sw.writeStartDocument(QStringLiteral("1.0"), true); sw.writeStartElement(QStringLiteral("BuildingConfiguration")); { sw.writeTextElement(QStringLiteral("BuildingName"), m_buildingInfo.name); sw.writeTextElement(QStringLiteral("ModelId"), QString::number(m_buildingInfo.modelId)); for (const BsmInfo &bsm : m_buildingInfo.bsms) outputBsmData(sw, bsm); for (const EgcuInfo &egcu : m_buildingInfo.egcus) outputEgcuData(sw, egcu); } sw.writeEndElement(); sw.writeEndDocument(); return ret; } QByteArray BuildingProcess::generateChargesData() const { const QMap buildingCharges = m_mainHandler->pdmHandler()->buildingChargeEffects(); const QMap chargeTypes = m_mainHandler->pdmHandler()->chargeTypes(); QByteArray ret; QString properties = QStringLiteral("TODO fill properties"); QXmlStreamWriter sw(&ret); sw.setAutoFormatting(true); sw.setAutoFormattingIndent(2); sw.writeStartDocument(QStringLiteral("1.0"), true); sw.writeStartElement(QStringLiteral("Charges")); { for (const ProjectDataManagement::BuildingChargeEffect &charge : buildingCharges) { auto it = chargeTypes.find(charge.chargeTypeId); if (it == chargeTypes.end()) { WARNING_LOG(m_logger) << "Could not find charge type with id " << charge.chargeTypeId << " - skipping"; continue; } ProjectDataManagement::ChargeTypeProperty chargeTypeProperty = m_mainHandler->ddsHandler()->dtcPdm()->getChargeTypeSpecialProperty(charge.chargeTypeId,0,0); sw.writeStartElement(QStringLiteral("Charge")); sw.writeTextElement(QStringLiteral("Id"), QString::number(charge.id)); sw.writeTextElement(QStringLiteral("ChargeTypeId"), QString::number(charge.chargeTypeId)); sw.writeTextElement(QStringLiteral("DestructiveFactor"), QString::number(charge.destructiveFactor)); sw.writeTextElement(QStringLiteral("PenetrationEffect"), QString::number(charge.penetrationFactor)); sw.writeTextElement(QStringLiteral("properties"), properties); sw.writeTextElement(QStringLiteral("EffectSound"), QString::number(charge.hitSound)); sw.writeTextElement(QStringLiteral("DifcueLight"), QString::number(charge.difcueLight)); sw.writeTextElement(QStringLiteral("DifcuePyro"), QString::number(charge.difcuePyro)); if (chargeTypeProperty == ProjectDataManagement::ChargeTypeProperty::None) if (charge.destructiveFactor > 0) sw.writeTextElement(QStringLiteral("showHitLine"), QString::number(1)); else sw.writeTextElement(QStringLiteral("showHitLine"), QString::number(0)); else sw.writeTextElement(QStringLiteral("showHitLine"), QString::number(1)); sw.writeEndElement(); } } sw.writeEndElement(); sw.writeEndDocument(); return ret; } void BuildingProcess::openWebSocket() { closeWebSocket(); m_wsSocket = new QWebSocket; connect(m_wsSocket, &QWebSocket::errorOccurred, this, &BuildingProcess::onWebSocketError); connect(m_wsSocket, &QWebSocket::connected, this, &BuildingProcess::onWebSocketConnected); connect(m_wsSocket, &QWebSocket::disconnected, this, &BuildingProcess::onWebSocketDisconnected); connect(m_wsSocket, &QWebSocket::textMessageReceived, this, &BuildingProcess::onTextMessageReceived); WARNING_LOG(m_logger) << "openWebSocket:" << m_wsUrl.toLocal8Bit() << " (buildingId: " << m_buildingInfo.mseId << ") - socket: " << m_wsSocket; m_wsSocket->open(QUrl::fromEncoded(m_wsUrl.toLocal8Bit())); } void BuildingProcess::closeWebSocket() { if (m_wsSocket) { WARNING_LOG(m_logger) << "closeWebSocket (buildingId : " << m_buildingInfo.mseId << ") - socket: " << m_wsSocket << " localPort=" << m_wsSocket->localPort() << ", error: " << ", " << m_wsSocket->errorString(); m_wsSocket->disconnect(this); m_wsSocket->close(); m_wsSocket->deleteLater(); m_wsSocket = nullptr; } m_wsReconnectTimer->stop(); } void BuildingProcess::onWebSocketError(QAbstractSocket::SocketError error) { if (!m_wsSocket) return; WARNING_LOG(m_logger) << "Error connecting to " << m_wsSocket->peerAddress().toString() << ": " << error << " (buildingId: " << m_buildingInfo.mseId << ") - socket: " << m_wsSocket; closeWebSocket(); m_wsReconnectTimer->start(1000); } void BuildingProcess::onWebSocketConnected() { INFO_LOG(m_logger) << "connected to building control process " << m_buildingInfo.mseId << " via websockets localPort="<localPort(); QMapIterator i(m_effectStates); while (i.hasNext()) { i.next(); DEBUG_LOG(m_logger) << "setEffectStates websocket send: " << i.value(); qint64 sendBytesCount = m_wsSocket->sendTextMessage(i.value()); DEBUG_LOG(m_logger) << "sendBytesCount= " << sendBytesCount; } m_wsReconnectTimer->stop(); } void BuildingProcess::onWebSocketDisconnected() { WARNING_LOG(m_logger) << "Websocket to " << m_wsUrl << " closed - trying to reconnect in 1 sec" << " (buildingId : " << m_buildingInfo.mseId << ") - socket: " << m_wsSocket << " localPort=" << m_wsSocket->localPort(); closeWebSocket(); m_wsReconnectTimer->start(1000); } }