Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/Proxies/Buildings/building_process.cpp
T
2026-10-03 09:20:13 +02:00

1210 lines
45 KiB
C++
Executable File

#include "building_process.h"
#include <utility>
#include "main_handler.h"
#include "BuildingControlUnit/ControlEffects/xmlreader.h"
#include <QStandardPaths>
#include <QtCore/QFile>
#include <QtCore/QTimer>
#include <QtGui/QTransform>
#include <QtWebSockets/QWebSocket>
#include <QXmlStreamWriter>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <ngltools/ngltools_filesystem.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtils/LtsUtilsCore/outdated_position.h>
#include <bcu_types.h>
#include <ProjectDataManagement/pdm_to_string.h>
#include <AssetManagement/asm.hpp>
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<BuildingControlUnit::Effect*> &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<int>(effectPtr->override);
if (effectPtr->effectType == 1)
{
m_effectOverridesReconnect.insert(effectPtr->id, static_cast<int>(effectPtr->override));
}
}
delete xmlReader;
connect(&m_proc, QOverload<int, QProcess::ExitStatus>::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<int> &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: "<<bldId;
if (!m_wsSocket || !m_wsSocket->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<Buildings::FortifyFactor> &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<int, AssetManagement::RelayInfo> &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<int> allRelayIds;
for (auto it = m_relayInfos.keyBegin(); it != m_relayInfos.keyEnd(); ++it)
allRelayIds.insert(*it);
#else
QSet<int> 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<arrEgcus.size(); ++i)
{
QJsonObject objState = arrEgcus[i].toObject();
QStringList keys = objState.keys();
QString relayIdString = keys.at(0);
int relayId = relayIdString.toInt();
allRelayIds.remove(relayId);
int value = objState.value(relayIdString).toInt();
BuildingControlUnit::BcuEffectState bcuEffectState;
bcuEffectState.id = relayId;
bcuEffectState.type = BuildingControlUnit::BET_Relay;
if (relayId == 99)
{
bcuEffectState.id = 1;
bcuEffectState.type = BuildingControlUnit::BET_BackgroundSound;
}
bcuEffectState.effectOverride = static_cast<BuildingControlUnit::BcuEffectOverride>(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<BuildingControlUnit::BcuEffectOverride>(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<Buildings::RelayStateItem> &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 <BuildingControlUnit::BcuRelayState>(static_cast<int>(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<int, BsmInfo> 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<BuildingControlUnit::Json::Telegrams::BsmState *>(tel));
else if (tel->telegramId() == BuildingControlUnit::Json::Telegrams::HitResult::telegramId())
handleHitResult(static_cast<BuildingControlUnit::Json::Telegrams::HitResult *>(tel));
else if (tel->telegramId() == BuildingControlUnit::Json::Telegrams::RelayState::telegramId())
handleRelayState(static_cast<BuildingControlUnit::Json::Telegrams::RelayState *>(tel));
else if (tel->telegramId() == BuildingControlUnit::Json::Telegrams::EffectOverride::telegramId())
handleEffectOverride(static_cast<BuildingControlUnit::Json::Telegrams::EffectOverride *>(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<int, ProjectDataManagement::BuildingChargeEffect> buildingCharges = m_mainHandler->pdmHandler()->buildingChargeEffects();
const QMap<int, ProjectDataManagement::ChargeType> 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="<<m_wsSocket->localPort();
QMapIterator<int, QString> 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);
}
}