Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+182
View File
@@ -0,0 +1,182 @@
#
# $Id$
# $HeadURL$
# $Author$
#
INCLUDE(MacroRTIDDSGen)
INCLUDE(MacroCodeGenIdl)
SET(xml_FILE eem_service.xml)
#
# --- Library svcEemCommon --------------------------------------------------------------------------------
#
SET(SRCS_common
eem_export.h
logger.cpp
logger.h
)
# Generate eem_enums.idl / eem.idl
CODEGEN_IDL_SVC(SRCS_common ${xml_FILE})
# Generate eem_db_structs.cpp/.h
CODEGEN_DATABASE_STRUCTS(SRCS_common ${xml_FILE})
# Generate eem_db_impl.cpp/.h
CODEGEN_DATABASE_SQL(SRCS_common ${xml_FILE})
# Generate *.cpp/*.hpp from idl
RTIDDSGEN(SRCS_common eem_enums.idl)
RTIDDSGEN(SRCS_common eem.idl)
# Generate eem_dt[s,c]_types.h, eem_ft[s,c]_types.h
CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
# generate eem_[d|f]tc.h
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE} PARTS DTC)
NGL_ADD_LIBRARY(AAR SV WS svcEemCommon SHARED ${SRCS_common})
NGL_TARGET_INCLUDE_DIRECTORIES(svcEemCommon
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcEemCommon
PRIVATE
RTI::nddscpp2
PUBLIC
Qt5::Core
ddscore
LtsCommandLineParser
NGLLogger
ngldb
svcPdmCommon
)
NGL_INSTALL_TARGET(svcEemCommon)
#
# --- Library svcEemClient --------------------------------------------------------------------------------
#
SET(SRCS_client
eem_export.h
)
# generate eem_[d|f]tc.h, eem_[d|f]tc_impl.[cpp|h]
CODEGEN_CLIENT_TOPICS_IMPL(SRCS_client ${xml_FILE} PARTS DTC)
NGL_ADD_LIBRARY(AAR SV WS svcEemClient SHARED ${SRCS_client})
NGL_TARGET_INCLUDE_DIRECTORIES(svcEemClient
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcEemClient
PRIVATE
RTI::nddscpp2
ddsconnector
PUBLIC
svcEemCommon
ddsftc
)
NGL_INSTALL_TARGET(svcEemClient)
#
# --- Library svcEemServer --------------------------------------------------------------------------------
#
SET(SRCS_server
eem_export.h
)
# generate eem_[d|f]ts.h, eem_[d|f]ts_impl.[cpp|h]
CODEGEN_SERVER_TOPICS(SRCS_server ${xml_FILE} PARTS DTS)
NGL_ADD_LIBRARY(AAR SV svcEemServer SHARED ${SRCS_server})
NGL_TARGET_INCLUDE_DIRECTORIES(svcEemServer
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcEemServer
PRIVATE
RTI::nddscpp2
ddsconnector
ddsfts
PUBLIC
svcEemCommon
ngldb
)
NGL_INSTALL_TARGET(svcEemServer)
#
# --- Topic Type Plugin --------------------------------------------------------------
#
CREATE_TOPIC_TYPE_PLUGIN(${xml_FILE})
#
# --- Executable svcTimeLineManagement (sTLM) -------------------------------------------------
#
SET(SRCS_exe
main.cpp
main_handler.cpp
main_handler.h
dds_handler.cpp
dds_handler.h
handler_dynamic_participant_data.cpp
handler_dynamic_participant_data.h
handler_equipment_error_registration.cpp
handler_equipment_error_registration.h
handler_participant_event.cpp
handler_participant_event.h
settings.cpp
settings.h
web_socket_avs.cpp
web_socket_avs.h
web_socket_server.cpp
web_socket_server.h
${xml_FILE}
)
# Generate eem.sql, eem.xml
CODEGEN_SQL(SRCS_exe ${xml_FILE})
NGL_ADD_SVC_ICON(SRCS_exe)
NGL_ADD_EXECUTABLE(AAR SV svcExerciseEventManagement ${SRCS_exe})
NGL_TARGET_LINK_LIBRARIES(svcExerciseEventManagement
Qt5::Core
svcAlmServer
svcFltServer
svcMrkServer
svcEvlServer
svcBldClient
svcCesClient
svcAesClient
svcCoreClient
svcEemCommon
svcEemServer
svcOssClient
svcPpmClient
svcAvmClient
svcIsmCommon
svcIsmClient
svcPdmClient
ddsconnector
nglsettings
ProjectConfig
LtsCommandLineParser
LtsUtilsCoreHeaderOnly
NGLLogger
nglcoreapplication
ngltools
Qt5::WebSockets
)
NGL_INSTALL_TARGET(svcExerciseEventManagement)
NGL_INSTALL(AAR SV FILES log4cxx_eem.props DESTINATION bin)
NGL_ADD_TARGET_LINK(svcExerciseEventManagement sEEM)
# Uncomment if necessary
#ADD_SUBDIRECTORY(UnitTests)
ADD_SUBDIRECTORY(ModuleTests)
ADD_SUBDIRECTORY(WebSocketServer)
@@ -0,0 +1,46 @@
#
# $Id$
# $HeadURL$
# $Author$
#
IF(BUILD_TESTING AND BUILD_SV)
SET(SRCS_TEST_EEM
eem_tests.cpp
)
SET(HDRS_TEST_EEM
eem_tests.h
)
NGL_TARGET_INCLUDE_DIRECTORIES(eemTests
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
# Testing FunctionTopic Functionality (FTC)
ADD_EXECUTABLE(eemTests ${SRCS_TEST_EEM} ${HDRS_TEST_EEM})
TARGET_LINK_LIBRARIES(eemTests
Qt5::Core
Qt5::Test
RTI::nddscpp2
svcEemClient
svcTabServer
svcPpmServer
svcBldServer
svcGimServer
svcCoreClient
LtsCommandLineParser
ddsconnector
nglcoreapplication
)
IF(NOT BUILD_RPM)
NGL_INSTALL(AAR SV TARGETS eemTests DESTINATION bin/test)
NGL_INSTALL(AAR SV FILES ../log4cxx_eem.props DESTINATION bin/test)
ENDIF()
ENDIF()
@@ -0,0 +1,468 @@
/**
* Tlm Function Topic Module Test
* $Id$
* $HeadURL$
* $Author$
*/
#include "eem_tests.h"
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <QTimer>
#include <QDateTime>
#include <QTest>
#include <QJsonObject>
#include <QJsonDocument>
#include <LtsUtilsCore/json_object.h>
/* Module tests for ExerciseEventManagement
* 1. Dynamic data
* 2. IED Events
* 3. Fire events
* 4. Bite messages
* 5. Hit Building
* 6. Sanitation events
*/
//-----------------------------------------------------------------------------------------------------------------------
namespace ExerciseEventManagement
{
EemTests::EemTests(QObject *parent)
: QObject(parent)
, m_logger(getLogger("Test"))
{}
//-----------------------------------------------------------------------------------------------------------------------
bool EemTests::init(int domainId, int exerciseId, int testId)
{
bool bRet = true;
DEBUG_LOG(m_logger) << "init begin";
m_testId = testId;
m_ddsConnector42 = std::make_shared<DDSConnector::DdsConnector>(domainId, IDL::PartitionType::Exercise, exerciseId);
m_ddsConnector42->init();
m_ddsConnector42->initPublisherSubscriber();
m_ppmDts = ParticipantManagement::Interface::Dts::create(m_ddsConnector42);
bRet &= m_ppmDts->enableDynamicParticipantData();
m_tabDts = TrainingAreaBase::Interface::Dts::create(m_ddsConnector42);
bRet &= m_tabDts->enableEquipmentGearEvent();
bRet &= m_tabDts->enableEquipmentGearChargeState();
bRet &= m_tabDts->enableEquipmentGearAssignment();
bRet &= m_tabDts->enableEquipmentBite();
m_gimDts = GuezIedManagement::Interface::Dts::create(m_ddsConnector42);
bRet &= m_gimDts->enableIed();
m_dtc = ExerciseEventManagement::Interface::Dtc::create(m_ddsConnector42);
bRet &= m_dtc->enableEvent();
m_bldDts = Buildings::Interface::Dts::create(m_ddsConnector42);
bRet &= m_bldDts->enableBuildingState();
DEBUG_LOG(m_logger) << "bRet: " << bRet;
return bRet;
}
//-----------------------------------------------------------------------------------------------------------------------
void EemTests::startTest()
{
INFO_LOG(m_logger) << "startTest m_testId = " << m_testId;
if (m_testId == 1) // publish dynamicParticipantData
{
const qint64 time = QDateTime::currentMSecsSinceEpoch();
tryCreateDynamicParticipantData(1, ProjectDataManagement::VulnerabilityState::MediumDamage, time - 15000 * 60, 38);
QTest::qWait(500);
tryCreateDynamicParticipantData(2, ProjectDataManagement::VulnerabilityState::MediumDamage, time - 13000 * 30, 38);
QTest::qWait(500);
tryCreateDynamicParticipantData(1, ProjectDataManagement::VulnerabilityState::HeavyDamage, time - 10000 * 60, 38);
QTest::qWait(500);
tryCreateDynamicParticipantData(1, ProjectDataManagement::VulnerabilityState::MobilityKill, time - 8000 * 30, 38);
QTest::qWait(500);
tryCreateDynamicParticipantData(2, ProjectDataManagement::VulnerabilityState::MobilityKill, time - 6000 * 60, 38);
QTest::qWait(500);
tryCreateDynamicParticipantData(1, ProjectDataManagement::VulnerabilityState::CatastrophicKill, time - 4000 * 30, 38);
QTest::qWait(500);
tryCreateDynamicParticipantData(2, ProjectDataManagement::VulnerabilityState::CatastrophicKill, time - 2000, 38);
QTest::qWait(500);
}
if (m_testId == 2)
{
//ied
const qint64 time = QDateTime::currentMSecsSinceEpoch();
tryCreateIed(time - 1000 * 60);
QTest::qWait(500);
// dynamic participant data for ied
tryCreateDynamicParticipantData(1, ProjectDataManagement::VulnerabilityState::HeavyDamage, time + 1000 * 60, 49, ProjectDataManagement::StateChangeOriginator::ImprovisedExplosiveDevice);
QTest::qWait(500);
tryCreateDynamicParticipantData(1, ProjectDataManagement::VulnerabilityState::MobilityKill, time + 2000 * 60, 49, ProjectDataManagement::StateChangeOriginator::ImprovisedExplosiveDevice);
QTest::qWait(500);
tryCreateDynamicParticipantData(1, ProjectDataManagement::VulnerabilityState::CatastrophicKill, time + 3000 * 60, 70, ProjectDataManagement::StateChangeOriginator::ImprovisedExplosiveDevice);
QTest::qWait(5000);
checkEvents(ProjectDataManagement::EventType::HitEffectSimulation, 3);
}
if (m_testId == 3)
trySendFireEvent();
if (m_testId == 4)
trySendBiteMessage();
if (m_testId == 5)
trySendBuildingState();
if (m_testId == 6)
{
const qint64 time = QDateTime::currentMSecsSinceEpoch();
tryCreateDynamicParticipantData(2, ProjectDataManagement::VulnerabilityState::HeavyDamage, time - 10000 * 60, 0, ProjectDataManagement::StateChangeOriginator::Sanitation);
QTest::qWait(500);
tryCreateDynamicParticipantData(2, ProjectDataManagement::VulnerabilityState::MediumDamage, time - 8000 * 30, 0, ProjectDataManagement::StateChangeOriginator::Sanitation);
QTest::qWait(500);
tryCreateDynamicParticipantData(2, ProjectDataManagement::VulnerabilityState::LightDamage, time - 5000 * 30, 0, ProjectDataManagement::StateChangeOriginator::Sanitation);
QTest::qWait(500);
checkEvents(ProjectDataManagement::EventType::Sanitation, 3);
}
if (m_testId <= 0 || m_testId > 6)
{
ERROR_LOG(m_logger) << "Unknown test case: " << m_testId;
failExit();
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
//-----------------------------------------------------------------------------------------------------------------------
void EemTests::tryCreateDynamicParticipantData(int participantId, ProjectDataManagement::VulnerabilityState vulnState, qint64 time, int chargeTypeId,
ProjectDataManagement::StateChangeOriginator originator)
{
ParticipantManagement::DynamicParticipantData dp;
const rti::core::int64 esTime = static_cast<rti::core::int64>(time);
ParticipantManagement::StateInformation si;
si.stateTime(esTime);
si.vulnerabilityState(vulnState);
if (chargeTypeId > 0)
si.originatorChargeTypeId(chargeTypeId);
si.originatorId(participantId + 1);
si.originatorType(originator);
si.vitality(100);
if (originator == ProjectDataManagement::StateChangeOriginator::Sanitation)
{
si.sanitationResult(ParticipantManagement::SanitationResult::Sufficient);
si.sanitationProceduresTaken(1);
}
dp.participantId(participantId);
//dp.isManipulated(true);
dp.stateHistory().push_back(si);
dp.sampleTime(esTime);
DEBUG_LOG(m_logger) << "Publishing DynamicParticipantData for participant " << participantId ;
if (!m_ppmDts->publish(dp))
{
ERROR_LOG(m_logger) << "Error requesting create DynamicParticipantData";
failExit();
}
}
//-----------------------------------------------------------------------------------------------------------------------
void EemTests::trySendFireEvent()
{
TrainingAreaBase::EquipmentGearAssignment gearAssigment;
gearAssigment.equipmentNumber(1002);
gearAssigment.sampleTime(QDateTime::currentMSecsSinceEpoch());
std::vector<TrainingAreaBase::Gear> gears;
gears.resize(2);
gears[0].gearId(132456);
gears[0].gearTypeId(1003); // Sniper
gears[1].gearId(45778);
gears[1].gearTypeId(1006); // HMG
gearAssigment.gears(gears);
DEBUG_LOG(m_logger) << "Publish EquipmentGearAssigment";
if (!m_tabDts->publish(gearAssigment))
{
ERROR_LOG(m_logger) << "Error while publishing EquipmentGearAssigment";
failExit();
}
TrainingAreaBase::EquipmentGearChargeState gearChargeState;
gearChargeState.equipmentNumber(1002);
gearChargeState.sampleTime(QDateTime::currentMSecsSinceEpoch());
std::vector<TrainingAreaBase::GearCharge> vGearCharge;
vGearCharge.resize(1);
vGearCharge[0].gearId(132456);
vGearCharge[0].gearTypeId(1003);
vGearCharge[0].chargeTypeId(44);
vGearCharge[0].remain(250);
gearChargeState.charge(vGearCharge);
DEBUG_LOG(m_logger) << "Publish EquipmentGearChargeState";
if (!m_tabDts->publish(gearChargeState))
{
ERROR_LOG(m_logger) << "Error while publishing EquipmentGearChargeState";
failExit();
}
TrainingAreaBase::EquipmentGearEvent gearEvent;
gearEvent.equipmentNumber(1002);
gearEvent.sampleTime(QDateTime::currentMSecsSinceEpoch()+1000);
TrainingAreaBase::GearCharge gc;
gc.gearId(132456);
gc.gearTypeId(1003);
gc.chargeTypeId(44);
gc.remain(200);
gearEvent.chargeAmount(200);
gearEvent.charge(gc);
DEBUG_LOG(m_logger) << "Publish EquipmentGearEvent";
if (!m_tabDts->publish(gearEvent))
{
ERROR_LOG(m_logger) << "Error while publishing EquipmentGearEvent";
failExit();
}
gearEvent.equipmentNumber(1003);
gc.gearId(45678);
gc.gearTypeId(1009);
gc.chargeTypeId(9);
gc.remain(350);
gearEvent.chargeAmount(100);
gearEvent.charge(gc);
DEBUG_LOG(m_logger) << "Publish EquipmentGearEvent";
if (!m_tabDts->publish(gearEvent))
{
ERROR_LOG(m_logger) << "Error while publishing EquipmentGearEvent";
failExit();
}
QTest::qWait(2000);
const QMap<qint64, ExerciseEventManagement::Event> events = m_dtc->events();
DEBUG_LOG(m_logger) << "events: " << events.size();
int noEvents = 0;
for(const ExerciseEventManagement::Event &e : events)
{
INFO_LOG(m_logger) << "eventId: " << e.id() << ", eventType: " << e.eventTypeId().underlying();
if (e.eventTypeId() == ProjectDataManagement::EventType_def::WeaponAssignment ||
e.eventTypeId() == ProjectDataManagement::EventType_def::FireEvent)
noEvents ++;
}
if (noEvents != 5)
{
ERROR_LOG(m_logger) << "Error number of events (" << QString::number(noEvents) << ") not as expected (5)";
failExit();
}
QTimer::singleShot(500, qApp, &QCoreApplication::quit);
}
//-----------------------------------------------------------------------------------------------------------------------
void EemTests::trySendBiteMessage()
{
TrainingAreaBase::EquipmentBite bite;
bite.batteryState(2);
bite.equipmentNumber(1002);
bite.biteState(16);
bite.configError(true);
bite.configState(8);
bite.sampleTime(QDateTime::currentMSecsSinceEpoch());
std::vector<TrainingAreaBase::EquipmentBiteError> beVec;
beVec.resize(2);
beVec[0].canDeviceNumber(1000);
beVec[0].errorNumber(23);
beVec[0].subdeviceNumber(110);
beVec[0].uniqueId(198765);
beVec[1].canDeviceNumber(1001);
beVec[1].errorNumber(24);
beVec[1].subdeviceNumber(111);
beVec[1].uniqueId(9876);
bite.biteErrors(beVec);
if (!m_tabDts->publish(bite))
{
ERROR_LOG(m_logger) << "Error while publishing EquipmentBite";
failExit();
}
}
//-----------------------------------------------------------------------------------------------------------------------
void EemTests::trySendBuildingState()
{
Buildings::BuildingState bState;
bState.id(301);
bState.damagePercent(80);
bState.damagePercentOld(0);
bState.sampleTime(QDateTime::currentMSecsSinceEpoch());
bState.vulnerabilityState(ProjectDataManagement::VulnerabilityState::CatastrophicKill);
bState.structureElementId(26);
// bState.aesId(1);
// bState.assetId(10);
// bState.chargeTypeId(44);
// bState.originatorType(ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation);
// bState.originatorParticipantId(6);
if (!m_bldDts->publish(bState))
{
ERROR_LOG(m_logger) << "Error while publishing building state";
failExit();
}
QTest::qWait(2000); // wait until it is really published
}
//-----------------------------------------------------------------------------------------------------------------------
void EemTests::tryCreateIed(qint64 time)
{
GuezIedManagement::Ied ied;
ied.id(100);
ied.name("Test IED");
ied.partyId(4);
ied.equipmentNumber(1003);
ied.type(GuezIedManagement::IedType::IedBigPyro);
ied.explosiveForce(100);
ied.pairingState(GuezIedManagement::IedPairingState_def::NotPaired);
ied.tacticalState(GuezIedManagement::IedTacticalState_def::TriggeredByControlDevice);
ied.pyrotechnics(true);
ied.symbol("AP6D|CMPL|10014000001103000000");
const rti::core::int64 esTime = static_cast<rti::core::int64>(time);
ied.sampleTime(esTime);
DEBUG_LOG(m_logger) << "Publishing Ied " ;
if (!m_gimDts->publish(ied))
{
ERROR_LOG(m_logger) << "Error requesting publish Ied";
failExit();
}
}
//-----------------------------------------------------------------------------------------------------------------------
void EemTests::checkEvents(const ProjectDataManagement::EventType &et, int expected)
{
const QMap<qint64, ExerciseEventManagement::Event> events = m_dtc->events();
DEBUG_LOG(m_logger) << "All published events: " << events.size();
int noEvents = 0;
for (const ExerciseEventManagement::Event &e : events)
{
INFO_LOG(m_logger) << "eventId: " << e.id() << ", eventType: " << e.eventTypeId().underlying();
if (e.eventTypeId() == et)
{
QJsonObject prop = LtsUtils::JsonObject::fromJson(e.properties());
int ot = prop[QStringLiteral("OriginatorType")].toInt();
if (et == ProjectDataManagement::EventType::HitEffectSimulation && ot == 20) //ImprovisedExplosiveDevice
noEvents ++;
if (et == ProjectDataManagement::EventType::Sanitation && ot == 10) // Sanitation
noEvents ++;
}
}
DEBUG_LOG(m_logger) << "No events: " << noEvents;
if (noEvents != expected)
{
ERROR_LOG(m_logger) << "Error number of events (" << QString::number(noEvents) << ") not as expected (" << QString::number(expected) << ")";
failExit();
}
QTimer::singleShot(500, qApp, &QCoreApplication::quit);
}
//-----------------------------------------------------------------------------------------------------------------------
void EemTests::failExit()
{
m_bTestsSuccessful = false;
QTimer::singleShot(50, qApp, QCoreApplication::quit);
}
//-----------------------------------------------------------------------------------------------------------------------
void EemTests::ExitSuccess()
{
m_bTestsSuccessful = true;
QTimer::singleShot(50, qApp, QCoreApplication::quit);
}
}
// ------------------------------------------------------------------
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser()
{
addOption({ QStringList() << QLatin1String("t") << QLatin1String("testId")
, QLatin1String("TestId.")
, QLatin1String("Default: -1 (Unknown)")
, QLatin1String("0")
});
}
int testId() const { return isSet(QLatin1String("t")) ? value(QLatin1String("t")).toInt() : -1; }
};
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
NGLCoreApplication app(argc, argv);
int iRet = -1;
CmdLineParser options;
if (!options.init())
return iRet;
app.createLogger("EEM", options.exerciseId());
std::unique_ptr<ExerciseEventManagement::EemTests> EemTests(new ExerciseEventManagement::EemTests);
if (!EemTests->init(options.domainId(), options.exerciseId(), options.testId()))
return iRet;
EemTests->startTest();
STARTUP_LOG(app.baseLogger());
app.exec();
iRet = EemTests->testsSuccessful() ? 0 : 1;
fprintf(stderr, "%d\n", iRet);
DEBUG_LOG(app.baseLogger()) << "** TEST result = " << iRet;
SHUTDOWN_LOG(app.baseLogger());
return iRet;
}
@@ -0,0 +1,62 @@
/**
* ModuleTests for TimeLimeManagement
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <ParticipantManagement/ppm_dts.h>
#include <TrainingAreaBase/tab_dts.h>
#include <GuezIedManagement/gim_dts.h>
#include <Proxies/Buildings/bld_dts.h>
#include <ExerciseEventManagement/eem_dtc.h>
#include <DDS/DDSConnector/dds_connector.h>
#include <ExerciseEventManagement/logger.h>
namespace ExerciseEventManagement
{
class EemTests : public QObject
{
Q_OBJECT
public:
explicit EemTests(QObject *parent = nullptr);
~EemTests() = default;
bool init(int domainId, int exerciseId, int testId);
bool testsSuccessful() const
{
return m_bTestsSuccessful;
}
public Q_SLOTS:
void startTest();
protected Q_SLOTS:
void tryCreateDynamicParticipantData(int participantId, ProjectDataManagement::VulnerabilityState vulnState, qint64 time, int chargeTypeId,
ProjectDataManagement::StateChangeOriginator originator = ProjectDataManagement::StateChangeOriginator::Participant);
void trySendFireEvent();
void trySendBiteMessage();
void trySendBuildingState();
void tryCreateIed(qint64 time);
void checkEvents(const ProjectDataManagement::EventType &et, int expected);
void failExit();
void ExitSuccess();
private:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector42;
std::shared_ptr<ParticipantManagement::Interface::Dts> m_ppmDts;
std::shared_ptr<TrainingAreaBase::Interface::Dts> m_tabDts;
std::shared_ptr<Buildings::Interface::Dts> m_bldDts;
std::shared_ptr<GuezIedManagement::Interface::Dts> m_gimDts;
std::shared_ptr<ExerciseEventManagement::Interface::Dtc> m_dtc;
bool m_bTestsSuccessful{true};
NGL::INGLLogger &m_logger;
int m_testId{0};
};
} // namespace ExerciseEventManagement
@@ -0,0 +1,22 @@
#
# $Id$
# $HeadURL$
# $Author$
#
SET(websocket_SRCS
main.cpp
web_socket_handler.cpp
web_socket_handler.h
)
NGL_ADD_EXECUTABLE(AAR SV webSocketServer ${websocket_SRCS})
NGL_TARGET_LINK_LIBRARIES(webSocketServer
Qt5::WebSockets
Qt5::Core
Qt5::Network
NGLLogger
nglcoreapplication
svcEemCommon
)
@@ -0,0 +1,39 @@
/**
* @RDE_HEADER@
* main for TimeLineManagement
* $Id$
* $HeadURL$
* $Author$
*/
#include <nglapplication/nglcoreapplication.h>
#include "web_socket_handler.h"
#include "../logger.h"
int main(int argc, char **argv)
{
NGLCoreApplication app(argc, argv);
app.createLogger("WebSocketServer", -1);
int iRet = -3;
{
ExerciseEventManagement::WebSocketHandler webSocketServer;
webSocketServer.init(7020);
STARTUP_LOG(app.baseLogger());
iRet = app.exec();
SHUTDOWN_LOG(app.baseLogger());
}
return iRet;
}
@@ -0,0 +1,115 @@
/**
* Websocket interface for building control unit
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-01-30
*/
#include "web_socket_handler.h"
#include <QWebSocket>
#include <iostream>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonDocument>
namespace ExerciseEventManagement
{
WebSocketHandler::WebSocketHandler(QObject *parent)
: QObject(parent)
, m_webSocketServer(new QWebSocketServer(QStringLiteral("AVS-Server"), QWebSocketServer::NonSecureMode, this))
, m_timer(new QTimer())
, m_logger(getLogger("Json.WebSocketHandler"))
{
connect(m_timer, &QTimer::timeout,
this, &WebSocketHandler::onSendMessage);
m_timer->start(1000);
}
WebSocketHandler::~WebSocketHandler()
{
for (auto s : qAsConst(m_webSockets))
{
s->disconnect(this);
s->close();
}
qDeleteAll(m_webSockets);
}
bool WebSocketHandler::init(quint16 listenPort)
{
if (!m_webSocketServer->listen(QHostAddress::Any, listenPort))
{
WARNING_LOG(m_logger) << "Can not listen on port " << listenPort << " - aborting";
return false;
}
// inform calling process via stdout about the port/url
std::cout << "web socket listen port: " << m_webSocketServer->serverPort() << std::endl;
std::cout << "web socket url: " << m_webSocketServer->serverUrl().toString() << std::endl;
connect(m_webSocketServer, &QWebSocketServer::newConnection,
this, &WebSocketHandler::onNewConnection);
return true;
}
bool WebSocketHandler::sendTelegram(const QString &jsonStr)
{
if (!isConnected())
return false;
ERROR_LOG(m_logger) << "jsonStr" << jsonStr;
bool bOk = false;
for (QWebSocket *ws : qAsConst(m_webSockets))
{
if (ws->sendTextMessage(jsonStr))
bOk = true;
}
return bOk;
}
void WebSocketHandler::onNewConnection()
{
QWebSocket *ws = m_webSocketServer->nextPendingConnection();
if (!ws)
return;
INFO_LOG(m_logger) << "New websocket connection from " << ws->peerAddress().toString() << ":" << ws->peerPort();
connect(ws, &QWebSocket::disconnected,
this, &WebSocketHandler::onWebSocketDisconnected);
m_webSockets += ws;
emit webSocketConnected(ws);
}
void WebSocketHandler::onSendMessage()
{
ERROR_LOG(m_logger) << "Send message: ";
QJsonObject obj;
obj[QStringLiteral("eventTime")] = QJsonValue(QDateTime::currentMSecsSinceEpoch());
QString jsonStr = QString::fromUtf8(QJsonDocument(obj).toJson());
sendTelegram(jsonStr);
}
void WebSocketHandler::onWebSocketDisconnected()
{
QWebSocket *ws = qobject_cast<QWebSocket *>(sender());
if (!ws)
return;
m_webSockets.remove(ws);
ws->deleteLater();
}
} // namespace ExerciseEventManagement
@@ -0,0 +1,50 @@
/**
* Websocket interface for building control unit
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-01-30
*/
#pragma once
#include <QObject>
#include <QQueue>
#include <QWebSocketServer>
#include <memory>
#include <QTimer>
#include "../logger.h"
namespace ExerciseEventManagement
{
class WebSocketHandler : public QObject
{
Q_OBJECT
public:
explicit WebSocketHandler(QObject *parent = nullptr);
~WebSocketHandler();
bool init(quint16 listenPort);
bool isConnected() const
{
return !m_webSockets.isEmpty();
}
bool sendTelegram(const QString &jsonStr);
Q_SIGNALS:
void webSocketConnected(QWebSocket *ws);
private Q_SLOTS:
void onNewConnection();
void onWebSocketDisconnected();
void onSendMessage();
private:
QWebSocketServer *m_webSocketServer;
QSet<QWebSocket *> m_webSockets;
QTimer *m_timer;
LOGGER_T m_logger;
};
} // namespace ExerciseEventManagement
+74
View File
@@ -0,0 +1,74 @@
/**
* DdsHandler for DDS
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-09-02
*/
#include "dds_handler.h"
#include <QTimer>
namespace ExerciseEventManagement
{
DdsHandler::DdsHandler(int domainId, int partitionId, IDL::PartitionType partitionType)
: m_ddsConnector(std::make_shared<DDSConnector::DdsConnector>(domainId, partitionType, partitionId))
, m_logger(getLogger("DdsHandler"))
{
DEBUG_LOG(m_logger) << "DdsHandler(" << domainId << ") for partition: " << m_ddsConnector->getDefaultPartition();
}
DdsHandler::~DdsHandler()
{
}
bool DdsHandler::init(const QStringList &qosFileList)
{
TRACE_LOG(m_logger) << " START, this=" << this;
if (!m_ddsConnector->init(qosFileList))
{
ERROR_LOG(m_logger) << "Error initializing dds connections - aborting";
return false;
}
// default initialize Publisher and Subscriber
m_ddsConnector->initPublisherSubscriber();
// has to be false, because Snapshot Data Topics can be very large and fragmentation is not supported for batched writers!
m_ddsConnector->enableBatchedWriter(false);
// Pacemaker
m_pacemaker = new Core::Pacemaker(m_ddsConnector);
m_ddsDts = ExerciseEventManagement::Interface::Dts::create(m_ddsConnector);
bool bRet = m_ddsDts->enableAll();
INFO_LOG(m_logger) << "Dts initialised";
// Subsribe for InfrastructureManagement::MainStructureElement
m_dtcIsm = InfrastructureManagement::Interface::Dtc::create(m_ddsConnector);
TRACE_LOG(m_logger) << " END, this=" << this;
return bRet;
}
void DdsHandler::setRunningState(Core::RunningState rs)
{
if (!m_pacemaker)
return;
m_pacemaker->setNewState(rs);
}
void DdsHandler::addPortToPacemaker(quint16 port, Core::PortType type)
{
if (!m_pacemaker)
return;
m_pacemaker->addPort(port, type);
}
} // Namespace ExerciseEventManagement
+49
View File
@@ -0,0 +1,49 @@
/**
* DdsHandler for DDS
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-09-02
*/
#pragma once
#include <DDS/DDSConnector/dds_connector.h>
#include <DDS/Core/pacemaker.h>
#include <ExerciseEventManagement/eem_dts.h>
#include <InfrastructureManagement/ism_dtc.h>
#include "logger.h"
class QThread;
namespace Core
{
class Pacemaker;
}
namespace ExerciseEventManagement
{
class DdsHandler final
{
public:
DdsHandler(int domainId, int partitionId, IDL::PartitionType partitionType);
~DdsHandler();
bool init(const QStringList &qosFileList = QStringList());
void setRunningState(Core::RunningState rs);
void addPortToPacemaker(quint16 port, Core::PortType type);
std::shared_ptr<DDSConnector::DdsConnector> ddsConnector() const { return m_ddsConnector; }
std::shared_ptr<ExerciseEventManagement::Interface::Dts> dts() const { return m_ddsDts; }
std::shared_ptr<InfrastructureManagement::Interface::Dtc> dtcIsm() const { return m_dtcIsm;} ///< Pointer to DTC InfrastructureManagement interface object
private:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<ExerciseEventManagement::Interface::Dts> m_ddsDts;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_dtcIsm; ///< Pointer to DTC InfrastructureManagement interface object
Core::Pacemaker *m_pacemaker = nullptr;
NGL::INGLLogger &m_logger;
};
}
+52
View File
@@ -0,0 +1,52 @@
/**
* Export definitions
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-08-04
*/
#pragma once
#include <QtCore/qglobal.h>
#ifndef EEM_COMMON_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define EEM_COMMON_EXPORT
# else
# if defined(MAKE_SVCEEMCOMMON_LIB)
# define EEM_COMMON_EXPORT Q_DECL_EXPORT
# ifndef WIN32
# ifdef NDDSUSERDllExport
# undef NDDSUSERDllExport
# endif
# define NDDSUSERDllExport Q_DECL_EXPORT
# endif
# else
# define EEM_COMMON_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
#ifndef EEM_CLIENT_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define EEM_CLIENT_EXPORT
# else
# if defined(MAKE_SVCEEMCLIENT_LIB)
# define EEM_CLIENT_EXPORT Q_DECL_EXPORT
# else
# define EEM_CLIENT_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
#ifndef EEM_SERVER_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define EEM_SERVER_EXPORT
# else
# if defined(MAKE_SVCEEMSERVER_LIB)
# define EEM_SERVER_EXPORT Q_DECL_EXPORT
# else
# define EEM_SERVER_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
+78
View File
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<service namespace="ExerciseEventManagement" abbreviation="EEM" procNames="ExerciseEventManagement" exerciseDependency="Exercise" groups="ExercisePreparation,ExerciseExecution,ExercisePaused,ExerciseAfterActionReview" prio="4">
<rcs>
<Id Id="$Id$"/>
<HeadURL HeadURL="$HeadURL$"/>
<Author Author="$Author$"/>
</rcs>
<operatorRights offset="11">
<guiPlugin name="ExerciseEventManagement" offset="1">
<operatorRightGroup name="Exercise Event Management" description="Show Exercise Events">
<operatorRight right="EventsDialogShow" value="1" description="Show the events dialog"/>
<operatorRight right="HitEventsShow" value="2" description="Show Hit Events on Participants and Buildings"/>
<operatorRight right="WeaponAssignmentEventsShow" value="3" description="Show Weapon Assignments to Participants"/>
<operatorRight right="BiteMessageEventsShow" value="4" description="Show Bite Message Events"/>
<operatorRight right="FireEventsShow" value="5" description="Show Participant Firing Events"/>
<operatorRight right="ManipulationEventsShow" value="6" description="Show Manipulation Events"/>
<operatorRight right="AvEventsShow" value="7" description="Show Audio-/Video Events"/>
<operatorRight right="AesEventsShow" value="8" description="Show Area Effect Simulation Events"/>
</operatorRightGroup>
</guiPlugin>
</operatorRights>
<tables>
<table name="eem_event">
<column name="id" datatype="integer" isPrimaryKey="1">
<sequence start="1" increment="1"/>
</column>
<column name="event_type_id" datatype="integer" isForeignKey="1"/>
<column name="short_name" datatype="text"/>
<column name="participant_id" datatype="integer" comment="no foreign key since parameter can contain -1"/>
<column name="party_id" datatype="integer" comment="no foreign key since parameter can contain -1"/>
<column name="equipment_number" datatype="integer"/>
<column name="properties" datatype="jsonb"/>
<column name="sample_time" datatype="bigint"/>
<column name="position" datatype="point_3d"/>
<foreignKey name="FK_eem_event_pdm_event_type">
<source table="eem_event" column="event_type_id"/>
<destination service="ProjectDataManagement" serviceAbbr="pdm" schema="pdm" table="pdm_event_type" column="id"/>
</foreignKey>
<constraint name="IDX_eem_event_sample_time" type="index">
<column name="sample_time"/>
</constraint>
<constraint name="IDX_eem_event_event_type_id" type="index">
<column name="event_type_id"/>
</constraint>
<constraint name="UQ_eem_event_event_type_id_sample_time_participant_id_party_id_" type="unique">
<column name="event_type_id"/>
<column name="sample_time"/>
<column name="participant_id"/>
<column name="party_id"/>
<column name="equipment_number"/>
<column name="properties"/>
</constraint>
</table>
</tables>
<views/>
<dbServiceStructs>
<serviceStruct name="EemEvent" table="eem_event"/>
<table name="eem_event"/>
</dbServiceStructs>
<topicTypes>
<topicType name="Event" isDataObject="1" routeToClient="0" routeToServer="0" routeInReplay="0" recordTopic="0">
<column name="id" idlType="long long" isKey="1"/>
<column name="sampleTime" idlType="long long"/>
<column name="eventTypeId" idlType="ProjectDataManagement::EventType"/>
<column name="participantId" idlType="long"/>
<column name="partyId" idlType="long"/>
<column name="equipmentNumber" idlType="long"/>
<column name="properties" idlType="string" comment="&lt;u&gt;Properties fuer EventType &lt;/u&gt;&lt;b&gt;&lt;u&gt;Hit&lt;/u&gt;&lt;/b&gt;&lt;u&gt;:&lt;/u&gt; VulnerabilityState OriginatorId OriginatorType OriginatorChargeTypeId FriendlyFire Optional: OriginatorEquipmentNumber &lt;u&gt;Properties fuer EventType &lt;/u&gt;&lt;b&gt;&lt;u&gt;FireEvent&lt;/u&gt;&lt;/b&gt;&lt;u&gt;:&lt;/u&gt; GearId GearTypeId ChargeTypeId Remain &lt;u&gt;Properties fuer EventType &lt;/u&gt;&lt;b&gt;&lt;u&gt;WeaponAssignment&lt;/u&gt;&lt;/b&gt;&lt;u&gt;:&lt;/u&gt; GearId GearTypeId DryFire &lt;u&gt;Properties fuer EventType &lt;/u&gt;&lt;b&gt;&lt;u&gt;Manipulation&lt;/u&gt;&lt;/b&gt;&lt;u&gt;:&lt;/u&gt; DeviceType &lt;u&gt;Properties fuer EventType &lt;/u&gt;&lt;b&gt;&lt;u&gt;BiteMessage&lt;/u&gt;&lt;/b&gt;&lt;u&gt;:&lt;/u&gt; BatteryState BiteState ConfigState JSonArray BiteErrors: ErrorNo CanDeviceNumber SubDeviceNumber UniqueId &lt;u&gt;Properties fuer EventType &lt;/u&gt;&lt;b&gt;&lt;u&gt;HitBuilding&lt;/u&gt;&lt;/b&gt;&lt;u&gt;:&lt;/u&gt; &lt;u&gt; &lt;/u&gt;ChargetTypeId damagePercent damagePercentOld AesId VunerabilityState OrigiatorParticipantId StructureElementId &lt;u&gt; &lt;/u&gt;&lt;u&gt;Properties fuer EventType &lt;/u&gt;&lt;b&gt;&lt;u&gt;HitIndrect, HitMine, HitN, HitC, EnterDangerArea, LeaveDangerArea ..&lt;/u&gt;&lt;/b&gt; &lt;b&gt;&lt;u&gt;Alle EventTypes von CES&lt;/u&gt;&lt;/b&gt;&lt;u&gt;:&lt;/u&gt; AesId FriendlyFire"/>
<column name="position" idlType="Core::GeoPosition"/>
</topicType>
</topicTypes>
<functionTopics/>
<enumerations>
<enumeration name="EnumDummy">
<attribute key="Dummy" value="0"/>
</enumeration>
</enumerations>
</service>
@@ -0,0 +1,607 @@
/**
* Handler for infrastructure data
* $Id$
* $HeadURL$
* $Author:
* Created by hjl, SLSE, 2016-09-21
*/
#include "handler_dynamic_participant_data.h"
#include <ExerciseEventManagement/eem_db.h>
#include <ParticipantManagement/ppm.hpp>
#include "web_socket_server.h"
#include "settings.h"
#include <QSet>
#include <QtCore/QThread>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <LtsUtilsCore/json_object.h>
#include <QTimer>
using namespace ExerciseEventManagement;
//-----------------------------------------------------------------------------
HandlerDynamicParticipantData::HandlerDynamicParticipantData(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DbHandler> &db,
const std::shared_ptr<WebSocketEventServer> &eventServer)
: m_ddsHandler(ddsHandler)
, m_dbHandler(db)
, m_eventServer(eventServer)
, m_mainHandler(mainHandler)
, m_logger(getLogger("HandlerDynamicParticipantData"))
{
} // end of ctor()
//-----------------------------------------------------------------------------
HandlerDynamicParticipantData::~HandlerDynamicParticipantData()
{
} // end of dtor()
//-----------------------------------------------------------------------------
bool HandlerDynamicParticipantData::init()
{
INFO_LOG(m_logger) << "start init() DTC ParticipantManagement ";
// DTC ParticipantManagement
m_ppmDtc = ParticipantManagement::Interface::Dtc::create(m_ddsHandler->ddsConnector());
if (m_ppmDtc == nullptr)
{
ERROR_LOG(m_logger) << "Could not create DTC ParticipantManagement";
return false;
}
// Subscribe for DynamicParticipantData (for analyzing data)
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged,
this, &HandlerDynamicParticipantData::onDynamicParticipantDataChanged);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantGearEventChanged,
this, &HandlerDynamicParticipantData::onParticipantGearEventChanged);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged,
this, &HandlerDynamicParticipantData::onParticipantChanged);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::biteChanged,
this, &HandlerDynamicParticipantData::onBiteChanged);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantPositionChanged,
this, &HandlerDynamicParticipantData::onParticipantPositionChanged);
if (!m_ppmDtc->enableDynamicParticipantData() ||
!m_ppmDtc->enableParticipantGearEvent() ||
!m_ppmDtc->enableParticipant() ||
!m_ppmDtc->enableBite() ||
!m_ppmDtc->enableParticipantPosition())
{
ERROR_LOG(m_logger) << "init() DTC ParticipantManagement failed";
return false;
}
m_bldDtc = Buildings::Interface::Dtc::create(m_ddsHandler->ddsConnector());
if (m_bldDtc == nullptr)
{
ERROR_LOG(m_logger) << "Could not create DTC Buildings";
return false;
}
connect(m_bldDtc.get(), &Buildings::Interface::Dtc::buildingStateChanged,
this, &HandlerDynamicParticipantData::onBuildingStateChanged);
if (!m_bldDtc->enableBuildingState())
{
ERROR_LOG(m_logger) << "Enable buildingState failed";
return false;
}
m_aesDtc = AreaEffectSimulation::Interface::Dtc::create(m_ddsHandler->ddsConnector());
m_aesDtc->enableAesItemView();
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged,
this, &HandlerDynamicParticipantData::onAesItemViewChanged);
// Infrastructure Management
auto dtcIsm = m_ddsHandler->dtcIsm();
dtcIsm->enableMainStructureElement();
connect(dtcIsm.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged,
this, &HandlerDynamicParticipantData::onMainStructureElementChanged);
// Get 'static' data for all MainStructureElements
{
auto cb = [this](const InfrastructureManagement::MainStructureElement & mse)
{
onMainStructureElementChanged(mse, DataModification::eAdded);
};
if (!dtcIsm->mainStructureElements(cb))
return false;
}
return true;
} // end of init()
void HandlerDynamicParticipantData::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
case DataModification::eModified:
m_itemViews.insert(data.aesItemId(), data.aesTypeId());
break;
case DataModification::eRemoved:
m_itemViews.remove(data.aesItemId());
}
}
void HandlerDynamicParticipantData::tryCreateHitEvent(Database::EemEvent eData,
const ParticipantManagement::StateInformation &stateInfo)
{
DEBUG_LOG(m_logger) << "tryCreateHitEvent";
// special values - ignore
const ProjectDataManagement::VulnerabilityState vulnState = stateInfo.vulnerabilityState();
if (vulnState == ProjectDataManagement::VulnerabilityState_def::VulnerabilityBitsChanged ||
vulnState == ProjectDataManagement::VulnerabilityState_def::TemporarySeverance)
return;
// combat ready is no hit
if (vulnState == ProjectDataManagement::VulnerabilityState_def::CombatReady)
return;
// only handle 'real' hit events
switch (stateInfo.originatorType().underlying())
{
case ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation:
case ProjectDataManagement::StateChangeOriginator::ImprovisedExplosiveDevice:
case ProjectDataManagement::StateChangeOriginator::DusMine:
eData.eventTypeId = ProjectDataManagement::EventType::HitEffectSimulation;
break;
case ProjectDataManagement::StateChangeOriginator::Participant:
case ProjectDataManagement::StateChangeOriginator::ObserverController:
case ProjectDataManagement::StateChangeOriginator::ExerciseControl:
eData.eventTypeId = ProjectDataManagement::EventType::Hit;
break;
case ProjectDataManagement::StateChangeOriginator::Sanitation:
eData.eventTypeId = ProjectDataManagement::EventType::Sanitation;
break;
case ProjectDataManagement::StateChangeOriginator::Unknown:
case ProjectDataManagement::StateChangeOriginator::WoundTreatmentDevice:
case ProjectDataManagement::StateChangeOriginator::MaintainanceDevice:
case ProjectDataManagement::StateChangeOriginator::FunctionalArea:
case ProjectDataManagement::StateChangeOriginator::Initialization:
case ProjectDataManagement::StateChangeOriginator::MedicalTimeout:
return;
}
QJsonObject obj;
obj[QStringLiteral("VulnerabilityState")] = vulnState.underlying();
obj[QStringLiteral("OriginatorId")] = stateInfo.originatorId();
obj[QStringLiteral("OriginatorType")] = stateInfo.originatorType().underlying();
obj[QStringLiteral("OriginatorChargeTypeId")] = stateInfo.originatorChargeTypeId();
if (stateInfo.originatorType() == ProjectDataManagement::StateChangeOriginator::Participant)
{
ParticipantManagement::Participant originData = m_partMap.value(stateInfo.originatorId());
if (originData.equipmentNumber().is_set())
obj[QStringLiteral("OriginatorEquipmentNumber")] = originData.equipmentNumber().get();
if (originData.partyId() == eData.partyId)
obj[QStringLiteral("FriendlyFire")] = true;
else
obj[QStringLiteral("FriendlyFire")] = false;
}
if (eData.eventTypeId == ProjectDataManagement::EventType::HitEffectSimulation)
{
DEBUG_LOG(m_logger) << "Event HitEffectSimulation from originatorId = " << stateInfo.originatorId();
obj[QStringLiteral("AesId")] = stateInfo.originatorId();
obj[QStringLiteral("AesType")] = m_itemViews.value(stateInfo.originatorId()).underlying();
AreaEffectSimulation::AesItem aesItem;
if (m_aesDtc->aesItemFromId(stateInfo.originatorId(), aesItem))
obj[QStringLiteral("AesName")] = QJsonValue(QString::fromStdString(aesItem.name()));
}
if (eData.eventTypeId == ProjectDataManagement::EventType::Sanitation)
{
// ignore these events
if (stateInfo.sanitationProceduresTaken() == 0 && stateInfo.originatorChargeTypeId() == 0) //qualification
return;
DEBUG_LOG(m_logger) << "Event sanitation ";
obj[QStringLiteral("Vitality")] = stateInfo.vitality();
obj[QStringLiteral("SanitationResult")] = stateInfo.sanitationResult().underlying();
obj[QStringLiteral("SanitationProceduresTaken")] = stateInfo.sanitationProceduresTaken();
}
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
eData.id = 0;
if (eData.sampleTime <= 0)
eData.sampleTime = stateInfo.stateTime();
if (!m_dbHandler->db()->insert(eData))
ERROR_LOG(m_logger) << "Error while inserting event data for participant: " << eData.participantId;
else
publishData(eData);
}
//-----------------------------------------------------------------------------
void HandlerDynamicParticipantData::onDynamicParticipantDataChanged(const ParticipantManagement::DynamicParticipantData &data,
DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "onDynamicParticipantDataChanged()"
<< ", participantId: " << data.participantId()
<< ", size stateHistory: " << data.stateHistory().size();
auto itLastDynData = m_lastDynData.find(data.participantId());
// the first insert does not trigger any updates
if (itLastDynData == m_lastDynData.end())
{
itLastDynData = m_lastDynData.insert(data.participantId(), data);
return;
}
ParticipantManagement::DynamicParticipantData &lastDynData = itLastDynData.value();
// get corresponding participant data
auto itParticipant = m_partMap.find(data.participantId());
if (itParticipant == m_partMap.end())
{
m_ppmDtc->waitForHistoricalDataParticipant(2000);
itParticipant = m_partMap.find(data.participantId());
if (itParticipant == m_partMap.end())
{
ERROR_LOG(m_logger) << "Participant does not exist: participantId: " << data.participantId();
return;
}
}
static const ParticipantManagement::ParticipantPosition emptyPos;
auto itParticipantPos = m_partPosMap.find(data.participantId());
const ParticipantManagement::ParticipantPosition &pPos = itParticipantPos == m_partPosMap.end() ? emptyPos : itParticipantPos.value();
const ParticipantManagement::Participant &pData = itParticipant.value();
Database::EemEvent eData;
eData.participantId = data.participantId();
eData.equipmentNumber = pData.equipmentNumber().is_set() ? pData.equipmentNumber().get() : -1;
eData.partyId = pData.partyId();
eData.sampleTime = data.sampleTime();
eData.eventTypeId = ProjectDataManagement::EventType_def::Unknown;
eData.position = LtsUtils::QPoint3D(pPos.pos().lon(), pPos.pos().lat(), pPos.pos().z());
if (data.stateHistory() != lastDynData.stateHistory())
{
// we assume that the order of the state information do *not* change (it's current true: january 2020)
for (size_t i = 0; i < data.stateHistory().size(); ++i)
{
if (i < lastDynData.stateHistory().size() &&
data.stateHistory().at(i) == lastDynData.stateHistory().at(i))
continue;
tryCreateHitEvent(eData, data.stateHistory().at(i));
}
}
const auto gearIsKnown = [ &lastDynData](const ParticipantManagement::GearInformation & gi) -> bool
{
return std::any_of(lastDynData.gear().begin(), lastDynData.gear().end(),
[&gi](const ParticipantManagement::GearInformation & other)
{
return gi.gearTypeId() == other.gearTypeId() && gi.gearId() == other.gearId();
});
};
for (const ParticipantManagement::GearInformation &gi : data.gear())
{
if (gearIsKnown(gi))
continue;
DEBUG_LOG(m_logger) << "Found new gear " << gi << " for participant " << data.participantId();
QJsonObject obj;
obj[QStringLiteral("GearId")] = QJsonValue(gi.gearId());
obj[QStringLiteral("GearTypeId")] = QJsonValue(gi.gearTypeId());
obj[QStringLiteral("DryFire")] = QJsonValue(gi.chargeCounting());
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
eData.sampleTime = gi.lastUpdate();
eData.eventTypeId = ProjectDataManagement::EventType_def::WeaponAssignment;
eData.id = 0;
if (!m_dbHandler->db()->insert(eData))
ERROR_LOG(m_logger) << "Error while inserting event data for participant: " << data.participantId();
else
publishData(eData);
}
if (data.manipulation() != lastDynData.manipulation())
{
eData.eventTypeId = ProjectDataManagement::EventType_def::Manipulation;
const auto isOldManipulation = [&lastDynData](const ParticipantManagement::ManipulationInformation & mani) -> bool
{
auto it = std::find(lastDynData.manipulation().begin(), lastDynData.manipulation().end(), mani);
return it != lastDynData.manipulation().end();
};
// we can *not* assume that the old and the new manipulation is in the same order
for (const auto &mani : data.manipulation())
{
if (isOldManipulation(mani))
continue;
const QJsonObject obj({{QStringLiteral("DeviceType"), mani.equipmentFunctionId()}});
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
eData.sampleTime = mani.manipulationTime();
eData.id = 0;
if (!m_dbHandler->db()->insert(eData))
ERROR_LOG(m_logger) << "Error while inserting event data for participant: " << data.participantId();
else
publishData(eData);
}
}
lastDynData = data;
} // end of onDynamicParticipantDataChanged()
//-----------------------------------------------------------------------------
void HandlerDynamicParticipantData::onParticipantGearEventChanged(const ParticipantManagement::ParticipantGearEvent &data,
DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
// get corresponding participant data
auto itParticipant = m_partMap.find(data.participantId());
if (itParticipant == m_partMap.end())
{
m_ppmDtc->waitForHistoricalDataParticipant(2000);
itParticipant = m_partMap.find(data.participantId());
if (itParticipant == m_partMap.end())
{
ERROR_LOG(m_logger) << "Participant does not exist: participantId: " << data.participantId();
return;
}
}
static const ParticipantManagement::ParticipantPosition emptyPos;
auto itParticipantPos = m_partPosMap.find(data.participantId());
const ParticipantManagement::ParticipantPosition &pPos = itParticipantPos == m_partPosMap.end() ? emptyPos : itParticipantPos.value();
const ParticipantManagement::Participant &pData = itParticipant.value();
Database::EemEvent eData;
eData.id = 0;
eData.participantId = data.participantId();
eData.equipmentNumber = pData.equipmentNumber().is_set() ? pData.equipmentNumber().get() : -1;
eData.partyId = pData.partyId();
eData.sampleTime = data.sampleTime();
eData.eventTypeId = ProjectDataManagement::EventType_def::FireEvent;
eData.position = LtsUtils::QPoint3D(pPos.pos().lon(), pPos.pos().lat(), pPos.pos().z());
QJsonObject obj;
obj[QStringLiteral("GearId")] = QJsonValue(data.charge().gearId());
obj[QStringLiteral("GearTypeId")] = QJsonValue(data.charge().gearTypeId());
obj[QStringLiteral("ChargeTypeId")] = QJsonValue(data.charge().chargeTypeId());
obj[QStringLiteral("Amount")] = QJsonValue(data.chargeAmount());
if (data.charge().remain() >= 0)
obj[QStringLiteral("Remain")] = QJsonValue(data.charge().remain());
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
eData.id = 0;
DEBUG_LOG(m_logger) << "create fire event for participant " << data.participantId();
if (!m_dbHandler->db()->insert(eData))
ERROR_LOG(m_logger) << "Error while inserting event data for participant: " << data.participantId();
else
publishData(eData);
}
//-----------------------------------------------------------------------------
void HandlerDynamicParticipantData::onParticipantChanged(const ParticipantManagement::Participant &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
case DataModification::eModified:
DEBUG_LOG(m_logger) << "added/modified";
m_partMap.insert(data.id(), data);
DEBUG_LOG(m_logger) << "data.participantId: " << data.id()
<< ", data.partyId: " << data.partyId()
<< ". data.equipmentNumber: " << data.equipmentNumber();
break;
case DataModification::eRemoved:
m_partMap.remove(data.id());
break;
}
}
//-----------------------------------------------------------------------------
void HandlerDynamicParticipantData::onParticipantPositionChanged(const ParticipantManagement::ParticipantPosition &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
case DataModification::eModified:
m_partPosMap.insert(data.participantId(), data);
DEBUG_LOG(m_logger) << "data.participantId: " << data.participantId()
<< ", position lat: " << data.pos().lat()
<< ", position lon: " << data.pos().lon();
break;
case DataModification::eRemoved:
m_partPosMap.remove(data.participantId());
break;
}
}
//-----------------------------------------------------------------------------
void HandlerDynamicParticipantData::onBiteChanged(const ParticipantManagement::Bite &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
Database::EemEvent eData;
eData.id = 0;
eData.eventTypeId = ProjectDataManagement::EventType_def::BiteMessage;
eData.equipmentNumber = data.equipmentNumber();
eData.participantId = data.participantId();
eData.sampleTime = data.sampleTime();
QJsonObject obj;
obj[QStringLiteral("BatteryState")] = QJsonValue(data.batteryState());
obj[QStringLiteral("BiteState")] = QJsonValue(data.biteState());
obj[QStringLiteral("ConfigState")] = QJsonValue(data.configState());
QJsonArray array;
for (const ParticipantManagement::BiteError &be : data.biteErrors())
{
QJsonObject object;
object[QStringLiteral("ErrorNo")] = QJsonValue(be.errorNumber());
object[QStringLiteral("CanDeviceNumber")] = QJsonValue(be.canDeviceNumber());
object[QStringLiteral("SubdeviceNumber")] = QJsonValue(be.subdeviceNumber());
object[QStringLiteral("UniqueId")] = QJsonValue(be.uniqueId());
array.append(object);
}
obj[QStringLiteral("BiteErrors")] = QJsonValue(array);
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
ParticipantManagement::Participant part = m_partMap.value(data.participantId());
if (part.id() > 0)
{
eData.partyId = part.partyId();
ParticipantManagement::ParticipantPosition pPos = m_partPosMap.value(eData.participantId);
eData.position = LtsUtils::QPoint3D(pPos.pos().lon(), pPos.pos().lat(), pPos.pos().z());
}
else
{
eData.participantId = -1;
eData.partyId = -1;
}
if (!m_dbHandler->db()->insert(eData))
ERROR_LOG(m_logger) << "Error while inserting event data for participantId: " << data.participantId();
else
publishData(eData);
}
//-----------------------------------------------------------------------------
void HandlerDynamicParticipantData::onBuildingStateChanged(const Buildings::BuildingState &data, DataModification modType)
{
if (modType == DataModification::eRemoved || data.damagePercent() <= 0) // only add hits
return;
Database::EemEvent eData;
eData.id = 0;
eData.eventTypeId = ProjectDataManagement::EventType_def::HitBuilding;
eData.sampleTime = data.sampleTime();
QJsonObject obj;
obj[QStringLiteral("ChargeTypeId")] = QJsonValue(data.chargeTypeId());
obj[QStringLiteral("damagePercent")] = QJsonValue(data.damagePercent());
obj[QStringLiteral("damagePercentOld")] = QJsonValue(data.damagePercentOld());
// obj[QStringLiteral("Name")] = QJsonValue(data.name());
obj[QStringLiteral("VulnerabilityState")] = QJsonValue(data.vulnerabilityState().underlying());
if (data.originatorType() == ProjectDataManagement::StateChangeOriginator::Participant)
{
ParticipantManagement::Participant originData = m_partMap.value(data.originatorParticipantId());
if (originData.equipmentNumber().is_set())
obj[QStringLiteral("OriginatorEquipmentNumber")] = originData.equipmentNumber().get();
}
if (data.originatorType() == ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation)
{
obj[QStringLiteral("AesId")] = data.aesId();
obj[QStringLiteral("AesType")] = m_itemViews.value(data.aesId()).underlying();
AreaEffectSimulation::AesItem aesItem;
if (m_aesDtc->aesItemFromId(data.aesId(), aesItem))
obj[QStringLiteral("AesName")] = QJsonValue(QString::fromStdString(aesItem.name()));
}
//obj[QStringLiteral("StructureElementId")] = QJsonValue(data.structureElementId());
obj[QStringLiteral("MainStructureElementId")] = QJsonValue(data.id());
QJsonArray contaminationArr;
for (const auto &cs: data.contaminationStates())
{
QJsonArray roomArr;
for (const auto &roomId : qAsConst(cs.rooms()))
roomArr.append(QJsonValue(roomId));
contaminationArr.append(QJsonObject({{QStringLiteral("aesId") , cs.aesId()},
{QStringLiteral("rooms"), roomArr}}));
}
obj[QStringLiteral("contaminationStates")] = contaminationArr;
int mainStructureElementId = data.id();
Core::GeoPosition geoPosition = m_mainStructureElements.value(mainStructureElementId).geoPosition();
LtsUtils::QPoint3D pos3D(geoPosition.lon(), geoPosition.lat(), geoPosition.z());
eData.position = pos3D;
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
if (!m_dbHandler->db()->insert(eData))
ERROR_LOG(m_logger) << "Error while inserting event data for building ";
else
publishData(eData);
}
//-----------------------------------------------------------------------------
void HandlerDynamicParticipantData::publishData(const Database::EemEvent &data)
{
m_eventServer->sendEvent(Database::toJson(data));
ExerciseEventManagement::Event ddsData = toDdsData(data);
if (!m_ddsHandler->dts()->publish(ddsData))
ERROR_LOG(m_logger) << "Error publishing event-data to dds";
// take data from dds-bus after 10s, we need just to make sure the recording service got it
m_mainHandler->addEventIdToRemove(ddsData.id());
}
//-----------------------------------------------------------------------------
ExerciseEventManagement::Event HandlerDynamicParticipantData::toDdsData(const Database::EemEvent &data)
{
ExerciseEventManagement::Event ddsData;
ddsData.id(data.id);
ddsData.sampleTime(data.sampleTime);
ddsData.participantId(data.participantId);
ddsData.partyId(data.partyId);
ddsData.equipmentNumber(data.equipmentNumber);
ddsData.eventTypeId(data.eventTypeId);
ddsData.properties(data.properties.toStdString());
ddsData.position(Core::GeoPosition(data.position.x(), data.position.y(), data.position.z()));
if (data.eventTypeId == ProjectDataManagement::EventType_def::HitBuilding)
{
int mainStructureElementId = LtsUtils::JsonObject::fromJson(ddsData.properties()).valueOr<int>(QStringLiteral("MainStructureElementId"), 0);
DEBUG_LOG(m_logger) << "MainStructureElementId=" << mainStructureElementId;
ddsData.position(m_mainStructureElements.value(mainStructureElementId).geoPosition());
}
return ddsData;
}
// ///////////////////////////////////
// I S M (InfrastructureManagement)
// ///////////////////////////////////
void HandlerDynamicParticipantData::onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "onMainStructureElementChanged";
if (modType != DataModification::eAdded &&
modType != DataModification::eModified)
{
m_mainStructureElements.remove(data.id());
return;
}
m_mainStructureElements.insert(data.id(), data);
DEBUG_LOG(m_logger) << "Got MainStructureElement: " << data;
}
//-----------------------------------------------------------------------------
@@ -0,0 +1,95 @@
/**
* Handler for infrastructure data
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2016-09-21
*/
#pragma once
#include "main_handler.h"
#include "logger.h"
#include <ParticipantManagement/ppm_dtc.h>
#include <AreaEffectSimulation/aes_dtc.h>
#include <Proxies/Buildings/bld_dtc.h>
#include <Proxies/Buildings/bld.hpp>
#include <QtCore/QObject>
#include <QtCore/QMap>
namespace ExerciseEventManagement
{
class WebSocketEventServer;
class HandlerDynamicParticipantData : public QObject
{
Q_OBJECT
public:
explicit HandlerDynamicParticipantData(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DbHandler> &db,
const std::shared_ptr<WebSocketEventServer> &eventServer);
~HandlerDynamicParticipantData();
/**
* Initialize the handler to manage information from infrastructure management
*/
bool init();
/**
* Returns the shared pointer to PPM DTC Interface.
*/
std::shared_ptr<ParticipantManagement::Interface::Dtc> dtc() const
{
return m_ppmDtc;
}
private Q_SLOTS:
void tryCreateHitEvent(Database::EemEvent eData,
const ParticipantManagement::StateInformation &stateInfo);
/**
* Handle the signals from 'ParticipantManagement'
*/
void onDynamicParticipantDataChanged(const ParticipantManagement::DynamicParticipantData &data, DataModification modType);
void onParticipantGearEventChanged(const ParticipantManagement::ParticipantGearEvent &data, DataModification modType);
void onParticipantChanged(const ParticipantManagement::Participant &data, DataModification modType);
void onBiteChanged(const ParticipantManagement::Bite &data, DataModification modType);
void onBuildingStateChanged(const Buildings::BuildingState &data, DataModification modType);
void onParticipantPositionChanged(const ParticipantManagement::ParticipantPosition &data, DataModification modType);
void onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType);
/**
* Handle MainStructureElement from 'ISM'
*/
void onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &data, DataModification modType);
private: // methoden
void publishData(const Database::EemEvent &data);
ExerciseEventManagement::Event toDdsData(const Database::EemEvent &data);
private: // attributes
std::shared_ptr<DdsHandler> m_ddsHandler; ///< Pointer to DDS handler
std::shared_ptr<DbHandler> m_dbHandler;
std::shared_ptr<WebSocketEventServer> m_eventServer;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc; ///< Pointer to DTC ParticipantManagement interface object
std::shared_ptr<Buildings::Interface::Dtc> m_bldDtc; ///< Pointer to DTC ParticipantManagement interface object
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
QHash<qint32, ParticipantManagement::Participant> m_partMap;
QHash<qint32, ParticipantManagement::ParticipantPosition> m_partPosMap;
QHash<qint32, ParticipantManagement::DynamicParticipantData> m_lastDynData;
QHash<int, AreaEffectSimulation::AesType> m_itemViews;
QHash<int, InfrastructureManagement::MainStructureElement> m_mainStructureElements;
MainHandler *m_mainHandler;
NGL::INGLLogger &m_logger; ///< Interface to the Logging-Facility
};
} // namespace ExerciseEventManagement
@@ -0,0 +1,132 @@
/**
* Handler for infrastructure data
* $Id$
* $HeadURL$
* $Author:
* Created by Christian Roeskens, NLSE, 2018-05-02
*/
#include "handler_equipment_error_registration.h"
#include "web_socket_server.h"
#include "settings.h"
#include "eem_db.h"
#include <OcSituationServer/oss.hpp>
#include <ProjectDataManagement/pdm_enums.hpp>
#include <QSet>
#include <QtCore/QThread>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QTimer>
using namespace ExerciseEventManagement;
HandlerEquipmentErrorRegistration::HandlerEquipmentErrorRegistration(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DbHandler> &db,
const std::shared_ptr<WebSocketEventServer> &eventServer)
: m_mainHandler(mainHandler)
, m_ddsHandler(ddsHandler)
, m_dbHandler(db)
, m_eventServer(eventServer)
, m_ossDtc(nullptr)
, m_logger(getLogger("HandlerEquipmentErrorRegistration"))
{
} // end of ctor()
//-----------------------------------------------------------------------------
HandlerEquipmentErrorRegistration::~HandlerEquipmentErrorRegistration()
{
} // end of dtor()
//-----------------------------------------------------------------------------
bool HandlerEquipmentErrorRegistration::init()
{
INFO_LOG(m_logger) << "start init() DTC OcSituationServer ";
// DTC OcSituationServer
m_ossDtc = OcSituationServer::Interface::Dtc::create(m_ddsHandler->ddsConnector());
if (m_ossDtc == nullptr)
{
ERROR_LOG(m_logger) << "Could not create DTC OcSituationServer";
return false;
}
// Subsribe for EquipmentErrorRegistration
connect(m_ossDtc.get(), &OcSituationServer::Interface::Dtc::equipmentErrorRegistrationChanged,
this, &HandlerEquipmentErrorRegistration::onEquipmentErrorRegistrationChanged);
if (!m_ossDtc->enableEquipmentErrorRegistration())
{
ERROR_LOG(m_logger) << "init() DTC OcSituationServer failed";
return false;
}
return true;
}
// end of init()
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void HandlerEquipmentErrorRegistration::onEquipmentErrorRegistrationChanged(const OcSituationServer::EquipmentErrorRegistration &data, DataModification modType)
{
INFO_LOG(m_logger) << "onEquipmentErrorRegistrationChanged()"
<< ", equipmentNr: " << data.equipmentNumber()
<< ", text: " << QString::fromStdString(data.text());
if (modType == DataModification::eRemoved)
return;
Database::EemEvent eData;
eData.sampleTime = data.sampleTime();
eData.equipmentNumber = data.equipmentNumber();
eData.eventTypeId = ProjectDataManagement::EventType_def::EquipmentErrorReport;
QJsonObject obj;
obj[QStringLiteral("text")] = QString::fromStdString(data.text());
obj[QStringLiteral("device")] = QString::fromStdString(data.device());
obj[QStringLiteral("participant")] = QString::fromStdString(data.participant());
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
if (!m_dbHandler->db()->insert(eData))
ERROR_LOG(m_logger) << "Error while inserting event into database";
else
publishData(eData);
}// end of onEquipmentErrorRegistrationChanged()
//-----------------------------------------------------------------------------
void HandlerEquipmentErrorRegistration::publishData(const Database::EemEvent &data)
{
m_eventServer->sendEvent(Database::toJson(data));
ExerciseEventManagement::Event ddsData = toDdsData(data);
if (!m_ddsHandler->dts()->publish(ddsData))
ERROR_LOG(m_logger) << "Error publishing event-data to dds";
// take data from dds-bus after 10s, we need just to make sure the recording service got it
m_mainHandler->addEventIdToRemove(ddsData.id());
}
ExerciseEventManagement::Event HandlerEquipmentErrorRegistration::toDdsData(const Database::EemEvent &data)
{
ExerciseEventManagement::Event ddsData;
ddsData.id(data.id);
ddsData.sampleTime(data.sampleTime);
ddsData.participantId(data.participantId);
ddsData.partyId(data.partyId);
ddsData.equipmentNumber(data.equipmentNumber);
ddsData.eventTypeId(data.eventTypeId);
ddsData.properties(data.properties.toStdString());
ddsData.position(Core::GeoPosition(data.position.x(), data.position.y(), data.position.z()));
return ddsData;
}
@@ -0,0 +1,74 @@
/**
* Handler for infrastructure data
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Roeskens, SLSE, 2016-09-21
*/
#pragma once
#include "main_handler.h"
#include "logger.h"
#include <OcSituationServer/oss_dtc.h>
//#include <Proxies/Buildings/bld_dtc.h>
//#include <Proxies/Buildings/bld.hpp>
#include <QtCore/QObject>
#include <QtCore/QMap>
namespace ExerciseEventManagement
{
class HandlerEquipmentErrorRegistration : public QObject
{
Q_OBJECT
public:
explicit HandlerEquipmentErrorRegistration(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DbHandler> &db,
const std::shared_ptr<WebSocketEventServer> &eventServer);
~HandlerEquipmentErrorRegistration();
/**
* Initialize the handler to manage information from infrastructure management
*/
bool init();
/**
* Returns the shared pointer to OSS DTC Interface.
*/
std::shared_ptr<OcSituationServer::Interface::Dtc> dtc() const
{
return m_ossDtc;
}
Q_SIGNALS:
private Q_SLOTS:
/**
* Handle the signals from 'OcSituationServer'
*/
void onEquipmentErrorRegistrationChanged(const OcSituationServer::EquipmentErrorRegistration &data, DataModification modType);
private: // methoden
void publishData(const Database::EemEvent &data);
ExerciseEventManagement::Event toDdsData(const Database::EemEvent &data);
private: // attributes
MainHandler *m_mainHandler;
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DbHandler> m_dbHandler;
std::shared_ptr<WebSocketEventServer> m_eventServer;
std::shared_ptr<OcSituationServer::Interface::Dtc> m_ossDtc;
NGL::INGLLogger &m_logger;
};
} // namespace ExerciseEventManagement
@@ -0,0 +1,437 @@
/**
* Handler for infrastructure data
* $Id$
* $HeadURL$
* $Author:
* Created by hjl, SLSE, 2016-09-21
*/
#include "handler_participant_event.h"
#include <ExerciseEventManagement/eem_db.h>
#include "web_socket_server.h"
#include "settings.h"
#include <ProjectDataManagement/pdm_enums.hpp>
#include <QJsonObject>
#include <QJsonDocument>
#include <QTimer>
using namespace ExerciseEventManagement;
HandlerParticipantEvent::HandlerParticipantEvent(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DbHandler> &db,
const std::shared_ptr<WebSocketEventServer> &eventServer)
: m_ddsHandler(ddsHandler)
, m_dbHandler(db)
, m_eventServer(eventServer)
, m_mainHandler(mainHandler)
, m_logger(getLogger("HandlerParticipantEvent"))
{
} // end of ctor()
//-----------------------------------------------------------------------------
HandlerParticipantEvent::~HandlerParticipantEvent()
{
} // end of dtor()
//-----------------------------------------------------------------------------
bool HandlerParticipantEvent::init()
{
INFO_LOG(m_logger) << "start init() DTC CentralEffectSimulation ";
// DTC CentralEffectSimulation
m_cesDtc = CentralEffectSimulation::Interface::Dtc::create(m_ddsHandler->ddsConnector());
if (!m_cesDtc)
{
ERROR_LOG(m_logger) << "Could not create DTC CentralEffectSimulation";
return false;
}
// Subsribe for ParticipantEvent (for analyzing data)
connect(m_cesDtc.get(), &CentralEffectSimulation::Interface::Dtc::participantEventChanged,
this, &HandlerParticipantEvent::onParticipantEventChanged);
if (!m_cesDtc->enableParticipantEvent() )
{
ERROR_LOG(m_logger) << "init() DTC CentralEffectSimulation failed";
return false;
}
// DTC ProjectDataManagement
m_pdmDtc = ProjectDataManagement::Interface::Dtc::create(m_ddsHandler->ddsConnector());
if (!m_pdmDtc)
{
ERROR_LOG(m_logger) << "Could not create DTC ProjectDataManagement";
return false;
}
m_pdmDtc->enableChargeType();
m_aesDtc = AreaEffectSimulation::Interface::Dtc::create(m_ddsHandler->ddsConnector());
m_aesDtc->enableAll();
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged,
this, &HandlerParticipantEvent::onAesItemViewChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleChanged,
this, &HandlerParticipantEvent::onExplosionSingleChanged);
return true;
} // end of init()
//-----------------------------------------------------------------------------
void HandlerParticipantEvent::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "AesItemId: " << data.aesItemId() << ", effectState: " << data.effectStateId();
Database::EemEvent eData = m_aesMap[data.id()];
eData.sampleTime = data.effectStart();
if (data.partyId().is_set())
eData.partyId = data.partyId().get();
switch(modType)
{
case DataModification::eAdded:
m_itemViews.insert(data.aesItemId(), data.aesTypeId());
DEBUG_LOG(m_logger) << "eAdded: AesItemId: " << data.aesItemId() << ", effectState: " << data.effectStateId();
m_aesEffectState.insert(data.aesItemId(), data.effectStateId());
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtBorderline)
{
AreaEffectSimulation::AesItem aesItem;
m_aesDtc->aesItemFromId(data.aesItemId(), aesItem);
QJsonObject obj;
obj[QStringLiteral("AesId")] = QJsonValue(aesItem.id());
obj[QStringLiteral("AesName")] = QJsonValue(QString::fromStdString(aesItem.name()));
obj[QStringLiteral("AesType")] = QJsonValue(data.aesTypeId().underlying());
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
eData.eventTypeId = ProjectDataManagement::EventType::Borderline;
eData.position = LtsUtils::QPoint3D(data.boundingBoxBottomLeft().lon(),
data.boundingBoxBottomLeft().lat(), 0.0);
if (!m_dbHandler->db()->insert(eData))
ERROR_LOG(m_logger) << "Error while inserting event data from AES ";
else
publishData(eData);
}
break;
case DataModification::eModified:
{
AreaEffectSimulation::AesEffectState effectState = m_aesEffectState.value(data.aesItemId());
DEBUG_LOG(m_logger) << "eModified: AesItemId: " << data.aesItemId() << ", effectState: " << data.effectStateId();
DEBUG_LOG(m_logger) << "effectState: " << effectState;
// insert event only once
if (effectState != AreaEffectSimulation::AesEffectState::EsPrepared &&
(effectState == AreaEffectSimulation::AesEffectState_def::EsActivated && data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsExpired))
return;
INFO_LOG(m_logger) << "new/update event: aesId: " << data.aesItemId();
m_aesEffectState.insert(data.aesItemId(), data.effectStateId());
m_itemViews.insert(data.aesItemId(), data.aesTypeId());
eData.position = LtsUtils::QPoint3D(data.boundingBoxBottomLeft().lon(),
data.boundingBoxBottomLeft().lat(), 0.0);
INFO_LOG(m_logger) << "aesType: " << data.aesTypeId();
switch (data.aesTypeId().underlying())
{
case AreaEffectSimulation::AesType::AtMineArea:
eData.eventTypeId = ProjectDataManagement::EventType::MineArea;
break;
case AreaEffectSimulation::AesType::AtExplosionArea:
eData.eventTypeId = ProjectDataManagement::EventType::HitEffectSimulation;
break;
case AreaEffectSimulation::AesType::AtExplosionCluster:
eData.eventTypeId = ProjectDataManagement::EventType::ExplosionCluster;
break;
case AreaEffectSimulation::AesType::AtMineAreaSingleMines:
return;
case AreaEffectSimulation::AesType::AtNContamination:
eData.eventTypeId = ProjectDataManagement::EventType::NContamination;
break;
case AreaEffectSimulation::AesType::AtBCContamination:
eData.eventTypeId = ProjectDataManagement::EventType::BCContamination;
break;
case AreaEffectSimulation::AesType::AtBorderline:
eData.eventTypeId = ProjectDataManagement::EventType::Borderline;
break;
case AreaEffectSimulation::AesType::AtDangerArea:
eData.eventTypeId = ProjectDataManagement::EventType::DangerArea;
break;
case AreaEffectSimulation::AesType::AtFunctionalArea:
eData.eventTypeId = ProjectDataManagement::EventType::FunctionalArea;
break;
case AreaEffectSimulation::AesType::AtMineCorridor:
eData.eventTypeId = ProjectDataManagement::EventType::MineCorridor;
break;
case AreaEffectSimulation::AesType::AtTechnicalArea:
eData.eventTypeId = ProjectDataManagement::EventType::TechnicalArea;
break;
case AreaEffectSimulation::AesType::AtGasPipe:
eData.eventTypeId = ProjectDataManagement::EventType::GasPipe;
break;
// No ExerciseEvent for AesType
default:
{
INFO_LOG(m_logger) << "No ExerciseEvent for AesType:" << data.aesTypeId().underlying();
return;
}
}
AreaEffectSimulation::AesItem aesItem;
m_aesDtc->aesItemFromId(data.aesItemId(), aesItem);
if (eData.eventTypeId == ProjectDataManagement::EventType::ExplosionCluster)
{
ProjectDataManagement::GearClass gearClass = m_pdmDtc->gearClassFromChargeTypeId(aesItem.chargeStruct().chargeTypeId());
if (gearClass == ProjectDataManagement::GearClass_def::AirSupport)
eData.eventTypeId = ProjectDataManagement::EventType::AirSupport;
}
QJsonObject obj;
obj[QStringLiteral("AesId")] = QJsonValue(aesItem.id());
obj[QStringLiteral("AesName")] = QJsonValue(QString::fromStdString(aesItem.name()));
obj[QStringLiteral("AesType")] = QJsonValue(data.aesTypeId().underlying());
if (data.aesTypeId().underlying() != AreaEffectSimulation::AesType::AtBorderline)
{
obj[QStringLiteral("duration")] = QJsonValue(aesItem.effectDuration());
obj[QStringLiteral("ChargeTypeId")] = QJsonValue(data.chargeStruct().chargeTypeId());
obj[QStringLiteral("endTime")] = QJsonValue(data.effectStart() + aesItem.effectDuration());
}
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
if (eData.id <= 0)
{
if (!m_dbHandler->db()->insert(eData))
{
ERROR_LOG(m_logger) << "Error while inserting event data from AES ";
break;
}
m_aesMap.insert(data.id(), eData);
}
else
{
if (!m_dbHandler->db()->update(eData))
{
ERROR_LOG(m_logger) << "Error while updating event data from AES ";
break;
}
}
publishData(eData);
break;
}
case DataModification::eRemoved:
{
m_itemViews.remove(data.aesItemId());
m_aesMap.remove(data.id());
break;
}
}
}
//-----------------------------------------------------------------------------
void HandlerParticipantEvent::onExplosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType)
{
Database::EemEvent eData = m_aesMap[data.id()];
if ( !data.effectStart().is_set())
{
return;
}
eData.sampleTime = data.effectStart().get();
if (data.partyId().is_set())
eData.partyId = data.partyId().get();
switch(modType)
{
case DataModification::eAdded:
m_aesEffectState.insert(data.id(), data.effectStateId());
break;
case DataModification::eModified:
{
AreaEffectSimulation::AesEffectState effectState = m_aesEffectState.value(data.id());
// insert event only once
if (effectState != AreaEffectSimulation::AesEffectState::EsPrepared &&
(effectState == AreaEffectSimulation::AesEffectState_def::EsActivated && data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsExpired))
return;
m_aesEffectState.insert(data.id(), data.effectStateId());
//m_itemViews.insert(data.id(), data.aesTypeId());
eData.eventTypeId = ProjectDataManagement::EventType::FireEvent;
AreaEffectSimulation::AesItem aesItem;
m_aesDtc->aesItemFromId(data.id(), aesItem);
QJsonObject obj;
obj[QStringLiteral("AesId")] = QJsonValue(aesItem.id());
obj[QStringLiteral("AesName")] = QJsonValue(QString::fromStdString(aesItem.name()));
obj[QStringLiteral("AesType")] = QJsonValue(AreaEffectSimulation::AesType::AtExplosionSingle);
obj[QStringLiteral("ChargeTypeId")] = QJsonValue(data.chargeStruct().chargeTypeId());
obj[QStringLiteral("duration")] = QJsonValue(aesItem.effectDuration());
obj[QStringLiteral("endTime")] = QJsonValue(data.effectStart().get() + aesItem.effectDuration());
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
//const Core::GeoPosition geoPosition ( data.impactPosition().lon(), data.impactPosition().lat(), 0);
const LtsUtils::QPoint3D pos3D (data.impactPosition().lon(), data.impactPosition().lat(), 0);
eData.position = pos3D;
if (eData.id <= 0)
{
if (!m_dbHandler->db()->insert(eData))
{
ERROR_LOG(m_logger) << "Error while inserting event data from AES ";
break;
}
m_aesMap.insert(data.id(), eData);
}
else
{
if (!m_dbHandler->db()->update(eData))
{
ERROR_LOG(m_logger) << "Error while updating event data from AES ";
break;
}
}
publishData(eData);
break;
}
default:
DEBUG_LOG(m_logger) << "Nothing to do";
}
}
//-----------------------------------------------------------------------------
void HandlerParticipantEvent::onParticipantEventChanged(const CentralEffectSimulation::ParticipantEvent &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
Database::EemEvent eData;
eData.participantId = data.participantId();
eData.equipmentNumber = data.equipmentNumber();
eData.partyId = data.partyId();
eData.sampleTime = data.eventTime();
eData.position = LtsUtils::QPoint3D(data.position().lon(), data.position().lat(), data.position().z());
DEBUG_LOG(m_logger) << " onParticipantEventChanged "
<< " position=" << data.position()
<< " eData.position=" << eData.position;
eData.eventTypeId = ProjectDataManagement::EventType_def::Unknown;
QJsonObject obj;
obj[QStringLiteral("AesId")] = QJsonValue(data.aesItemId());
obj[QStringLiteral("AesType")] = QJsonValue(m_itemViews.value(data.aesItemId()).underlying());
obj[QStringLiteral("AesName")] = QJsonValue(QString::fromStdString(data.aesName()));
obj[QStringLiteral("FriendlyFire")] = QJsonValue(data.friendlyFire());
switch(data.eventTypeId().underlying())
{
case ProjectDataManagement::EventType_def::HitEffectSimulation:
eData.eventTypeId = ProjectDataManagement::EventType_def::HitEffectSimulation;
break;
case ProjectDataManagement::EventType_def::HitMine:
eData.eventTypeId = ProjectDataManagement::EventType_def::HitMine;
break;
case ProjectDataManagement::EventType_def::HitN:
eData.eventTypeId = ProjectDataManagement::EventType_def::HitN;
break;
case ProjectDataManagement::EventType_def::HitC:
eData.eventTypeId = ProjectDataManagement::EventType_def::HitC;
break;
case ProjectDataManagement::EventType_def::EnterDangerArea:
eData.eventTypeId = ProjectDataManagement::EventType_def::EnterDangerArea;
break;
case ProjectDataManagement::EventType_def::LeaveDangerArea:
eData.eventTypeId = ProjectDataManagement::EventType_def::LeaveDangerArea;
break;
case ProjectDataManagement::EventType_def::EnterMineArea:
eData.eventTypeId = ProjectDataManagement::EventType_def::EnterMineArea;
break;
case ProjectDataManagement::EventType_def::LeaveMineArea:
eData.eventTypeId = ProjectDataManagement::EventType_def::LeaveMineArea;
break;
case ProjectDataManagement::EventType_def::EnterNuclear:
eData.eventTypeId = ProjectDataManagement::EventType_def::EnterNuclear;
break;
case ProjectDataManagement::EventType_def::LeaveNuclear:
eData.eventTypeId = ProjectDataManagement::EventType_def::LeaveNuclear;
break;
case ProjectDataManagement::EventType_def::CrossLocalBorderline:
eData.eventTypeId = ProjectDataManagement::EventType_def::CrossLocalBorderline;
obj[QStringLiteral("Global")] = QJsonValue(false);
break;
case ProjectDataManagement::EventType_def::CrossGlobalBorderline:
eData.eventTypeId = ProjectDataManagement::EventType_def::CrossGlobalBorderline;
obj[QStringLiteral("Global")] = QJsonValue(true);
break;
case ProjectDataManagement::EventType_def::EnterFunctionalArea:
eData.eventTypeId = ProjectDataManagement::EventType_def::EnterFunctionalArea;
break;
case ProjectDataManagement::EventType_def::LeaveFunctionalArea:
eData.eventTypeId = ProjectDataManagement::EventType_def::LeaveFunctionalArea;
break;
case ProjectDataManagement::EventType_def::EnterHazardDistance:
eData.eventTypeId = ProjectDataManagement::EventType_def::EnterHazardDistance;
break;
case ProjectDataManagement::EventType_def::LeaveHazardDistance:
eData.eventTypeId = ProjectDataManagement::EventType_def::LeaveHazardDistance;
break;
case ProjectDataManagement::EventType_def::AirSupport:
eData.eventTypeId = ProjectDataManagement::EventType_def::AirSupport;
break;
default:
return;
}
eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
if (!m_dbHandler->db()->insert(eData))
ERROR_LOG(m_logger) << "Error while inserting event data from CES ";
else
publishData(eData);
} // end of onParticipantEventChanged()
//-----------------------------------------------------------------------------
void HandlerParticipantEvent::publishData(const Database::EemEvent &data)
{
m_eventServer->sendEvent(Database::toJson(data));
ExerciseEventManagement::Event ddsData = toDdsData(data);
if (!m_ddsHandler->dts()->publish(ddsData))
ERROR_LOG(m_logger) << "Error publishing event-data to dds";
// take data from dds-bus after 10s, we need just to make sure the recording service got it
m_mainHandler->addEventIdToRemove(ddsData.id());
}
ExerciseEventManagement::Event HandlerParticipantEvent::toDdsData(const Database::EemEvent &data)
{
ExerciseEventManagement::Event ddsData;
ddsData.id(data.id);
ddsData.sampleTime(data.sampleTime);
ddsData.participantId(data.participantId);
ddsData.partyId(data.partyId);
ddsData.equipmentNumber(data.equipmentNumber);
ddsData.eventTypeId(data.eventTypeId);
ddsData.properties(data.properties.toStdString());
// Core::GeoPosition LtsUtils::QPoint3D
Core::GeoPosition position(data.position.x(), data.position.y(),data.position.z());
ddsData.position(position);
return ddsData;
}
@@ -0,0 +1,72 @@
/**
* Handler for infrastructure data
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2016-09-21
*/
#pragma once
#include "main_handler.h"
#include "logger.h"
#include <CentralEffectSimulation/ces_dtc.h>
#include <AreaEffectSimulation/aes_dtc.h>
#include <ProjectDataManagement/pdm_dtc.h>
#include <QtCore/QObject>
#include <QtCore/QMap>
class QTimer;
namespace ExerciseEventManagement
{
class HandlerParticipantEvent : public QObject
{
Q_OBJECT
public:
explicit HandlerParticipantEvent(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DbHandler> &db,
const std::shared_ptr<WebSocketEventServer> &eventServer);
~HandlerParticipantEvent();
/**
* Initialize the handler to manage information from CES
*/
bool init();
private Q_SLOTS:
/**
* Handle the signals from 'CentralEffectSimulation'
*/
void onParticipantEventChanged(const CentralEffectSimulation::ParticipantEvent &data, DataModification modType);
void onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType);
void onExplosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType);
private: // methoden
void publishData(const Database::EemEvent &data);
ExerciseEventManagement::Event toDdsData(const Database::EemEvent &data);
private: // attributes
std::shared_ptr<DdsHandler> m_ddsHandler; ///< Pointer to DDS handler
std::shared_ptr<DbHandler> m_dbHandler;
std::shared_ptr<WebSocketEventServer> m_eventServer;
std::shared_ptr<CentralEffectSimulation::Interface::Dtc> m_cesDtc; ///< Pointer to DTC ParticipantManagement interface object
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
QHash<int, AreaEffectSimulation::AesType> m_itemViews;
QHash<int, AreaEffectSimulation::AesEffectState> m_aesEffectState;
// <aesItemId, event>
QMap<int, Database::EemEvent> m_aesMap;
MainHandler *m_mainHandler;
NGL::INGLLogger &m_logger; ///< Interface to the Logging-Facility
};
} // namespace ExerciseEventManagement
+105
View File
@@ -0,0 +1,105 @@
log4j.reset=true
log4j.rootLogger=WARN, file
# Service: Exercise Event Management
###########################################################
#log4j.logger.EEM=WARN
#log4j.logger.EEM.main=WARN
#log4j.logger.EEM.DbAdmin=INFO
#log4j.logger.EEM.HandlerDynamicParticipantData=INFO
#log4j.logger.EEM.Settings=INFO
#log4j.logger.EEM.Publisher=INFO
#log4j.logger.EEM.FtsImpl=INFO
#log4j.logger.EEM.FtcImpl=INFO
#log4j.logger.EEM.HandlerGetEvents=INFO
#log4j.logger.EEM.Test=INFO
#log4j.logger.EEM.WebSocketAvs=INFO
# AlarmManagement
###########################################################
#log4j.logger.ALM.main=INFO
#log4j.logger.ALM.DbAdmin=INFO
#log4j.logger.ALM.DdsHandler=INFO
#log4j.logger.ALM.HandlerConfigureAlarms=INFO
#log4j.logger.ALM.HandlerParticipant=INFO
#log4j.logger.ALM.HandlerEmergencyCall=INFO
#log4j.logger.ALM.ObjectEmergencyCall=INFO
#log4j.logger.ALM.Initialization=INFO
#log4j.logger.ALM.HandlerAes=INFO
#log4j.logger.ALM.MainHandler=INFO
#log4j.logger.ALM.Settings=INFO
#log4j.logger.ALM.Publisher=INFO
#log4j.logger.ALM.FtsImpl=INFO
#log4j.logger.ALM.FtcImpl=INFO
# File
###########################################################
log4j.appender.file=Lts::RollingFileAppender
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=9
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
# Appnd_Console
###########################################################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n
# Appnd_Syslog
###########################################################
log4j.appender.syslog=org.apache.log4j.SyslogAppender
log4j.appender.syslog.Threshold=ERROR
log4j.appender.syslog.SysLogHost=localhost
log4j.appender.syslog.Facility=LOCAL7
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
###########################################################
###########################################################
###########################################################
# 1) Available Levels in LOG4CXX Version 0.10.0:
# TRACE,
# DEBUG,
# INFO,
# WARN,
# ERROR
# FATAL
#
# d = date/time
# p = priority (Level)
# c = category/channel
# m = message
# F = file
# L = line
# n = platform dependent line separator character or characters.
# ....
#
# Examples for console:
# a)
# %d %p %c #%m# (%.50F:%L)%n
# date/time
# priority
# category/channel
# #message#
# (Maximal 50Character des Filename von rechts:Line)
# line separator
#
# b)
# %d (%.15F:%L) %-5p %c |#| %m%n
# date/time
# (Maximal 15Character des Filename von rechts:Line)
# priority, Right pad with spaces if the priority name is less than 5 characters long.
# category/channel
# message
# line separator
#
# Examples for files:
+37
View File
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id$
* $HeadURL$
* $Author$
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace ExerciseEventManagement
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "EEM.";
std::string loggerName;
const size_t len = strlen(name);
loggerName.reserve(sizeof(ns) + len);
loggerName.append(ns, sizeof(ns) - 1);
loggerName.append(name, len);
try
{
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
getNGLLogger(loggerName);
}
catch (const std::exception &ex)
{
fprintf(stderr, "Error retrieving logger: %s", ex.what());
abort();
}
}
} // namespace ExerciseEventManagement
+23
View File
@@ -0,0 +1,23 @@
/**
* @RDE_HEADER@
* Logging for ExerciseEventManagement
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
#include "eem_export.h"
namespace ExerciseEventManagement
{
using LOGGER_T = NGL::INGLLogger &;
EEM_COMMON_EXPORT LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace ExerciseEventManagement
+46
View File
@@ -0,0 +1,46 @@
/**
* @RDE_HEADER@
* main for ExerciseEventManagement
* $Id$
* $HeadURL$
* $Author$
*/
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <QNetworkProxy>
#include <Evaluations/settings.h>
#include "main_handler.h"
#include "logger.h"
#include "settings.h"
int main(int argc, char **argv)
{
NGLCoreApplication app(argc, argv);
QNetworkProxy::setApplicationProxy(QNetworkProxy());
LtsCommandLineParser::LtsCommandLineParser options;
if (!options.init())
return -1;
app.createLogger("EEM", options.exerciseId());
ExerciseEventManagement::Settings::setIniFile(options.iniFile());
Evaluations::Settings::setIniFile(options.iniFile());
int iRet = -3;
{
std::unique_ptr<ExerciseEventManagement::MainHandler> mainHandler(new ExerciseEventManagement::MainHandler);
if (!mainHandler->init(options.domainId(), options.exerciseId()))
return iRet;
STARTUP_LOG(app.baseLogger());
iRet = app.exec();
SHUTDOWN_LOG(app.baseLogger());
}
return iRet;
}
+192
View File
@@ -0,0 +1,192 @@
/**
* MainHandler for RecordingManagement
* $Id$
* $HeadURL$
* $Author$
*/
#include "main_handler.h"
#include "settings.h"
#include "handler_dynamic_participant_data.h"
#include "handler_participant_event.h"
#include "handler_equipment_error_registration.h"
#include "web_socket_avs.h"
#include "web_socket_server.h"
#include <DDS/Core/pacemaker.h>
#include <AlarmManagement/initialization.h>
#include <FilterManagement/initialization.h>
#include <MarkerManagement/initialization.h>
#include <Evaluations/initialization.h>
namespace ExerciseEventManagement
{
MainHandler::MainHandler()
: m_almService(nullptr)
, m_fltService(nullptr)
, m_mrkService(nullptr)
, m_evlService(nullptr)
, m_removeTimer(new QTimer(this))
, m_logger(getLogger("MainHandler"))
{
connect(m_removeTimer, &QTimer::timeout, this, &MainHandler::onRemoveKeys);
m_removeTimer->setInterval(5000);
m_removeTimer->setSingleShot(false);
m_removeTimer->start();
}
MainHandler::~MainHandler()
{
delete m_almService;
delete m_fltService;
delete m_mrkService;
delete m_evlService;
m_webSocketAvs = nullptr;
m_webSocketEventServer = nullptr;
m_handlerEerEvent = nullptr;
m_handlerPartEvent = nullptr;
m_dynPartData = nullptr;
m_avmDtc = nullptr;
m_ismDtc = nullptr;
m_ddsHandler = nullptr;
m_dbHandler = nullptr;
}
bool MainHandler::init(int domainId, int exerciseId)
{
bool bRet = initInternal(domainId, exerciseId);
if (bRet)
{
INFO_LOG(m_logger) << "svcExerciseEventManagement successfully initialized";
m_ddsHandler->setRunningState(Core::RunningState::Running);
}
else
{
ERROR_LOG(m_logger) << "Error initializing svcExerciseEventManagement...";
if (m_ddsHandler)
m_ddsHandler->setRunningState(Core::RunningState::StartingError);
}
return bRet;
}
bool MainHandler::initInternal(int domainId, int exerciseId)
{
// initialize database
m_dbHandler = std::make_shared<DbHandler>();
if (!m_dbHandler->init(exerciseId, Settings::settings()))
return false;
// initialize DDS
m_ddsHandler = std::make_shared<DdsHandler>(domainId, exerciseId, IDL::PartitionType::Exercise);
if (!m_ddsHandler->init())
return false;
m_webSocketEventServer = std::make_shared<WebSocketEventServer>(m_dbHandler->db()->database());
if (!m_ismDtc)
m_ismDtc = InfrastructureManagement::Interface::Dtc::create(m_ddsHandler->ddsConnector());
m_ismDtc->enableMainStructureElement();
if (!m_avmDtc)
m_avmDtc = AudioVideoManagement::Interface::Dtc::create(m_ddsHandler->ddsConnector());
m_avmDtc->enableStreamName();
m_dynPartData = std::make_shared<HandlerDynamicParticipantData>(this, m_ddsHandler, m_dbHandler, m_webSocketEventServer);
m_handlerPartEvent = std::make_shared<HandlerParticipantEvent>(this, m_ddsHandler, m_dbHandler, m_webSocketEventServer);
m_handlerEerEvent = std::make_shared<HandlerEquipmentErrorRegistration>(this, m_ddsHandler, m_dbHandler, m_webSocketEventServer);
m_webSocketAvs = std::make_shared<WebSocketAvs>(this, m_ddsHandler, m_dbHandler, m_webSocketEventServer, m_avmDtc, m_ismDtc, exerciseId);
m_webSocketAvs->setWsUrl(Settings::getWebSocketUrl());
m_webSocketAvs->init();
m_almService = new AlarmManagement::Initialization;
if (!m_almService->initialize(m_ddsHandler->ddsConnector(), m_dbHandler->db()->database()))
return false;
m_fltService = new FilterManagement::Initialization;
if (!m_fltService->initialize(m_ddsHandler->ddsConnector(), m_dbHandler->db()->database()))
return false;
m_mrkService = new MarkerManagement::Initialization;
if (!m_mrkService->initialize(m_ddsHandler->ddsConnector(), m_dbHandler->db()->database()))
return false;
m_evlService = new Evaluations::Initialization;
if (!m_evlService->initialize(m_ddsHandler->ddsConnector(), m_dbHandler->db()->database()) ||
!m_evlService->loadFromDatabase())
return false;
if (!loadFromDatabase())
return false;
if (!m_almService->loadFromDatabase())
return false;
if (!m_fltService->loadFromDatabase())
return false;
if (!m_mrkService->loadFromDatabase())
return false;
return true;
}
bool MainHandler::loadFromDatabase()
{
if (!m_webSocketEventServer || !m_webSocketEventServer->listen(Settings::getWebSocketServerPortRange(1)))
{
ERROR_LOG(m_logger) << "Can not initialize WebSocket Event server";
return false;
}
m_ddsHandler->addPortToPacemaker(m_webSocketEventServer->listenPort(), Core::PortType::WebSocketServer);
if (!m_handlerEerEvent || !m_handlerEerEvent->init())
{
ERROR_LOG(m_logger) << "Can not initialize EquipmentErrorRegistration handler";
return false;
}
if (!m_dynPartData || !m_dynPartData->init())
{
ERROR_LOG(m_logger) << "Can not initialize Participant Data handler";
return false;
}
if (!m_handlerPartEvent || !m_handlerPartEvent->init())
{
ERROR_LOG(m_logger) << "Can not initialize Participant Event handler";
return false;
}
DEBUG_LOG(m_logger) << "loadFromDatabase successfully finished";
return true;
}
void MainHandler::addEventIdToRemove(qint64 id)
{
m_keysToRemove[time(nullptr)].push_back(id);
}
void MainHandler::onRemoveKeys()
{
auto dts = m_ddsHandler->dts();
qint64 toRemove = time(nullptr) - 10;
auto it = m_keysToRemove.begin();
while (it != m_keysToRemove.end() && it.key() < toRemove)
{
const auto &cont = it.value();
for (const auto id : cont)
dts->removeEventByKey(id);
it = m_keysToRemove.erase(it);
}
}
} // ExerciseEventManagement
+79
View File
@@ -0,0 +1,79 @@
/**
* MainHandler for RecordingManagement
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <ExerciseEventManagement/eem_db.h>
#include "dds_handler.h"
#include "logger.h"
#include <ngldb/db_service_handler.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <InfrastructureManagement/ism_dtc.h>
namespace AlarmManagement
{
class Initialization;
}
namespace MarkerManagement
{
class Initialization;
}
namespace FilterManagement
{
class Initialization;
}
namespace Evaluations
{
class Initialization;
}
class QTimer;
namespace ExerciseEventManagement
{
using DbHandler = NGLDB::DbServiceHandler<DatabaseInterface>;
class HandlerDynamicParticipantData;
class HandlerParticipantEvent;
class HandlerEquipmentErrorRegistration;
class WebSocketAvs;
class WebSocketEventServer;
class MainHandler : public QObject
{
Q_OBJECT
public:
MainHandler();
~MainHandler();
bool init(int domainId, int exerciseId);
void addEventIdToRemove(qint64 id);
private:
bool initInternal(int domainId, int exerciseId);
void onRemoveKeys();
bool loadFromDatabase();
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DbHandler> m_dbHandler;
std::shared_ptr<HandlerDynamicParticipantData> m_dynPartData;
std::shared_ptr<HandlerParticipantEvent> m_handlerPartEvent;
std::shared_ptr<HandlerEquipmentErrorRegistration> m_handlerEerEvent;
std::shared_ptr<WebSocketAvs> m_webSocketAvs;
std::shared_ptr<WebSocketEventServer> m_webSocketEventServer;
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_avmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
AlarmManagement::Initialization *m_almService;
FilterManagement::Initialization *m_fltService;
MarkerManagement::Initialization *m_mrkService;
Evaluations::Initialization *m_evlService;
QTimer *m_removeTimer;
QMap<qint64, QVector<qint64>> m_keysToRemove;
LOGGER_T m_logger;
};
} // namespace ExerciseEventManagement
+65
View File
@@ -0,0 +1,65 @@
/**
* Settings for ExerciseEventManagement
* $Id$
* $HeadURL$
* $Author$
*/
#include "settings.h"
#include <QtCore/QSettings>
#include <QPoint>
#include <nglsettings/nglsettings_settings.h>
#include <ngltools/ngltools_filesystem.h>
#include <LtsUtils/LtsUtilsCore/lts_portrange.h>
namespace ExerciseEventManagement
{
class NGLSettingsClass : public NGL::Settings
{
public:
NGLSettingsClass()
: NGL::Settings(s_iniFile.isEmpty() ? NGL::findProjectConfigFile() : s_iniFile)
{}
static void setIniFile(const QString &fn)
{
s_iniFile = fn;
}
static QString s_iniFile;
};
QString NGLSettingsClass::s_iniFile;
Q_GLOBAL_STATIC(NGLSettingsClass, s)
//-----------------------------------------------------------------------------
NGL::Settings *Settings::settings()
{
return s();
}
//-----------------------------------------------------------------------------
void Settings::setIniFile(const QString &ini)
{
NGLSettingsClass::setIniFile(ini);
}
QString Settings::getWebSocketUrl()
{
QString url = settings()->settings()->value(QStringLiteral("AVS_SERVER/url"), QStringLiteral("ws://EXCAVS01:7020")).toString();
return url;
}
int Settings::getCleanupTimeout()
{
int timeout = settings()->settings()->value(QStringLiteral("EEM/cleanupTimeout"), 600000).toInt();
return timeout;
}
QPair<quint16, quint16> Settings::getWebSocketServerPortRange(int typeId)
{
return LtsUtils::fromQPoint(settings()->settings()->value(QString(QStringLiteral("EEM/webSocketServerPortRange/%1")).arg(typeId), QPoint()).toPoint());
}
} // namespace ExerciseEventManagement
+28
View File
@@ -0,0 +1,28 @@
/**
* Settings for ExerciseEventManagement
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QString>
#include <QPair>
namespace NGL
{
class Settings;
}
namespace ExerciseEventManagement
{
namespace Settings
{
NGL::Settings *settings();
void setIniFile(const QString &ini);
QString getWebSocketUrl();
int getCleanupTimeout();
QPair<quint16, quint16> getWebSocketServerPortRange(int typeId);
}
} // namespace ExerciseEventManagement
+716
View File
@@ -0,0 +1,716 @@
/**
* WebSocket connection to AVS to retrieve AVS events
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-02-06
*/
#include "web_socket_avs.h"
#include "web_socket_server.h"
#include "main_handler.h"
#include "settings.h"
#include <ExerciseEventManagement/eem_db.h>
#include <QTimer>
#include <QWebSocket>
#include <QJsonDocument>
#include <QJsonObject>
#include <ngldb/nsqlquery.h>
ProjectDataManagement::EventType avsEventTypeToPdmEventType(int avsEventType)
{
ProjectDataManagement::EventType eventType;
switch(avsEventType)
{
case 305: //MVS
case 301: // CAMERA_AUDIO
case 302: // CAMERA_VIDEO
eventType = ProjectDataManagement::EventType::CameraVideo;
break;
case 303: // RADIO_CNR
eventType = ProjectDataManagement::EventType::RadioCnr;
break;
case 304: // RADIO_TVC
eventType = ProjectDataManagement::EventType::RadioTvc;
break;
default:
eventType = ProjectDataManagement::EventType::Unknown;
break;
}
return eventType;
}
namespace ExerciseEventManagement
{
WebSocketAvs::WebSocketAvs(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DbHandler> &db,
const std::shared_ptr<WebSocketEventServer> &eventServer,
const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &avmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
int exerciseId,
QObject *parent)
: QObject(parent)
, m_ddsHandler(ddsHandler)
, m_dbHandler(db)
, m_eventServer(eventServer)
, m_avmDtc(avmDtc)
, m_ismDtc(ismDtc)
, m_mainHandler(mainHandler)
, m_wsSocket(nullptr)
, m_wsReconnectTimer(new QTimer(this))
, m_cleanupTimer(new QTimer(this))
, m_exerciseId(exerciseId)
, m_lastUpdate(0)
, m_bWebSocketDisconnectedWarnedOnce(false)
, m_logger(getLogger("WebSocketAvs"))
{
connect(m_wsReconnectTimer, &QTimer::timeout,
this, &WebSocketAvs::openWebSocket);
connect(m_cleanupTimer, &QTimer::timeout,
this, &WebSocketAvs::onCleanUpDbInternal);
//if there is a positive time start it
//if not: don't do it!
int cleanupTimeout = Settings::getCleanupTimeout();
INFO_LOG(m_logger) << "cleanup Timer Interval: " << cleanupTimeout;
if (cleanupTimeout > 0)
{
m_cleanupTimer->setInterval(cleanupTimeout);
m_cleanupTimer->start();
INFO_LOG(m_logger) << "Started cleanup Timer with Interval: " << cleanupTimeout;
}
}
WebSocketAvs::~WebSocketAvs()
{
}
void WebSocketAvs::init()
{
DEBUG_LOG(m_logger) << "WebSocketAvs::init";
m_wsReconnectTimer->start(1000);
//onCleanUpDbInternal();
}
void WebSocketAvs::setWsUrl(const QString &url)
{
DEBUG_LOG(m_logger) << "Url: " << url;
m_wsUrl = url;
}
AvsData WebSocketAvs::jsonObjectToAvsData(const QJsonObject &obj)
{
AvsData data;
data.id = 0;
data.eventTime = obj[QStringLiteral("eventTime")].toDouble();
data.endTime = obj[QStringLiteral("endTime")].toDouble();
data.streamName = obj[QStringLiteral("streamName")].toString();
int avsEventType = obj[QStringLiteral("eventType")].toInt();
ProjectDataManagement::EventType eventType;
switch(avsEventType)
{
case 301: // CAMERA_AUDIO
case 302: // CAMERA_VIDEO
eventType = ProjectDataManagement::EventType::CameraVideo;
break;
case 303: // RADIO_CNR
eventType = ProjectDataManagement::EventType::RadioCnr;
break;
case 304: // RADIO_TVC
eventType = ProjectDataManagement::EventType::RadioTvc;
break;
default:
eventType = ProjectDataManagement::EventType::Unknown;
ERROR_LOG(m_logger) << "Unknown avsEventType: " << avsEventType;
break;
}
data.eventType = eventType.underlying();
int mountingPointId = 0;
if (m_avmDtc->mountingPointForStreamName(data.streamName, mountingPointId))
{
int mainStructureElementId = 0;
if (m_ismDtc->getBuildingId(mountingPointId, mainStructureElementId))
data.mainStructureElementId = mainStructureElementId;
}
return data;
}
Database::EemEvent WebSocketAvs::avsDataToEemEvent(const AvsData &data)
{
Database::EemEvent eemEvent;
eemEvent.id = data.id;
eemEvent.eventTypeId = data.eventType.underlying();
eemEvent.sampleTime = data.eventTime;
QJsonObject obj;
obj[QStringLiteral("streamName")] = data.streamName;
if (data.endTime > 0)
{
qint64 duration = data.endTime - data.eventTime;
obj[QStringLiteral("duration")] = duration;
}
obj[QStringLiteral("endTime")] = data.endTime;
obj[QStringLiteral("MainStructureElementId")] = data.mainStructureElementId;
eemEvent.properties = QString::fromUtf8(QJsonDocument(obj).toJson());
return eemEvent;
}
ExerciseEventManagement::Event WebSocketAvs::avsDataToEvent(const AvsData &data)
{
ExerciseEventManagement::Event event;
event.id(data.id);
event.sampleTime(data.eventTime);
event.eventTypeId(data.eventType);
QJsonObject obj;
obj[QStringLiteral("streamName")] = data.streamName;
if (data.endTime > 0)
{
qint64 duration = data.endTime - data.eventTime;
obj[QStringLiteral("duration")] = duration;
}
obj[QStringLiteral("endTime")] = data.endTime;
obj[QStringLiteral("MainStructureElementId")] = data.mainStructureElementId;
event.properties(QJsonDocument(obj).toJson(QJsonDocument::Compact).toStdString());
return event;
}
void WebSocketAvs::onTextMessageReceived(const QString &message)
{
INFO_LOG(m_logger) << "Got message from AVS control process: " << message;
const QJsonDocument doc(QJsonDocument::fromJson(message.toUtf8()));
const QJsonObject obj = doc.object();
AvsData data = jsonObjectToAvsData(obj);
bool lastRangeClosed = obj[QStringLiteral("lastRangeClosed")].toBool();
handleAvsData(data, lastRangeClosed);
}
void WebSocketAvs::handleAvsData(const AvsData &data, const bool &lastRangeClosed)
{
QString streamName = data.streamName;
DEBUG_LOG(m_logger) << "handleAvsData for stream: " << streamName;
auto it = m_avsDataMap.find(streamName);
// existing stream ..........................................................
if (it != m_avsDataMap.end())
{
DEBUG_LOG(m_logger) << "handleAvsData for stream 1: " << streamName;
AvsData &avsData = m_avsDataMap[streamName];
if ( (avsData.endTime == -1) && (data.endTime == -1))
{
DEBUG_LOG(m_logger) << "kein Publish (avsData.endTime == -1) && (data.endTime == -1)";
return;
}
if ( (data.endTime == -1) && (data.eventType == ProjectDataManagement::EventType::RadioCnr))
{
DEBUG_LOG(m_logger) << "kein Publish (avsData.endTime == -1) && RadioCnr";
return;
}
if (avsData.eventTime != data.eventTime) // new range -> close old range
{
DEBUG_LOG(m_logger) << "handleAvsData for stream 2: " << streamName;
if (avsData.eventTime == -1) // missing end time -> set artificial end time
{
WARNING_LOG(m_logger) << "Missing end time for stream: " << streamName << " -> setting artificial end time: " << avsData.eventTime + 1000;
avsData.eventTime = avsData.eventTime + 1000;
publishData(streamName);
}
DEBUG_LOG(m_logger) << "handleAvsData for stream remove: " << streamName;
m_avsDataMap.remove(streamName);
}
else // update range
{
DEBUG_LOG(m_logger) << "handleAvsData update range publish" << streamName;
avsData.endTime = data.endTime;
publishData(streamName);
// if (lastRangeClosed)
// m_avsDataMap.remove(streamName);
return;
}
}
// new range ................................................................
DEBUG_LOG(m_logger) << "m_avsDataMap.insert(streamName";
m_avsDataMap.insert(streamName, data);
AvsData &avsData = m_avsDataMap[streamName];
if (!lastRangeClosed)
{
DEBUG_LOG(m_logger) << "!lastRangeClosed";
avsData.endTime = -1;
}
else if (avsData.endTime == -1) // range is closed but end time is missing
{
DEBUG_LOG(m_logger) << "avsData.endTime == -1";
avsData.endTime = avsData.eventTime + 1000; // -> set artificial end time
}
DEBUG_LOG(m_logger) << "publishData";
avsData.id = publishData(streamName);
// if (lastRangeClosed)
// m_avsDataMap.remove(streamName);
// // new stream ...............................................................
// if (it == m_avsDataMap.end())
// {
// DEBUG_LOG(m_logger) << "received event on new stream: " << streamName;
// AvsData avsData = data;
// if (!lastRangeClosed) // waiting for end time
// avsData.endTime = -1;
// else if (avsData.endTime == -1) // missing end time -> set artificial end time
// avsData.endTime = avsData.eventTime + 1000;
// m_avsDataMap.insert(streamName, avsData);
// m_avsDataMap[streamName].id = publishData(streamName);
// if (lastRangeClosed) // event is over -> data not needed anymore
// {
// m_avsDataMap.remove(streamName);
// return;
// }
// }
// // ..........................................................................
// AvsData &avsData = m_avsDataMap[streamName]; // working on reference
// if (avsData.eventTime != data.eventTime) // same stream, different time -> new event received
// {
// DEBUG_LOG(m_logger) << "new event on stream: " << streamName;
// // close old event ........................................................
// if (avsData.endTime == -1) // last event on the same stream was not closed
// {
// WARNING_LOG(m_logger) << "Missing end time for stream: " << streamName << ". Setting artificial end time.";
// avsData.endTime = avsData.eventTime + 1000;
// publishData(streamName);
// }
// // ........................................................................
// avsData = data;
// avsData.id = 0;
// m_avsDataMap[streamName].id = publishData(streamName);
// if (lastRangeClosed) // event is over -> data not needed anymore
// {
// m_avsDataMap.remove(streamName);
// return;
// }
// }
// else // same event
// {
// avsData.endTime = data.endTime;
// DEBUG_LOG(m_logger) << "received new end time: " << data.endTime << " for stream: " << streamName;
// if (lastRangeClosed)
// {
// publishData(streamName);
// m_avsDataMap.remove(streamName); // event is over -> data not needed anymore
// }
// }
}
int WebSocketAvs::publishData(const QString &streamName)
{
DEBUG_LOG(m_logger) << "WebSocketAvs::publishData";
if (streamName.isEmpty())
{
WARNING_LOG(m_logger) << "Ignoring empty stream name";
return 0;
}
AvsData data = m_avsDataMap.value(streamName);
Database::EemEvent dbData = avsDataToEemEvent(data);
ExerciseEventManagement::Event ddsEvent = avsDataToEvent(data);
qint64 endTime = data.endTime;
bool isAlreadyInDb = false;
auto cb = [this, dbData, &isAlreadyInDb, streamName, endTime] (const Database::EemEvent &ev)
{
// endTime and streamName aus DB Event (ev) Properties lesen!
const QJsonDocument evDoc = QJsonDocument::fromJson(ev.properties.toUtf8());
const QJsonObject evObj = evDoc.object();
const qint64 evEndTime = evObj[(QStringLiteral("end_time"))].toInt();
const QString streamNameDb = evObj[QStringLiteral("streamName")].toString();
if ((ev.sampleTime == dbData.sampleTime && ev.eventTypeId == dbData.eventTypeId) ||
(evEndTime > 0 && endTime == -1 ))
{
if (streamName == streamNameDb)
isAlreadyInDb = true;
}
};
if (!m_dbHandler->db()->selectBySampleTime(cb, dbData.sampleTime))
{
if (isAlreadyInDb)
{
WARNING_LOG(m_logger) << "Ignoring existing event at " << dbData.sampleTime;
return 0;
}
}
// write to database
if (dbData.id > 0)
{
if (!m_dbHandler->db()->update(dbData))
ERROR_LOG(m_logger) << "Error while updating dbData with id : " << data.id;
}
else
{
if (!m_dbHandler->db()->insert(dbData))
ERROR_LOG(m_logger) << "Error while inserting avsData in db: streamName: " << data.streamName;
ddsEvent.id(dbData.id);
}
// publish data
INFO_LOG(m_logger) << "Publish data for stream: " << streamName << " and database id=" << dbData.id;
m_eventServer->sendEvent(Database::toJson(dbData));
m_ddsHandler->dts()->publish(ddsEvent);
m_mainHandler->addEventIdToRemove(dbData.id); // take data from dds-bus after 10s, we need just to make sure the recording service got it
return dbData.id;
}
void WebSocketAvs::openWebSocket()
{
DEBUG_LOG(m_logger) << "openWebSocket";
closeWebSocket();
m_wsSocket = new QWebSocket;
connect(m_wsSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error),
this, &WebSocketAvs::onWebSocketError);
connect(m_wsSocket, &QWebSocket::connected,
this, &WebSocketAvs::onWebSocketConnected);
connect(m_wsSocket, &QWebSocket::disconnected,
this, &WebSocketAvs::onWebSocketDisconnected);
connect(m_wsSocket, &QWebSocket::textMessageReceived,
this, &WebSocketAvs::onTextMessageReceived);
m_wsSocket->open(QUrl::fromEncoded(m_wsUrl.toLocal8Bit()));
}
void WebSocketAvs::closeWebSocket()
{
DEBUG_LOG(m_logger) << "closeWebSocket";
if (m_wsSocket)
{
m_wsSocket->disconnect(this);
m_wsSocket->close();
m_wsSocket->deleteLater();
m_wsSocket = nullptr;
}
m_wsReconnectTimer->stop();
}
void WebSocketAvs::onWebSocketError(QAbstractSocket::SocketError error)
{
if (!m_wsSocket)
return;
WARNING_LOG(m_logger) << "Error connecting to " << m_wsSocket->peerAddress().toString() << ": " << error;
closeWebSocket();
m_wsReconnectTimer->start(1000);
}
void WebSocketAvs::onWebSocketConnected()
{
DEBUG_LOG(m_logger) << "connected to via websockets";
m_wsReconnectTimer->stop();
m_bWebSocketDisconnectedWarnedOnce = false;
QTimer::singleShot(300, this, &WebSocketAvs::onSendExerciseId);
}
void WebSocketAvs::onWebSocketDisconnected()
{
DEBUG_LOG(m_logger) << "WebSocketAvs::onWebSocketDisconnected()";
if (!m_bWebSocketDisconnectedWarnedOnce)
{
m_bWebSocketDisconnectedWarnedOnce = true;
WARNING_LOG(m_logger) << "Websocket to " << m_wsUrl << " closed - trying to reconnect in 1 sec";
}
closeWebSocket();
m_wsReconnectTimer->start(1000);
}
void WebSocketAvs::onSendExerciseId()
{
ERROR_LOG(m_logger) << "Send exerciseId: ";
QJsonObject obj;
obj[QStringLiteral("exerciseId")] = QJsonValue(m_exerciseId);
QString jsonStr = QString::fromUtf8(QJsonDocument(obj).toJson());
m_wsSocket->sendTextMessage(jsonStr);
}
void WebSocketAvs::onCleanUpDbInternal()
{
m_cleanupTimer->stop();
const auto allDataMap = getAllAvsData();
const auto dbData = getAllInternalDbData();
//qCritical() << " START CLEANUP with " << allDataMap.keys().size() << " keys";
m_avmDtc->waitForHistoricalDataStreamName(1500);
m_ismDtc->waitForHistoricalDataMainStructureElement(1500);
// qCritical() << "CHECK STREAMNAME " << m_avmDtc->streamNames().size();
for (const auto &stream : allDataMap.keys())
{
QVector<AvsData> externalData = allDataMap.value(stream);
//This is the fixed map ... there shouldn't be multiple entries!
QMap<quint64, AvsData> ntpRangeSortExternal;
for (const auto &eavItem : externalData)
{
ntpRangeSortExternal.insert(eavItem.eventTime, eavItem);
}
QMultiMap<quint64, AvsData> ntpRangeSortLocal;
for (const auto &avItem : dbData.value(stream))
{
ntpRangeSortLocal.insert(avItem.eventTime, avItem);
}
DEBUG_LOG(m_logger) <<" Start with key: '" << stream << "' external = " << externalData.size() << " internal: " << ntpRangeSortLocal.size();
QVector<qint64> itemsToRemoveFromInternal;
QMap<qint64, AvsData> collection;
QVector<AvsData> toAdd;
QVector<AvsData> toIgnore;
QVector<AvsData> toUpdate;
for (const auto &eItem : ntpRangeSortExternal)
{
QList<AvsData> list = ntpRangeSortLocal.values(eItem.eventTime);
if (list.isEmpty())
{
//there is no external item so add
if (!collection.contains(eItem.eventTime))
{
AvsData add = eItem;
bool checkMsi = eItem.eventType == ProjectDataManagement::EventType::CameraVideo;
int mountingPointId = 0;
if (checkMsi && m_avmDtc->mountingPointForStreamName(eItem.streamName, mountingPointId))
{
//FATAL_LOG(m_logger) << "FOUND MPI ";
int mainStructureElementId = 0;
if (m_ismDtc->getBuildingId(mountingPointId, mainStructureElementId))
add.mainStructureElementId = mainStructureElementId;
}
DEBUG_LOG(m_logger) << "need to add: " << add.eventType << " st: " << add.streamName << " mpi:" << add.mainStructureElementId;
toAdd.append(add);
collection.insert(add.eventTime, add);
}
continue;
}
for ( auto item : list)
{
bool checkMsi = eItem.eventType == ProjectDataManagement::EventType::CameraVideo;
if (item.endTime == eItem.endTime )
{
//FINF MSE ID!
int mountingPointId = 0;
int mainStructureElementId = 0;
if (checkMsi && m_avmDtc->mountingPointForStreamName(item.streamName, mountingPointId))
{
//qCritical() << "FOUND mpi" << mountingPointId << " for Streamname: " << item.streamName;
m_ismDtc->getBuildingId(mountingPointId, mainStructureElementId);
//qCritical() << "FOUND msi" << mainStructureElementId;
}
if(item.mainStructureElementId == 0 && mainStructureElementId > 0)
{
item.mainStructureElementId = mainStructureElementId;
toUpdate.append(item);
}
else
toIgnore.append(item);
collection.insert(item.eventTime, item);
continue;
}else // if (eItem.endTime != item.endTime) is always true
{
item.endTime = eItem.endTime;
//item.eventType = lItem.eventType;
int mountingPointId = 0;
if (checkMsi && m_avmDtc->mountingPointForStreamName(item.streamName, mountingPointId))
{
int mainStructureElementId = 0;
if (m_ismDtc->getBuildingId(mountingPointId, mainStructureElementId))
{
item.mainStructureElementId = mainStructureElementId;
}
}
toUpdate.append(item);
collection.insert(item.eventTime, item);
}
}
}
for (const AvsData &oldEntry : qAsConst(ntpRangeSortLocal))
{
const auto &item = collection.value(oldEntry.eventTime);
if (item.id <= 0 )
itemsToRemoveFromInternal.append(oldEntry.id);
}
//now remove old entries
//REMOVE DATA
for( const auto &del: itemsToRemoveFromInternal)
{
// FATAL_LOG(m_logger) << "need to remove " << del;
m_dbHandler->db()->removeEemEvent(del);
}
for( const auto &update: toUpdate)
{
//UPDATE DATA
Database::EemEvent dbDataUpdate = avsDataToEemEvent(update);
m_dbHandler->db()->update(dbDataUpdate);
}
for( const auto &add: toAdd)
{
//ADD
Database::EemEvent dbDataAdd = avsDataToEemEvent(add);
dbDataAdd.id = 0;
m_dbHandler->db()->insert(dbDataAdd);
}
DEBUG_LOG(m_logger) << "DONE Sync wite remote DB "<< stream << " to Ignore" << toIgnore.size()<< " to Add" << toAdd.size() << " to Update:" << toUpdate.size()<< " to remove:" << itemsToRemoveFromInternal.size();
}
m_cleanupTimer->start();
m_lastUpdate = QDateTime::currentMSecsSinceEpoch()-2*60*1000;
DEBUG_LOG(m_logger) << "DONE Sync wite remote DB next check starts at: " << QDateTime::fromMSecsSinceEpoch(m_lastUpdate).toString();
}
QMap<QString, QVector<AvsData> > WebSocketAvs::getAllAvsData()
{
//select all Data from AVS avm_timeline events
// order by stream
QMultiMap<QString, AvsData> ret;
QMap<QString, QVector<AvsData> > retu;
//QString tableNameCameraEvents = (QLatin1String("avm") + QLatin1String(".avs_camera_events")); // todo get shema name
QString stmt(QLatin1String("SELECT id, time_start_msec, time_end_msec, stream_name, event_type, last_update FROM ") + QLatin1String("avm.avs_camera_events") +
QLatin1String(" WHERE context_id = :context"));
if (m_lastUpdate > 0)
stmt += QLatin1String(" and time_start_msec > ") + QString::number(m_lastUpdate);
DEBUG_LOG(m_logger) << "START Sync read remote DB at " << QDateTime::fromMSecsSinceEpoch(m_lastUpdate).toString();
NSqlQuery q(m_dbHandler->db()->database());
if (!q.prepare(stmt))
{
WARNING_LOG(m_logger) << "Could not prepare statement" << stmt << q.lastError().text() << q.lastError().databaseText();
return retu;
} // if
q.setForwardOnly(true);
QString val = QStringLiteral("ex_") + QString::number(m_exerciseId);
q.bindValue(QLatin1String(":context"),val);
// qWarning() << "frist try statement fallback" << stmt ;
if (!q.exec())
{
DEBUG_LOG(m_logger) << "Could not execute" << stmt << q.lastError().text() << q.lastError().databaseText();
return retu;
} // if
//qWarning() << "frist try FOUND" << q.size() ;
AvsData item;
int j =0;
while(q.next())
{
int i = 0;
item.id = q.value(i++).toInt();
item.eventTime = q.value(i++).toDouble();
item.endTime = q.value(i++).toDouble();
item.streamName = q.value(i++).toString().trimmed();
item.eventType = avsEventTypeToPdmEventType(q.value(i++).toInt());
ret.insert(item.streamName, item);
j++;
} // while
for (const auto &stream: ret.uniqueKeys())
{
retu.insert(stream, ret.values(stream).toVector());
}
// qCritical() << "DONE reading external db " << j;
return retu;
}
QMap<QString, QVector<AvsData>> WebSocketAvs::getAllInternalDbData()
{
//get all local db data
QMap<QString, QVector<AvsData> > retu;
QMultiMap<QString, AvsData> ret;
QVector<Database::EemEvent> dataVec;
//qCritical() << "START reading internal db ";
m_dbHandler->db()->selectByEventTypeId(dataVec, ProjectDataManagement::EventType::CameraVideo);
m_dbHandler->db()->selectByEventTypeId(dataVec, ProjectDataManagement::EventType::RadioCnr);
m_dbHandler->db()->selectByEventTypeId(dataVec, ProjectDataManagement::EventType::RadioTvc);
// m_dbHandler->db()->select(dataVec);
//qCritical() << "END select reading internal db ";
DEBUG_LOG(m_logger) << "START Sync read local DB at " << QDateTime::fromMSecsSinceEpoch(m_lastUpdate).toString();
int j = 0;
for (const auto &dataI : qAsConst(dataVec))
{
if (m_lastUpdate > 0 && dataI.sampleTime < m_lastUpdate)
continue;
QJsonDocument job = QJsonDocument::fromJson(dataI.properties.toUtf8());
if(!job.isObject())
{
WARNING_LOG(m_logger) << "ERROR Parsing Json";
}
QJsonObject obj = job.object();
AvsData data;
data.id = dataI.id;
data.eventTime = dataI.sampleTime;
data.endTime = obj.value(QStringLiteral("endTime")).toDouble();
data.eventType = dataI.eventTypeId;
data.mainStructureElementId =obj.value(QStringLiteral("MainStructureElementId")).toDouble();
data.streamName =obj.value(QStringLiteral("streamName")).toString();
ret.insert(data.streamName, data);
j++;
//ok add to map
}
for (const auto &stream: ret.uniqueKeys())
{
retu.insert(stream, ret.values(stream).toVector());
}
// qCritical() << "DONE reading internal db" << j;
return retu;
}
} // namespace ExerciseEventManagement
+110
View File
@@ -0,0 +1,110 @@
/**
* WebSocket connection to AVS to retrieve AVS events
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-02-06
*/
#pragma once
#include <QAbstractSocket>
#include <QTimer>
#include <QVector>
#include <QSharedPointer>
#include "logger.h"
#include "dds_handler.h"
#include "eem_db.h"
#include <ngldb/db_service_handler.h>
#include <ExerciseEventManagement/eem.hpp>
#include <ProjectDataManagement/pdm_enums.hpp>
#include <AudioVideoManagement/avm_dtc.h>
#include <InfrastructureManagement/ism_dtc.h>
class QWebSocket;
namespace ExerciseEventManagement
{
class MainHandler;
class WebSocketEventServer;
using DbHandler = NGLDB::DbServiceHandler<DatabaseInterface>;
struct AvsData
{
int id;
qint64 eventTime;
qint64 endTime;
ProjectDataManagement::EventType eventType;
QString streamName;
int mainStructureElementId;
QSharedPointer<QTimer> timer; // no plain ptr here since AvsData is copied
AvsData()
: id(0)
, eventTime(0)
, endTime(-1)
, eventType(ProjectDataManagement::EventType::Unknown)
, mainStructureElementId(0)
, timer(new QTimer)
{
timer->setSingleShot(true);
timer->setInterval(3000);
}
};
class WebSocketAvs : public QObject
{
Q_OBJECT
public:
WebSocketAvs(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DbHandler> &db,
const std::shared_ptr<WebSocketEventServer> &eventServer,
const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &avmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
int exerciseId,
QObject *parent = nullptr);
~WebSocketAvs();
void setWsUrl(const QString &url);
void init();
private Q_SLOTS:
void onTextMessageReceived(const QString &message);
void handleAvsData(const AvsData &data, const bool &lastRangeClosed);
int publishData(const QString &streamName);
void openWebSocket();
void closeWebSocket();
void onWebSocketError(QAbstractSocket::SocketError error);
void onWebSocketConnected();
void onWebSocketDisconnected();
void onSendExerciseId();
void onCleanUpDbInternal();
QMap<QString, QVector<AvsData>> getAllAvsData();
QMap<QString, QVector<AvsData>> getAllInternalDbData();
private:
AvsData jsonObjectToAvsData(const QJsonObject &obj);
Database::EemEvent avsDataToEemEvent(const AvsData &data);
ExerciseEventManagement::Event avsDataToEvent(const AvsData &data);
private:
std::shared_ptr<DdsHandler> m_ddsHandler; ///< Pointer to DDS handler
std::shared_ptr<DbHandler> m_dbHandler;
std::shared_ptr<WebSocketEventServer> m_eventServer;
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_avmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
MainHandler *m_mainHandler;
QWebSocket *m_wsSocket;
QTimer *m_wsReconnectTimer;
QTimer *m_cleanupTimer;
QString m_wsUrl;
QMap<QString, AvsData> m_avsDataMap;
int m_exerciseId;
qint64 m_lastUpdate;
bool m_bWebSocketDisconnectedWarnedOnce;
LOGGER_T m_logger;
};
} // namespace ExerciseEventManagement
+266
View File
@@ -0,0 +1,266 @@
/**
* WebSocketServer to publish Events
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-02-06
*/
#include "web_socket_server.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSqlError>
#include <QTimer>
#include <QUrlQuery>
#include <QWebSocket>
#include <QWebSocketServer>
#include <ngldb/nsqlquery.h>
namespace ExerciseEventManagement
{
WebSocketEventServer::WebSocketEventServer(const QSqlDatabase &db, QObject *parent)
: QObject(parent)
, m_wsServer(nullptr)
, m_cleanupTimer(new QTimer(this))
, m_db(db)
, m_logger(getLogger("WebSocketEventServer"))
{
m_cleanupTimer->setInterval(10000); // clear every 10s
connect(m_cleanupTimer, &QTimer::timeout, this, &WebSocketEventServer::onCleanupLiveHistory);
}
WebSocketEventServer::~WebSocketEventServer()
{
for (auto s : qAsConst(m_liveSockets))
{
s->disconnect(this);
s->close();
}
qDeleteAll(m_liveSockets);
}
bool WebSocketEventServer::listen(const QPair<quint16, quint16> &portRange)
{
for (quint16 currentPort = portRange.first; currentPort <= portRange.second; ++currentPort)
{
if (listen(currentPort))
return true;
}
return listen(0);
}
bool WebSocketEventServer::listen(quint16 port)
{
if (m_wsServer && m_wsServer->isListening())
return true;
m_cleanupTimer->start();
delete m_wsServer;
m_wsServer = new QWebSocketServer(QStringLiteral("EEM-Events"), QWebSocketServer::NonSecureMode, this);
connect(m_wsServer, &QWebSocketServer::newConnection,
this, &WebSocketEventServer::onNewConnection);
bool bRet = m_wsServer->listen(QHostAddress::Any, port);
if (bRet)
NOTICE_LOG(m_logger) << "Listening on port " << listenPort();
return bRet;
}
quint16 WebSocketEventServer::listenPort() const
{
return m_wsServer ? m_wsServer->serverPort() : 0;
}
void WebSocketEventServer::sendEvent(const QJsonObject &obj)
{
const QJsonDocument doc(obj);
const QString msg = QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
for (QWebSocket *ws : qAsConst(m_liveSockets))
ws->sendTextMessage(msg);
m_liveHistory[time(nullptr)].push_back(msg);
}
void WebSocketEventServer::onNewConnection()
{
QWebSocket *ws = m_wsServer->nextPendingConnection();
if (!ws)
return;
DEBUG_LOG(m_logger) << "New incoming connection from "
<< ws->peerAddress().toString() << ":"
<< ws->peerPort() << ", "
<< ws->requestUrl().toString();
const QUrl requestUrl = ws->requestUrl();
const QString requestPath = requestUrl.path();
if (requestPath == QStringLiteral("/live"))
{
connect(ws, &QWebSocket::disconnected,
this, &WebSocketEventServer::onSocketDisconnected);
for (const auto &historyVec : qAsConst(m_liveHistory))
{
for (const auto &msg : historyVec)
ws->sendTextMessage(msg);
}
m_liveSockets += ws;
}
else if (requestPath.startsWith(QLatin1String("/request")))
historyDataRequested(m_db, ws, requestUrl);
else
{
WARNING_LOG(m_logger) << "Unknown request path: '" << requestPath << "' - closing";
ws->close();
ws->deleteLater();
}
}
void WebSocketEventServer::onSocketDisconnected()
{
QWebSocket *ws = qobject_cast<QWebSocket *>(sender());
if (!ws)
return;
DEBUG_LOG(m_logger) << "Disconnected from "
<< ws->peerAddress().toString() << ":"
<< ws->peerPort() << ", "
<< ws->requestUrl().toString();
ws->deleteLater();
m_liveSockets -= ws;
}
void WebSocketEventServer::onCleanupLiveHistory()
{
qint64 tNow = time(nullptr) - 120; // 2min
auto it = m_liveHistory.begin();
while (it != m_liveHistory.end() && it.key() < tNow)
it = m_liveHistory.erase(it);
}
void WebSocketEventServer::historyDataRequested(const QSqlDatabase &db, QWebSocket *ws, const QUrl &requestUrl)
{
const QUrlQuery query(requestUrl);
const int requestId = query.queryItemValue(QStringLiteral("requestId")).toInt();
const qint64 from = query.queryItemValue(QStringLiteral("from")).toLongLong();
const qint64 to = query.queryItemValue(QStringLiteral("to")).toLongLong();
if (requestId == 0 || from <= 0 || to <= 0 || from > to)
{
WARNING_LOG(m_logger) << "Unknown request: '" << requestUrl.toString() << "' - closing, "
<< requestId << ", " << from << ", to " << to;
ws->sendTextMessage(QStringLiteral("{}"));
ws->close();
ws->deleteLater();
return;
}
QString stmt(QLatin1String(" SELECT id, event_type_id, participant_id, party_id, equipment_number, properties, sample_time, position_x, position_y, position_z"
" FROM eem_event WHERE sample_time > ") + QString::number(from) +
QLatin1String(" AND sample_time <= ") + QString::number(to));
// add event type filter to query if preset
if (query.hasQueryItem(QStringLiteral("eventTypeId")))
{
const QString ids = query.queryItemValue(QStringLiteral("eventTypeId"));
// split/unsplit to make sure only integers are there
const QVector<QStringRef> idVec = ids.splitRef(QLatin1Char(','));
QStringList eventList;
eventList.reserve(idVec.size());
for (const auto &id : idVec)
{
int i = id.toInt();
if (i == 0)
continue;
eventList += QString::number(i);
}
const QString eventStr = eventList.join(QLatin1Char(','));
stmt += QStringLiteral(" AND event_type_id IN (") + eventStr + QLatin1Char(')');
}
// add Json properties filter to query if present
if (query.hasQueryItem(QStringLiteral("filter")))
{
const QString filterString = query.queryItemValue(QStringLiteral("filter"));
DEBUG_LOG(m_logger) << "filter string: " << filterString;
const QJsonObject obj(QJsonDocument::fromJson(filterString.toUtf8()).object());
if (obj.keys().size() == 1)
{
const QJsonValue val = obj.value(obj.keys().constFirst());
if (val.isObject())
stmt += QStringLiteral(" AND properties @> '") + filterString + QLatin1Char('\'');
else if (val.isArray())
{
// currently we only support int arrays
const QJsonArray arr(val.toArray());
QStringList sl;
sl.reserve(arr.size());
for (const auto &ele : arr)
sl += QString::number(ele.toInt());
if (sl.size() > 0)
stmt += QStringLiteral(" AND CAST (properties ->> '%1' AS int) IN (").arg(obj.keys().constFirst()) +
sl.join(QLatin1Char(',')) + QLatin1Char(')');
}
}
}
// add a limit for max query rows
if (query.hasQueryItem(QStringLiteral("limit")))
{
const QString limitString = query.queryItemValue(QStringLiteral("limit"));
DEBUG_LOG(m_logger) << "limiting query to " << limitString << " events";
stmt += QStringLiteral(" LIMIT ") + limitString;
}
DEBUG_LOG(m_logger) << "stmt: " << stmt;
NSqlQuery q(db);
if (!q.exec(stmt))
{
ERROR_LOG(m_logger) << "Error while selecting event-data: " << stmt << ", " << q.lastError().databaseText();
ws->sendTextMessage(QStringLiteral("{}"));
ws->close();
ws->deleteLater();
return;
}
QJsonArray events;
Database::EemEvent data;
while (q.next())
{
int i = 0;
data.id = q.value(i++).toInt();
data.eventTypeId = static_cast<ProjectDataManagement::EventType_def::type>(q.value(i++).toInt());
data.participantId = q.value(i++).toInt();
data.partyId = q.value(i++).toInt();
data.equipmentNumber = q.value(i++).toInt();
data.properties = q.value(i++).toString();
data.sampleTime = q.value(i++).toLongLong();
double pos_lon = q.value(i++).toDouble();
double pos_lat = q.value(i++).toDouble();
double pos_z = q.value(i++).toDouble();
data.position = LtsUtils::QPoint3D(pos_lon, pos_lat, pos_z);
events.append(Database::toJson(data));
if (events.size() > 100)
{
INFO_LOG(m_logger) << "Sending an answer with " << events.size() << " events for query " << stmt;
const QJsonDocument doc(events);
ws->sendTextMessage(QString::fromUtf8(doc.toJson(QJsonDocument::Compact)));
events = QJsonArray();
}
}
if (!events.isEmpty())
{
INFO_LOG(m_logger) << "Sending an answer with " << events.size() << " events for query " << stmt;
const QJsonDocument doc(events);
ws->sendTextMessage(QString::fromUtf8(doc.toJson(QJsonDocument::Compact)));
}
// finished
ws->sendTextMessage(QStringLiteral("{}"));
}
}
+50
View File
@@ -0,0 +1,50 @@
/**
* WebSocketServer to publish Events
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-02-06
*/
#pragma once
#include <QMap>
#include <QObject>
#include <QSet>
#include <QSqlDatabase>
#include <QVector>
#include <ExerciseEventManagement/eem_db_structs.h>
#include "logger.h"
class QTimer;
class QUrl;
class QWebSocket;
class QWebSocketServer;
namespace ExerciseEventManagement
{
class WebSocketEventServer : public QObject
{
Q_OBJECT
public:
WebSocketEventServer(const QSqlDatabase &db, QObject *parent = nullptr);
~WebSocketEventServer();
bool listen(const QPair<quint16, quint16> &portRange);
bool listen(quint16 port);
quint16 listenPort() const;
void sendEvent(const QJsonObject &obj);
private:
void onNewConnection();
void onSocketDisconnected();
void onCleanupLiveHistory();
void historyDataRequested(const QSqlDatabase &db, QWebSocket *ws, const QUrl &requestUrl);
private:
QSet<QWebSocket *> m_liveSockets;
QMap<qint64, QVector<QString>> m_liveHistory;
QWebSocketServer *m_wsServer;
QTimer *m_cleanupTimer;
QSqlDatabase m_db;
NGL::INGLLogger &m_logger;
};
} // namespace ExerciseEventManagement