Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+188
@@ -0,0 +1,188 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
INCLUDE(MacroRTIDDSGen)
|
||||
INCLUDE(MacroCodeGenIdl)
|
||||
|
||||
SET(xml_FILE scm_service.xml)
|
||||
|
||||
#
|
||||
# --- Library svcScmCommon --------------------------------------------------------------------------------
|
||||
#
|
||||
SET(SRCS_common
|
||||
scm_export.h
|
||||
logger.cpp
|
||||
logger.h
|
||||
)
|
||||
|
||||
# Generate scm_enums.idl / scm.idl
|
||||
CODEGEN_IDL_SVC(SRCS_common ${xml_FILE})
|
||||
# Generate scm_db_impl.cpp/.h, scm_db.h
|
||||
CODEGEN_DATABASE(SRCS_common ${xml_FILE})
|
||||
|
||||
# Generate *.cpp/*.hpp from idl
|
||||
RTIDDSGEN(SRCS_common scm_enums.idl)
|
||||
RTIDDSGEN(SRCS_common scm.idl)
|
||||
|
||||
# Generate scm_dt[s,c]_types.h, scm_ft[s,c]_types.h
|
||||
CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
|
||||
# generate scm_[d|f]tc.h
|
||||
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE})
|
||||
|
||||
NGL_ADD_LIBRARY(SV AAR WS svcScmCommon SHARED ${SRCS_common})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(svcScmCommon
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(svcScmCommon
|
||||
PRIVATE
|
||||
RTI::nddscpp2
|
||||
PUBLIC
|
||||
Qt5::Core
|
||||
ddscore
|
||||
NGLLogger
|
||||
ngldb
|
||||
)
|
||||
NGL_INSTALL_TARGET(svcScmCommon)
|
||||
|
||||
#
|
||||
# --- Library svcScmClient --------------------------------------------------------------------------------
|
||||
#
|
||||
SET(SRCS_client
|
||||
scm_export.h
|
||||
)
|
||||
# generate scm_ftc_impl.[cpp|h]
|
||||
CODEGEN_CLIENT_TOPICS_IMPL(SRCS_client ${xml_FILE})
|
||||
|
||||
NGL_ADD_LIBRARY(WS AAR SV svcScmClient SHARED ${SRCS_client})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(svcScmClient
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../
|
||||
)
|
||||
|
||||
NGL_TARGET_LINK_LIBRARIES(svcScmClient
|
||||
PRIVATE
|
||||
RTI::nddscpp2
|
||||
ddsconnector
|
||||
PUBLIC
|
||||
svcScmCommon
|
||||
ddsftc
|
||||
)
|
||||
NGL_INSTALL_TARGET(svcScmClient)
|
||||
|
||||
#
|
||||
# --- Library svcScmServer --------------------------------------------------------------------------------
|
||||
#
|
||||
SET(SRCS_server
|
||||
scm_export.h
|
||||
)
|
||||
# generate scm_[d|f]ts.h, scm_[d|f]ts_impl.[cpp|h]
|
||||
CODEGEN_SERVER_TOPICS(SRCS_server ${xml_FILE})
|
||||
|
||||
NGL_ADD_LIBRARY(SV AAR svcScmServer SHARED ${SRCS_server})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(svcScmServer
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../
|
||||
)
|
||||
|
||||
NGL_TARGET_LINK_LIBRARIES(svcScmServer
|
||||
PRIVATE
|
||||
RTI::nddscpp2
|
||||
ddsconnector
|
||||
ddsfts
|
||||
PUBLIC
|
||||
svcScmCommon
|
||||
ngldb
|
||||
)
|
||||
NGL_INSTALL_TARGET(svcScmServer)
|
||||
|
||||
#
|
||||
# --- Topic Type Plugin --------------------------------------------------------------
|
||||
#
|
||||
CREATE_TOPIC_TYPE_PLUGIN(${xml_FILE})
|
||||
|
||||
#
|
||||
# --- Executable ServiceManagement(svcServiceManagement) -------------------------------------------------
|
||||
#
|
||||
SET(SRCS_exe
|
||||
dds_handler.cpp
|
||||
dds_handler.h
|
||||
group.cpp
|
||||
group.h
|
||||
group_handler.cpp
|
||||
group_handler.h
|
||||
heartbeat_listener.cpp
|
||||
heartbeat_listener.h
|
||||
licence_donator.h
|
||||
licence_handler.cpp
|
||||
licence_handler.h
|
||||
main.cpp
|
||||
service.cpp
|
||||
service.h
|
||||
service_donator.h
|
||||
service_handler.cpp
|
||||
service_handler.h
|
||||
service_process.cpp
|
||||
service_process.h
|
||||
main_handler.cpp
|
||||
main_handler.h
|
||||
settings.cpp
|
||||
settings.h
|
||||
|
||||
WebSocketInterface/data_manager.cpp
|
||||
WebSocketInterface/data_manager.h
|
||||
WebSocketInterface/json_keys.h
|
||||
WebSocketInterface/websocket_interface_handler.cpp
|
||||
WebSocketInterface/websocket_interface_handler.h
|
||||
WebSocketInterface/Messages/client_message.cpp
|
||||
WebSocketInterface/Messages/client_message.h
|
||||
WebSocketInterface/Messages/reply_message.cpp
|
||||
WebSocketInterface/Messages/reply_message.h
|
||||
|
||||
${xml_FILE}
|
||||
log4cxx_scm.props
|
||||
)
|
||||
|
||||
# Generate scm.sql, scm.xml
|
||||
CODEGEN_SQL(SRCS_exe ${xml_FILE})
|
||||
# install dummy licence file for developers
|
||||
IF(NOT BUILD_RPM)
|
||||
INSTALL_SHARED_FILES(SRCS_exe
|
||||
FILES "licence_file.crl" "oc_licence_file.crl"
|
||||
DESTINATION "Licence"
|
||||
SV
|
||||
AAR
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
NGL_ADD_SVC_ICON(SRCS_exe)
|
||||
NGL_ADD_EXECUTABLE(SV AAR svcServiceManagement ${SRCS_exe})
|
||||
NGL_TARGET_LINK_LIBRARIES(svcServiceManagement
|
||||
Qt5::Core
|
||||
svcCoreClient
|
||||
ddsconnector
|
||||
codegen_base
|
||||
LtsCommandLineParser
|
||||
nglsinglecoreapplication
|
||||
NGLLogger
|
||||
nglsettings
|
||||
ngltools
|
||||
ProjectConfig
|
||||
svcScmCommon
|
||||
svcScmServer
|
||||
LtsLicence
|
||||
LtsWebSocketServer
|
||||
)
|
||||
NGL_INSTALL_TARGET(svcServiceManagement)
|
||||
NGL_INSTALL(SV AAR FILES log4cxx_scm.props DESTINATION bin)
|
||||
NGL_INSTALL(SV AAR PROGRAMS check_start DESTINATION bin)
|
||||
|
||||
# Uncomment if necessary
|
||||
ADD_SUBDIRECTORY(UnitTests)
|
||||
#ADD_SUBDIRECTORY(ModuleTests)
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
SET(SRCS_DT_TEST
|
||||
data_topic_tests.cpp
|
||||
data_topic_tests_main.cpp
|
||||
log4cxx_scm_unit_tests.props
|
||||
)
|
||||
|
||||
SET(HDRS_DT_TEST
|
||||
data_topic_tests.h
|
||||
data_topic_tests_main.h
|
||||
)
|
||||
|
||||
IF(BUILD_TESTING AND TARGET svcScmCommon)
|
||||
# Testing DataTopic Functionality (DTS/DTC)
|
||||
ADD_EXECUTABLE(scmDtUnitTest ${SRCS_DT_TEST} ${HDRS_DT_TEST})
|
||||
TARGET_LINK_LIBRARIES(scmDtUnitTest
|
||||
nglcoreapplication
|
||||
NGLLogger
|
||||
RTI::nddscpp2
|
||||
svcScmCommon
|
||||
ddsgen
|
||||
ddsdts
|
||||
ddsdtc
|
||||
ddsQoS
|
||||
ProjectConfig
|
||||
Qt5::Core
|
||||
Qt5::Test
|
||||
)
|
||||
|
||||
ADD_TEST(NAME scmDtUnitTest COMMAND $<TARGET_FILE:scmDtUnitTest>)
|
||||
|
||||
IF(NOT BUILD_RPM)
|
||||
NGL_INSTALL(AAR SV TARGETS scmDtUnitTest DESTINATION bin/test)
|
||||
NGL_INSTALL(AAR SV FILES log4cxx_scm_unit_tests.props DESTINATION bin/test)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Unit test for ServiceManagement (SCM)
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2015-03-14
|
||||
*/
|
||||
|
||||
#include "data_topic_tests.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
#include <DDS/QosProfile/DDS_DefaultQos.h>
|
||||
#include <Common/ProjectConfig/project_defines.h>
|
||||
#include <DDS/DTS/generic_dts_impl.h>
|
||||
#include <DDS/DTC/generic_dtc_impl.h>
|
||||
|
||||
#include <QtTest/QTest>
|
||||
#include <QVariant>
|
||||
#include <QThread>
|
||||
|
||||
|
||||
|
||||
DataTopicTests::DataTopicTests()
|
||||
: m_domainParticipantPub(nullptr)
|
||||
, m_domainParticipantSub(nullptr)
|
||||
, m_domainId(0)
|
||||
, m_ddsPublisher(nullptr)
|
||||
, m_dtsServiceInformation(nullptr)
|
||||
, m_dtsGroupInformation(nullptr)
|
||||
, m_ddsSubscriber(nullptr)
|
||||
, m_subscriberListener(nullptr)
|
||||
, m_subscriberListenerBinder(nullptr)
|
||||
, m_dtcServiceInformation(nullptr)
|
||||
, m_dtcGroupInformation(nullptr)
|
||||
, m_mainThreadId(QThread::currentThreadId())
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTests.DataTopicTests"))
|
||||
{
|
||||
if (!DDS::QosProfile::setDefault())
|
||||
ERROR_LOG(m_logger) << "Error setting default QoS from xml-file ";
|
||||
|
||||
DEBUG_LOG(m_logger) << "Main thread ID is " << m_mainThreadId;
|
||||
}
|
||||
|
||||
DataTopicTests::~DataTopicTests()
|
||||
{
|
||||
// Publisher
|
||||
delete m_dtsServiceInformation;
|
||||
delete m_dtsGroupInformation;
|
||||
|
||||
// Subscriber
|
||||
delete m_dtcServiceInformation;
|
||||
delete m_dtcGroupInformation;
|
||||
|
||||
m_subscriberListener->prepareShutdown();
|
||||
m_subscriberListenerBinder = nullptr;
|
||||
|
||||
m_ddsSubscriber = nullptr;
|
||||
m_ddsPublisher = nullptr;
|
||||
|
||||
m_domainParticipantSub = nullptr;
|
||||
m_domainParticipantPub = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publisher
|
||||
**/
|
||||
|
||||
bool DataTopicTests::initPublisher()
|
||||
{
|
||||
m_domainParticipantPub =
|
||||
dds::domain::DomainParticipant(m_domainId,
|
||||
DDS::QosProfile::domainParticipantShm());
|
||||
|
||||
dds::pub::qos::PublisherQos publisherQos = m_domainParticipantPub.default_publisher_qos();
|
||||
|
||||
// ExerciseData will be published to Partition 0
|
||||
// use only selected partition
|
||||
const std::string partition = IDL::exercisePartition(0);
|
||||
|
||||
dds::core::policy::Partition partitionQos;
|
||||
partitionQos.name( { partition } );
|
||||
publisherQos << partitionQos;
|
||||
|
||||
DEBUG_LOG(m_logger) << "Publish on partition " << partition;
|
||||
|
||||
m_ddsPublisher = dds::pub::Publisher(m_domainParticipantPub, publisherQos);
|
||||
|
||||
// Create DTSs
|
||||
m_dtsServiceInformation = new DTS::ServiceInformationImpl(m_domainParticipantPub, m_ddsPublisher);
|
||||
m_dtsGroupInformation = new DTS::GroupInformationImpl(m_domainParticipantPub, m_ddsPublisher);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DataTopicTests::write(const ServiceManagement::ServiceInformation &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Publish topic ServiceInformation ";
|
||||
|
||||
DEBUG_LOG(m_logger) << "Id: " << data.id()
|
||||
<< ", Name: " << data.name()
|
||||
<< ", State: " << data.state()
|
||||
<< ", Timestamp: " << data.timestamp()
|
||||
<< ", ErrorString: " << data.errorString()
|
||||
;
|
||||
|
||||
return m_dtsServiceInformation->write(data);
|
||||
}
|
||||
|
||||
bool DataTopicTests::write(const ServiceManagement::GroupInformation &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Publish topic GroupInformation ";
|
||||
|
||||
if(m_logger.isDebugEnabled())
|
||||
{
|
||||
QString services;
|
||||
|
||||
for(int serviceId : data.services())
|
||||
{
|
||||
services.append(QString::number(serviceId));
|
||||
services.append(QLatin1Char(','));
|
||||
}
|
||||
|
||||
if(services.endsWith(QLatin1Char(',')))
|
||||
services.chop(1);
|
||||
|
||||
DEBUG_LOG(m_logger) << "Type: " << data.type()
|
||||
<< ", Domain: " << data.domainId()
|
||||
<< ", Partition: " << data.exercisePartitionId()
|
||||
<< ", Partition: " << data.replayPartitionId()
|
||||
<< ", Services: " << services
|
||||
<< ", Timestamp: " << data.timestamp();
|
||||
}
|
||||
|
||||
return m_dtsGroupInformation->write(data);
|
||||
}
|
||||
|
||||
ServiceManagement::ServiceInformation DataTopicTests::getReceivedServiceInformationData() const
|
||||
{
|
||||
return m_receivedServiceInformationData;
|
||||
}
|
||||
|
||||
ServiceManagement::GroupInformation DataTopicTests::getReceivedGroupInformationData() const
|
||||
{
|
||||
return m_receivedGroupInformationData;
|
||||
}
|
||||
|
||||
void DataTopicTests::processData(const ServiceManagement::ServiceInformation &instance, DataModification modType)
|
||||
{
|
||||
Q_UNUSED(modType);
|
||||
INFO_LOG(m_logger) << "process Lts_ServiceInformation";
|
||||
|
||||
DEBUG_LOG(m_logger) << "Id: " << instance.id();
|
||||
DEBUG_LOG(m_logger) << "Name: " << instance.name();
|
||||
DEBUG_LOG(m_logger) << "State: " << instance.state();
|
||||
DEBUG_LOG(m_logger) << "Timestamp: " << instance.timestamp();
|
||||
DEBUG_LOG(m_logger) << "ErrorString: " << instance.errorString();
|
||||
|
||||
m_receivedServiceInformationData = instance;
|
||||
|
||||
QCOMPARE(m_mainThreadId, QThread::currentThreadId());
|
||||
|
||||
emit serviceInformationDataArrived();
|
||||
}
|
||||
|
||||
void DataTopicTests::processData(const ServiceManagement::GroupInformation &instance, DataModification modType)
|
||||
{
|
||||
Q_UNUSED(modType);
|
||||
INFO_LOG(m_logger) << "process Lts_ServiceInformation";
|
||||
|
||||
if(m_logger.isDebugEnabled())
|
||||
{
|
||||
QString services;
|
||||
|
||||
for(int serviceId : instance.services())
|
||||
{
|
||||
services.append(QString::number(serviceId));
|
||||
services.append(QLatin1Char(','));
|
||||
}
|
||||
|
||||
if(services.endsWith(QLatin1Char(',')))
|
||||
services.chop(1);
|
||||
|
||||
DEBUG_LOG(m_logger) << "Type: " << instance.type();
|
||||
DEBUG_LOG(m_logger) << "Domain: " << instance.domainId();
|
||||
DEBUG_LOG(m_logger) << "Partition: " << instance.exercisePartitionId();
|
||||
DEBUG_LOG(m_logger) << "Replay Partition: " << instance.replayPartitionId();
|
||||
DEBUG_LOG(m_logger) << "Services: " << services;
|
||||
DEBUG_LOG(m_logger) << "Timestamp: " << instance.timestamp();
|
||||
}
|
||||
|
||||
m_receivedGroupInformationData = instance;
|
||||
|
||||
QCOMPARE(m_mainThreadId, QThread::currentThreadId());
|
||||
|
||||
emit groupInformationDataArrived();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscriber
|
||||
**/
|
||||
|
||||
bool DataTopicTests::initSubscriber()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Initialisation DDS (Subscriber/Reader)";
|
||||
|
||||
m_domainParticipantSub =
|
||||
dds::domain::DomainParticipant(m_domainId,
|
||||
DDS::QosProfile::domainParticipantShm());
|
||||
|
||||
dds::sub::qos::SubscriberQos subscriberQos = m_domainParticipantSub.default_subscriber_qos();
|
||||
|
||||
// Initial Partition for listening on topic Exercise => partition 0
|
||||
// + Listening for Topics in partition 1
|
||||
dds::core::StringSeq partitions;
|
||||
partitions.reserve(2);
|
||||
for(int partitionId : { 0, 1 } )
|
||||
{
|
||||
partitions.push_back(IDL::exercisePartition(partitionId));
|
||||
}
|
||||
|
||||
dds::core::policy::Partition partitionQos;
|
||||
partitionQos.name( partitions );
|
||||
subscriberQos << partitionQos;
|
||||
|
||||
DEBUG_LOG(m_logger) << "Initialise DTC::SubscriberListener";
|
||||
m_subscriberListener = new DDSGeneric::SubscriberListener;
|
||||
|
||||
m_ddsSubscriber = dds::sub::Subscriber(m_domainParticipantSub,
|
||||
subscriberQos);
|
||||
m_subscriberListenerBinder = rti::core::bind_and_manage_listener(m_ddsSubscriber,
|
||||
m_subscriberListener,
|
||||
DDSGeneric::SubscriberListener::mask());
|
||||
|
||||
// Initialise DTCs
|
||||
m_dtcServiceInformation = new DTC::ServiceInformationImpl(m_domainParticipantSub, m_ddsSubscriber, m_subscriberListener);
|
||||
m_dtcGroupInformation = new DTC::GroupInformationImpl(m_domainParticipantSub, m_ddsSubscriber, m_subscriberListener);
|
||||
|
||||
m_dtcServiceInformation->setSubscriberCallback([this] (const ServiceManagement::ServiceInformation &data, DataModification modType) { processData(data, modType); } );
|
||||
m_dtcGroupInformation->setSubscriberCallback([this] (const ServiceManagement::GroupInformation &data, DataModification modType) { processData(data, modType); } );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Unit test for ServiceManagement (SCM)
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2015-03-14
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
// For Topic Exercise
|
||||
#include <ServiceManagement/scm_dtc_types.h>
|
||||
#include <ServiceManagement/scm_dts_types.h>
|
||||
|
||||
#include <ServiceManagement/scm.hpp>
|
||||
#include <DDS/Generic/subscriber_listener.h>
|
||||
#include <LtsUtilsCore/data_modification_enum.h>
|
||||
#include <rti/core/ListenerBinder.hpp>
|
||||
|
||||
|
||||
namespace NGL {
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
class DataTopicTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataTopicTests();
|
||||
virtual ~DataTopicTests();
|
||||
|
||||
// Initialisation
|
||||
bool initPublisher();
|
||||
bool initSubscriber();
|
||||
|
||||
// Publisher
|
||||
bool write(const ServiceManagement::ServiceInformation &data);
|
||||
bool write(const ServiceManagement::GroupInformation &data);
|
||||
|
||||
// Subscriber
|
||||
ServiceManagement::ServiceInformation getReceivedServiceInformationData() const;
|
||||
ServiceManagement::GroupInformation getReceivedGroupInformationData() const;
|
||||
|
||||
protected:
|
||||
// Subscriber Methods
|
||||
void processData(const ServiceManagement::ServiceInformation& instance, DataModification modType);
|
||||
void processData(const ServiceManagement::GroupInformation& instance, DataModification modType);
|
||||
|
||||
|
||||
Q_SIGNALS:
|
||||
void serviceInformationDataArrived();
|
||||
void groupInformationDataArrived();
|
||||
|
||||
private:
|
||||
dds::domain::DomainParticipant m_domainParticipantPub;
|
||||
dds::domain::DomainParticipant m_domainParticipantSub;
|
||||
int m_domainId;
|
||||
|
||||
// Publisher
|
||||
dds::pub::Publisher m_ddsPublisher;
|
||||
DTS::ServiceInformation *m_dtsServiceInformation;
|
||||
DTS::GroupInformation *m_dtsGroupInformation;
|
||||
// Subscriber
|
||||
dds::sub::Subscriber m_ddsSubscriber;
|
||||
DDSGeneric::SubscriberListener *m_subscriberListener;
|
||||
rti::core::ListenerBinder< dds::sub::Subscriber, dds::sub::SubscriberListener> m_subscriberListenerBinder;
|
||||
DTC::ServiceInformation *m_dtcServiceInformation;
|
||||
DTC::GroupInformation *m_dtcGroupInformation;
|
||||
|
||||
Qt::HANDLE m_mainThreadId;
|
||||
// For returning TestData
|
||||
ServiceManagement::ServiceInformation m_receivedServiceInformationData;
|
||||
ServiceManagement::GroupInformation m_receivedGroupInformationData;
|
||||
|
||||
NGL::INGLLogger& m_logger;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* UnitTests for ExerciseManagement (EXM)
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hanna E. Rathje, SLSE, 2015-08-20
|
||||
*/
|
||||
|
||||
#include "data_topic_tests_main.h"
|
||||
#include "data_topic_tests.h"
|
||||
|
||||
#include <Common/ProjectConfig/project_defines.h>
|
||||
|
||||
#include <QtTest/QTest>
|
||||
|
||||
#include <nglapplication/nglcoreapplication.h>
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
NGLCoreApplication app(argc, argv);
|
||||
app.createLogger("UnitTests", "log4cxx_exm_unit_tests.props");
|
||||
|
||||
DataTopicTestsMain *tc = new DataTopicTestsMain;
|
||||
|
||||
int retCode = QTest::qExec(tc, argc, argv);
|
||||
|
||||
delete tc;
|
||||
|
||||
return retCode;
|
||||
}
|
||||
|
||||
DataTopicTestsMain::DataTopicTestsMain()
|
||||
: m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTests.DataTopicTestsMain"))
|
||||
{
|
||||
}
|
||||
|
||||
void DataTopicTestsMain::initTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<"DataTopicTestsMain: initTestCase";
|
||||
|
||||
m_dtTest = new DataTopicTests();
|
||||
|
||||
QVERIFY2(m_dtTest, "Initialisation of DataTopicTest failed");
|
||||
QVERIFY2(m_dtTest->initPublisher(), "Initialisation Publisher failed");
|
||||
QVERIFY2(m_dtTest->initSubscriber(), "Initialisation Subscriber failed");
|
||||
|
||||
m_serviceInformationDataArrivedSpy = new QSignalSpy(m_dtTest, &DataTopicTests::serviceInformationDataArrived);
|
||||
m_groupInformationDataArrivedSpy = new QSignalSpy(m_dtTest, &DataTopicTests::groupInformationDataArrived);
|
||||
}
|
||||
|
||||
void DataTopicTestsMain::publishServiceInformationTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<"DataTopicTestsMain: publish ServiceInformation data";
|
||||
|
||||
// Topic ServiceInformation
|
||||
m_sentServiceInformationData.id(13);
|
||||
m_sentServiceInformationData.name("UnitTest dummy Service");
|
||||
m_sentServiceInformationData.state(Core::RunningState::inner_enum::NotYetStarted);
|
||||
m_sentServiceInformationData.timestamp(QDateTime::currentMSecsSinceEpoch());
|
||||
m_sentServiceInformationData.errorString(QStringLiteral("UnitTest dummy Error").toStdString());
|
||||
|
||||
QVERIFY2(m_dtTest->write(m_sentServiceInformationData), "Publishing Topic ServiceInformation failed");
|
||||
}
|
||||
|
||||
void DataTopicTestsMain::subscribeServiceInformationTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN subscribeServiceInformationTestCase";
|
||||
|
||||
QTRY_COMPARE(m_serviceInformationDataArrivedSpy->count(), 1);
|
||||
|
||||
ServiceManagement::ServiceInformation recvServiceInformation = m_dtTest->getReceivedServiceInformationData();
|
||||
|
||||
QCOMPARE(recvServiceInformation.id(), m_sentServiceInformationData.id());
|
||||
QCOMPARE(recvServiceInformation.name(), m_sentServiceInformationData.name());
|
||||
QCOMPARE(recvServiceInformation.state(), m_sentServiceInformationData.state());
|
||||
QCOMPARE(recvServiceInformation.timestamp(), m_sentServiceInformationData.timestamp());
|
||||
QCOMPARE(recvServiceInformation.errorString(), m_sentServiceInformationData.errorString());
|
||||
}
|
||||
|
||||
void DataTopicTestsMain::publishGroupInformationTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "DataTopicTestsMain: publish GroupInformation data";
|
||||
|
||||
std::vector<int> services = {1, 3, 5, 7, 11, 13, 17, 19, 23, 42};
|
||||
|
||||
// Topic GroupInformation
|
||||
m_sentGroupInformationData.type(Core::ServiceGroupType::inner_enum::Master);
|
||||
m_sentGroupInformationData.exercisePartitionId(113);
|
||||
m_sentGroupInformationData.domainId(42);
|
||||
m_sentGroupInformationData.services(services);
|
||||
m_sentGroupInformationData.timestamp(QDateTime::currentMSecsSinceEpoch());
|
||||
|
||||
QVERIFY2(m_dtTest->write(m_sentGroupInformationData), "Publishing Topic GroupInformation failed");
|
||||
}
|
||||
|
||||
void DataTopicTestsMain::subscribeGroupInformationTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN subscribeGroupInformationTestCase";
|
||||
|
||||
QTRY_COMPARE(m_groupInformationDataArrivedSpy->count(), 1);
|
||||
|
||||
ServiceManagement::GroupInformation recvGroupInformation = m_dtTest->getReceivedGroupInformationData();
|
||||
|
||||
QCOMPARE(recvGroupInformation.type(), m_sentGroupInformationData.type());
|
||||
QCOMPARE(recvGroupInformation.exercisePartitionId(), m_sentGroupInformationData.exercisePartitionId());
|
||||
QCOMPARE(recvGroupInformation.domainId(), m_sentGroupInformationData.domainId());
|
||||
QCOMPARE(recvGroupInformation.services(), m_sentGroupInformationData.services());
|
||||
QCOMPARE(recvGroupInformation.timestamp(), m_sentGroupInformationData.timestamp());
|
||||
}
|
||||
|
||||
void DataTopicTestsMain::cleanupTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<"DataTopicTestsMain: cleanupTestCase";
|
||||
delete m_dtTest; m_dtTest = nullptr;
|
||||
delete m_serviceInformationDataArrivedSpy; m_serviceInformationDataArrivedSpy = nullptr;
|
||||
delete m_groupInformationDataArrivedSpy; m_groupInformationDataArrivedSpy = nullptr;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* UnitTests for ExerciseManagement (EXM)
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hanna E. Rathje, SLSE, 2015-08-20
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QSignalSpy>
|
||||
#include <ServiceManagement/scm_dts_types.h>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
class DataTopicTests;
|
||||
|
||||
class DataTopicTestsMain : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataTopicTestsMain();
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void publishServiceInformationTestCase();
|
||||
void subscribeServiceInformationTestCase();
|
||||
void publishGroupInformationTestCase();
|
||||
void subscribeGroupInformationTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
ServiceManagement::ServiceInformation m_sentServiceInformationData;
|
||||
ServiceManagement::GroupInformation m_sentGroupInformationData;
|
||||
QSignalSpy *m_serviceInformationDataArrivedSpy = nullptr;
|
||||
QSignalSpy *m_groupInformationDataArrivedSpy = nullptr;
|
||||
DataTopicTests *m_dtTest = nullptr;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
log4j.reset=true
|
||||
log4j.rootLogger=WARN, file
|
||||
|
||||
# Service: ServiceManagement
|
||||
###########################################################
|
||||
|
||||
log4j.logger.UnitTests.main=WARN
|
||||
|
||||
# GenericDataTests
|
||||
log4j.logger.UnitTests.Initialisation=WARN
|
||||
log4j.logger.UnitTests.GenericDataTests=WARN
|
||||
|
||||
# DataTopicTests
|
||||
log4j.logger.UnitTests.DataTopicTestsMain=WARN
|
||||
log4j.logger.UnitTests.DataTopicTests=WARN
|
||||
|
||||
# FunctionTopicTests
|
||||
log4j.logger.UnitTests.FtTest=INFO
|
||||
|
||||
# Generic
|
||||
log4j.logger.GenericBase=WARN
|
||||
log4j.logger.Generic.DataWriter=WARN
|
||||
log4j.logger.Generic.DataReader=WARN
|
||||
log4j.logger.Generic.DroTypeInfo=WARN
|
||||
|
||||
# DTCs
|
||||
log4j.logger.Generic.DTC=WARN
|
||||
log4j.logger.DTC.SubscriberListener=WARN
|
||||
log4j.logger.DTC.Topic=WARN
|
||||
log4j.logger.DTC=WARN
|
||||
|
||||
# FTSs / FTCs
|
||||
log4j.logger.FTS=WARN
|
||||
log4j.logger.FTC=INFO
|
||||
log4j.logger.Generic.FTS=WARN
|
||||
log4j.logger.Generic.FTC=INFO
|
||||
|
||||
# connext (request/reply feature)
|
||||
log4j.logger.connext=WARN
|
||||
|
||||
|
||||
log4j.appender.file=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.file.MaxFileSize=1MB
|
||||
log4j.appender.file.MaxBackupIndex=9
|
||||
log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_dds_unittests.log
|
||||
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:
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The ClientMessage class is an abstract base class for concrete ClientMessage objects
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-12-05
|
||||
*/
|
||||
#include "client_message.h"
|
||||
|
||||
#include "WebSocketInterface/Messages/reply_message.h"
|
||||
#include "WebSocketInterface/json_keys.h"
|
||||
#include "WebSocketInterface/data_manager.h"
|
||||
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
namespace WebSocketInterface
|
||||
{
|
||||
namespace Messages
|
||||
{
|
||||
using namespace JsonKeys;
|
||||
|
||||
// ####### ClientMessageFactory begin #######
|
||||
ClientMessageFactory::ClientMessageFactory(DataManager *dataManager)
|
||||
: LtsWebSocket::AbstractClientMessageFactory(getLogger("ClientMessageFactory"))
|
||||
, m_dataManager(dataManager)
|
||||
{}
|
||||
|
||||
LtsWebSocket::AbstractClientMessage *ClientMessageFactory::createMessage(LtsWebSocket::ReplyHandler *client,
|
||||
const QString &message)
|
||||
{
|
||||
LtsWebSocket::AbstractClientMessage *result = checkAttributes(client, message);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (m_type == SERVICE_PORTS_INFORMATION_REQUEST())
|
||||
result = new ServicePortsInformationRequest(m_dataManager, client, m_type, m_typeId, m_uuid);
|
||||
|
||||
return checkResult(client, message, result);
|
||||
}
|
||||
// ####### ClientMessageFactory end #######
|
||||
|
||||
// ####### ClientMessage begin #######
|
||||
ClientMessage::ClientMessage(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data)
|
||||
: LtsWebSocket::AbstractClientMessage(client, type, typeId, uuid, data)
|
||||
, m_dataManager(dataManager)
|
||||
{}
|
||||
// ####### ClientMessage end #######
|
||||
|
||||
// ####### ServicesInformation begin #######
|
||||
ServicePortsInformationRequest::ServicePortsInformationRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid)
|
||||
: ClientMessage (dataManager,client, type, typeId, uuid)
|
||||
{}
|
||||
|
||||
void ServicePortsInformationRequest::process()
|
||||
{
|
||||
m_client->addReply(m_dataManager->servicesInformation(m_uuid));
|
||||
}
|
||||
|
||||
bool ServicePortsInformationRequest::hasValidData() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// ####### ServicesInformation end #######
|
||||
|
||||
} // namespace Messages
|
||||
} // namespace WebSocketInterface
|
||||
} // namespace ServiceManagement
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The ClientMessage class is an abstract base class for concrete ClientMessage objects
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-12-05
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
|
||||
#include <LtsWebSocket/client_message_factory.h>
|
||||
#include <LtsWebSocket/client_message.h>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
namespace WebSocketInterface
|
||||
{
|
||||
class DataManager;
|
||||
|
||||
namespace Messages
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The ClientMessageFactory class creates ClientMessages which will then be processed
|
||||
*/
|
||||
class ClientMessageFactory final : public LtsWebSocket::AbstractClientMessageFactory
|
||||
{
|
||||
public:
|
||||
explicit ClientMessageFactory(DataManager *dataManager);
|
||||
|
||||
/**
|
||||
* @brief create Only way to create a ClientMessage object
|
||||
* @param message Text message which will be converted to a ClientMessage object
|
||||
* @return A ClientMessage object, which represents a concrete ClientMessage
|
||||
*/
|
||||
LtsWebSocket::AbstractClientMessage *createMessage(LtsWebSocket::ReplyHandler *client,
|
||||
const QString &message);
|
||||
|
||||
private:
|
||||
DataManager *m_dataManager;
|
||||
|
||||
}; // class ClientMessageFactory
|
||||
|
||||
/**
|
||||
* @brief The ClientMessage class is an abstract base class for concrete ClientMessage objects
|
||||
*/
|
||||
class ClientMessage : public LtsWebSocket::AbstractClientMessage
|
||||
{
|
||||
protected: // method
|
||||
explicit ClientMessage(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data = QJsonValue());
|
||||
|
||||
protected: // attributes
|
||||
DataManager *m_dataManager;
|
||||
}; // class ClientMessage
|
||||
|
||||
// ################## Message Implementations ##################
|
||||
class ServicePortsInformationRequest : public ClientMessage
|
||||
{
|
||||
public:
|
||||
explicit ServicePortsInformationRequest (DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid);
|
||||
|
||||
void process();
|
||||
bool hasValidData() const;
|
||||
|
||||
}; // class ServicesInformation
|
||||
|
||||
} // namespace Messages
|
||||
} // namespace WebSocketInterface
|
||||
} // namespace ServiceManagement
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The ReplyMessage class is an abstract base class for all possible messages which are sent to the client
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-12-05
|
||||
*/
|
||||
#include "reply_message.h"
|
||||
|
||||
#include <LtsWebSocket/json_keys.h>
|
||||
#include "WebSocketInterface/json_keys.h"
|
||||
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtCore/QJsonDocument>
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
namespace WebSocketInterface
|
||||
{
|
||||
namespace Messages
|
||||
{
|
||||
using namespace JsonKeys;
|
||||
using namespace LtsWebSocket::JsonKeys;
|
||||
|
||||
// ####### ServicePortsInformationMessage begin #######
|
||||
/********************************************************
|
||||
* Warning: 6002 has been negotiated with ESG for ADLER *
|
||||
********************************************************/
|
||||
ServicePortsInformationMessage::ServicePortsInformationMessage(const QVector<ExerciseInformation> &data, const QString &uuid)
|
||||
: ReplyMessage (SERVICE_PORTS_INFORMATION(), 6002, uuid) // Todo check whether TypeID is correct.
|
||||
{
|
||||
QJsonArray exercises;
|
||||
for (const auto &exercise : data)
|
||||
{
|
||||
QJsonArray services;
|
||||
for (const auto &service : exercise.services)
|
||||
{
|
||||
QJsonArray ports;
|
||||
|
||||
for (const auto &port : service.ports)
|
||||
{
|
||||
ports.append(QJsonObject({{NUMBER(),port.number},
|
||||
{TYPE_ID(),port.type},
|
||||
{PROPERTIES(), port.properties}
|
||||
}));
|
||||
}
|
||||
|
||||
services.append(QJsonObject({{NAME(), service.name},
|
||||
{NAME_ABBR(), service.abbreviation},
|
||||
{PORTS(), ports}
|
||||
}));
|
||||
}
|
||||
QJsonObject obj({{EXERCISE_ID(), exercise.exerciseId},
|
||||
{SERVICES(), services}
|
||||
});
|
||||
if (exercise.replayPartitionId.is_set())
|
||||
obj.insert(REPLAY_PARTITION_ID(), exercise.replayPartitionId.get());
|
||||
|
||||
exercises.append(obj);
|
||||
}
|
||||
|
||||
m_jsonRepresentation.insert(DATA(), exercises);
|
||||
}
|
||||
// ####### ServicePortsInformationMessage end #######
|
||||
|
||||
} // namespace Messages
|
||||
} // namespace WebSocketInterface
|
||||
} // namespace ServiceManagement
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The ReplyMessage class is an abstract base class for all possible messages which are sent to the client
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-12-05
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <LtsWebSocket/client_message_factory.h>
|
||||
|
||||
#include <DDS/Core/core.hpp>
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonArray>
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
namespace WebSocketInterface
|
||||
{
|
||||
namespace Messages
|
||||
{
|
||||
/**
|
||||
* @brief The ServicePortsInformationMessage class represents the service informations collected by the svcServiceManagement
|
||||
*/
|
||||
class ServicePortsInformationMessage final : public LtsWebSocket::ReplyMessage
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief The PortInformation struct holds the information what kind of port we have here
|
||||
*/
|
||||
struct PortInformation
|
||||
{
|
||||
int32_t number;
|
||||
int32_t type;
|
||||
QString properties;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The ServiceInformation struct information about a service
|
||||
*/
|
||||
struct ServiceInformation
|
||||
{
|
||||
QString name;
|
||||
QString abbreviation;
|
||||
QVector<PortInformation> ports;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The ExerciseInformation struct with information about an exercise (could also be a replay)
|
||||
*/
|
||||
struct ExerciseInformation
|
||||
{
|
||||
int32_t exerciseId;
|
||||
dds::core::optional<int32_t> replayPartitionId;
|
||||
QVector<ServiceInformation> services;
|
||||
};
|
||||
|
||||
explicit ServicePortsInformationMessage (const QVector<ExerciseInformation> &data, const QString &uuid);
|
||||
}; // class ServicePortsInformationMessage
|
||||
|
||||
} // namespace Messages
|
||||
} // namespace WebSocketInterface
|
||||
} // namespace ServiceManagement
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The DataManager class takes strucure element data from the HandlerStructureElement,
|
||||
* offers an interface to get and set structure data
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-12-05
|
||||
*/
|
||||
#include "WebSocketInterface/data_manager.h"
|
||||
|
||||
#include "main_handler.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QMap>
|
||||
#include <QVector>
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
namespace WebSocketInterface
|
||||
{
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
DataManager::DataManager(MainHandler *mainHandler)
|
||||
: m_mainHandler(mainHandler)
|
||||
, m_logger(getLogger(QStringLiteral("DataManager")))
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
bool DataManager::init()
|
||||
{
|
||||
m_groupHandler = m_mainHandler->groupHandler();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
LtsWebSocket::ReplyMessage *DataManager::servicesInformation(const QString &uuid) const
|
||||
{
|
||||
using namespace Messages;
|
||||
|
||||
const QVector<Group *> groups = m_groupHandler->groups();
|
||||
QVector<ServicePortsInformationMessage::ExerciseInformation> data;
|
||||
data.reserve(groups.size());
|
||||
for (auto group : groups)
|
||||
{
|
||||
const QMap<int, ServiceProcess *> serviceProcs = group->serviceProcs();
|
||||
ServicePortsInformationMessage::ExerciseInformation exerciseInfo;
|
||||
|
||||
for (ServiceProcess *service : serviceProcs)
|
||||
{
|
||||
if (service->ports().is_set())
|
||||
{
|
||||
ServicePortsInformationMessage::ServiceInformation serviceInfo;
|
||||
serviceInfo.name = service->serviceName();
|
||||
serviceInfo.abbreviation = service->abbreviation();
|
||||
|
||||
const auto ports = service->ports().get();
|
||||
for (const auto &port : ports)
|
||||
serviceInfo.ports.append(ServicePortsInformationMessage::PortInformation({port.number(), port.type().underlying(), QString::fromStdString(port.properties())}));
|
||||
|
||||
exerciseInfo.services.append(serviceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (!exerciseInfo.services.isEmpty())
|
||||
{
|
||||
exerciseInfo.exerciseId = group->exercisePartitionId();
|
||||
exerciseInfo.replayPartitionId = group->replayPartitionId();
|
||||
data.append(exerciseInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return new ServicePortsInformationMessage(data, uuid);
|
||||
}
|
||||
|
||||
} // namespace WebSocketInterface
|
||||
} // namespace ServiceManagement
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The DataManager class is a convenience class for managing structure data
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-12-05
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "WebSocketInterface/Messages/reply_message.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class MainHandler;
|
||||
class GroupHandler;
|
||||
|
||||
namespace WebSocketInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The DataManager class is a convenience class for managing structure data
|
||||
*/
|
||||
class DataManager final
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief DataManager Constructor
|
||||
* @param mainHandler Source for needed handlers
|
||||
*/
|
||||
explicit DataManager(MainHandler *mainHandler);
|
||||
|
||||
/**
|
||||
* @brief init Initialises a DataManager object
|
||||
* @return True on sucess, otherwise false
|
||||
*/
|
||||
bool init();
|
||||
|
||||
LtsWebSocket::ReplyMessage *servicesInformation(const QString &uuid) const;
|
||||
|
||||
private: // attributes
|
||||
MainHandler *m_mainHandler;
|
||||
std::shared_ptr <GroupHandler> m_groupHandler;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
}; // class DataManager
|
||||
|
||||
} // namespace WebSocketInterface
|
||||
} // namespace ServiceManagement
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Header file which holds all occuring JSON keys
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-12-05
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
namespace WebSocketInterface
|
||||
{
|
||||
/**
|
||||
* @brief Namespace which holds all occuring JSON keys as const QString objects
|
||||
*/
|
||||
namespace JsonKeys
|
||||
{
|
||||
// types for client --> server messages
|
||||
inline QString SERVICE_PORTS_INFORMATION_REQUEST() { return QStringLiteral("servicePortsInformationRequest"); }
|
||||
|
||||
// types for server --> client messages
|
||||
inline QString SERVICE_PORTS_INFORMATION() { return QStringLiteral("servicePortsInformation"); }
|
||||
|
||||
// data keys
|
||||
inline QString EXERCISE_ID() { return QStringLiteral("exerciseId"); }
|
||||
inline QString NAME() { return QStringLiteral("name"); }
|
||||
inline QString NAME_ABBR() { return QStringLiteral("nameAbbrevation"); }
|
||||
inline QString NUMBER() { return QStringLiteral("number"); }
|
||||
inline QString PORTS() { return QStringLiteral("ports"); }
|
||||
inline QString REPLAY_PARTITION_ID() { return QStringLiteral("replayPartitionId"); }
|
||||
inline QString SERVICES() { return QStringLiteral("services"); }
|
||||
inline QString TYPE_ID() { return QStringLiteral("typeId"); }
|
||||
inline QString PROPERTIES() { return QStringLiteral("properties"); }
|
||||
|
||||
} // namespace JsonKeys
|
||||
} // namespace WebSocketInterface
|
||||
} // namespace ServiceManagement
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Handler for the WebSocket Interface
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-12-05
|
||||
*/
|
||||
#include "websocket_interface_handler.h"
|
||||
|
||||
#include "main_handler.h"
|
||||
#include "data_manager.h"
|
||||
#include "WebSocketInterface/Messages/client_message.h"
|
||||
#include <nglsettings/nglsettings_connections.h>
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
namespace WebSocketInterface
|
||||
{
|
||||
|
||||
WebSocketInterfaceHandler::WebSocketInterfaceHandler(MainHandler *mainHandler, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_webSocketServer(nullptr)
|
||||
, m_messageFactory(nullptr)
|
||||
, m_dataManager(nullptr)
|
||||
, m_mainHandler(mainHandler)
|
||||
, m_logger(getLogger("WebSocketInterfaceHandler"))
|
||||
{
|
||||
}
|
||||
|
||||
WebSocketInterfaceHandler::~WebSocketInterfaceHandler()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
bool WebSocketInterfaceHandler::init()
|
||||
{
|
||||
cleanup();
|
||||
|
||||
DEBUG_LOG(m_logger) << "... ok";
|
||||
|
||||
DEBUG_LOG(m_logger) << "Initialising DataManager";
|
||||
m_dataManager = new DataManager(m_mainHandler);
|
||||
if (!m_dataManager->init())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Failed to initialise DataManager";
|
||||
return false;
|
||||
}
|
||||
DEBUG_LOG(m_logger) << "... ok";
|
||||
|
||||
|
||||
m_messageFactory = new Messages::ClientMessageFactory(m_dataManager);
|
||||
|
||||
m_webSocketServer = new LtsWebSocket::LtsWebSocketServer(m_messageFactory, this);
|
||||
|
||||
DEBUG_LOG(m_logger) << "Initialising LtsWebSocketServer";
|
||||
if (!m_webSocketServer->init(QStringLiteral("SCM"), NGL::Connections::port(NGL::Connections::eConnection::SCM_WEBSOCKET)))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Failed to initialise LtsWebSocketServer";
|
||||
return false;
|
||||
}
|
||||
DEBUG_LOG(m_logger) << "... ok";
|
||||
|
||||
DEBUG_LOG(m_logger) << "Connection signals and slots";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebSocketInterfaceHandler::cleanup()
|
||||
{
|
||||
delete m_webSocketServer;
|
||||
delete m_messageFactory;
|
||||
delete m_dataManager;
|
||||
}
|
||||
|
||||
} // namespace WebSocketInterface
|
||||
} // namespace ServiceManagement
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Handler for the WebSocket Interface
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-12-05
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <LtsWebSocket/lts_web_socket_server.h>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class MainHandler;
|
||||
namespace WebSocketInterface
|
||||
{
|
||||
namespace Messages
|
||||
{
|
||||
class ClientMessageFactory;
|
||||
}
|
||||
class DataManager;
|
||||
|
||||
/**
|
||||
* @brief The WebSocketInterfaceHandler class is the main class for the WebSocketInterface
|
||||
*/
|
||||
class WebSocketInterfaceHandler final : public QObject
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief WebSocketInterfaceHandler Constructor
|
||||
* @param mainHandler MainHandler to get the HandlerStructureElement from
|
||||
*/
|
||||
explicit WebSocketInterfaceHandler(MainHandler *mainHandler, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief ~WebSocketInterfaceHandler Destructor
|
||||
*/
|
||||
~WebSocketInterfaceHandler();
|
||||
|
||||
/**
|
||||
* @brief init Initialises all needed classes for the WebSocketInterface
|
||||
* @return True on success, otherwise false
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief webSocketPort returns the listen port
|
||||
* @return listen port
|
||||
*/
|
||||
unsigned short webSocketPort() { return m_webSocketServer->webSocketPort(); }
|
||||
|
||||
private:
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
LtsWebSocket::LtsWebSocketServer *m_webSocketServer;
|
||||
Messages::ClientMessageFactory *m_messageFactory;
|
||||
DataManager *m_dataManager;
|
||||
MainHandler *m_mainHandler;
|
||||
LOGGER_T m_logger;
|
||||
}; // class WebSocketInterfaceHandle
|
||||
|
||||
} // namespace WebSocketInterface
|
||||
} // namespace ServiceManagement
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
#!/bin/sh
|
||||
## --------------------------------------------------------------------
|
||||
## $HeadURL$
|
||||
## --------------------------------------------------------------------
|
||||
##!
|
||||
## --------------------------------------------------------------------
|
||||
## COPYRIGHT (C) 1999: STN ATLAS ELEKTRONIK GMBH, D-28305 BREMEN
|
||||
## --------------------------------------------------------------------
|
||||
##
|
||||
## HIER/ITEM : CSU: RP 5182 Q 100 SRC 00102
|
||||
## BRIEF DESC: Prozesse des eingeloggten Benutzers darstellen
|
||||
## AUTHOR : Norbert Thoden
|
||||
## RELEASE :
|
||||
## MODIF/STAT: 12.05.00
|
||||
## ORIGIN REF:
|
||||
##
|
||||
## Revision INFO:
|
||||
## --------------
|
||||
## $Revision$
|
||||
## $Date$
|
||||
## $Id$
|
||||
##
|
||||
## LANGUAGE: (TCL/TK-Version)
|
||||
##
|
||||
## PURPOSE:
|
||||
## Prozesse des eingeloggten Benutzers darstellen
|
||||
##
|
||||
## ERROR HANDLING:
|
||||
## keine
|
||||
##
|
||||
##!
|
||||
|
||||
|
||||
|
||||
PROZESS=p
|
||||
NETSTAT=n
|
||||
|
||||
|
||||
prozesse()
|
||||
{
|
||||
echo -e "\nProcs for $1:"
|
||||
# Alle Prozesse des eingeloggten Benutzers werden darstellt
|
||||
echo -e "\nServices:"
|
||||
ps -u $1 -o suser,pid,pcpu,pmem,start_time,command --sort command,pid | grep -E "svc|SUSER|PrtCnv|BuildingControlUnit" | grep -v grep
|
||||
}
|
||||
|
||||
netstat_proc()
|
||||
{
|
||||
echo "NETZWERK-VERBINDUNGEN:"
|
||||
|
||||
OFFSET=`echo $PORT_OFFSET | cut -c1-2`
|
||||
|
||||
netstat -an | grep $OFFSET
|
||||
|
||||
}
|
||||
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "usage: check_start [p|n] (p=Prozesse - n=netstat)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
prozesse $USER
|
||||
netstat_proc
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
|
||||
# Wenn Parameter vorhanden ist:
|
||||
if [ $# -eq 1 ]; then
|
||||
|
||||
if [ $1 == $PROZESS ]; then
|
||||
prozesse $USER
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $1 == $NETSTAT ]; then
|
||||
netstat_proc
|
||||
exit 0
|
||||
else
|
||||
usage
|
||||
fi
|
||||
|
||||
|
||||
else
|
||||
|
||||
usage
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
||||
exit 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
##------------------------------------------------------------------
|
||||
## END $HeadURL$
|
||||
##------------------------------------------------------------------
|
||||
## *** PCO - DOMAIN ***
|
||||
##
|
||||
## n/ H. Rathje:SGE2, 4417 / 12.05.00
|
||||
## zz / no.
|
||||
##
|
||||
##
|
||||
## E:
|
||||
##
|
||||
## $LOG$
|
||||
##
|
||||
## B:
|
||||
##
|
||||
## D:
|
||||
##
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* DdsHandler for ServiceManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#include "dds_handler.h"
|
||||
#include "scm_dts.h"
|
||||
#include "scm_fts.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
|
||||
DdsHandler::DdsHandler(int domainId)
|
||||
: m_ddsConnector(std::make_shared<DDSConnector::DdsConnector>(domainId, IDL::PartitionType::Any))
|
||||
, m_logger(getLogger("DdsHandler"))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "DdsHandler(" << domainId << ") for partition: " << m_ddsConnector->getDefaultPartition();
|
||||
}
|
||||
|
||||
bool DdsHandler::init(const QStringList &qosFileList)
|
||||
{
|
||||
if (!m_ddsConnector->init(qosFileList))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error initializing dds connections - aborting";
|
||||
return false;
|
||||
}
|
||||
|
||||
// default initialize Publisher and Subscriber
|
||||
m_ddsConnector->initPublisherSubscriber();
|
||||
m_ddsConnector->enableBatchedWriter(false);
|
||||
|
||||
m_ddsDts = ServiceManagement::Interface::Dts::create(m_ddsConnector);
|
||||
bool bRet = m_ddsDts->enableAll();
|
||||
INFO_LOG(m_logger) << "initialized Dts";
|
||||
|
||||
m_ddsFts = ServiceManagement::Interface::Fts::create(m_ddsConnector);
|
||||
bRet &= m_ddsFts->enableAll();
|
||||
INFO_LOG(m_logger) << "initialized Fts";
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
std::shared_ptr<ServiceManagement::Interface::Dts> ServiceManagement::DdsHandler::dts() const
|
||||
{
|
||||
return m_ddsDts;
|
||||
}
|
||||
|
||||
std::shared_ptr<ServiceManagement::Interface::Fts> ServiceManagement::DdsHandler::fts() const
|
||||
{
|
||||
return m_ddsFts;
|
||||
}
|
||||
|
||||
std::shared_ptr<DDSConnector::DdsConnector> DdsHandler::connector() const
|
||||
{
|
||||
return m_ddsConnector;
|
||||
}
|
||||
|
||||
} // namespace ServiceManagement
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* DdsHandler for ServiceManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <DDS/DDSConnector/dds_connector.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "scm_dts.h"
|
||||
#include "scm_fts.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class DdsHandler final
|
||||
{
|
||||
public:
|
||||
explicit DdsHandler(int domainId);
|
||||
~DdsHandler() = default;
|
||||
|
||||
bool init(const QStringList &qosFileList = QStringList());
|
||||
|
||||
std::shared_ptr<ServiceManagement::Interface::Dts> dts() const;
|
||||
std::shared_ptr<ServiceManagement::Interface::Fts> fts() const;
|
||||
std::shared_ptr<DDSConnector::DdsConnector> connector() const;
|
||||
private:
|
||||
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
|
||||
std::shared_ptr<ServiceManagement::Interface::Dts> m_ddsDts;
|
||||
std::shared_ptr<ServiceManagement::Interface::Fts> m_ddsFts;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
} // namespace ServiceManagement
|
||||
Executable
+377
@@ -0,0 +1,377 @@
|
||||
/**
|
||||
* The Group class represents a group which was already startet.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#include "group.h"
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtAlgorithms>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "group_handler.h"
|
||||
#include "service_handler.h"
|
||||
#include "service_process.h"
|
||||
#include "service_donator.h"
|
||||
|
||||
#include <ServiceManagement/scm.hpp>
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
|
||||
int32_t Group::s_idCounter = 0;
|
||||
|
||||
Group::Group(const std::shared_ptr<DdsHandler> &ddsHandler,
|
||||
const std::shared_ptr<ServiceDonator> &serviceDonator,
|
||||
const GroupRequest &groupData,
|
||||
GroupScheme *scheme,
|
||||
QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_ddsHandler(ddsHandler)
|
||||
, m_serviceDonator(serviceDonator)
|
||||
, m_scheme(scheme)
|
||||
, m_groupId(s_idCounter++)
|
||||
, m_groupData(groupData)
|
||||
, m_completeCoreTimeout(0)
|
||||
, m_logger(getLogger("Group"))
|
||||
{
|
||||
INFO_LOG(m_logger) << "Group "
|
||||
<< m_scheme->groupType
|
||||
<< " for partition "
|
||||
<< groupData
|
||||
<< " created.";
|
||||
m_elapsedTimer.start();
|
||||
}
|
||||
|
||||
Group::~Group()
|
||||
{
|
||||
setRunningState(Core::RunningState::StoppedNormally);
|
||||
for (ServiceProcess *serviceProc :qAsConst(m_serviceProcs))
|
||||
m_ddsHandler->dts()->removeServiceInformationByKey(serviceProc->serviceProcessId());
|
||||
qDeleteAll(m_serviceProcs); // in Service they are removed via ServiceProcess::destroyed signal
|
||||
m_ddsHandler->dts()->removeGroupInformationByKey(m_groupId);
|
||||
}
|
||||
|
||||
void Group::init()
|
||||
{
|
||||
for (int serviceId : qAsConst(m_scheme->services))
|
||||
{
|
||||
if (m_serviceProcs.contains(serviceId))
|
||||
continue;
|
||||
|
||||
ServiceProcess *proc = m_serviceDonator->getProcess(serviceId, m_groupData);
|
||||
if (proc != nullptr)
|
||||
{
|
||||
connect(proc, &ServiceProcess::newServiceInformation,
|
||||
this, &Group::onNewServiceInfo);
|
||||
m_serviceProcs.insert(serviceId, proc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Group::setRunningState(Core::RunningState newState)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Setting RunningState of group "
|
||||
<< m_scheme->groupType
|
||||
<< " in exercisePartition "
|
||||
<< m_groupData.exercisePartitionId()
|
||||
<< ", replayPartition "
|
||||
<< m_groupData.replayPartitionId()
|
||||
<< " to "
|
||||
<< newState;
|
||||
|
||||
switch (newState.underlying())
|
||||
{
|
||||
case Core::RunningState::Running:
|
||||
case Core::RunningState::Starting:
|
||||
// make sure all services are there
|
||||
init();
|
||||
for (ServiceProcess *serviceProc : qAsConst(m_serviceProcs))
|
||||
serviceProc->start();
|
||||
return true;
|
||||
case Core::RunningState::StoppedNormally:
|
||||
for (ServiceProcess *serviceProc : qAsConst(m_serviceProcs))
|
||||
serviceProc->terminate();
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Group::startCore()
|
||||
{
|
||||
if (m_scheme->groupType != Core::ServiceGroupType::Core)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "startCore() was invoked on a non Core Group object";
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(m_logger) << "Starting Core";
|
||||
|
||||
// make sure all services are there
|
||||
init();
|
||||
|
||||
// ExerciseManagement must be started ...
|
||||
for (ServiceProcess *proc : qAsConst(m_serviceProcs))
|
||||
{
|
||||
if (proc->serviceName() == QLatin1String("ExerciseManagement"))
|
||||
{
|
||||
if ( proc->prio() == m_actPrio)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "startCore proc->start() "
|
||||
<< proc->serviceName()
|
||||
<< " prio=" << proc->prio()
|
||||
<< " m_actPrio=" <<m_actPrio;
|
||||
|
||||
proc->start();
|
||||
}
|
||||
// waiting 15 secs for EXM startup
|
||||
m_completeCoreTimeout = QDateTime::currentMSecsSinceEpoch() + 15000;
|
||||
|
||||
onCompleteCoreTick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Group::getNextPrio(QList <int> &prioList)
|
||||
{
|
||||
if ( m_actPrio == 0)
|
||||
return -1;
|
||||
std::sort(prioList.begin(), prioList.end());
|
||||
for ( int i = 0; i < prioList.size(); i++)
|
||||
{
|
||||
if ( m_actPrio < prioList.at(i))
|
||||
return prioList.at(i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Group::start()
|
||||
{
|
||||
QList <int> prioList;
|
||||
DEBUG_LOG(m_logger) << "Group::start() m_actPrio=" << m_actPrio
|
||||
<< " groupId=" << m_groupId;
|
||||
// make sure all services are there
|
||||
init();
|
||||
// get list of prio
|
||||
for (ServiceProcess *proc : qAsConst(m_serviceProcs))
|
||||
{
|
||||
if ( !prioList.contains(proc->prio()))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "prioList.append " << proc->prio()
|
||||
<< " m_actPrio=" << m_actPrio
|
||||
<< " groupId=" << m_groupId;
|
||||
prioList.append(proc->prio());
|
||||
}
|
||||
}
|
||||
|
||||
//if ( !prioList.contains(m_actPrio))
|
||||
//{
|
||||
// m_actPrio = getNextPrio(prioList);
|
||||
//}
|
||||
|
||||
for (ServiceProcess *proc : qAsConst(m_serviceProcs))
|
||||
{
|
||||
if ( m_actPrio == proc->prio())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Group::start(): proc->start() "
|
||||
<< proc->serviceName()
|
||||
<< " prio=" << proc->prio()
|
||||
<< " m_actPrio=" << m_actPrio
|
||||
<< " exId=" << proc->exercisePartitionId()
|
||||
<< " lastHeartbeat=" << proc->lastHeartbeatMSec();
|
||||
proc->start();
|
||||
}
|
||||
}
|
||||
if ( m_actPrio > 0)
|
||||
{
|
||||
m_actPrio= getNextPrio(prioList);
|
||||
QTimer::singleShot(1000, this, &Group::start);
|
||||
}
|
||||
}
|
||||
|
||||
void Group::stop()
|
||||
{
|
||||
for (ServiceProcess *proc : qAsConst(m_serviceProcs))
|
||||
{
|
||||
if (proc->state() != QProcess::NotRunning)
|
||||
proc->stopFromRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void Group::restart()
|
||||
{
|
||||
// make sure all services are there
|
||||
init();
|
||||
for (ServiceProcess *proc : qAsConst(m_serviceProcs))
|
||||
proc->restart();
|
||||
}
|
||||
|
||||
void Group::takeServicesFromGroup(Group *otherGroup)
|
||||
{
|
||||
auto otherServiceProcs = otherGroup->m_serviceProcs;
|
||||
for (auto it = otherServiceProcs.begin(); it != otherServiceProcs.end(); ++it)
|
||||
{
|
||||
int serviceId = it.key();
|
||||
// already there? -> skip
|
||||
if (m_serviceProcs.contains(serviceId))
|
||||
continue;
|
||||
// not in our scheme? -> skip
|
||||
if (!m_scheme->services.contains(serviceId))
|
||||
continue;
|
||||
ServiceProcess *proc = it.value();
|
||||
|
||||
// remove from otherGroup
|
||||
otherGroup->m_serviceProcs.remove(serviceId);
|
||||
proc->disconnect(otherGroup);
|
||||
|
||||
// add to ourself
|
||||
m_serviceProcs.insert(serviceId, proc);
|
||||
connect(proc, &ServiceProcess::newServiceInformation,
|
||||
this, &Group::onNewServiceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Core::ServiceGroupType Group::type() const
|
||||
{
|
||||
return m_scheme->groupType;
|
||||
}
|
||||
|
||||
qint64 Group::lastHeartbeatMSec() const
|
||||
{
|
||||
qint64 result = 0;
|
||||
|
||||
for (ServiceProcess *proc : m_serviceProcs)
|
||||
{
|
||||
if (proc->lastHeartbeatMSec() > result)
|
||||
result = proc->lastHeartbeatMSec();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QMap<int, ServiceProcess *> Group::serviceProcs() const
|
||||
{
|
||||
return m_serviceProcs;
|
||||
}
|
||||
|
||||
void Group::onNewServiceInfo()
|
||||
{
|
||||
if (m_elapsedTimer.elapsed() < 2 * 1000)
|
||||
return;
|
||||
m_elapsedTimer.start();
|
||||
std::vector<int32_t> serviceIds;
|
||||
serviceIds.reserve(m_serviceProcs.size());
|
||||
for (ServiceProcess *proc : qAsConst(m_serviceProcs))
|
||||
serviceIds.push_back(proc->serviceProcessId());
|
||||
|
||||
GroupInformation info(m_groupId,
|
||||
lastHeartbeatMSec(),
|
||||
m_scheme->groupType,
|
||||
m_groupData.domainId(),
|
||||
m_groupData.exercisePartitionId(),
|
||||
m_groupData.replayPartitionId(),
|
||||
serviceIds);
|
||||
|
||||
TRACE_LOG(m_logger) << "Publishing new GroupInformation: " << info;
|
||||
|
||||
m_ddsHandler->dts()->publish(info);
|
||||
}
|
||||
|
||||
void Group::onCompleteCoreTick()
|
||||
{
|
||||
QList <int> prioList;
|
||||
if (QDateTime::currentMSecsSinceEpoch() > m_completeCoreTimeout)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not start group Core, because no Heartbeat for EXM with RunningState 'Running' was received.";
|
||||
return;
|
||||
}
|
||||
|
||||
ServiceProcess *exmProc = nullptr;
|
||||
|
||||
// get EXM proc
|
||||
for (ServiceProcess *proc : qAsConst(m_serviceProcs))
|
||||
{
|
||||
if (proc->serviceName() == QLatin1String("ExerciseManagement"))
|
||||
{
|
||||
exmProc = proc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exmProc == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not find Process for EXM";
|
||||
return;
|
||||
}
|
||||
|
||||
// check if EXM got RunningState from Heartbeat
|
||||
if (exmProc->runningState() == Core::RunningState::Running)
|
||||
{
|
||||
// get list of prio
|
||||
for (ServiceProcess *proc : qAsConst(m_serviceProcs))
|
||||
{
|
||||
if ( !prioList.contains(proc->prio()))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "prioList.append " << proc->prio()
|
||||
<< " m_actPrio=" << m_actPrio
|
||||
<< " groupId=" << m_groupId;
|
||||
prioList.append(proc->prio());
|
||||
}
|
||||
}
|
||||
|
||||
// start rest of group core
|
||||
for (ServiceProcess *proc : qAsConst(m_serviceProcs))
|
||||
{
|
||||
if (proc->state() != QProcess::Running && proc->prio() == m_actPrio)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "onCompleteCoreTick proc->start() "
|
||||
<< proc->serviceName()
|
||||
<< " prio=" << proc->prio();
|
||||
proc->start();
|
||||
}
|
||||
}
|
||||
m_actPrio = getNextPrio(prioList);
|
||||
if (-1 != m_actPrio)
|
||||
{
|
||||
QTimer::singleShot(1000, this, &Group::onCompleteCoreTick);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "EXM has runningState " << exmProc->runningState();
|
||||
// start next completion attemp
|
||||
QTimer::singleShot(1000, this, &Group::onCompleteCoreTick);
|
||||
}
|
||||
|
||||
ServiceProcess *Group::procFromPid(Q_PID pid)
|
||||
{
|
||||
for (ServiceProcess *proc :qAsConst(m_serviceProcs))
|
||||
{
|
||||
if (proc->pid() == pid)
|
||||
return proc;
|
||||
}
|
||||
DEBUG_LOG(m_logger) << "Searched for not existing process with PID "
|
||||
<< pid;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Group::allProcsStopped() const
|
||||
{
|
||||
for (ServiceProcess *proc : m_serviceProcs)
|
||||
{
|
||||
if (proc->state() != QProcess::NotRunning)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ServiceManagement
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* The Group class represents a group which was already startet.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QElapsedTimer>
|
||||
|
||||
#include <DDS/Core/core_enums.hpp>
|
||||
|
||||
#include "logger.h"
|
||||
#include "dds_handler.h"
|
||||
#include "service_process.h"
|
||||
|
||||
class QTimer;
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
struct GroupScheme;
|
||||
|
||||
class GroupInformation;
|
||||
class ServiceDonator;
|
||||
class ServiceInformation;
|
||||
|
||||
class Group final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Group(const std::shared_ptr<DdsHandler> &ddsHandler,
|
||||
const std::shared_ptr<ServiceDonator> &serviceDonator,
|
||||
const GroupRequest &groupData,
|
||||
GroupScheme *scheme,
|
||||
QObject *parent);
|
||||
~Group();
|
||||
|
||||
bool setRunningState(Core::RunningState newState);
|
||||
|
||||
void startCore();
|
||||
void start();
|
||||
void stop();
|
||||
void restart();
|
||||
|
||||
void takeServicesFromGroup(Group *otherGroup);
|
||||
|
||||
bool allProcsStopped() const;
|
||||
Core::ServiceGroupType type() const;
|
||||
int32_t domainId() const { return m_groupData.domainId(); }
|
||||
int32_t exercisePartitionId() const { return m_groupData.exercisePartitionId(); }
|
||||
int32_t replayPartitionId() const { return m_groupData.replayPartitionId(); }
|
||||
int32_t id() const { return m_groupId; }
|
||||
qint64 lastHeartbeatMSec() const;
|
||||
|
||||
QMap<int, ServiceProcess *> serviceProcs() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onNewServiceInfo();
|
||||
void onCompleteCoreTick();
|
||||
|
||||
private: // methods
|
||||
void init();
|
||||
ServiceProcess *procFromPid(Q_PID pid);
|
||||
|
||||
private: // attributes
|
||||
std::shared_ptr<DdsHandler> m_ddsHandler;
|
||||
std::shared_ptr<ServiceDonator> m_serviceDonator;
|
||||
GroupScheme *m_scheme;
|
||||
int32_t m_groupId;
|
||||
GroupRequest m_groupData;
|
||||
QMap<int, ServiceProcess *> m_serviceProcs;
|
||||
qint64 m_completeCoreTimeout;
|
||||
QElapsedTimer m_elapsedTimer;
|
||||
int m_actPrio = 1;
|
||||
LOGGER_T m_logger;
|
||||
|
||||
static int32_t s_idCounter;
|
||||
int getNextPrio(QList<int> &prioList);
|
||||
};
|
||||
} // namespace ServiceManagement
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* The GroupHandler class handles all group related classes and writes the groups state to DDS.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#include "group_handler.h"
|
||||
#include "group.h"
|
||||
#include "heartbeat_listener.h"
|
||||
#include "service_donator.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
|
||||
GroupHandler::GroupHandler(const std::shared_ptr<DdsHandler> &ddsHandler,
|
||||
const std::shared_ptr<ServiceDonator> &donator)
|
||||
: QObject()
|
||||
, Interface::Fts::StartGroupRequestHandler(ddsHandler->fts())
|
||||
, Interface::Fts::StopGroupRequestHandler(ddsHandler->fts())
|
||||
, Interface::Fts::RestartGroupRequestHandler(ddsHandler->fts())
|
||||
, m_serviceDonator(donator)
|
||||
, m_ddsHandler(ddsHandler)
|
||||
, m_heartbeatListener(new HeartbeatListener(ddsHandler->connector(), donator))
|
||||
, m_logger(getLogger("GroupHandler"))
|
||||
{
|
||||
}
|
||||
|
||||
GroupHandler::~GroupHandler()
|
||||
{
|
||||
qDeleteAll(m_groups);
|
||||
m_groups.clear();
|
||||
|
||||
qDeleteAll(m_groupSchemes);
|
||||
m_groupSchemes.clear();
|
||||
|
||||
delete m_heartbeatListener;
|
||||
}
|
||||
|
||||
bool GroupHandler::init(int32_t coreDomain)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Initializing GroupHandler ...";
|
||||
|
||||
bool bAllPartsInitialized = m_heartbeatListener->init() &&
|
||||
startGroupCore(coreDomain);
|
||||
|
||||
if (bAllPartsInitialized)
|
||||
DEBUG_LOG(m_logger) << "GroupHandler successfully initialized.";
|
||||
else
|
||||
ERROR_LOG(m_logger) << "GroupHandler initialization failed.";
|
||||
|
||||
return bAllPartsInitialized;
|
||||
}
|
||||
|
||||
void GroupHandler::addServiceLink(int serviceId, Core::ServiceGroupType groupType)
|
||||
{
|
||||
groupSchemeFromType(groupType)->addServiceId(serviceId);
|
||||
}
|
||||
|
||||
QVector<Group *> GroupHandler::groups() const
|
||||
{
|
||||
return m_groups;
|
||||
}
|
||||
|
||||
GroupScheme *GroupHandler::groupSchemeFromType(Core::ServiceGroupType type)
|
||||
{
|
||||
for (GroupScheme *scheme : qAsConst(m_groupSchemes))
|
||||
{
|
||||
if (scheme->groupType == type)
|
||||
return scheme;
|
||||
}
|
||||
|
||||
// TODO: make generic ...
|
||||
QMap<Core::ServiceGroupType, QString> groupNameMapping;
|
||||
groupNameMapping.insert(Core::ServiceGroupType_def::Master, QLatin1String("Master"));
|
||||
groupNameMapping.insert(Core::ServiceGroupType_def::Core, QLatin1String("Core"));
|
||||
groupNameMapping.insert(Core::ServiceGroupType_def::ExercisePreparation, QLatin1String("ExercisePreparation"));
|
||||
groupNameMapping.insert(Core::ServiceGroupType_def::ExerciseExecution, QLatin1String("ExerciseExecution"));
|
||||
groupNameMapping.insert(Core::ServiceGroupType_def::ExercisePaused, QLatin1String("ExercisePaused"));
|
||||
groupNameMapping.insert(Core::ServiceGroupType_def::ExerciseAfterActionReview, QLatin1String("ExerciseAfterActionReview"));
|
||||
groupNameMapping.insert(Core::ServiceGroupType_def::Playback, QLatin1String("Playback"));
|
||||
|
||||
// if no scheme with type type exists, it will be newly generated and added
|
||||
GroupScheme *newScheme = new GroupScheme(type, groupNameMapping.value(type));
|
||||
m_groupSchemes.append(newScheme);
|
||||
|
||||
return newScheme;
|
||||
}
|
||||
|
||||
Group *GroupHandler::getGroup(const GroupRequest &groupToFind) const
|
||||
{
|
||||
for (Group *group : m_groups)
|
||||
{
|
||||
if (group->type() == groupToFind.type() &&
|
||||
group->exercisePartitionId() == groupToFind.exercisePartitionId() &&
|
||||
group->domainId() == groupToFind.domainId() &&
|
||||
group->replayPartitionId() == groupToFind.replayPartitionId())
|
||||
return group;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Group *GroupHandler::getSimilarGroup(const GroupRequest &groupToFind) const
|
||||
{
|
||||
const std::vector<Core::ServiceGroupType> possibleTypes = {Core::ServiceGroupType::ExercisePreparation,
|
||||
Core::ServiceGroupType::ExerciseExecution,
|
||||
Core::ServiceGroupType::ExercisePaused,
|
||||
Core::ServiceGroupType::ExerciseAfterActionReview};
|
||||
bool bSimilar = false;
|
||||
for (const auto &type : possibleTypes)
|
||||
{
|
||||
if (type != groupToFind.type())
|
||||
continue;
|
||||
bSimilar = true;
|
||||
break;
|
||||
}
|
||||
if (!bSimilar)
|
||||
return nullptr;
|
||||
|
||||
for(auto type : possibleTypes)
|
||||
{
|
||||
// don't find ourself ...
|
||||
if (type == groupToFind.type())
|
||||
continue;
|
||||
GroupRequest similarGroup(groupToFind);
|
||||
similarGroup.type(type);
|
||||
Group *groupFound = getGroup(similarGroup);
|
||||
if (groupFound)
|
||||
return groupFound;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool GroupHandler::startGroupCore(int32_t domainId)
|
||||
{
|
||||
if (domainId != m_ddsHandler->connector()->getDomainId())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not start group Core for domain "
|
||||
<< domainId
|
||||
<< ", this ServiceManagement instance only supports domain "
|
||||
<< m_ddsHandler->connector()->getDomainId();
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "Starting core group ...";
|
||||
|
||||
GroupRequest groupToSearch;
|
||||
groupToSearch.type(Core::ServiceGroupType::Core);
|
||||
groupToSearch.domainId(domainId);
|
||||
Group *coreGroup = getGroup(groupToSearch);
|
||||
|
||||
if (coreGroup == nullptr)
|
||||
{
|
||||
GroupScheme *coreScheme = groupSchemeFromType(groupToSearch.type());
|
||||
|
||||
if (coreScheme == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not start core group because there are no services linked to it";
|
||||
return false;
|
||||
}
|
||||
|
||||
coreGroup = new Group(m_ddsHandler, m_serviceDonator, groupToSearch, coreScheme, this);
|
||||
m_groups.append(coreGroup);
|
||||
}
|
||||
|
||||
coreGroup->start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GroupHandler::onStartGroupRequest(const rti::core::SampleIdentity &cookie, const GroupRequest &request)
|
||||
{
|
||||
auto ret = onStartGroup(request);
|
||||
if (ret.error())
|
||||
WARNING_LOG(m_logger) << ret();
|
||||
m_ddsHandler->fts()->startGroupReply(cookie, ret);
|
||||
}
|
||||
|
||||
void GroupHandler::slotStartGroupRequest(const GroupRequest &request)
|
||||
{
|
||||
auto ret = onStartGroup(request);
|
||||
if (ret.error())
|
||||
WARNING_LOG(m_logger) << ret();
|
||||
emit startGroupReply(ret);
|
||||
}
|
||||
|
||||
Core::FunctionReplyObject GroupHandler::onStartGroup(const GroupRequest &request)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "New StartGroupRequest: " << request;
|
||||
|
||||
if (request.domainId() != m_ddsHandler->connector()->getDomainId())
|
||||
return Core::FunctionReplyObject(request.requestId(),
|
||||
tr_lts("dds", "This instance of ServiceManagement only allows to start a group in domain %1. Domain %2 was provided"))
|
||||
.arg(m_ddsHandler->connector()->getDomainId())
|
||||
.arg(request.domainId());
|
||||
|
||||
// special case: request.type == Master, ExerciseId != 0 -> stopp all
|
||||
if (request.type() == Core::ServiceGroupType::Master && request.exercisePartitionId() != 0)
|
||||
return onStopAllForExercise(request.domainId(), request.exercisePartitionId());
|
||||
|
||||
Group *existingGroup = getGroup(request);
|
||||
// check if it's a Exercise group and search a group with the same exercise/domain -id
|
||||
|
||||
// if the group does not exist, it is newly created (normal case)
|
||||
if (existingGroup == nullptr)
|
||||
{
|
||||
GroupScheme *scheme = groupSchemeFromType(request.type());
|
||||
if (scheme == nullptr)
|
||||
return Core::FunctionReplyObject(request.requestId(),
|
||||
tr_lts("dds", "Starting attempt of non existing group: %1")).arg(request.type().underlying());
|
||||
|
||||
existingGroup = new Group(m_ddsHandler, m_serviceDonator, request, scheme, this);
|
||||
m_groups.append(existingGroup);
|
||||
}
|
||||
|
||||
Group *similarGroup = getSimilarGroup(request);
|
||||
// if there is a similar group, we should take the services from there (to avoid restarting services on exeuction -> pause or pause -> resume)
|
||||
if (similarGroup)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Taking existing services from group " << similarGroup->type()
|
||||
<< " to " << existingGroup->type();
|
||||
existingGroup->takeServicesFromGroup(similarGroup);
|
||||
similarGroup->stop();
|
||||
m_groups.removeAll(similarGroup);
|
||||
similarGroup->deleteLater();
|
||||
similarGroup = nullptr;
|
||||
}
|
||||
|
||||
existingGroup->start();
|
||||
|
||||
return Core::FunctionReplyObject(request.requestId());
|
||||
}
|
||||
|
||||
Core::FunctionReplyObject GroupHandler::onStopAllForExercise(int domainId, int exercisePartitionId)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Stopping all proces for domain " << domainId << ", partition " << exercisePartitionId;
|
||||
QVector<Group *> stillAliveGroups;
|
||||
|
||||
int cntStopped = 0;
|
||||
int cntAlive = 0;
|
||||
for (Group *group : qAsConst(m_groups))
|
||||
{
|
||||
if (group->exercisePartitionId() == exercisePartitionId &&
|
||||
group->domainId() == domainId)
|
||||
{
|
||||
group->stop();
|
||||
group->deleteLater();
|
||||
++cntStopped;
|
||||
}
|
||||
else
|
||||
{
|
||||
stillAliveGroups += group;
|
||||
++cntAlive;
|
||||
}
|
||||
}
|
||||
|
||||
m_groups = stillAliveGroups;
|
||||
DEBUG_LOG(m_logger) << "Stopping all proces for domain " << domainId << ", partition " << exercisePartitionId
|
||||
<< " - done. Stopped " << cntStopped << " groups"
|
||||
<< ", still alive: " << cntAlive;
|
||||
return Core::FunctionReplyObject(exercisePartitionId);
|
||||
}
|
||||
|
||||
void GroupHandler::onStopGroupRequest(const rti::core::SampleIdentity &cookie, const GroupRequest &request)
|
||||
{
|
||||
auto ret = onStopGroup(request);
|
||||
if (ret.error())
|
||||
WARNING_LOG(m_logger) << ret();
|
||||
m_ddsHandler->fts()->stopGroupReply(cookie, ret);
|
||||
}
|
||||
|
||||
void GroupHandler::slotStopGroupRequest(const GroupRequest &request)
|
||||
{
|
||||
auto ret = onStopGroup(request);
|
||||
if (ret.error())
|
||||
WARNING_LOG(m_logger) << ret();
|
||||
emit stopGroupReply(ret);
|
||||
}
|
||||
|
||||
Core::FunctionReplyObject GroupHandler::onStopGroup(const GroupRequest &request)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "New StopGroupRequest: " << request;
|
||||
|
||||
Group *existingGroup = getGroup(request);
|
||||
|
||||
if (existingGroup == nullptr)
|
||||
return Core::FunctionReplyObject(request.requestId(),
|
||||
tr_lts("dds", "Could not stop Group, because it does not exist"));
|
||||
|
||||
existingGroup->stop();
|
||||
|
||||
// delete this group since no longer needed
|
||||
m_groups.removeAll(existingGroup);
|
||||
delete existingGroup;
|
||||
|
||||
return Core::FunctionReplyObject(request.requestId());
|
||||
}
|
||||
|
||||
void GroupHandler::onRestartGroupRequest(const rti::core::SampleIdentity &cookie,
|
||||
const GroupRequest &request)
|
||||
{
|
||||
auto ret = onRestartGroup(request);
|
||||
if (ret.error())
|
||||
WARNING_LOG(m_logger) << ret();
|
||||
m_ddsHandler->fts()->restartGroupReply(cookie, ret);
|
||||
}
|
||||
|
||||
Core::FunctionReplyObject GroupHandler::onRestartGroup(const GroupRequest &request)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "New RestartGroupRequest: " << request;
|
||||
|
||||
Group *existingGroup = getGroup(request);
|
||||
|
||||
if (existingGroup == nullptr)
|
||||
return Core::FunctionReplyObject(tr_lts("dds", "Could not restart Group, because it does not exist"));
|
||||
|
||||
existingGroup->restart();
|
||||
return Core::FunctionReplyObject(existingGroup->id());
|
||||
}
|
||||
|
||||
|
||||
} // namespace ServiceManagement
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* The GroupHandler class handles all group related classes and writes the groups state to DDS.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QVector>
|
||||
|
||||
#include "logger.h"
|
||||
#include "dds_handler.h"
|
||||
#include "group.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class HeartbeatListener;
|
||||
class ServiceDonator;
|
||||
|
||||
struct GroupScheme
|
||||
{
|
||||
const Core::ServiceGroupType groupType;
|
||||
const QString groupName;
|
||||
QSet<int> services;
|
||||
|
||||
explicit GroupScheme(Core::ServiceGroupType type, const QString &name) : groupType(type) , groupName(name) {}
|
||||
|
||||
void addServiceId(int serviceId)
|
||||
{
|
||||
services += serviceId;
|
||||
}
|
||||
};
|
||||
|
||||
class GroupHandler final
|
||||
: public QObject
|
||||
, protected Interface::Fts::StartGroupRequestHandler
|
||||
, protected Interface::Fts::StopGroupRequestHandler
|
||||
, protected Interface::Fts::RestartGroupRequestHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GroupHandler(const std::shared_ptr<DdsHandler> &ddsHandler,
|
||||
const std::shared_ptr<ServiceDonator> &donator);
|
||||
~GroupHandler();
|
||||
|
||||
bool init(int32_t coreDomain);
|
||||
|
||||
void addServiceLink(int serviceId, Core::ServiceGroupType groupType);
|
||||
|
||||
QVector<Group *> groups() const;
|
||||
|
||||
virtual void onStartGroupRequest(const rti::core::SampleIdentity &cookie,
|
||||
const GroupRequest &request) override final;
|
||||
virtual void onStopGroupRequest(const rti::core::SampleIdentity &cookie,
|
||||
const GroupRequest &request) override final;
|
||||
virtual void onRestartGroupRequest(const rti::core::SampleIdentity &cookie,
|
||||
const GroupRequest &request) override final;
|
||||
public Q_SLOTS:
|
||||
void slotStartGroupRequest(const GroupRequest &request);
|
||||
void slotStopGroupRequest(const GroupRequest &request);
|
||||
Q_SIGNALS:
|
||||
void startGroupReply(const Core::FunctionReplyObject &reply);
|
||||
void stopGroupReply(const Core::FunctionReplyObject &reply);
|
||||
private:
|
||||
Core::FunctionReplyObject onStartGroup(const GroupRequest &request);
|
||||
Core::FunctionReplyObject onStopAllForExercise(int domainId, int exercisePartitionId);
|
||||
Core::FunctionReplyObject onStopGroup(const GroupRequest &request);
|
||||
Core::FunctionReplyObject onRestartGroup(const GroupRequest &request);
|
||||
private: // methods
|
||||
GroupScheme *groupSchemeFromType(Core::ServiceGroupType type);
|
||||
Group *getGroup(const GroupRequest &groupToFind) const;
|
||||
Group *getSimilarGroup(const GroupRequest &groupToFind) const;
|
||||
bool startGroupCore(int32_t domainId);
|
||||
|
||||
private: // attributes
|
||||
QVector<GroupScheme *> m_groupSchemes;
|
||||
QVector<Group *> m_groups;
|
||||
std::shared_ptr<ServiceDonator>m_serviceDonator;
|
||||
std::shared_ptr<DdsHandler> m_ddsHandler;
|
||||
HeartbeatListener *m_heartbeatListener;
|
||||
LOGGER_T m_logger;
|
||||
|
||||
static qint64 groupIdCounter;
|
||||
};
|
||||
} // namespace ServiceManagement
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* The HeartbeatListener class subscribes in one domain for topic CurrentRunningState
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-03-31
|
||||
*/
|
||||
|
||||
#include "heartbeat_listener.h"
|
||||
#include "service_donator.h"
|
||||
|
||||
ServiceManagement::HeartbeatListener::HeartbeatListener(const std::shared_ptr<DDSConnector::DdsConnector> &connector, const std::shared_ptr<ServiceDonator> &serviceDonator)
|
||||
: m_ddsConnector(connector)
|
||||
, m_serviceDonator(serviceDonator)
|
||||
, m_logger(getLogger("HeartbeatListener"))
|
||||
{}
|
||||
|
||||
ServiceManagement::HeartbeatListener::~HeartbeatListener()
|
||||
{}
|
||||
|
||||
bool ServiceManagement::HeartbeatListener::init()
|
||||
{
|
||||
// this handler is just there for subsbscribing to heartbeats ...
|
||||
m_ddsConnector->initSubscriber();
|
||||
m_coreDtc = Core::Interface::Dtc::create(m_ddsConnector);
|
||||
|
||||
if (!m_coreDtc->enableCurrentRunningState())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Core DTC for HeartbeatListener in domain " << m_ddsConnector->getDomainId()
|
||||
<< " could not be initialized.";
|
||||
m_coreDtc.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
QObject::connect(m_coreDtc.get(), &Core::Interface::Dtc::currentRunningStateChanged,
|
||||
m_serviceDonator.get(), &ServiceDonator::onNewServiceInfoFromDds);
|
||||
|
||||
DEBUG_LOG(m_logger) << "HeartbeatListener for domain " << m_ddsConnector->getDomainId()
|
||||
<< " successfully initialized.";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<Core::Interface::Dtc> ServiceManagement::HeartbeatListener::dtc() const
|
||||
{
|
||||
return m_coreDtc;
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* The HeartbeatListener class subscribes in one domain for topic CurrentRunningState
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-03-31
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <DDS/DDSConnector/dds_connector.h>
|
||||
#include <DDS/Core/core_dtc.h>
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class CurrentRunningState;
|
||||
namespace Interface
|
||||
{
|
||||
class DtcImpl;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class ServiceDonator;
|
||||
|
||||
class HeartbeatListener
|
||||
{
|
||||
public:
|
||||
explicit HeartbeatListener(const std::shared_ptr<DDSConnector::DdsConnector> &connector, const std::shared_ptr<ServiceDonator> &serviceDonator);
|
||||
~HeartbeatListener();
|
||||
|
||||
bool init();
|
||||
|
||||
std::shared_ptr<Core::Interface::Dtc> dtc() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Core::Interface::Dtc> m_coreDtc;
|
||||
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
|
||||
std::shared_ptr<ServiceDonator> m_serviceDonator;
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
} // namespace ServiceManagement
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* LicenceDonator is an interface for a class which manages the availability licences.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-03-31
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class QString;
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class LicenceDonator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The implementation of this method should proof if the licence licenceName
|
||||
* is available.
|
||||
* @param licenceName - The licence to be looked up.
|
||||
* @return True if a licence for licenceName is available, otherwise false.
|
||||
*/
|
||||
virtual bool getLicence(const QString &licenceName) = 0;
|
||||
|
||||
/**
|
||||
* @brief The implementation of this method should free one licence licenceName.
|
||||
* @param licenceName
|
||||
* @return True if licence licenceName exists and it is possible to free one licence,
|
||||
* otherwise false.
|
||||
*/
|
||||
virtual bool freeLicence(const QString &licenceName) = 0;
|
||||
}; // class LicenceDonator
|
||||
} // namespace ServiceManagement
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
+339
@@ -0,0 +1,339 @@
|
||||
/**
|
||||
* Implementation LicenceHandler class
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by hjl, SLSE, 2016-05-11
|
||||
*/
|
||||
|
||||
#include "licence_handler.h"
|
||||
#include "dds_handler.h"
|
||||
#include <QFile>
|
||||
#include <LtsLicence/aes_crypt.h>
|
||||
|
||||
|
||||
|
||||
using namespace ServiceManagement;
|
||||
|
||||
LicenceHandler::LicenceHandler(const std::shared_ptr<DdsHandler> &ddsHandler, const QString &licenceFile)
|
||||
: Interface::Fts::GetLicenceRequestHandler(ddsHandler->fts())
|
||||
, Interface::Fts::FreeLicenceRequestHandler(ddsHandler->fts())
|
||||
, m_ddsHandler(ddsHandler)
|
||||
, m_xmlFileName(licenceFile)
|
||||
, m_logger(getLogger("LicenceHandler"))
|
||||
{
|
||||
INFO_LOG(m_logger) << "Start ServiceManagement LicenceHandler. Filename: " << m_xmlFileName;
|
||||
} // end of ctor()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
LicenceHandler::~LicenceHandler()
|
||||
{
|
||||
for (const LtsLicence::Licence &info : qAsConst(m_licenceVec))
|
||||
m_ddsHandler->dts()->removeLicenceInformationByKey(info.name.toStdString());
|
||||
} // end of dtor()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool LicenceHandler::init()
|
||||
{
|
||||
INFO_LOG(m_logger) << "init() Start ServiceManagement LicenceHandler.";
|
||||
|
||||
// read all licences from licencefile
|
||||
if (!readLicenceFile())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error reading data from licencefile!";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Publish Data via DTS
|
||||
publishAllData();
|
||||
|
||||
return true;
|
||||
}// end of init()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool LicenceHandler::isServiceLicensed(const QString &licenceName) const
|
||||
{
|
||||
for (const auto &licence : m_licenceVec)
|
||||
if (licence.name == licenceName)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
} // end of isServiceLicensed()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void LicenceHandler::onGetLicenceRequest(const rti::core::SampleIdentity &cookie,
|
||||
const LicenceRequest &request)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Try to getLicence (from DDS) for '" << request.name() << "' ...";
|
||||
|
||||
Core::FunctionReplyObject ret = getLicenceInternal(QString::fromStdString(request.name()));
|
||||
m_ddsHandler->fts()->getLicenceReply(cookie, ret);
|
||||
|
||||
return;
|
||||
} // end of onGetLicenceRequest()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void LicenceHandler::onFreeLicenceRequest(const rti::core::SampleIdentity &cookie,
|
||||
const LicenceRequest &request)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Try to freeLicence (from DDS) for '" << request.name() << "' ...";
|
||||
|
||||
Core::FunctionReplyObject ret = freeLicenceInternal(QString::fromStdString(request.name()));
|
||||
m_ddsHandler->fts()->freeLicenceReply(cookie, ret);
|
||||
|
||||
return;
|
||||
} // end of onFreeLicenceRequest()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool LicenceHandler::getLicence(const QString &licenceName)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Try to getLicence (from SCM) for '" << licenceName << "' ...";
|
||||
|
||||
Core::FunctionReplyObject fro = getLicenceInternal(licenceName);
|
||||
|
||||
if (fro.error())
|
||||
INFO_LOG(m_logger) << "getLicence() errorText: " << fro.translated();
|
||||
|
||||
return !fro.error();
|
||||
} // end of getLicence()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool LicenceHandler::freeLicence(const QString &licenceName)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Try to freeLicence (from SCM) for '" << licenceName << "' ...";
|
||||
|
||||
Core::FunctionReplyObject fro = freeLicenceInternal(licenceName);
|
||||
|
||||
if (fro.error())
|
||||
INFO_LOG(m_logger) << "freeLicence() errorText: " << fro.translated();
|
||||
|
||||
return !fro.error();
|
||||
} // end of freeLicence()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool LicenceHandler::readLicenceFile()
|
||||
{
|
||||
QFile f(m_xmlFileName);
|
||||
if (!f.exists())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error loading licencefile ... file not found. '" << m_xmlFileName << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error loading licencefile ... file open failed.'" << m_xmlFileName << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray qbaIn(f.readAll());
|
||||
|
||||
if (m_xmlFileName.endsWith(QLatin1String(".crl")))
|
||||
{
|
||||
qbaIn = LtsLicence::decryptData(qbaIn);
|
||||
INFO_LOG(m_logger) << "ReadAll() from encrypted licencefile ... bytesAvailable:" << qbaIn.size();
|
||||
}
|
||||
else if (m_xmlFileName.endsWith(QLatin1String(".lic")))
|
||||
INFO_LOG(m_logger) << "ReadAll() from unencrypted licencefile ... bytesAvailable:" << qbaIn.size();
|
||||
|
||||
f.close();
|
||||
|
||||
INFO_LOG(m_logger) << "Try to parse xml file '" << m_xmlFileName << "'";
|
||||
|
||||
m_licenceVec = LtsLicence::readLicences(qbaIn);
|
||||
|
||||
if (m_licenceVec.isEmpty())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Parse error reading xml file '" << m_xmlFileName << "'";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} // end of readLicenceFile()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Core::FunctionReplyObject LicenceHandler::getLicenceInternal(const QString &licenceName)
|
||||
{
|
||||
for (int i = 0; i < m_licenceVec.size(); ++i)
|
||||
{
|
||||
LtsLicence::Licence &licence = m_licenceVec[i];
|
||||
|
||||
if (licence.name == licenceName)
|
||||
{
|
||||
if (!licence.ignoreExpiration)
|
||||
{
|
||||
if (!isLicenceValid(licence))
|
||||
return Core::FunctionReplyObject(0, "No valid licence available (outdated).");
|
||||
}
|
||||
|
||||
if (!licence.ignoreMaxCount)
|
||||
{
|
||||
if (licence.maxCount <= licence.licenceRefCount)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "getLicence() for '" << licence.name << "' failed. - No more licences available.";
|
||||
return Core::FunctionReplyObject(0, "No more licences available.");
|
||||
}
|
||||
else
|
||||
++licence.licenceRefCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
++licence.licenceRefCount; // fuer korrekte dumpLicenceInformation()
|
||||
}
|
||||
|
||||
if (m_logger.isInfoEnabled())
|
||||
{
|
||||
const QString info = dumpLicenceInformation(licence);
|
||||
INFO_LOG(m_logger) << "getLicence() for '" << licenceName << "' ok.\n" << info;
|
||||
}
|
||||
publishData(licence);
|
||||
return Core::FunctionReplyObject(i + 1); // id im FRO >0 ; - m_licenceVec beginnt bei [0]
|
||||
}
|
||||
}
|
||||
|
||||
ERROR_LOG(m_logger) << "getLicence() for '" << licenceName << "' failed. - No licence with this name.";
|
||||
return Core::FunctionReplyObject(0, "No licence with this name.");
|
||||
} // end of getLicenceInternal()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Core::FunctionReplyObject LicenceHandler::freeLicenceInternal(const QString &licenceName)
|
||||
{
|
||||
for (int i = 0; i < m_licenceVec.size(); ++i)
|
||||
{
|
||||
LtsLicence::Licence &licence = m_licenceVec[i];
|
||||
|
||||
if (licence.name == licenceName)
|
||||
{
|
||||
if (!licence.ignoreExpiration)
|
||||
{
|
||||
if (!isLicenceValid(licence))
|
||||
return Core::FunctionReplyObject(0, "No valid licence available (outdated).");
|
||||
}
|
||||
|
||||
if (!licence.ignoreMaxCount)
|
||||
{
|
||||
if (licence.licenceRefCount <= 0)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "freeLicence() for '" << licence.name << "' failed. - Max number of licences are free.";
|
||||
return Core::FunctionReplyObject(0, "Max number of licences are free.");
|
||||
}
|
||||
else
|
||||
--licence.licenceRefCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (licence.licenceRefCount > 0) // keine negativen frees eintragen
|
||||
{
|
||||
--licence.licenceRefCount; // fuer korrekte dumpLicenceInformation()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_logger.isInfoEnabled())
|
||||
{
|
||||
const QString info = dumpLicenceInformation(licence);
|
||||
INFO_LOG(m_logger) << "freeLicence() for '" << licenceName << "' ok.\n" << info;
|
||||
}
|
||||
publishData(licence);
|
||||
return Core::FunctionReplyObject(i + 1); // id im FRO >0 ; - m_licenceVec beginnt bei [0]
|
||||
}
|
||||
}
|
||||
|
||||
ERROR_LOG(m_logger) << "freeLicence() for '" << licenceName << "' failed. - No licence with this name.";
|
||||
return Core::FunctionReplyObject(0, "No licence with this name.");
|
||||
}// end of freeLicence()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool LicenceHandler::isLicenceValid(const LtsLicence::Licence &licence) const
|
||||
{
|
||||
const QDateTime now(QDateTime::currentDateTimeUtc());
|
||||
const QDateTime end(licence.expiration);
|
||||
|
||||
if (now < licence.expiration)
|
||||
{
|
||||
if (!licence.ignoreExpiration)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Licence '" << licence.name << "' is valid for " << now.daysTo(end) << " days."
|
||||
<< " Expiration: " << licence.expiration.date().toString()
|
||||
<< " " << licence.expiration.time().toString();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
WARNING_LOG(m_logger) << "Licence '" << licence.name << "' is expired since " << end.daysTo(now) << " days."
|
||||
<< " Expiration: " << licence.expiration.date().toString()
|
||||
<< " " << licence.expiration.time().toString();
|
||||
return false;
|
||||
} // end of isLicenceValid()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
QString LicenceHandler::dumpLicenceInformation(const LtsLicence::Licence &licence)
|
||||
{
|
||||
QString info;
|
||||
|
||||
info.append(QLatin1String("Licence information for '"));
|
||||
info.append(licence.name);
|
||||
info.append(QLatin1String("'"));
|
||||
|
||||
if (licence.ignoreMaxCount)
|
||||
{
|
||||
info.append(QLatin1String(" Number of licences is unlimited. "));
|
||||
info.append(QString::number(licence.licenceRefCount));
|
||||
info.append(QLatin1String(" licences in use."));
|
||||
}
|
||||
else
|
||||
{
|
||||
info.append(QLatin1String(" Max licences: "));
|
||||
info.append(QString::number(licence.maxCount));
|
||||
|
||||
info.append(QLatin1String(" Number of remaining licences: "));
|
||||
info.append(QString::number(licence.maxCount - licence.licenceRefCount));
|
||||
}
|
||||
|
||||
if (licence.ignoreExpiration)
|
||||
info.append(QLatin1String(" Licence never expires. "));
|
||||
else
|
||||
{
|
||||
info.append(QLatin1String(" Expiration: "));
|
||||
info.append(licence.expiration.date().toString());
|
||||
info.append(QLatin1String(" "));
|
||||
info.append(licence.expiration.time().toString());
|
||||
}
|
||||
return info;
|
||||
} // end of licenceInformation()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void LicenceHandler::publishAllData()
|
||||
{
|
||||
for (const LtsLicence::Licence &info : qAsConst(m_licenceVec))
|
||||
publishData(info);
|
||||
} // end of publishAllData()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void LicenceHandler::publishData(const LtsLicence::Licence &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Publish '" << data.name << "' Licence-data to dds";
|
||||
|
||||
if (!m_ddsHandler->dts()->publish(toDdsData(data)))
|
||||
ERROR_LOG(m_logger) << "Error publishing '" << data.name << "' Licence-data to dds";
|
||||
|
||||
} // end of publishData()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// convert methods
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
//ServiceManagement::LicenceInformation LicenceHandler::toDdsData(const ServiceManagement::Licence &data)
|
||||
ServiceManagement::LicenceInformation LicenceHandler::toDdsData(const LtsLicence::Licence &data)
|
||||
{
|
||||
ServiceManagement::LicenceInformation ddsData;
|
||||
|
||||
ddsData.name(data.name.toStdString());
|
||||
ddsData.ignoreMaxCount(data.ignoreMaxCount);
|
||||
ddsData.maxCount(data.maxCount);
|
||||
ddsData.ignoreExpiration(data.ignoreExpiration);
|
||||
ddsData.expiration(data.expiration.toMSecsSinceEpoch());
|
||||
ddsData.refCount(data.licenceRefCount);
|
||||
|
||||
return ddsData;
|
||||
} // end of toDdsData()
|
||||
//-----------------------------------------------------------------------------
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* LicenceHandler class.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by hjl, SLSE, 2016-05-11
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "licence_donator.h"
|
||||
#include "dds_handler.h"
|
||||
#include "logger.h"
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <LtsLicence/lts_licence.h>
|
||||
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
|
||||
class LicenceHandler final
|
||||
: public QObject
|
||||
, public LicenceDonator
|
||||
, protected Interface::Fts::GetLicenceRequestHandler
|
||||
, protected Interface::Fts::FreeLicenceRequestHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LicenceHandler(const std::shared_ptr<DdsHandler> &ddsHandler,
|
||||
const QString &licenceFile);
|
||||
|
||||
~LicenceHandler();
|
||||
|
||||
bool init();
|
||||
|
||||
bool isServiceLicensed(const QString &licenceName) const;
|
||||
|
||||
void onGetLicenceRequest(const rti::core::SampleIdentity &cookie,
|
||||
const LicenceRequest &request) override;
|
||||
void onFreeLicenceRequest(const rti::core::SampleIdentity &cookie,
|
||||
const LicenceRequest &request) override;
|
||||
|
||||
bool getLicence(const QString &licenceName) override;
|
||||
bool freeLicence(const QString &licenceName) override;
|
||||
|
||||
private: // Methoden
|
||||
bool readLicenceFile();
|
||||
|
||||
Core::FunctionReplyObject getLicenceInternal(const QString &licenceName);
|
||||
Core::FunctionReplyObject freeLicenceInternal(const QString &licenceName);
|
||||
|
||||
bool isLicenceValid(const LtsLicence::Licence &licence) const;
|
||||
static QString dumpLicenceInformation(const LtsLicence::Licence &licence);
|
||||
|
||||
void publishAllData();
|
||||
void publishData(const LtsLicence::Licence &data);
|
||||
|
||||
ServiceManagement::LicenceInformation toDdsData(const LtsLicence::Licence &data);
|
||||
|
||||
|
||||
private: // attributes
|
||||
std::shared_ptr<DdsHandler> m_ddsHandler;
|
||||
QString m_xmlFileName;
|
||||
QVector<LtsLicence::Licence> m_licenceVec;
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
}; // class LicenceHandler
|
||||
} // namespace ServiceManagement
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
log4j.reset=true
|
||||
log4j.rootLogger=WARN, file
|
||||
|
||||
# Service: ServiceManagement
|
||||
###########################################################
|
||||
|
||||
#log4j.logger.SCM=DEBUG
|
||||
#log4j.logger.SCM.main=DEBUG
|
||||
#log4j.logger.SCM.DdsHandler=DEBUG
|
||||
#log4j.logger.SCM.Fts=DEBUG
|
||||
#log4j.logger.SCM.Group=DEBUG
|
||||
#log4j.logger.SCM.GroupHandler=DEBUG
|
||||
#log4j.logger.SCM.HeartbeatListener=WARN
|
||||
#log4j.logger.SCM.LicenceHandler=DEBUG
|
||||
#log4j.logger.SCM.Service=DEBUG
|
||||
#log4j.logger.SCM.ServiceHandler=DEBUG
|
||||
#log4j.logger.SCM.ServiceProcess=DEBUG
|
||||
#log4j.logger.SCM.WebSocketInterfaceHandler=DEBUG
|
||||
#log4j.logger.SCM.DataManager=DEBUG
|
||||
#log4j.logger.SCM.ClientMessageFactory=DEBUG
|
||||
#log4j.logger.SCM.ReplyMessageF=DEBUG
|
||||
|
||||
# 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:
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Building Config - logging facility
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "SCM.";
|
||||
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 ServiceManagement
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Logging for ServiceManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include "scm_export.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
using LOGGER_T = NGL::INGLLogger &;
|
||||
|
||||
SCM_COMMON_EXPORT LOGGER_T getLogger(const char *name);
|
||||
inline LOGGER_T getLogger(const QString &loggerName)
|
||||
{
|
||||
return getLogger(loggerName.toUtf8().constData());
|
||||
}
|
||||
} // namespace ServiceManagement
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* main for ServiceManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include <nglapplication/nglsinglecoreapplication.h>
|
||||
#include <LtsCommandLineParser/LtsCommandLineParser.h>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include "main_handler.h"
|
||||
#include "logger.h"
|
||||
#ifndef WIN32
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Become a daemon
|
||||
// ---------------------------------------------------------------
|
||||
static void daemonize()
|
||||
{
|
||||
#ifndef WIN32
|
||||
pid_t pid, sid;
|
||||
|
||||
/* already a daemon */
|
||||
if (getppid() == 1)
|
||||
return;
|
||||
|
||||
/* Fork off the parent process */
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
/* If we got a good PID, then we can exit the parent process. */
|
||||
if (pid > 0)
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
/* At this point we are executing as the child process */
|
||||
|
||||
/* Change the file mode mask */
|
||||
umask(0);
|
||||
|
||||
/* Create a new SID for the child process */
|
||||
sid = setsid();
|
||||
if (sid < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
/* Change the current working directory. This prevents the current
|
||||
directory from being locked; hence not being able to remove it. */
|
||||
if ((chdir("/")) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
/* Redirect standard files to /dev/null */
|
||||
freopen("/dev/null", "r", stdin);
|
||||
freopen("/dev/null", "w", stdout);
|
||||
freopen("/dev/null", "w", stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
|
||||
{
|
||||
public:
|
||||
CmdLineParser()
|
||||
: LtsCommandLineParser(ProvideCommandLineParameterOptions(CreateDomainIdFromIp|DomainId|CustomConfigFile))
|
||||
, m_defLicenceFile(QLatin1String("licence_file.crl"))
|
||||
{}
|
||||
|
||||
bool daemonize() const
|
||||
{
|
||||
return isSet(QStringLiteral("z"));
|
||||
}
|
||||
|
||||
QString licenceFile() const
|
||||
{
|
||||
return isSet(QLatin1String("l")) ? value(QLatin1String("l")) : m_defLicenceFile;
|
||||
}
|
||||
|
||||
bool doShutdown() const
|
||||
{
|
||||
return isSet(QLatin1String("s"));
|
||||
}
|
||||
|
||||
bool init()
|
||||
{
|
||||
addOption({{QStringLiteral("l"), QStringLiteral("licence_file")},
|
||||
QStringLiteral("Default: \"") + m_defLicenceFile + QStringLiteral("\""),
|
||||
QStringLiteral("filename"),
|
||||
m_defLicenceFile
|
||||
});
|
||||
|
||||
addOption({{QStringLiteral("s"), QStringLiteral("shutdown")},
|
||||
QStringLiteral("Shut down running service management")
|
||||
});
|
||||
|
||||
addOption({{QStringLiteral("z"), QStringLiteral("daemonize")},
|
||||
QStringLiteral("Start in background")
|
||||
});
|
||||
|
||||
return (LtsCommandLineParser::init());
|
||||
}
|
||||
private:
|
||||
QString m_defLicenceFile;
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
NGLSingleCoreApplication app(argc, argv);
|
||||
|
||||
CmdLineParser options;
|
||||
if (!options.init())
|
||||
return -1;
|
||||
app.createLogger("SCM", options.exerciseId());
|
||||
|
||||
if (options.doShutdown())
|
||||
{
|
||||
if (app.isRunning())
|
||||
{
|
||||
if (app.sendMessage(QStringLiteral("shutdown")))
|
||||
std::cout << "Sent shutdown message to " << argv[0] << std::endl;
|
||||
else
|
||||
std::cerr << "Could not send shutdown message to " << argv[0] << std::endl;
|
||||
return 0;
|
||||
}
|
||||
std::cerr << argv[0] << " is not running - can not shutdown app" << std::endl;
|
||||
return -3;
|
||||
}
|
||||
// vor app.isRunning() da sonst die file-locks durcheinander geraten und ein shutdown mittels '-z' nicht mehr möglich ist
|
||||
if (options.daemonize())
|
||||
daemonize();
|
||||
|
||||
if (app.isRunning())
|
||||
{
|
||||
std::cerr << argv[0] << " is already running - can not be started twice!" << std::endl;
|
||||
return -2;
|
||||
}
|
||||
|
||||
// relock lock file which was unlocked due to daemonizing
|
||||
WARNING_LOG(app.baseLogger()) << "app is running: " << app.isRunning();
|
||||
|
||||
int iRet = 0;
|
||||
{
|
||||
ServiceManagement::MainHandler mainHandler;
|
||||
if (!mainHandler.init(options.domainId(), options.licenceFile()))
|
||||
return -1;
|
||||
|
||||
STARTUP_LOG(app.baseLogger());
|
||||
|
||||
iRet = app.exec();
|
||||
|
||||
SHUTDOWN_LOG(app.baseLogger());
|
||||
}
|
||||
|
||||
return iRet;
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* MainHandler for ServiceManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 15.02.2016
|
||||
*/
|
||||
|
||||
#include "main_handler.h"
|
||||
#include "licence_handler.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QDirIterator>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QXmlStreamReader>
|
||||
#include <ngltools/ngltools_filesystem.h>
|
||||
#include <nglapplication/nglsinglecoreapplication.h>
|
||||
|
||||
#include <CodeGen/XmlReader.h>
|
||||
|
||||
#include "service.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
|
||||
MainHandler::MainHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_ddsHandler(nullptr)
|
||||
, m_groupHandler(nullptr)
|
||||
, m_licenceHandler(nullptr)
|
||||
, m_serviceHandler(nullptr)
|
||||
, m_websocketHandler(nullptr)
|
||||
, m_logger(getLogger("MainHandler"))
|
||||
{
|
||||
QtSingleCoreApplication *sa = qobject_cast<QtSingleCoreApplication*>(QCoreApplication::instance());
|
||||
Q_ASSERT(sa);
|
||||
connect(sa, &QtSingleCoreApplication::messageReceived,
|
||||
this, &MainHandler::onMessageFromCommandlineReceived);
|
||||
}
|
||||
|
||||
MainHandler::~MainHandler()
|
||||
{
|
||||
m_websocketHandler = nullptr;
|
||||
m_groupHandler = nullptr;
|
||||
m_serviceHandler = nullptr;
|
||||
m_licenceHandler = nullptr;
|
||||
m_ddsHandler = nullptr;
|
||||
}
|
||||
|
||||
bool MainHandler::init(int domainId, const QString &licenceFile)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Initializing MainHandler ...";
|
||||
|
||||
m_ddsHandler = std::make_shared<DdsHandler>(domainId);
|
||||
bool bAllPartsInitialized = m_ddsHandler->init();
|
||||
|
||||
ERROR_LOG(m_logger) << "Searching for licence file: " << licenceFile;
|
||||
QString licenceFileName = NGL::findFile(NGL::FileType::ConfigFile, licenceFile);
|
||||
|
||||
m_licenceHandler = std::make_shared<LicenceHandler>(m_ddsHandler, licenceFileName);
|
||||
if (!m_licenceHandler->init())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Initialisation LicenceHandler failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_serviceHandler = std::make_shared<ServiceHandler>(m_ddsHandler, m_licenceHandler);
|
||||
m_groupHandler = std::make_shared<GroupHandler>(m_ddsHandler, m_serviceHandler);
|
||||
|
||||
bAllPartsInitialized &=
|
||||
m_serviceHandler->init() &&
|
||||
readServices() &&
|
||||
m_groupHandler->init(domainId);
|
||||
|
||||
if (bAllPartsInitialized)
|
||||
{
|
||||
// WebSocketServer
|
||||
m_websocketHandler = std::make_shared<WebSocketInterface::WebSocketInterfaceHandler>(this);
|
||||
if (!m_websocketHandler->init())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Initialisation WebSocketInterfaceHandler failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "MainHandler successfully initialized :)";
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(m_logger) << "MainHandler initialization failed";
|
||||
}
|
||||
|
||||
return bAllPartsInitialized;
|
||||
}
|
||||
|
||||
std::shared_ptr<GroupHandler> MainHandler::groupHandler() const
|
||||
{
|
||||
return m_groupHandler;
|
||||
}
|
||||
|
||||
bool MainHandler::readServices()
|
||||
{
|
||||
CodeGen::XmlReader xmlReader;
|
||||
const QString xmlPath = NGL::directory(NGL::FileType::ConfigFile, QLatin1String("xml"));
|
||||
DEBUG_LOG(m_logger) << "Using xml path " << xmlPath;
|
||||
QDirIterator directoryIterator(xmlPath);
|
||||
int serviceCount = 0;
|
||||
|
||||
// TODO: make generic ... will be implemented in a future core library
|
||||
QMap<QString, Core::ServiceGroupType_def::type> groupNameMapping;
|
||||
groupNameMapping.insert(QLatin1String("Master"), Core::ServiceGroupType_def::Master);
|
||||
groupNameMapping.insert(QLatin1String("Core"), Core::ServiceGroupType_def::Core);
|
||||
groupNameMapping.insert(QLatin1String("ExercisePreparation"), Core::ServiceGroupType_def::ExercisePreparation);
|
||||
groupNameMapping.insert(QLatin1String("ExerciseExecution"), Core::ServiceGroupType_def::ExerciseExecution);
|
||||
groupNameMapping.insert(QLatin1String("ExercisePaused"), Core::ServiceGroupType_def::ExercisePaused);
|
||||
groupNameMapping.insert(QLatin1String("ExerciseAfterActionReview"), Core::ServiceGroupType_def::ExerciseAfterActionReview);
|
||||
groupNameMapping.insert(QLatin1String("Playback"), Core::ServiceGroupType_def::Playback);
|
||||
|
||||
while (directoryIterator.hasNext())
|
||||
{
|
||||
QString currentFileName = directoryIterator.next();
|
||||
|
||||
if (currentFileName.endsWith(QLatin1String(".xml")))
|
||||
{
|
||||
if (xmlReader.readServiceFile(currentFileName, QString()))
|
||||
{
|
||||
const auto procNames = xmlReader.getProcNames();
|
||||
for (const QString &serviceName : procNames)
|
||||
{
|
||||
const QString exeName = QLatin1String("svc") + serviceName;
|
||||
const QStringList groupNames = xmlReader.getGroupNames();
|
||||
|
||||
if (serviceName.isEmpty() || exeName.length() < 4 || groupNames.isEmpty())
|
||||
{
|
||||
INFO_LOG(m_logger) << currentFileName
|
||||
<< " does not include complete service information.";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_licenceHandler->isServiceLicensed(serviceName))
|
||||
{
|
||||
const QString serviceNameShort = xmlReader.getServiceAbbr();
|
||||
QString fnNoRestart = xmlPath + serviceNameShort + QLatin1String(".dontRestart");
|
||||
bool bDevMode = xmlReader.getDevMode() || QFile::exists(fnNoRestart);
|
||||
const int prioService = xmlReader.getPrio();
|
||||
if (bDevMode)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Developer mode for service " << serviceName
|
||||
<< " activated - no automatic restart on crash!";
|
||||
}
|
||||
Service *newService = new Service(serviceName,
|
||||
exeName,
|
||||
QCoreApplication::applicationDirPath(),
|
||||
serviceNameShort,
|
||||
groupNames,
|
||||
bDevMode,
|
||||
prioService);
|
||||
m_serviceHandler->addService(newService);
|
||||
|
||||
DEBUG_LOG(m_logger) << "Added new service \""
|
||||
<< serviceName
|
||||
<< "\""
|
||||
<< " prio=" << prioService;
|
||||
|
||||
for (const QString &groupName : groupNames)
|
||||
{
|
||||
if (groupNameMapping.contains(groupName))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << " --> Linking to group \""
|
||||
<< groupName
|
||||
<< "\"";
|
||||
if (serviceName == QStringLiteral("ReportAnalyzer") && (groupName == QStringLiteral("ExercisePaused") || groupName == QStringLiteral("ExerciseAfterActionReview")))
|
||||
// Do not start ReportAnalyzer for stopped exercises
|
||||
continue;
|
||||
m_groupHandler->addServiceLink(newService->id(), groupNameMapping.value(groupName));
|
||||
}
|
||||
else
|
||||
INFO_LOG(m_logger) << "Found unknown group name: " << groupName;
|
||||
}
|
||||
++ serviceCount;
|
||||
}
|
||||
else
|
||||
NOTICE_LOG(m_logger) << "Service " << serviceName << " is not licensed.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serviceCount)
|
||||
return true;
|
||||
ERROR_LOG(m_logger) << "Could not find any valid service in directory " << xmlPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainHandler::onMessageFromCommandlineReceived()
|
||||
{
|
||||
NOTICE_LOG(m_logger) << "Got shutdown message from command line - trying to properly shut down all running applications";
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
|
||||
} // namespace ServiceManagement
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* MainHandler for ServiceManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "logger.h"
|
||||
#include "group_handler.h"
|
||||
#include "licence_handler.h"
|
||||
#include "service_handler.h"
|
||||
#include "dds_handler.h"
|
||||
#include "WebSocketInterface/websocket_interface_handler.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class MainHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainHandler(QObject *parent = nullptr);
|
||||
~MainHandler();
|
||||
|
||||
bool init(int domainId, const QString &licenceFile);
|
||||
|
||||
std::shared_ptr<GroupHandler> groupHandler() const;
|
||||
|
||||
private: // methods
|
||||
bool readServices();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onMessageFromCommandlineReceived();
|
||||
|
||||
private: // attributes
|
||||
std::shared_ptr<DdsHandler> m_ddsHandler;
|
||||
std::shared_ptr<GroupHandler> m_groupHandler;
|
||||
std::shared_ptr<LicenceHandler> m_licenceHandler;
|
||||
std::shared_ptr<ServiceHandler> m_serviceHandler;
|
||||
std::shared_ptr<WebSocketInterface::WebSocketInterfaceHandler> m_websocketHandler;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
} // namespace ServiceManagement
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
+52
@@ -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 SCM_COMMON_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define SCM_COMMON_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_SVCSCMCOMMON_LIB)
|
||||
# define SCM_COMMON_EXPORT Q_DECL_EXPORT
|
||||
# ifndef WIN32
|
||||
# ifdef NDDSUSERDllExport
|
||||
# undef NDDSUSERDllExport
|
||||
# endif
|
||||
# define NDDSUSERDllExport Q_DECL_EXPORT
|
||||
# endif
|
||||
# else
|
||||
# define SCM_COMMON_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef SCM_CLIENT_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define SCM_CLIENT_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_SVCSCMCLIENT_LIB)
|
||||
# define SCM_CLIENT_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define SCM_CLIENT_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef SCM_SERVER_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define SCM_SERVER_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_SVCSCMSERVER_LIB)
|
||||
# define SCM_SERVER_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define SCM_SERVER_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<service namespace="ServiceManagement" abbreviation="SCM" procNames="ServiceManagement" exerciseDependency="Global" groups="Master">
|
||||
<rcs>
|
||||
<Id Id="$Id$"/>
|
||||
<HeadURL HeadURL="$HeadURL$"/>
|
||||
<Author Author="$Author$"/>
|
||||
</rcs>
|
||||
<operatorRights offset="1">
|
||||
<guiPlugin name="ServiceEdit" offset="1">
|
||||
<operatorRightGroup name="Service Management" description="Edit Services (Restart/Stop)">
|
||||
<operatorRight right="ServicesShow" value="1" description="Show Service Information"/>
|
||||
<operatorRight right="ServicesEdit" value="2" description="Edit Services (Restart/Stop)"/>
|
||||
</operatorRightGroup>
|
||||
</guiPlugin>
|
||||
</operatorRights>
|
||||
<tables/>
|
||||
<views/>
|
||||
<dbServiceStructs/>
|
||||
<topicTypes>
|
||||
<topicType name="GroupInformation" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="InformationBase">
|
||||
<column name="type" idlType="Core::ServiceGroupType"/>
|
||||
<column name="domainId" idlType="long"/>
|
||||
<column name="exercisePartitionId" idlType="long"/>
|
||||
<column name="replayPartitionId" idlType="long"/>
|
||||
<column name="services" idlType="sequence<long>"/>
|
||||
</topicType>
|
||||
<topicType name="InformationBase">
|
||||
<column name="id" idlType="long" isKey="1"/>
|
||||
<column name="timestamp" idlType="long long"/>
|
||||
</topicType>
|
||||
<topicType name="LicenceInformation" isDataObject="1" autosubscribe="0" routeToClient="0" routeToServer="0" routeInReplay="0">
|
||||
<column name="name" idlType="string" isKey="1" comment="Contains the name of the service or functionality for which the licence is granted."/>
|
||||
<column name="ignoreMaxCount" idlType="boolean" comment="Ignore the maximum number of licences which can be awarded. Unlimited number of licenes usable."/>
|
||||
<column name="maxCount" idlType="short" comment="Maximum number of licences which can be awarded."/>
|
||||
<column name="ignoreExpiration" idlType="boolean" comment="Ignore the expiration date , licence never expires."/>
|
||||
<column name="expiration" idlType="long long" comment="licence expiration date"/>
|
||||
<column name="refCount" idlType="short" comment="Number of used licences at time."/>
|
||||
</topicType>
|
||||
<topicType name="ServiceInformation" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="InformationBase">
|
||||
<column name="name" idlType="string"/>
|
||||
<column name="state" idlType="Core::RunningState"/>
|
||||
<column name="errorString" idlType="string"/>
|
||||
<column name="pid" idlType="long long"/>
|
||||
</topicType>
|
||||
<topicType name="GroupRequest">
|
||||
<column name="type" idlType="Core::ServiceGroupType"/>
|
||||
<column name="domainId" idlType="long"/>
|
||||
<column name="exercisePartitionId" idlType="long"/>
|
||||
<column name="replayPartitionId" idlType="long"/>
|
||||
<column name="requestId" idlType="long"/>
|
||||
<column name="additionalStartParameter" idlType="sequence<string>" comment="additional parameters to pass to the services to start"/>
|
||||
</topicType>
|
||||
<topicType name="LicenceRequest">
|
||||
<column name="name" idlType="string" comment="Contains the name of the service or functionality for which the licence is granted."/>
|
||||
</topicType>
|
||||
<topicType name="RestartServiceRequest">
|
||||
<column name="id" idlType="long"/>
|
||||
</topicType>
|
||||
</topicTypes>
|
||||
<functionTopics>
|
||||
<functionTopic name="FreeLicence" requestTopicType="LicenceRequest" replyTopicType="Core::FunctionReply" requestTopicName="FreeLicenceRequest" replyTopicName="FreeLicenceReply" routeInReplay="0"/>
|
||||
<functionTopic name="GetLicence" requestTopicType="LicenceRequest" replyTopicType="Core::FunctionReply" requestTopicName="GetLicenceRequest" replyTopicName="GetLicenceReply" routeInReplay="0"/>
|
||||
<functionTopic name="RestartGroup" requestTopicType="GroupRequest" replyTopicType="Core::FunctionReply" requestTopicName="RestartGroupRequest" replyTopicName="RestartGroupReply" routeInReplay="0"/>
|
||||
<functionTopic name="RestartService" requestTopicType="RestartServiceRequest" replyTopicType="Core::FunctionReply" requestTopicName="RestartServiceRequest" replyTopicName="RestartServiceReply" routeInReplay="0"/>
|
||||
<functionTopic name="StartGroup" requestTopicType="GroupRequest" replyTopicType="Core::FunctionReply" requestTopicName="StartGroupRequest" replyTopicName="StartGroupReply" routeInReplay="0"/>
|
||||
<functionTopic name="StopGroup" requestTopicType="GroupRequest" replyTopicType="Core::FunctionReply" requestTopicName="StopGroupRequest" replyTopicName="StopGroupReply" routeInReplay="0"/>
|
||||
</functionTopics>
|
||||
<enumerations>
|
||||
<enumeration name="TestEnum">
|
||||
<attribute key="ValOne" value="0"/>
|
||||
<attribute key="ValTwo" value="1"/>
|
||||
<attribute key="ValThree" value="2"/>
|
||||
</enumeration>
|
||||
</enumerations>
|
||||
</service>
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* The Service class holds information about a single service, which was read from a xxx_service.xml file.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#include "service.h"
|
||||
|
||||
#include <QtCore/QProcess>
|
||||
|
||||
#include "service_process.h"
|
||||
|
||||
int ServiceManagement::Service::idCounter = 0;
|
||||
|
||||
ServiceManagement::Service::Service(const QString &name, const QString &exe, const QString &dir, const QString &abbr, const QStringList &groups, bool isInDevMode, const int prio)
|
||||
: QObject()
|
||||
, m_name(name)
|
||||
, m_executable(exe)
|
||||
, m_directory(dir)
|
||||
, m_abbreviation(abbr)
|
||||
, m_groups(groups)
|
||||
, m_isInDevMode(isInDevMode)
|
||||
, m_id(idCounter ++)
|
||||
, m_licenceDonator(nullptr)
|
||||
, m_prio(prio)
|
||||
, m_logger(getLogger("Service"))
|
||||
{}
|
||||
|
||||
int ServiceManagement::Service::id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
const QString &ServiceManagement::Service::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
const int &ServiceManagement::Service::prio() const
|
||||
{
|
||||
return m_prio;
|
||||
}
|
||||
|
||||
const QString &ServiceManagement::Service::abbreviation() const
|
||||
{
|
||||
return m_abbreviation;
|
||||
}
|
||||
|
||||
const QStringList &ServiceManagement::Service::groups() const
|
||||
{
|
||||
return m_groups;
|
||||
}
|
||||
|
||||
bool ServiceManagement::Service::devMode() const
|
||||
{
|
||||
return m_isInDevMode;
|
||||
}
|
||||
|
||||
std::shared_ptr<ServiceManagement::LicenceDonator> ServiceManagement::Service::licenceDonator()
|
||||
{
|
||||
return m_licenceDonator;
|
||||
}
|
||||
|
||||
ServiceManagement::ServiceProcess *ServiceManagement::Service::toServiceProcess(const GroupRequest &groupData)
|
||||
{
|
||||
ServiceProcess *result = new ServiceProcess(groupData, this);
|
||||
|
||||
result->setProgram(m_directory + QLatin1Char('/') + m_executable);
|
||||
result->setWorkingDirectory(m_directory);
|
||||
|
||||
connect(result, &ServiceProcess::newServiceInformation,
|
||||
this, &Service::newServiceInfo);
|
||||
|
||||
connect(result, &ServiceProcess::destroyed,
|
||||
this, &Service::onServiceProcessDestroyed);
|
||||
|
||||
m_procs.append(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ServiceManagement::ServiceProcess *ServiceManagement::Service::procFromPid(qint64 pid)
|
||||
{
|
||||
for (ServiceProcess *currentProc : qAsConst(m_procs))
|
||||
{
|
||||
if (currentProc->usablePid() == pid)
|
||||
return currentProc;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ServiceManagement::ServiceProcess *ServiceManagement::Service::procFromServiceProcId(int32_t id)
|
||||
{
|
||||
for (ServiceProcess *currentProc : qAsConst(m_procs))
|
||||
{
|
||||
if (currentProc->serviceProcessId() == id)
|
||||
return currentProc;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ServiceManagement::Service::stopAllProcs()
|
||||
{
|
||||
for (ServiceProcess *currentProc : qAsConst(m_procs))
|
||||
currentProc->terminate();
|
||||
}
|
||||
|
||||
void ServiceManagement::Service::waitForStopped()
|
||||
{
|
||||
for (ServiceProcess *currentProc : qAsConst(m_procs))
|
||||
currentProc->waitForFinished(5000);
|
||||
}
|
||||
|
||||
void ServiceManagement::Service::onServiceProcessDestroyed(QObject *obj)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "onServiceProcessDestroyed: " << obj;
|
||||
ServiceProcess *sp = static_cast<ServiceProcess*>(obj);
|
||||
for (int i = 0; i < m_procs.size(); ++i)
|
||||
{
|
||||
if (m_procs.at(i) == sp)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Found... - removing";
|
||||
m_procs.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QVector<ServiceManagement::ServiceProcess *> &ServiceManagement::Service::allProcs() const
|
||||
{
|
||||
return m_procs;
|
||||
}
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* The Service class holds information about a single service, which was read from a xxx_service.xml file.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVector>
|
||||
|
||||
#include <ServiceManagement/scm.hpp>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
class QProcess;
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class LicenceDonator;
|
||||
class ServiceInformation;
|
||||
class ServiceProcess;
|
||||
|
||||
class Service final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Service(const QString &name, const QString &exe, const QString &dir, const QString &abbr, const QStringList &groups, bool isInDevMode, const int prio);
|
||||
|
||||
int id() const;
|
||||
const QString &name() const;
|
||||
const int &prio() const;
|
||||
const QString &abbreviation() const;
|
||||
const QStringList &groups() const;
|
||||
bool devMode() const;
|
||||
std::shared_ptr<LicenceDonator> licenceDonator();
|
||||
|
||||
ServiceProcess *toServiceProcess(const GroupRequest &groupData);
|
||||
ServiceProcess *procFromPid(qint64 pid);
|
||||
ServiceProcess *procFromServiceProcId(int32_t id);
|
||||
const QVector<ServiceProcess *> &allProcs() const;
|
||||
void stopAllProcs();
|
||||
void waitForStopped();
|
||||
|
||||
void setLicenceDonator(const std::shared_ptr<LicenceDonator> &licenceDonator)
|
||||
{
|
||||
m_licenceDonator = licenceDonator;
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void onServiceProcessDestroyed(QObject *obj);
|
||||
|
||||
Q_SIGNALS:
|
||||
void newServiceInfo(const ServiceInformation &info);
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QString m_executable;
|
||||
QString m_directory;
|
||||
QString m_abbreviation;
|
||||
QStringList m_groups;
|
||||
bool m_isInDevMode;
|
||||
unsigned int m_id;
|
||||
QVector<ServiceProcess *> m_procs;
|
||||
std::shared_ptr<LicenceDonator> m_licenceDonator;
|
||||
int m_prio;
|
||||
LOGGER_T m_logger;
|
||||
|
||||
static int32_t idCounter;
|
||||
};
|
||||
} // namespace ServiceManagement
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Abstract class to avoid a direct connection between ServiceHandler and GroupHandler class
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-29
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <ServiceManagement/scm.hpp>
|
||||
|
||||
enum class DataModification;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class CurrentRunningState;
|
||||
}
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class ServiceProcess;
|
||||
class DdsHandler;
|
||||
|
||||
class ServiceDonator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ServiceDonator() : QObject() {}
|
||||
virtual ServiceProcess *getProcess(int serviceId,
|
||||
const GroupRequest &groupData) = 0;
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual void onNewServiceInfoFromDds(const Core::CurrentRunningState &data, DataModification type) = 0;
|
||||
};
|
||||
|
||||
} // namespace ServiceManagement
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* The ServiceHandler class handles all service related classes and writes the services state to DDS.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#include "service_handler.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
|
||||
#include "service.h"
|
||||
#include "service_process.h"
|
||||
#include "dds_handler.h"
|
||||
#include "settings.h"
|
||||
#include <LtsUtilsCore/data_modification_enum.h>
|
||||
|
||||
ServiceManagement::ServiceHandler::ServiceHandler(const std::shared_ptr<DdsHandler> &ddsHandler,
|
||||
const std::shared_ptr<LicenceDonator> &licenceDonator)
|
||||
: ServiceDonator()
|
||||
, Interface::Fts::RestartServiceRequestHandler(ddsHandler->fts())
|
||||
, m_heartBeatObserverTimer(new QTimer(this))
|
||||
, m_ddsHandler(ddsHandler)
|
||||
, m_licenceDonator(licenceDonator)
|
||||
, m_logger(getLogger("ServiceHandler"))
|
||||
{
|
||||
connect(qApp, &QCoreApplication::aboutToQuit,
|
||||
this, &ServiceHandler::stopAllServices);
|
||||
}
|
||||
|
||||
ServiceManagement::ServiceHandler::~ServiceHandler()
|
||||
{
|
||||
qDeleteAll(m_services);
|
||||
m_services.clear();
|
||||
}
|
||||
|
||||
bool ServiceManagement::ServiceHandler::init()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Initializing ServiceHandler ...";
|
||||
|
||||
connect(m_heartBeatObserverTimer, &QTimer::timeout,
|
||||
this, &ServiceHandler::onHeartBeatObserverTimerTimeout);
|
||||
|
||||
|
||||
HEARTBEAT_OBSERVER_INTERVAL = Settings::observerIntervall();
|
||||
DEBUG_LOG(m_logger) << HEARTBEAT_OBSERVER_INTERVAL;
|
||||
m_heartBeatObserverTimer->start(HEARTBEAT_OBSERVER_INTERVAL);
|
||||
|
||||
DEBUG_LOG(m_logger) << "ServiceHandler successfully initialized.";
|
||||
return true;
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceHandler::addService(ServiceManagement::Service *service)
|
||||
{
|
||||
service->setParent(this);
|
||||
service->setLicenceDonator(m_licenceDonator);
|
||||
|
||||
connect(service, &Service::newServiceInfo,
|
||||
this, &ServiceHandler::onNewServiceInfoFromProc);
|
||||
|
||||
m_services.append(service);
|
||||
}
|
||||
|
||||
ServiceManagement::ServiceProcess *ServiceManagement::ServiceHandler::getProcess(int serviceId,
|
||||
const GroupRequest &groupData)
|
||||
{
|
||||
Service *service = serviceFromId(serviceId);
|
||||
|
||||
if (service == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return service->toServiceProcess(groupData);
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceHandler::onRestartServiceRequest(const rti::core::SampleIdentity &cookie,
|
||||
const RestartServiceRequest &request)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "onRestartServiceRequest " << request;
|
||||
|
||||
ServiceProcess *proc = serviceProcessFromServiceProcId(request.id());
|
||||
|
||||
Core::FunctionReplyObject ret(request.id());
|
||||
if (proc == nullptr)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got restart request for non existing service ID "
|
||||
<< request.id();
|
||||
ret.setStr(tr_lts("dds", "There is no ServiceProcess with id %1")).arg(request.id());
|
||||
m_ddsHandler->fts()->restartServiceReply(cookie, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "Restart request for service "
|
||||
<< proc->serviceName()
|
||||
<< ", domain "
|
||||
<< proc->domainId()
|
||||
<< ", exercisePartitionId "
|
||||
<< proc->exercisePartitionId()
|
||||
<< ", replayPartitionId "
|
||||
<< proc->replayPartitionId();
|
||||
|
||||
proc->restart();
|
||||
|
||||
m_ddsHandler->fts()->restartServiceReply(cookie, ret);
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceHandler::stopAllServices()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Stopping all Services ...";
|
||||
|
||||
// shutdown async
|
||||
for (Service *service : qAsConst(m_services))
|
||||
service->stopAllProcs();
|
||||
for (Service *service : qAsConst(m_services))
|
||||
service->waitForStopped();
|
||||
|
||||
INFO_LOG(m_logger) << "... all Services stopped :)";
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceHandler::onNewServiceInfoFromProc(const ServiceInformation &info)
|
||||
{
|
||||
TRACE_LOG(m_logger) << "Publishing new ServiceInformation: " << info;
|
||||
|
||||
m_ddsHandler->dts()->publish(info);
|
||||
|
||||
m_unknownPidsWarned.remove(info.pid());
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceHandler::onNewServiceInfoFromDds(const Core::CurrentRunningState &data, DataModification type)
|
||||
{
|
||||
if (type == DataModification::eRemoved)
|
||||
return;
|
||||
|
||||
TRACE_LOG(m_logger) << "Got new CurrentRunningState: " << data;
|
||||
|
||||
if (!data.pid())
|
||||
{
|
||||
INFO_LOG(m_logger) << "PID 0 is not allowed.";
|
||||
return;
|
||||
}
|
||||
|
||||
ServiceProcess *proc = serviceProcessFromPid(data.pid());
|
||||
Core::CurrentRunningState retData = data;
|
||||
|
||||
if (proc == nullptr)
|
||||
{
|
||||
bool found = false;
|
||||
for (Service *currentService : qAsConst(m_services))
|
||||
{
|
||||
for (ServiceProcess *currentProc : qAsConst(currentService->allProcs()))
|
||||
{
|
||||
if (currentProc->remotePid() == data.pid())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Found my prog " << data.pid() << " to "<< currentProc->pid() << " " << currentProc->serviceName();
|
||||
proc = currentProc;
|
||||
retData.pid() = proc->processId();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (proc == nullptr)
|
||||
{
|
||||
if (!m_unknownPidsWarned.contains(data.pid()))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Got CurrentRunningState for non existing process with PID "
|
||||
<< data.pid();
|
||||
m_unknownPidsWarned += data.pid();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
proc->setNewStateFromHeartbeat(retData);
|
||||
}
|
||||
|
||||
ServiceManagement::Service *ServiceManagement::ServiceHandler::serviceFromId(int id)
|
||||
{
|
||||
for (Service *service : qAsConst(m_services))
|
||||
{
|
||||
if (service->id() == id)
|
||||
return service;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ServiceManagement::ServiceProcess *ServiceManagement::ServiceHandler::serviceProcessFromPid(qint64 pid)
|
||||
{
|
||||
for (Service *service : qAsConst(m_services))
|
||||
{
|
||||
ServiceProcess *result = service->procFromPid(pid);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ServiceManagement::ServiceProcess *ServiceManagement::ServiceHandler::serviceProcessFromServiceProcId(int32_t id)
|
||||
{
|
||||
for (Service *service : qAsConst(m_services))
|
||||
{
|
||||
ServiceProcess *result = service->procFromServiceProcId(id);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceHandler::onHeartBeatObserverTimerTimeout()
|
||||
{
|
||||
const qint64 tNow = QDateTime::currentMSecsSinceEpoch();
|
||||
const qint64 minHeartbeatAge = tNow - HEARTBEAT_OBSERVER_INTERVAL;
|
||||
const qint64 minHeartbeatAgeStartup = tNow - HEARTBEAT_OBSERVER_INTERVAL * 6;
|
||||
|
||||
for (Service *currentService : qAsConst(m_services))
|
||||
{
|
||||
for (ServiceProcess *currentProc : qAsConst(currentService->allProcs()))
|
||||
{
|
||||
// dev mode for this proc -> we're not repsonsible for
|
||||
if (currentProc->devMode())
|
||||
continue;
|
||||
// stopped on operator demand -> we're not responsible for
|
||||
if (currentProc->stoppedFromRequest())
|
||||
continue;
|
||||
// there is something terribly wrong -> ignore
|
||||
if (currentProc->failedStartingAttempts() > 3)
|
||||
continue;
|
||||
|
||||
const qint64 minAge = (currentProc->runningState() == Core::RunningState_def::Starting)
|
||||
? minHeartbeatAgeStartup
|
||||
: minHeartbeatAge;
|
||||
|
||||
if (currentProc->lastHeartbeatMSec() < minAge)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Heartbeat TIMEOUT for " << currentProc->debugInfo()
|
||||
<< " - Restarting ..."
|
||||
<< " min heartbeat age: " << QDateTime::fromMSecsSinceEpoch(minHeartbeatAge).toString(QStringLiteral("HH:mm:ss"));
|
||||
currentProc->addStartAttempt();
|
||||
currentProc->restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* The ServiceHandler class handles all service related classes and writes the services state to DDS.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QSet>
|
||||
#include <QVector>
|
||||
|
||||
#include "logger.h"
|
||||
#include "service_donator.h"
|
||||
#include <ServiceManagement/scm_fts.h>
|
||||
|
||||
class QTimer;
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class DdsHandler;
|
||||
class LicenceDonator;
|
||||
class Service;
|
||||
class ServiceInformation;
|
||||
class ServiceProcess;
|
||||
|
||||
class ServiceHandler final
|
||||
: public ServiceDonator
|
||||
, protected Interface::Fts::RestartServiceRequestHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServiceHandler(const std::shared_ptr<DdsHandler> &ddsHandler,
|
||||
const std::shared_ptr<LicenceDonator> &licenceDonator);
|
||||
~ServiceHandler();
|
||||
|
||||
bool init();
|
||||
void addService(Service *service);
|
||||
|
||||
ServiceProcess *getProcess(int serviceId, const GroupRequest &groupData) override final;
|
||||
|
||||
void onRestartServiceRequest(const rti::core::SampleIdentity &cookie,
|
||||
const RestartServiceRequest &request) override final;
|
||||
|
||||
void stopAllServices();
|
||||
|
||||
|
||||
private Q_SLOTS:
|
||||
void onNewServiceInfoFromProc(const ServiceInformation &info);
|
||||
void onNewServiceInfoFromDds(const Core::CurrentRunningState &data, DataModification type) override final;
|
||||
void onHeartBeatObserverTimerTimeout();
|
||||
|
||||
private: // methods
|
||||
Service *serviceFromId(int id);
|
||||
ServiceProcess *serviceProcessFromPid(qint64 pid);
|
||||
ServiceProcess *serviceProcessFromServiceProcId(int32_t id);
|
||||
|
||||
private: // attributes
|
||||
QVector<Service *> m_services;
|
||||
QTimer *m_heartBeatObserverTimer;
|
||||
std::shared_ptr<DdsHandler> m_ddsHandler;
|
||||
std::shared_ptr<LicenceDonator> m_licenceDonator;
|
||||
|
||||
int HEARTBEAT_OBSERVER_INTERVAL = 10000;
|
||||
QSet<qint64> m_unknownPidsWarned;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
} // namespace ServiceManagement
|
||||
+496
@@ -0,0 +1,496 @@
|
||||
/**
|
||||
* The ServiceProcess class represents a service which was startet as a process
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#include "service_process.h"
|
||||
#include "service.h"
|
||||
#include "licence_donator.h"
|
||||
#include "settings.h"
|
||||
#include <ServiceManagement/scm_dts_types.h>
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
int ServiceManagement::ServiceProcess::s_serviceProcessIdCounter = 0;
|
||||
|
||||
ServiceManagement::ServiceProcess::ServiceProcess(const GroupRequest &groupData, ServiceManagement::Service *parentService)
|
||||
: QProcess(parentService)
|
||||
, m_serviceId(parentService->id())
|
||||
, m_serviceProcessId(s_serviceProcessIdCounter++)
|
||||
, m_abbreviation(parentService->abbreviation())
|
||||
, m_name(parentService->name())
|
||||
, m_prio(parentService->prio())
|
||||
, m_groupData(groupData)
|
||||
, m_remotePid(-1)
|
||||
, m_runningState(Core::RunningState::NotYetStarted)
|
||||
, m_lastHeartbeatMSec(0)
|
||||
, m_parentService(parentService)
|
||||
, m_hasLicence(false)
|
||||
, m_isInDevMode(parentService->devMode())
|
||||
, m_failedStartAttemptCounter(0)
|
||||
, m_stoppedFromRequest(false)
|
||||
, m_pidProc(new QProcess(this))
|
||||
, m_logger(getLogger("ServiceProcess"))
|
||||
{
|
||||
QStringList args({QStringLiteral("-e"), QString::number(m_groupData.exercisePartitionId()),
|
||||
QStringLiteral("-d"), QString::number(m_groupData.domainId())});
|
||||
|
||||
bool isInReplayGroup = false;
|
||||
for(const QString ¤tGroup : parentService->groups())
|
||||
{
|
||||
if (currentGroup.startsWith(QLatin1String("Replay")) || currentGroup.startsWith(QLatin1String("Playback")))
|
||||
{
|
||||
isInReplayGroup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInReplayGroup)
|
||||
args << QStringLiteral("-r") << QString::number(m_groupData.replayPartitionId());
|
||||
args.reserve(args.size() + m_groupData.additionalStartParameter().size());
|
||||
for (const std::string &arg : m_groupData.additionalStartParameter())
|
||||
args << QString::fromStdString(arg);
|
||||
|
||||
setArguments(args);
|
||||
|
||||
#ifndef LTS_DEBUG
|
||||
if (m_name == QStringLiteral("HighLevelArchitecture"))
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<" START HLA REMOTE";
|
||||
killallRemote();
|
||||
|
||||
//QString sshCmd = QStringLiteral("ssh ctcap03 '/shd/CTC/SV/bin/svcHighLevelArchitecture %1 &'").arg(arguments().join(QLatin1Char(' ')));
|
||||
QStringList remoteCommand;
|
||||
remoteCommand.append(Settings::hlaRemoteServer());
|
||||
remoteCommand.append(QStringLiteral("/shd/CTC/SV/bin/svcHighLevelArchitecture "));
|
||||
remoteCommand.append(arguments());
|
||||
setArguments(remoteCommand);
|
||||
setProgram(QStringLiteral("ssh"));
|
||||
}
|
||||
|
||||
if (m_name == QStringLiteral("Buildings"))
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<" START Buildings Remote";
|
||||
killallRemote();
|
||||
|
||||
//QString sshCmd = QStringLiteral("ssh ctcap03 '/shd/CTC/SV/bin/svcHighLevelArchitecture %1 &'").arg(arguments().join(QLatin1Char(' ')));
|
||||
QStringList remoteCommand;
|
||||
remoteCommand.append(Settings::buildingsRemoteServer());
|
||||
remoteCommand.append(QStringLiteral("/shd/CTC/SV/bin/svcBuildings "));
|
||||
remoteCommand.append(arguments());
|
||||
setArguments(remoteCommand);
|
||||
setProgram(QStringLiteral("ssh"));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
connect(this, &ServiceProcess::stateChanged,
|
||||
this, &ServiceProcess::onNewInfoFromProc);
|
||||
}
|
||||
|
||||
ServiceManagement::ServiceProcess::~ServiceProcess()
|
||||
{
|
||||
disconnect(this);
|
||||
#ifndef LTS_DEBUG
|
||||
if (m_name == QStringLiteral("HighLevelArchitecture"))
|
||||
{
|
||||
killallRemote();
|
||||
}
|
||||
#endif
|
||||
if (state() == QProcess::Running)
|
||||
{
|
||||
terminate();
|
||||
waitForFinished(5000);
|
||||
if (state() == QProcess::Running)
|
||||
kill();
|
||||
}
|
||||
}
|
||||
|
||||
Core::RunningState ServiceManagement::ServiceProcess::runningState() const
|
||||
{
|
||||
return m_runningState;
|
||||
}
|
||||
|
||||
bool ServiceManagement::ServiceProcess::start(OpenMode mode)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Starting ServiceProcess "
|
||||
<< debugInfo()
|
||||
<< " executable: " << program()
|
||||
<< " in dir: " << workingDirectory()
|
||||
<< " with arguments " << arguments().join(QLatin1Char(' '))
|
||||
<< " prio=" << m_prio;
|
||||
if (m_parentService->licenceDonator()->getLicence(m_name))
|
||||
{
|
||||
m_hasLicence = true;
|
||||
m_stoppedFromRequest = false;
|
||||
#ifndef LTS_DEBUG
|
||||
if (m_name == QStringLiteral("HighLevelArchitecture"))
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<" START HLA REMOTE";
|
||||
|
||||
//QString sshCmd = QStringLiteral("ssh ctcap03 '/shd/CTC/SV/bin/svcHighLevelArchitecture %1 &echo $! '").arg(arguments().join(QLatin1Char(' ')));
|
||||
//DEBUG_LOG(m_logger) << sshCmd;
|
||||
setProgram(QStringLiteral("ssh"));
|
||||
|
||||
if (state() == QProcess::NotRunning)
|
||||
{
|
||||
QProcess::start(mode);
|
||||
waitForStarted(5000);
|
||||
}
|
||||
DEBUG_LOG(m_logger) << program() << " " << arguments().join(QLatin1Char(' '));
|
||||
|
||||
m_pidProc->kill();
|
||||
m_pidProc->setProgram(QStringLiteral("ssh"));
|
||||
//DEBUG_LOG(m_logger) << sshCmd;
|
||||
QStringList args({QStringLiteral("-e"), QString::number(m_groupData.exercisePartitionId()),
|
||||
QStringLiteral("-d"), QString::number(m_groupData.domainId())});
|
||||
setProgram(QStringLiteral("ssh"));
|
||||
QStringList remoteCommand2;
|
||||
remoteCommand2.append(Settings::hlaRemoteServer());
|
||||
remoteCommand2.append(QStringLiteral("pgrep -f \'svcHighLevelArchitecture %1\'").arg(args.join(QLatin1Char(' '))));
|
||||
m_pidProc->setArguments(remoteCommand2);
|
||||
//args.append(QStringLiteral("pkill -9 -f \"svcHighLevelArchitecture %1\"").arg(arg.join(QLatin1Char(' '))));
|
||||
|
||||
|
||||
m_pidProc->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_pidProc->disconnect();
|
||||
connect(m_pidProc, &QProcess::readyRead, this , &ServiceProcess::onDataFromPidProc, Qt::UniqueConnection);
|
||||
|
||||
QTimer::singleShot(3000, this, [this](){
|
||||
m_pidProc->start();
|
||||
m_pidProc->waitForReadyRead(5000);
|
||||
});
|
||||
|
||||
QTimer::singleShot(15000, this, [this](){
|
||||
m_pidProc->start();
|
||||
m_pidProc->waitForReadyRead(5000);
|
||||
});
|
||||
|
||||
DEBUG_LOG(m_logger) << m_pidProc->program() << " " << m_pidProc->arguments().join(QLatin1Char(' '));
|
||||
|
||||
}else if (m_name == QStringLiteral("Buildings"))
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<" START Buildings REMOTE";
|
||||
setProgram(QStringLiteral("ssh"));
|
||||
//QString sshCmd = QStringLiteral("ssh ctcap03 '/shd/CTC/SV/bin/svcHighLevelArchitecture %1 &echo $! '").arg(arguments().join(QLatin1Char(' ')));
|
||||
//DEBUG_LOG(m_logger) << sshCmd;
|
||||
|
||||
if (state() == QProcess::NotRunning)
|
||||
{
|
||||
QProcess::start(mode);
|
||||
waitForStarted(5000);
|
||||
}
|
||||
DEBUG_LOG(m_logger) << program() << " " << arguments().join(QLatin1Char(' '));
|
||||
|
||||
|
||||
m_pidProc->kill();
|
||||
m_pidProc->setProgram(QStringLiteral("ssh"));
|
||||
//DEBUG_LOG(m_logger) << sshCmd;
|
||||
QStringList args({QStringLiteral("-e"), QString::number(m_groupData.exercisePartitionId()),
|
||||
QStringLiteral("-d"), QString::number(m_groupData.domainId())});
|
||||
setProgram(QStringLiteral("ssh"));
|
||||
QStringList remoteCommand2;
|
||||
remoteCommand2.append(Settings::hlaRemoteServer());
|
||||
remoteCommand2.append(QStringLiteral("pgrep -f \'svcBuildings %1\'").arg(args.join(QLatin1Char(' '))));
|
||||
m_pidProc->setArguments(remoteCommand2);
|
||||
//args.append(QStringLiteral("pkill -9 -f \"svcHighLevelArchitecture %1\"").arg(arg.join(QLatin1Char(' '))));
|
||||
|
||||
|
||||
m_pidProc->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_pidProc->disconnect();
|
||||
|
||||
connect(m_pidProc, &QProcess::readyRead, this , &ServiceProcess::onDataFromPidProc, Qt::UniqueConnection);
|
||||
|
||||
QTimer::singleShot(9000, this, [this](){
|
||||
m_pidProc->start();
|
||||
m_pidProc->waitForReadyRead(5000);
|
||||
});
|
||||
|
||||
|
||||
QTimer::singleShot(15000, this, [this](){
|
||||
m_pidProc->start();
|
||||
m_pidProc->waitForReadyRead(5000);
|
||||
});
|
||||
|
||||
DEBUG_LOG(m_logger) << m_pidProc->program() << " " << m_pidProc->arguments().join(QLatin1Char(' '));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (state() == QProcess::NotRunning)
|
||||
QProcess::start(mode);
|
||||
}
|
||||
|
||||
|
||||
m_lastHeartbeatMSec = QDateTime::currentMSecsSinceEpoch();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not start service "
|
||||
<< debugInfo()
|
||||
<< " because there is no licence for it.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int ServiceManagement::ServiceProcess::serviceProcessId() const
|
||||
{
|
||||
return m_serviceProcessId;
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceProcess::setNewStateFromHeartbeat(const Core::CurrentRunningState &data)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Heartbeat for " << debugInfo() << ", running state: " << data.runningState();
|
||||
m_failedStartAttemptCounter = 0;
|
||||
m_lastHeartbeatMSec = QDateTime::currentMSecsSinceEpoch();
|
||||
m_runningState = data.runningState();
|
||||
m_ports = data.ports();
|
||||
|
||||
std::string es;
|
||||
if (data.errorString().is_set())
|
||||
es = data.errorString().get();
|
||||
|
||||
ServiceInformation info(m_serviceProcessId,
|
||||
m_lastHeartbeatMSec,
|
||||
m_name.toStdString(),
|
||||
m_runningState,
|
||||
es,
|
||||
usablePid());
|
||||
|
||||
emit newServiceInformation(info);
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceProcess::killallRemote()
|
||||
{
|
||||
QStringList arg({QStringLiteral("-e"), QString::number(m_groupData.exercisePartitionId()),
|
||||
QStringLiteral("-d"), QString::number(m_groupData.domainId())});
|
||||
QProcess sh;
|
||||
sh.setProgram(QStringLiteral("ssh"));
|
||||
QStringList args;
|
||||
// QString killForm = QLatin1String("");
|
||||
QString killForm = QStringLiteral("-15");
|
||||
if (m_name == QStringLiteral("Buildings"))
|
||||
args.append(Settings::buildingsRemoteServer());
|
||||
else
|
||||
{
|
||||
args.append(Settings::hlaRemoteServer());
|
||||
killForm = QStringLiteral("-9");
|
||||
}
|
||||
|
||||
args.append(QStringLiteral("pkill %1 -f \"%2 %3\"").arg(killForm, m_name,arg.join(QLatin1Char(' '))));
|
||||
sh.setArguments(args);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
sh.startDetached();
|
||||
#else
|
||||
sh.start();
|
||||
#endif DEBUG_LOG(m_logger) << "kill remote " << sh.program() << " " << args.join(QLatin1Char(' '));
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceProcess::restart()
|
||||
{
|
||||
#ifndef LTS_DEBUG
|
||||
|
||||
if (m_name == QStringLiteral("HighLevelArchitecture") || m_name == QStringLiteral("Buildings"))
|
||||
{
|
||||
killallRemote();
|
||||
}
|
||||
#endif
|
||||
// else
|
||||
terminate(); // already trigger terminate here to shut down apps in parallel
|
||||
QTimer::singleShot(0, this, &ServiceProcess::doRestart);
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceProcess::doRestart()
|
||||
{
|
||||
if (m_hasLicence)
|
||||
{
|
||||
m_hasLicence = false;
|
||||
m_parentService->licenceDonator()->freeLicence(m_name);
|
||||
}
|
||||
#ifndef LTS_DEBUG
|
||||
|
||||
if (m_name == QStringLiteral("HighLevelArchitecture"))
|
||||
{
|
||||
//remoteKill
|
||||
killallRemote();
|
||||
}
|
||||
#endif
|
||||
// else
|
||||
{
|
||||
terminate();
|
||||
waitForFinished(5000);
|
||||
if (state() == QProcess::Running)
|
||||
kill();
|
||||
|
||||
}
|
||||
|
||||
QTimer::singleShot(100, this, [this]{ start(); });
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceProcess::onDataFromPidProc()
|
||||
{
|
||||
const QByteArray &ret = m_pidProc->readAll();
|
||||
FATAL_LOG(m_logger) << "New Data Id from remote" << ret;
|
||||
int newId = ret.trimmed().toInt();
|
||||
if (newId > 0)
|
||||
{
|
||||
m_remotePid = newId;
|
||||
FATAL_LOG(m_logger) << "New QProcess Id from remote" << m_name << " pid= "<< m_remotePid ;
|
||||
m_pidProc->kill();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceProcess::stopFromRequest()
|
||||
{
|
||||
m_stoppedFromRequest = true;
|
||||
QProcess::terminate();
|
||||
}
|
||||
|
||||
unsigned short ServiceManagement::ServiceProcess::failedStartingAttempts() const
|
||||
{
|
||||
return m_failedStartAttemptCounter;
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceProcess::addStartAttempt()
|
||||
{
|
||||
++m_failedStartAttemptCounter;
|
||||
}
|
||||
|
||||
qint64 ServiceManagement::ServiceProcess::lastHeartbeatMSec() const
|
||||
{
|
||||
return m_lastHeartbeatMSec;
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceProcess::setLastHeartbeatMSec(qint64 lastHeartbeatMSec)
|
||||
{
|
||||
m_lastHeartbeatMSec = lastHeartbeatMSec;
|
||||
}
|
||||
|
||||
QString ServiceManagement::ServiceProcess::serviceName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
QString ServiceManagement::ServiceProcess::abbreviation() const
|
||||
{
|
||||
return m_abbreviation;
|
||||
}
|
||||
|
||||
dds::core::optional<std::vector<Core::PortInformation> > ServiceManagement::ServiceProcess::ports() const
|
||||
{
|
||||
return m_ports;
|
||||
}
|
||||
|
||||
bool ServiceManagement::ServiceProcess::devMode() const
|
||||
{
|
||||
return m_isInDevMode;
|
||||
}
|
||||
|
||||
bool ServiceManagement::ServiceProcess::stoppedFromRequest() const
|
||||
{
|
||||
return m_stoppedFromRequest;
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceProcess::setNewStateFromChangeRequest(const Core::RunningState &newState)
|
||||
{
|
||||
if (m_runningState != newState)
|
||||
{
|
||||
switch (newState.underlying())
|
||||
{
|
||||
case Core::RunningState::Running:
|
||||
case Core::RunningState::Starting:
|
||||
if (state() != QProcess::Running)
|
||||
start();
|
||||
break;
|
||||
case Core::RunningState::StoppedNormally:
|
||||
case Core::RunningState::Stopping:
|
||||
terminate();
|
||||
waitForFinished(5000);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceManagement::ServiceProcess::onNewInfoFromProc(ProcessState state)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "New state for " << debugInfo() << ": " << state;
|
||||
QString errString;
|
||||
switch (state)
|
||||
{
|
||||
case QProcess::NotRunning:
|
||||
if (exitStatus() == QProcess::NormalExit)
|
||||
{
|
||||
if (exitCode() == 0)
|
||||
DEBUG_LOG(m_logger) << "Normal exit for " << debugInfo()
|
||||
<< ": " << readAllStandardError() << ", " << readAllStandardOutput();
|
||||
else
|
||||
ERROR_LOG(m_logger) << "Normal exit with exit code " << exitCode() << " for " << debugInfo()
|
||||
<< ": " << readAllStandardError() << ", " << readAllStandardOutput();
|
||||
m_runningState = Core::RunningState::StoppedNormally;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (error())
|
||||
{
|
||||
case QProcess::FailedToStart:
|
||||
errString = QLatin1String("Got QProcess::FailedToStart error from Service process with exit code ") + QString::number(exitCode());
|
||||
m_runningState = Core::RunningState::StartingError;
|
||||
break;
|
||||
case QProcess::Crashed:
|
||||
errString = QLatin1String("Got QProcess::Crashed error from Service Process with exit code ") + QString::number(exitCode());
|
||||
m_runningState = Core::RunningState::StoppedError;
|
||||
break;
|
||||
default:
|
||||
errString = QLatin1String("Got unkown error.");
|
||||
m_runningState = Core::RunningState::StoppedError;
|
||||
}
|
||||
errString += QLatin1String(" error string: ") + errorString();
|
||||
ERROR_LOG(m_logger) << "Crashed Error for " << debugInfo() << ", error: " << errorString() << ":\n"
|
||||
<< readAll();
|
||||
}
|
||||
m_parentService->licenceDonator()->freeLicence(m_name);
|
||||
m_hasLicence = false;
|
||||
m_lastHeartbeatMSec = 0;
|
||||
break;
|
||||
case QProcess::Starting:
|
||||
m_runningState = Core::RunningState::Starting;
|
||||
break;
|
||||
case QProcess::Running:
|
||||
DEBUG_LOG(m_logger) << "Process " << m_name
|
||||
<< " has now the PID " << usablePid();
|
||||
}
|
||||
|
||||
ServiceInformation info(m_serviceProcessId,
|
||||
QDateTime::currentMSecsSinceEpoch(),
|
||||
m_name.toStdString(),
|
||||
m_runningState,
|
||||
errString.toStdString(),
|
||||
usablePid());
|
||||
|
||||
emit newServiceInformation(info);
|
||||
}
|
||||
|
||||
QString ServiceManagement::ServiceProcess::debugInfo() const
|
||||
{
|
||||
QString partitionStr;
|
||||
if (replayPartitionId() > 0)
|
||||
partitionStr = QStringLiteral("%1 (replay)").arg(replayPartitionId());
|
||||
else
|
||||
partitionStr = QString::number(exercisePartitionId());
|
||||
return QStringLiteral("%1, domain: %2, partition: %3, PID: %4, last heartbeat: %5, arguments: '%6' this: 0x%7")
|
||||
.arg(serviceName())
|
||||
.arg(domainId())
|
||||
.arg(partitionStr)
|
||||
.arg(usablePid())
|
||||
.arg(QDateTime::fromMSecsSinceEpoch(m_lastHeartbeatMSec).toString(QStringLiteral("HH:mm:ss")),
|
||||
arguments().join(QLatin1Char(' ')))
|
||||
.arg(reinterpret_cast<qint64>(this), 8, 16, QLatin1Char('0'));
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* The ServiceProcess class represents a service which was startet as a process
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QDateTime>
|
||||
|
||||
#include <DDS/Core/core.hpp>
|
||||
#include <ServiceManagement/scm.hpp>
|
||||
#include "logger.h"
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
class Service;
|
||||
class ServiceInformation;
|
||||
|
||||
class ServiceProcess : public QProcess
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServiceProcess(const GroupRequest &groupData, Service *parentService);
|
||||
~ServiceProcess();
|
||||
|
||||
Core::RunningState runningState() const;
|
||||
int serviceProcessId() const;
|
||||
qint64 lastHeartbeatMSec() const;
|
||||
void setLastHeartbeatMSec(qint64 lastHeartbeatMSec);
|
||||
int32_t domainId() const { return m_groupData.domainId(); }
|
||||
int32_t prio() const { return m_prio; }
|
||||
int32_t exercisePartitionId() const { return m_groupData.exercisePartitionId(); }
|
||||
int32_t replayPartitionId() const { return m_groupData.replayPartitionId(); }
|
||||
QString serviceName() const;
|
||||
QString abbreviation() const;
|
||||
dds::core::optional<std::vector<Core::PortInformation>> ports() const;
|
||||
bool devMode() const;
|
||||
bool stoppedFromRequest() const;
|
||||
|
||||
void setNewStateFromChangeRequest(const Core::RunningState &newState);
|
||||
void setNewStateFromHeartbeat(const Core::CurrentRunningState &data);
|
||||
void killallRemote();
|
||||
qint64 usablePid() const
|
||||
{
|
||||
#ifdef WIN32
|
||||
auto myPid = pid();
|
||||
if (!myPid)
|
||||
return 0;
|
||||
return myPid->dwProcessId;
|
||||
#else
|
||||
return pid();
|
||||
#endif
|
||||
}
|
||||
|
||||
void restart();
|
||||
void stopFromRequest();
|
||||
int remotePid() {return m_remotePid;};
|
||||
unsigned short failedStartingAttempts() const;
|
||||
|
||||
void addStartAttempt();
|
||||
QString debugInfo() const;
|
||||
Q_SIGNALS:
|
||||
void newServiceInformation(const ServiceInformation &info);
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual bool start(OpenMode mode = ReadWrite);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onNewInfoFromProc(QProcess::ProcessState state);
|
||||
void doRestart();
|
||||
void onDataFromPidProc();
|
||||
|
||||
private:
|
||||
const int m_serviceId;
|
||||
const int m_serviceProcessId;
|
||||
const QString m_abbreviation;
|
||||
const QString m_name;
|
||||
const int m_prio;
|
||||
const GroupRequest m_groupData;
|
||||
int m_remotePid;
|
||||
Core::RunningState m_runningState;
|
||||
dds::core::optional<std::vector<Core::PortInformation>> m_ports;
|
||||
qint64 m_lastHeartbeatMSec;
|
||||
Service *m_parentService;
|
||||
bool m_hasLicence;
|
||||
bool m_isInDevMode;
|
||||
unsigned short m_failedStartAttemptCounter;
|
||||
bool m_stoppedFromRequest;
|
||||
QProcess *m_pidProc;
|
||||
LOGGER_T m_logger;
|
||||
|
||||
static int s_serviceProcessIdCounter;
|
||||
};
|
||||
} // namespace ServiceManagement
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Settings for ServiceManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#include <QtCore/QSettings>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include <ngltools/ngltools_filesystem.h>
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
int ServiceManagement::Settings::observerIntervall()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("ServiceManagement/ObserverIntervall"), 10000).toInt();
|
||||
}
|
||||
|
||||
QString ServiceManagement::Settings::hlaRemoteServer()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("ServiceManagement/HlaRemoteServer"), QStringLiteral("exctds05")).toString();
|
||||
}
|
||||
|
||||
QString ServiceManagement::Settings::buildingsRemoteServer()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("ServiceManagement/BuildingsRemoteServer"), QStringLiteral("exctds06")).toString();
|
||||
}
|
||||
} // namespace ServiceManagement
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Settings for ServiceManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <QString>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class Settings;
|
||||
}
|
||||
|
||||
namespace ServiceManagement
|
||||
{
|
||||
namespace Settings
|
||||
{
|
||||
NGL::Settings *settings();
|
||||
void setIniFile(const QString &ini);
|
||||
int observerIntervall();
|
||||
QString hlaRemoteServer();
|
||||
QString buildingsRemoteServer();
|
||||
}
|
||||
|
||||
} // namespace ServiceManagement
|
||||
|
||||
Reference in new issue
Block a user