1210 lines
45 KiB
C++
Executable File
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);
|
|
}
|
|
|
|
}
|