Continues migration with DDS, Common
This commit is contained in:
1 parent
9b15cee841
commit
fa48db111e
668 files changed
+111841
-2
No files matched your search
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
ADD_SUBDIRECTORY(DDSConnector)
|
||||
|
||||
ADD_SUBDIRECTORY(DTC)
|
||||
ADD_SUBDIRECTORY(DTS)
|
||||
ADD_SUBDIRECTORY(FTC)
|
||||
ADD_SUBDIRECTORY(FTS)
|
||||
ADD_SUBDIRECTORY(Generic)
|
||||
ADD_SUBDIRECTORY(Routing)
|
||||
|
||||
ADD_SUBDIRECTORY(Core)
|
||||
ADD_SUBDIRECTORY(QosProfile)
|
||||
|
||||
ADD_SUBDIRECTORY(DdsUnitTests)
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
INCLUDE(MacroRTIDDSGen)
|
||||
INCLUDE(MacroCodeGenIdl)
|
||||
|
||||
SET(xml_FILE core.xml)
|
||||
|
||||
#
|
||||
# --- Library svcCoreCommon / ddscore --------------------------------------------------------------------------------
|
||||
#
|
||||
SET(SRCS_common
|
||||
core_export.h
|
||||
function_reply_object.cpp
|
||||
function_reply_object.h
|
||||
logger.cpp
|
||||
logger.h
|
||||
)
|
||||
|
||||
# Generates core.idl / core_enums.idl
|
||||
CODEGEN_IDL_SVC(SRCS_common ${xml_FILE})
|
||||
|
||||
# Generate core.sql
|
||||
CODEGEN_SQL(SRCS_common ${xml_FILE})
|
||||
|
||||
# Generate *.cpp/*.hpp from core_enums.idl / core.idl
|
||||
RTIDDSGEN(SRCS_common core_enums.idl)
|
||||
RTIDDSGEN(SRCS_common core.idl)
|
||||
|
||||
# Generate core_dt[s,c]_types.h, core_ft[s,c]_types.h
|
||||
CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
|
||||
# generate core_[d|f]tc.h
|
||||
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE})
|
||||
|
||||
NGL_ADD_LIBRARY(WS AAR HLA SV ddscore SHARED ${SRCS_common})
|
||||
NGL_TARGET_LINK_LIBRARIES(ddscore
|
||||
PRIVATE
|
||||
NGLLogger
|
||||
PUBLIC
|
||||
LtsUtilsCoreHeaderOnly
|
||||
ddsdts
|
||||
Qt5::Core
|
||||
RTI::nddsc # DDS_GroupDataQosPolicy_initialize (wo auch immer das herkommt)
|
||||
RTI::nddscore # RTI_LOG_ANY_FAILURE_s (wo auch immer das herkommt)
|
||||
RTI::nddscpp2
|
||||
)
|
||||
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(ddscore
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(ddscore)
|
||||
|
||||
#
|
||||
# --- Library svcCoreClient --------------------------------------------------------------------------------
|
||||
#
|
||||
SET(SRCS_client
|
||||
core_export.h
|
||||
pacemaker.h
|
||||
pacemaker.cpp
|
||||
topic_lock_master.h
|
||||
topic_lock_master.cpp
|
||||
)
|
||||
|
||||
# generate core_[d|f]tc_impl.[cpp|h]
|
||||
CODEGEN_CLIENT_TOPICS_IMPL(SRCS_client ${xml_FILE})
|
||||
|
||||
NGL_ADD_LIBRARY(AAR SV WS HLA svcCoreClient SHARED ${SRCS_client})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(svcCoreClient
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../
|
||||
)
|
||||
|
||||
NGL_TARGET_LINK_LIBRARIES(svcCoreClient
|
||||
PRIVATE
|
||||
RTI::nddscpp2
|
||||
ddsconnector
|
||||
PUBLIC
|
||||
ddscore
|
||||
ddsftc
|
||||
)
|
||||
NGL_INSTALL_TARGET(svcCoreClient)
|
||||
|
||||
#
|
||||
# --- Library svcCoreServer --------------------------------------------------------------------------------
|
||||
# not used (no core service needed)
|
||||
#
|
||||
#
|
||||
# --- Topic Type Plugin --------------------------------------------------------------
|
||||
#
|
||||
CREATE_TOPIC_TYPE_PLUGIN(${xml_FILE})
|
||||
Executable
+204
@@ -0,0 +1,204 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<service namespace="Core" abbreviation="CORE" procNames="Core" exerciseDependency="Global">
|
||||
<rcs>
|
||||
<Id Id="$Id$"/>
|
||||
<HeadURL HeadURL="$HeadURL$"/>
|
||||
<Author Author="$Author$"/>
|
||||
</rcs>
|
||||
<tables>
|
||||
<table name="ft_reply_state" comment="static data (not project specific) generate from enum">
|
||||
<column name="id" datatype="integer" isPrimaryKey="1"/>
|
||||
<column name="name" datatype="text"/>
|
||||
</table>
|
||||
<table name="replay_type" comment="Type of replay (Data for reports/events or situation)">
|
||||
<column name="id" datatype="integer" isPrimaryKey="1"/>
|
||||
<column name="name" datatype="text"/>
|
||||
</table>
|
||||
<table name="running_state">
|
||||
<column name="id" datatype="integer" isPrimaryKey="1"/>
|
||||
<column name="name" datatype="text"/>
|
||||
</table>
|
||||
<table name="service_group_type">
|
||||
<column name="id" datatype="integer" isPrimaryKey="1"/>
|
||||
<column name="name" datatype="text"/>
|
||||
</table>
|
||||
<table name="z_reference">
|
||||
<column name="id" datatype="integer" isPrimaryKey="1"/>
|
||||
<column name="name" datatype="text"/>
|
||||
</table>
|
||||
</tables>
|
||||
<views/>
|
||||
<topicTypes>
|
||||
<topicType name="BaseEntity">
|
||||
<column name="id" idlType="long" isKey="1"/>
|
||||
<column name="name" idlType="string"/>
|
||||
<column name="isDeleted" idlType="boolean"/>
|
||||
<column name="isUsed" idlType="boolean" comment="Defines if it is used by others (i.e. foreign key constraint)"/>
|
||||
</topicType>
|
||||
<topicType name="BaseEntityTranslatable">
|
||||
<column name="id" idlType="long" isKey="1"/>
|
||||
<column name="name" idlType="string" isTranslatable="1"/>
|
||||
<column name="isDeleted" idlType="boolean"/>
|
||||
<column name="isUsed" idlType="boolean" comment="Defines if it is used by others (i.e. foreign key constraint)"/>
|
||||
</topicType>
|
||||
<topicType name="BaseEntityWithSampleTime" baseValueType="BaseEntity">
|
||||
<column name="sampleTime" idlType="long long" comment="Timestamp"/>
|
||||
</topicType>
|
||||
<topicType name="CurrentRunningState" isDataObject="1" routeToClient="0" routeToServer="0" routeInReplay="0">
|
||||
<column name="pid" idlType="long long" isKey="1"/>
|
||||
<column name="runningState" idlType="RunningState"/>
|
||||
<column name="ports" idlType="sequence<PortInformation>" isOptional="1"/>
|
||||
<column name="errorString" idlType="string" isOptional="1"/>
|
||||
</topicType>
|
||||
<topicType name="FunctionReply">
|
||||
<column name="id" idlType="long"/>
|
||||
<column name="errorString" idlType="string"/>
|
||||
<column name="errorParams" idlType="sequence<string>"/>
|
||||
<column name="additionalInfo" idlType="string"/>
|
||||
</topicType>
|
||||
<topicType name="FunctionReplyWithState" baseValueType="FunctionReply">
|
||||
<column name="replyState" idlType="FtReplyState"/>
|
||||
</topicType>
|
||||
<topicType name="GeoPositionAbsoluteTime" baseValueType="GeoPosition">
|
||||
<column name="absoluteTime" idlType="long long" comment="Timestamp"/>
|
||||
</topicType>
|
||||
<topicType name="GeoPositionRelativeTime" baseValueType="GeoPosition">
|
||||
<column name="relativeTime" idlType="long long" comment="Timestamp"/>
|
||||
</topicType>
|
||||
<topicType name="GeoPosition" baseValueType="GeoPosition2D">
|
||||
<column name="z" idlType="double" comment="height above ground"/>
|
||||
</topicType>
|
||||
<topicType name="GeoPosition2D">
|
||||
<column name="lon" idlType="double" comment="[m]"/>
|
||||
<column name="lat" idlType="double" comment="[m]"/>
|
||||
</topicType>
|
||||
<topicType name="LockEntityError">
|
||||
<column name="workstationName" idlType="string"/>
|
||||
<column name="operatorName" idlType="string"/>
|
||||
</topicType>
|
||||
<topicType name="TryLockEntityReply" baseValueType="FunctionReply">
|
||||
<column name="alreadyLocked" idlType="LockEntityError" isOptional="1"/>
|
||||
</topicType>
|
||||
<topicType name="LockedEntity" isDataObject="1" routeToClient="0" routeToServer="0" routeInReplay="0" baseValueType="LockedEntityBase">
|
||||
<column name="id" idlType="long long" isKey="1"/>
|
||||
</topicType>
|
||||
<topicType name="LockedEntityBase">
|
||||
<column name="operatorId" idlType="long"/>
|
||||
<column name="workstationId" idlType="long"/>
|
||||
<column name="exerciseId" idlType="long"/>
|
||||
<column name="entityId" idlType="long long"/>
|
||||
<column name="entityTypeName" idlType="string"/>
|
||||
</topicType>
|
||||
<topicType name="TryLockEntityRequest" baseValueType="LockedEntityBase">
|
||||
<column name="id" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="TryUnlockEntityRequest">
|
||||
<column name="id" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="Orientation">
|
||||
<column name="heave" idlType="double"/>
|
||||
<column name="pitch" idlType="double"/>
|
||||
<column name="roll" idlType="double"/>
|
||||
</topicType>
|
||||
<topicType name="PartitionTime" isDataObject="1" routeToClient="1" routeToServer="0" routeInReplay="0">
|
||||
<column name="timeStamp" idlType="long long"/>
|
||||
</topicType>
|
||||
<topicType name="PortInformation">
|
||||
<column name="number" idlType="long"/>
|
||||
<column name="type" idlType="PortType"/>
|
||||
<column name="properties" idlType="string"/>
|
||||
</topicType>
|
||||
<topicType name="RelativePosition">
|
||||
<column name="offsetX" idlType="double" comment="in meter"/>
|
||||
<column name="offsetY" idlType="double" comment="in meter"/>
|
||||
<column name="offsetZ" idlType="double" comment="in meter"/>
|
||||
</topicType>
|
||||
<topicType name="RelativePositionAbsoluteTime" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="RelativePosition">
|
||||
<column name="absoluteTime" idlType="long long" comment="TimeStamp"/>
|
||||
</topicType>
|
||||
<topicType name="RelativePositionRelativeTime" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="RelativePosition">
|
||||
<column name="relativeTime" idlType="long long" comment="TimeStamp"/>
|
||||
</topicType>
|
||||
<topicType name="RemoveElementRequest">
|
||||
<column name="id" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="ReplayPartition" isDataObject="1" routeToClient="1" routeToServer="0" routeInReplay="0">
|
||||
<column name="id" idlType="long" isKey="1"/>
|
||||
<column name="name" idlType="string"/>
|
||||
<column name="exerciseId" idlType="long"/>
|
||||
<column name="workstationId" idlType="long"/>
|
||||
<column name="operatorId" idlType="long"/>
|
||||
<column name="replayType" idlType="ReplayType"/>
|
||||
<column name="domainId" idlType="long"/>
|
||||
<column name="tcpConnectionUrl" idlType="string"/>
|
||||
</topicType>
|
||||
<topicType name="Velocity">
|
||||
<column name="x" idlType="double"/>
|
||||
<column name="y" idlType="double"/>
|
||||
<column name="z" idlType="double"/>
|
||||
</topicType>
|
||||
</topicTypes>
|
||||
<functionTopics>
|
||||
<functionTopic name="TryLockEntity" requestTopicType="TryLockEntityRequest" replyTopicType="TryLockEntityReply" requestTopicName="TryLockEntityRequest" replyTopicName="TryLockEntityReply" routeInReplay="1"/>
|
||||
<!-- <functionTopic name="TryLockEntity" requestTopicType="LockedEntityBase" replyTopicType="TryLockEntityReply" requestTopicName="TryLockEntityRequest" replyTopicName="TryLockEntityReply" routeInReplay="1"/>-->-->
|
||||
<functionTopic name="TryUnlockEntity" requestTopicType="TryUnlockEntityRequest" replyTopicType="FunctionReply" requestTopicName="TryUnlockEntityRequest" replyTopicName="TryUnlockEntityReply" routeInReplay="1"/>
|
||||
</functionTopics>
|
||||
<enumerations>
|
||||
<enumeration name="FtReplyState" table="ft_reply_state">
|
||||
<attribute key="NotStarted" value="0"/>
|
||||
<attribute key="InProgress" value="1"/>
|
||||
<attribute key="Completed" value="2"/>
|
||||
<attribute key="Error" value="4"/>
|
||||
</enumeration>
|
||||
<enumeration name="PortType">
|
||||
<attribute key="Unknown" value="0"/>
|
||||
<attribute key="WebSocketServer" value="1"/>
|
||||
<attribute key="WebSocketPositionServer" value="2"/>
|
||||
<attribute key="WebSocketAdlerServer" value="3"/>
|
||||
</enumeration>
|
||||
<enumeration name="PositionReportingSource">
|
||||
<attribute key="Gps" value="0"/>
|
||||
<attribute key="Wls" value="1"/>
|
||||
<attribute key="Ops" value="2"/>
|
||||
<attribute key="External" value="3"/>
|
||||
<attribute key="SetByOperator" value="4"/>
|
||||
<attribute key="MovementSimulation" value="5"/>
|
||||
<attribute key="OpsTunnel" value="6"/>
|
||||
<attribute key="OpsBuilding" value="7"/>
|
||||
</enumeration>
|
||||
<enumeration name="ReplayType" table="replay_type">
|
||||
<attribute key="UnknownData" value="0"/>
|
||||
<attribute key="SituationData" value="1"/>
|
||||
<attribute key="ReportData" value="2"/>
|
||||
<attribute key="EventData" value="3"/>
|
||||
<attribute key="OtherData" value="4"/>
|
||||
<attribute key="SnapshotData" value="5"/>
|
||||
</enumeration>
|
||||
<enumeration name="RunningState" table="running_state">
|
||||
<attribute key="NotYetStarted" value="0"/>
|
||||
<attribute key="Starting" value="1"/>
|
||||
<attribute key="StartingError" value="2"/>
|
||||
<attribute key="Running" value="3"/>
|
||||
<attribute key="Stopping" value="4"/>
|
||||
<attribute key="StoppedNormally" value="5"/>
|
||||
<attribute key="StoppedError" value="6"/>
|
||||
</enumeration>
|
||||
<enumeration name="ServiceGroupType" table="service_group_type">
|
||||
<attribute key="Master" value="0"/>
|
||||
<attribute key="Core" value="1"/>
|
||||
<attribute key="ExercisePreparation" value="2"/>
|
||||
<attribute key="ExerciseExecution" value="3"/>
|
||||
<attribute key="ExercisePaused" value="4"/>
|
||||
<attribute key="ExerciseAfterActionReview" value="5"/>
|
||||
<attribute key="Playback" value="6"/>
|
||||
</enumeration>
|
||||
<enumeration name="ZReference" table="z_reference">
|
||||
<attribute key="ZrUndefined" value="0"/>
|
||||
<attribute key="Zr_Unused" value="1"/>
|
||||
<attribute key="ZrRelativeAGL" value="100"/>
|
||||
<attribute key="ZrAbsolute" value="200"/>
|
||||
<attribute key="ZrDrapedOnGround" value="300"/>
|
||||
<attribute key="ZrRelativeToDefinedValue" value="400"/>
|
||||
</enumeration>
|
||||
</enumerations>
|
||||
</service>
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#ifndef CORE_COMMON_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define CORE_COMMON_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_DDSCORE_LIB)
|
||||
# define CORE_COMMON_EXPORT Q_DECL_EXPORT
|
||||
# ifndef WIN32
|
||||
# ifdef NDDSUSERDllExport
|
||||
# undef NDDSUSERDllExport
|
||||
# endif
|
||||
# define NDDSUSERDllExport Q_DECL_EXPORT
|
||||
# endif
|
||||
# else
|
||||
# define CORE_COMMON_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef CORE_CLIENT_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define CORE_CLIENT_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_SVCCORECLIENT_LIB)
|
||||
# define CORE_CLIENT_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define CORE_CLIENT_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef CORE_SERVER_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define CORE_SERVER_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_SVCCORESERVER_LIB)
|
||||
# define CORE_SERVER_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define CORE_SERVER_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
|
||||
|
||||
|
||||
#include "function_reply_object.h"
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
namespace Core
|
||||
{
|
||||
|
||||
FunctionReplyObject &FunctionReplyObject::arg(qint64 val)
|
||||
{
|
||||
m_args.push_back(std::to_string(val));
|
||||
return *this;
|
||||
}
|
||||
|
||||
FunctionReplyObject &FunctionReplyObject::arg(const std::string &str)
|
||||
{
|
||||
m_args.push_back(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FunctionReplyObject &FunctionReplyObject::arg(const QString &str)
|
||||
{
|
||||
m_args.push_back(str.toStdString());
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string FunctionReplyObject::operator()() const
|
||||
{
|
||||
if (m_args.empty())
|
||||
return m_str;
|
||||
|
||||
QString ret(QString::fromStdString(m_str));
|
||||
for (const std::string &s : m_args)
|
||||
ret = ret.arg(QString::fromStdString(s));
|
||||
return ret.toStdString();
|
||||
}
|
||||
|
||||
QString FunctionReplyObject::translated() const
|
||||
{
|
||||
QString ret(QCoreApplication::translate("dds", m_str.c_str()));
|
||||
for (const std::string &s : m_args)
|
||||
ret = ret.arg(QString::fromStdString(s));
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core_export.h"
|
||||
|
||||
#ifndef tr_lts
|
||||
#define tr_lts(ctx, t) (t)
|
||||
#endif
|
||||
|
||||
namespace Core
|
||||
{
|
||||
|
||||
|
||||
|
||||
class CORE_COMMON_EXPORT FunctionReplyObject
|
||||
{
|
||||
public:
|
||||
explicit FunctionReplyObject(int id = -1, const char *str = nullptr)
|
||||
: m_str(str ? str : std::string())
|
||||
, m_id(id)
|
||||
{}
|
||||
|
||||
explicit FunctionReplyObject(const char *str)
|
||||
: m_str(str ? str : std::string())
|
||||
{}
|
||||
|
||||
explicit FunctionReplyObject(int id, const std::string &errStr, const std::vector<std::string> &errArgs)
|
||||
: m_str(errStr)
|
||||
, m_args(errArgs)
|
||||
, m_id(id)
|
||||
{}
|
||||
|
||||
explicit FunctionReplyObject(int id, std::string &&errStr, std::vector<std::string> &&errArgs)
|
||||
: m_str(std::move(errStr))
|
||||
, m_args(std::move(errArgs))
|
||||
, m_id(id)
|
||||
{}
|
||||
|
||||
explicit FunctionReplyObject(int id, const std::string &errStr, const std::vector<std::string> &errArgs, const std::string &additionalInfo)
|
||||
: m_str(errStr)
|
||||
, m_additionalInfo(additionalInfo)
|
||||
, m_args(errArgs)
|
||||
, m_id(id)
|
||||
{}
|
||||
|
||||
explicit FunctionReplyObject(int id, std::string &&errStr, std::vector<std::string> &&errArgs, std::string &&additionalInfo)
|
||||
: m_str(std::move(errStr))
|
||||
, m_additionalInfo(std::move(additionalInfo))
|
||||
, m_args(std::move(errArgs))
|
||||
, m_id(id)
|
||||
{}
|
||||
|
||||
~FunctionReplyObject() = default;
|
||||
|
||||
|
||||
FunctionReplyObject &arg(qint64 val);
|
||||
FunctionReplyObject &arg(const std::string &str);
|
||||
FunctionReplyObject &arg(const QString &str);
|
||||
|
||||
|
||||
|
||||
bool error() const { return !m_str.empty(); }
|
||||
void setId(int id) { m_id = id; }
|
||||
FunctionReplyObject &setStr(const char *str) { m_str = str; return *this; }
|
||||
FunctionReplyObject &setAdditionalInfo(const char *additionalInfo) { m_additionalInfo = additionalInfo; return *this; }
|
||||
|
||||
int id() const { return m_id; }
|
||||
std::string str() const { return m_str; }
|
||||
std::string additionalInfo() const { return m_additionalInfo; }
|
||||
|
||||
std::vector<std::string> args() const { return m_args; }
|
||||
|
||||
|
||||
|
||||
|
||||
std::string operator()() const;
|
||||
|
||||
|
||||
QString translated() const;
|
||||
|
||||
int64_t uniqueId() const { return m_uniqueId; }
|
||||
void setUniqueId(int64_t uniqueId) { m_uniqueId = uniqueId; }
|
||||
private:
|
||||
std::string m_str;
|
||||
std::string m_additionalInfo;
|
||||
std::vector<std::string> m_args;
|
||||
int64_t m_uniqueId = 0;
|
||||
int m_id = -1;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace Core
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "CORE.";
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include "core_export.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
using LOGGER_T = NGL::INGLLogger&;
|
||||
CORE_COMMON_EXPORT LOGGER_T getLogger(const char *name);
|
||||
}
|
||||
Executable
+117
@@ -0,0 +1,117 @@
|
||||
|
||||
|
||||
|
||||
#include "pacemaker.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
#include <DDS/DTS/generic_dts.h>
|
||||
#include <DDS/DTS/generic_dts_impl.h>
|
||||
#include <DDS/DDSConnector/dds_connector.h>
|
||||
|
||||
namespace Core
|
||||
{
|
||||
Pacemaker::Pacemaker(DDSConnector::DdsConnector *ddsConnector)
|
||||
: QObject(nullptr)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Core.Pacemaker"))
|
||||
{
|
||||
init(ddsConnector);
|
||||
}
|
||||
|
||||
Pacemaker::~Pacemaker()
|
||||
{
|
||||
m_paceThread->requestInterruption();
|
||||
m_paceThread->quit();
|
||||
m_paceThread->wait();
|
||||
delete m_paceThread;
|
||||
m_publishTimer->deleteLater();
|
||||
delete m_dtsCurrentRunningState;
|
||||
}
|
||||
|
||||
|
||||
void Pacemaker::setNewState(Core::RunningState newState, const QString &errorString)
|
||||
{
|
||||
if (QThread::currentThread() == this->thread())
|
||||
setNewStateInternal(newState, errorString);
|
||||
else
|
||||
QTimer::singleShot(0, this, [this, newState, errorString]() { setNewStateInternal(newState, errorString); });
|
||||
}
|
||||
|
||||
void Pacemaker::setNewStateInternal(Core::RunningState newState, const QString &errorString)
|
||||
{
|
||||
m_state.runningState(newState);
|
||||
if (errorString.isEmpty())
|
||||
m_state.errorString().reset();
|
||||
else
|
||||
m_state.errorString(errorString.toStdString());
|
||||
publishState();
|
||||
}
|
||||
|
||||
void Pacemaker::addPort(unsigned short number, Core::PortType portType, const QString &properties)
|
||||
{
|
||||
if (QThread::currentThread() == this->thread())
|
||||
addPortInternal(number, portType, properties);
|
||||
else
|
||||
QTimer::singleShot(0, this, [this, number, portType, properties]() { addPortInternal(number, portType, properties); });
|
||||
}
|
||||
|
||||
void Pacemaker::addPortInternal(unsigned short number, Core::PortType portType,const QString &properties)
|
||||
{
|
||||
if (!m_state.ports().is_set())
|
||||
m_state.ports() = std::vector<Core::PortInformation>();
|
||||
m_state.ports().get().push_back(Core::PortInformation(number, portType, properties.toStdString()));
|
||||
|
||||
publishState();
|
||||
}
|
||||
|
||||
void Pacemaker::publishState()
|
||||
{
|
||||
if (!m_dtsCurrentRunningState)
|
||||
return;
|
||||
m_dtsCurrentRunningState->write(m_state);
|
||||
}
|
||||
|
||||
void Pacemaker::init(DDSConnector::DdsConnector *ddsConnector)
|
||||
{
|
||||
m_dtsCurrentRunningState = ddsConnector->createDTS<DTS::CurrentRunningStateImpl>();
|
||||
if (!m_dtsCurrentRunningState)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Can not initialize DTS::CurrentRunningState!";
|
||||
return;
|
||||
}
|
||||
m_state.pid(QCoreApplication::applicationPid());
|
||||
m_state.runningState(Core::RunningState::Starting);
|
||||
|
||||
m_paceThread = new QThread;
|
||||
this->moveToThread(m_paceThread);
|
||||
|
||||
startTimer();
|
||||
publishState();
|
||||
}
|
||||
|
||||
void Pacemaker::startTimer()
|
||||
{
|
||||
if (m_publishTimer)
|
||||
return;
|
||||
|
||||
m_publishTimer = new QTimer();
|
||||
|
||||
|
||||
m_publishTimer->moveToThread(m_paceThread);
|
||||
|
||||
connect(m_publishTimer, &QTimer::timeout,
|
||||
this, &Pacemaker::publishState);
|
||||
|
||||
m_publishTimer->setSingleShot(false);
|
||||
m_publishTimer->setInterval(m_publishTimerDelay);
|
||||
connect(m_paceThread, &QThread::started, this, [this](){
|
||||
m_publishTimer->start();
|
||||
});
|
||||
|
||||
|
||||
m_paceThread->start();
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QThread>
|
||||
#include <DDS/Core/core_dts_types.h>
|
||||
#include "core_export.h"
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
class DdsConnector;
|
||||
}
|
||||
class QTimer;
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class CORE_CLIENT_EXPORT Pacemaker final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Pacemaker(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
|
||||
: Pacemaker(ddsConnector.get())
|
||||
{}
|
||||
|
||||
explicit Pacemaker(DDSConnector::DdsConnector *ddsConnector);
|
||||
~Pacemaker();
|
||||
|
||||
void startTimer();
|
||||
|
||||
public Q_SLOTS:
|
||||
void setNewState(Core::RunningState newState, const QString &errorString = QString());
|
||||
void addPort(unsigned short number, Core::PortType portType, const QString &properties = QStringLiteral(""));
|
||||
|
||||
|
||||
void publishState();
|
||||
|
||||
private:
|
||||
void init(DDSConnector::DdsConnector *ddsConnector);
|
||||
void setNewStateInternal(Core::RunningState newState, const QString &errorString = QString());
|
||||
void addPortInternal(unsigned short number, Core::PortType portType, const QString &properties = QStringLiteral(""));
|
||||
private:
|
||||
static constexpr const int m_publishTimerDelay = 3000;
|
||||
|
||||
CurrentRunningState m_state;
|
||||
QTimer *m_publishTimer = nullptr;
|
||||
QThread *m_paceThread = nullptr;
|
||||
|
||||
DTS::CurrentRunningStateImpl *m_dtsCurrentRunningState;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
}
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#include "topic_lock_master.h"
|
||||
|
||||
#include <DDS/DDSConnector/dds_connector.h>
|
||||
|
||||
namespace Core
|
||||
{
|
||||
|
||||
TopicLockMaster::TopicLockMaster(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
|
||||
: QObject(nullptr)
|
||||
, m_coreDtc(nullptr)
|
||||
{
|
||||
init(ddsConnector);
|
||||
}
|
||||
|
||||
TopicLockMaster::~TopicLockMaster()
|
||||
{
|
||||
m_coreDtc.reset();
|
||||
}
|
||||
|
||||
bool TopicLockMaster::init(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
|
||||
{
|
||||
if (!ddsConnector)
|
||||
return false;
|
||||
|
||||
m_coreDtc = Core::Interface::Dtc::create(ddsConnector);
|
||||
if (!m_coreDtc)
|
||||
return false;
|
||||
m_coreDtc->enableAll();
|
||||
|
||||
m_coreDtc->lockedEntitys([this] (const LockedEntity &data) {
|
||||
m_lockedEntitys << key(data.entityId(), data.exerciseId(), QString::fromStdString(data.entityTypeName()));
|
||||
});
|
||||
|
||||
connect(m_coreDtc.get(), &Interface::Dtc::lockedEntityChanged, this,
|
||||
[this] (const LockedEntity &data, DataModification modType)
|
||||
{
|
||||
key k(data.entityId(), data.exerciseId(), QString::fromStdString(data.entityTypeName()));
|
||||
if (modType == DataModification::eAdded)
|
||||
m_lockedEntitys << k;
|
||||
else if (modType == DataModification::eRemoved)
|
||||
m_lockedEntitys.remove(k);
|
||||
} );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/core_dtc.h>
|
||||
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
class DdsConnector;
|
||||
}
|
||||
|
||||
namespace Core
|
||||
{
|
||||
|
||||
struct key
|
||||
{
|
||||
inline key() = default;
|
||||
inline key(int64_t id, int exId, const QString &name)
|
||||
: entityTypeName(name)
|
||||
, entityId(id)
|
||||
, exerciseId(exId)
|
||||
{}
|
||||
|
||||
inline bool operator==(const key &o) const
|
||||
{
|
||||
return entityId == o.entityId &&
|
||||
exerciseId == o.exerciseId &&
|
||||
entityTypeName == o.entityTypeName;
|
||||
}
|
||||
|
||||
QString entityTypeName;
|
||||
int64_t entityId = 0;
|
||||
int exerciseId = 0;
|
||||
};
|
||||
|
||||
inline uint qHash(const Core::key &k, uint seed = 0)
|
||||
{
|
||||
return ::qHash(k.entityTypeName, seed) ^ ::qHash(k.entityId, seed) ^ ::qHash(k.exerciseId, seed);
|
||||
}
|
||||
|
||||
class CORE_CLIENT_EXPORT TopicLockMaster : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TopicLockMaster(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
|
||||
~TopicLockMaster();
|
||||
|
||||
bool init(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
|
||||
|
||||
template<typename E>
|
||||
bool isLocked(const E &data, int exerciseId)
|
||||
{
|
||||
auto it = m_lockedEntitys.constFind(key(data.id(), exerciseId, QString::fromStdString(typeid(E).name())));
|
||||
return !(it == m_lockedEntitys.cend());
|
||||
}
|
||||
|
||||
private:
|
||||
QSet<key> m_lockedEntitys;
|
||||
std::shared_ptr<Core::Interface::Dtc> m_coreDtc;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
SET(SRCS
|
||||
dds_connector.cpp
|
||||
dds_connector.h
|
||||
publisher.cpp
|
||||
publisher.h
|
||||
pub_sub_base.cpp
|
||||
pub_sub_base.h
|
||||
pub_sub_base_p.h
|
||||
subscriber.cpp
|
||||
subscriber.h
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(AAR SV WS HLA ddsconnector SHARED ${SRCS})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(
|
||||
INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(ddsconnector
|
||||
PRIVATE
|
||||
NGLLogger
|
||||
PUBLIC
|
||||
RTI::nddsc
|
||||
RTI::nddscore
|
||||
RTI::nddscpp2
|
||||
ddsQoS
|
||||
ddsdtc
|
||||
ddsdts
|
||||
ProjectConfig
|
||||
Qt5::Core
|
||||
)
|
||||
Executable
+283
@@ -0,0 +1,283 @@
|
||||
|
||||
|
||||
|
||||
#include "dds_connector.h"
|
||||
#include "publisher.h"
|
||||
#include "subscriber.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
#include <DDS/QosProfile/DDS_DefaultQos.h>
|
||||
#include <DDS/Generic/wait_set_handler.h>
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
|
||||
DdsConnector::DdsConnector(int domainId,
|
||||
const dds::domain::DomainParticipant &domainParticipant,
|
||||
IDL::PartitionType defaultPartitionType,
|
||||
int partitionId,
|
||||
int exerciseId,
|
||||
DDS::QosProfile::ConnectionType connectionType,
|
||||
const QUrl &tcpConnectionUrl)
|
||||
: m_domainId(domainId)
|
||||
, m_defaultPartitionType(defaultPartitionType)
|
||||
, m_partitionId(partitionId)
|
||||
, m_exerciseId(exerciseId)
|
||||
, m_tcpConnectionUrl(tcpConnectionUrl)
|
||||
, m_connectionType(tcpConnectionUrl.isEmpty() ? connectionType : DDS::QosProfile::ConnectionType::Tcp)
|
||||
, m_domainParticipant(domainParticipant)
|
||||
, m_publisher(nullptr)
|
||||
, m_subscriber(nullptr)
|
||||
, m_wsHandler(nullptr)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Common.DDSConnector"))
|
||||
{
|
||||
initInternal();
|
||||
DEBUG_LOG(m_logger) << "DdsConnector ctor: " << m_domainId << "," << m_partitionId << ","<< m_exerciseId << "," << static_cast<int>(m_defaultPartitionType);
|
||||
}
|
||||
|
||||
DdsConnector::DdsConnector(int domainId,
|
||||
IDL::PartitionType defaultPartitionType,
|
||||
int partitionId, int exerciseId,
|
||||
const QUrl &tcpConnectionUrl)
|
||||
: DdsConnector(domainId,
|
||||
nullptr,
|
||||
defaultPartitionType,
|
||||
partitionId,
|
||||
exerciseId,
|
||||
DDS::QosProfile::ConnectionType::Server,
|
||||
tcpConnectionUrl)
|
||||
{}
|
||||
|
||||
DdsConnector::DdsConnector(const dds::domain::DomainParticipant &domainParticipant,
|
||||
IDL::PartitionType defaultPartitionType,
|
||||
int partitionId, int exerciseId,
|
||||
const QUrl &tcpConnectionUrl)
|
||||
: DdsConnector(domainParticipant.domain_id(),
|
||||
domainParticipant,
|
||||
defaultPartitionType,
|
||||
partitionId,
|
||||
exerciseId,
|
||||
DDS::QosProfile::ConnectionType::Server,
|
||||
tcpConnectionUrl)
|
||||
{}
|
||||
|
||||
|
||||
DdsConnector::DdsConnector(const dds::domain::DomainParticipant &domainParticipant,
|
||||
IDL::PartitionType defaultPartitionType,
|
||||
DDS::QosProfile::ConnectionType connectionType,
|
||||
int partitionId,
|
||||
int exerciseId
|
||||
)
|
||||
: DdsConnector(domainParticipant.domain_id(),
|
||||
domainParticipant,
|
||||
defaultPartitionType,
|
||||
partitionId,
|
||||
exerciseId,
|
||||
connectionType,
|
||||
QUrl())
|
||||
{}
|
||||
|
||||
DdsConnector::DdsConnector(int domainId,
|
||||
IDL::PartitionType defaultPartitionType,
|
||||
int partitionId, int exerciseId,
|
||||
DDS::QosProfile::ConnectionType connectionType)
|
||||
: DdsConnector(domainId,
|
||||
nullptr,
|
||||
defaultPartitionType,
|
||||
partitionId,
|
||||
exerciseId,
|
||||
connectionType,
|
||||
QUrl())
|
||||
{
|
||||
Q_ASSERT(connectionType != DDS::QosProfile::ConnectionType::Tcp);
|
||||
}
|
||||
|
||||
|
||||
DdsConnector::~DdsConnector()
|
||||
{
|
||||
delete m_subscriber;
|
||||
delete m_publisher;
|
||||
delete m_wsHandler;
|
||||
}
|
||||
|
||||
bool DdsConnector::init(const QStringList &qosFileList)
|
||||
{
|
||||
return DDS::QosProfile::setDefault(qosFileList);
|
||||
}
|
||||
|
||||
QUrl DdsConnector::getTcpConnectionUrl() const
|
||||
{
|
||||
DEBUG_LOG(m_logger) << this << " gettcpConnectionUrl: " << m_tcpConnectionUrl.toString();
|
||||
return m_tcpConnectionUrl;
|
||||
}
|
||||
|
||||
dds::pub::Publisher DdsConnector::getDdsPublisher() const
|
||||
{
|
||||
return m_publisher ? m_publisher->getDdsPublisher() : nullptr;
|
||||
}
|
||||
|
||||
dds::sub::Subscriber DdsConnector::getDdsSubscriber() const
|
||||
{
|
||||
return m_subscriber ? m_subscriber->getDdsSubscriber() : nullptr;
|
||||
}
|
||||
|
||||
DDSGeneric::SubscriberListener *DdsConnector::getSubscriberListener()
|
||||
{
|
||||
return m_subscriber ? m_subscriber->getSubscriberListener() : nullptr;
|
||||
}
|
||||
|
||||
void DdsConnector::initPublisherSubscriber()
|
||||
{
|
||||
initSubscriber();
|
||||
initPublisher();
|
||||
}
|
||||
|
||||
void DdsConnector::initPublisher()
|
||||
{
|
||||
if (m_publisher)
|
||||
return;
|
||||
initDomainParticipant();
|
||||
|
||||
setPublisher(new DDSConnector::Publisher(m_domainParticipant, getDefaultPartition()));
|
||||
}
|
||||
|
||||
void DdsConnector::initSubscriber()
|
||||
{
|
||||
if (m_subscriber)
|
||||
return;
|
||||
initDomainParticipant();
|
||||
|
||||
setSubscriber(new DDSConnector::Subscriber(m_domainParticipant, getDefaultPartition()));
|
||||
}
|
||||
|
||||
void DdsConnector::initDomainParticipant()
|
||||
{
|
||||
if (m_domainParticipant == nullptr)
|
||||
setDomainParticipant(dds::domain::DomainParticipant(m_domainId,
|
||||
DDS::QosProfile::domainParticipantDefault(m_connectionType, m_tcpConnectionUrl)));
|
||||
}
|
||||
|
||||
|
||||
std::string DdsConnector::getDefaultPartition() const
|
||||
{
|
||||
return getPartitionString(m_defaultPartitionType, m_partitionId);
|
||||
}
|
||||
|
||||
std::string DdsConnector::getPartitionString(IDL::PartitionType pt, int partitionId)
|
||||
{
|
||||
switch (pt)
|
||||
{
|
||||
case IDL::PartitionType::Any:
|
||||
return "*";
|
||||
case IDL::PartitionType::Global:
|
||||
return IDL::exercisePartition(0);
|
||||
case IDL::PartitionType::Exercise:
|
||||
case IDL::PartitionType::AllExercises:
|
||||
return IDL::exercisePartition(partitionId);
|
||||
case IDL::PartitionType::Replay:
|
||||
return IDL::replayPartition(partitionId);
|
||||
}
|
||||
|
||||
NGL::INGLLogger &m_logger = NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Common.DDSConnector");
|
||||
ERROR_LOG(m_logger) << "Invalid partition type: " << static_cast<int>(pt);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
void DdsConnector::setDomainParticipant(const dds::domain::DomainParticipant &participant)
|
||||
{
|
||||
m_domainParticipant = participant;
|
||||
if (m_domainParticipant == nullptr)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
dds::domain::qos::DomainParticipantQos dpQos(m_domainParticipant.qos());
|
||||
DDS::QosProfile::print(dpQos, m_logger);
|
||||
rti::core::policy::EntityName entityName;
|
||||
entityName.name(qAppName().toStdString() + (" - DP ") + std::to_string(m_domainId));
|
||||
dpQos << entityName;
|
||||
m_domainParticipant.qos(dpQos);
|
||||
m_domainParticipant->default_datareader_qos(DDS::QosProfile::dataReaderDefault(m_connectionType));
|
||||
m_domainParticipant.enable();
|
||||
}
|
||||
catch (const dds::core::Exception &ex)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Could not enable DomainParticipant: " << ex.what();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void DdsConnector::setPublisher(Publisher *publisher)
|
||||
{
|
||||
if (m_publisher && publisher != m_publisher)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Trying to set publisher but there is already a publisher set - overwriting!";
|
||||
delete m_publisher;
|
||||
}
|
||||
m_publisher = publisher;
|
||||
if (m_publisher)
|
||||
DDS::QosProfile::print(m_publisher->getDdsPublisher().default_datawriter_qos(), m_logger);
|
||||
}
|
||||
|
||||
void DdsConnector::enableBatchedWriter(bool bEnable)
|
||||
{
|
||||
if (m_publisher)
|
||||
m_publisher->enableBatchedWriter(bEnable);
|
||||
}
|
||||
|
||||
|
||||
void DdsConnector::setSubscriber(Subscriber *subscriber)
|
||||
{
|
||||
if (m_subscriber && subscriber != m_subscriber)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Trying to set subscriber but there is already a subscriber set - overwriting!";
|
||||
delete m_subscriber;
|
||||
}
|
||||
m_subscriber = subscriber;
|
||||
if (m_subscriber)
|
||||
DDS::QosProfile::print(m_subscriber->getDdsSubscriber().default_datareader_qos(), m_logger);
|
||||
}
|
||||
|
||||
void DdsConnector::initInternal()
|
||||
{
|
||||
if (m_tcpConnectionUrl.isValid())
|
||||
{
|
||||
m_connectionType = DDS::QosProfile::ConnectionType::Tcp;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tcpConnectionUrl.clear();
|
||||
if (m_connectionType == DDS::QosProfile::ConnectionType::Tcp)
|
||||
m_connectionType = DDS::QosProfile::ConnectionType::ShmUdp;
|
||||
}
|
||||
|
||||
if (m_defaultPartitionType == IDL::PartitionType::Any ||
|
||||
m_defaultPartitionType == IDL::PartitionType::AllExercises)
|
||||
m_partitionId = -1;
|
||||
if (m_defaultPartitionType == IDL::PartitionType::Exercise && m_partitionId < 0)
|
||||
WARNING_LOG(m_logger) << "Want to subscribe to an exercise, but no exercise Id is given!";
|
||||
if (m_defaultPartitionType == IDL::PartitionType::Global)
|
||||
{
|
||||
m_partitionId = 0;
|
||||
m_exerciseId = 0;
|
||||
}
|
||||
INFO_LOG(m_logger) << "DDSConnector(" << m_domainId << ") for partition: " << m_partitionId
|
||||
<< " with partition type " << static_cast<int>(m_defaultPartitionType);
|
||||
|
||||
|
||||
dds::domain::qos::DomainParticipantFactoryQos domainParticipantFactoryQos = dds::domain::DomainParticipant::participant_factory_qos();
|
||||
|
||||
domainParticipantFactoryQos << dds::core::policy::EntityFactory::ManuallyEnable();
|
||||
dds::domain::DomainParticipant::participant_factory_qos(domainParticipantFactoryQos);
|
||||
}
|
||||
|
||||
|
||||
void DdsConnector::createWaitSetHandler()
|
||||
{
|
||||
if (!m_wsHandler)
|
||||
m_wsHandler = new DDSGeneric::WaitSetHandler;
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+214
@@ -0,0 +1,214 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dds/pub/CoherentSet.hpp>
|
||||
#include <dds/pub/Publisher.hpp>
|
||||
#include <dds/sub/Subscriber.hpp>
|
||||
|
||||
#include <QtCore/QStringList>
|
||||
#include <Common/ProjectConfig/project_defines.h>
|
||||
#include <DDS/DDSConnector/ddsconnector_export.h>
|
||||
#include <DDS/Generic/generic_base.h>
|
||||
#include <QUrl>
|
||||
|
||||
class QObject;
|
||||
namespace DDS
|
||||
{
|
||||
namespace QosProfile
|
||||
{
|
||||
enum class ConnectionType;
|
||||
}
|
||||
}
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
namespace DDSGeneric
|
||||
{
|
||||
class WaitSetHandler;
|
||||
class SubscriberListener;
|
||||
}
|
||||
namespace DDSConnector
|
||||
{
|
||||
class Publisher;
|
||||
class Subscriber;
|
||||
|
||||
class DDSCONNECTOR_EXPORT DdsConnector
|
||||
{
|
||||
DdsConnector(int domainId,
|
||||
const dds::domain::DomainParticipant &domainParticipant,
|
||||
IDL::PartitionType defaultPartitionType,
|
||||
int partitionId,
|
||||
int exerciseId,
|
||||
DDS::QosProfile::ConnectionType connectionType,
|
||||
const QUrl &tcpConnectionUrl);
|
||||
public:
|
||||
DdsConnector(const DdsConnector &) = delete;
|
||||
DdsConnector &operator=(const DdsConnector &) = delete;
|
||||
|
||||
|
||||
|
||||
DdsConnector(int domainId,
|
||||
IDL::PartitionType defaultPartitionType,
|
||||
int partitionId = 0,
|
||||
int exerciseId = 0,
|
||||
const QUrl &tcpConnectionUrl = QUrl());
|
||||
|
||||
|
||||
DdsConnector(const dds::domain::DomainParticipant &domainParticipant,
|
||||
IDL::PartitionType defaultPartitionType,
|
||||
int partitionId = 0,
|
||||
int exerciseId = 0,
|
||||
const QUrl &tcpConnectionUrl = QUrl());
|
||||
|
||||
DdsConnector(const dds::domain::DomainParticipant &domainParticipant,
|
||||
IDL::PartitionType defaultPartitionType,
|
||||
DDS::QosProfile::ConnectionType connectionType,
|
||||
int partitionId = 0,
|
||||
int exerciseId = 0);
|
||||
|
||||
DdsConnector(int domainId,
|
||||
IDL::PartitionType defaultPartitionType,
|
||||
int partitionId,
|
||||
int exerciseId,
|
||||
DDS::QosProfile::ConnectionType connectionType);
|
||||
|
||||
virtual ~DdsConnector();
|
||||
|
||||
|
||||
|
||||
virtual bool init(const QStringList &qosFileList = QStringList());
|
||||
|
||||
dds::domain::DomainParticipant getDomainParticipant() const { return m_domainParticipant; }
|
||||
void setDomainParticipant(const dds::domain::DomainParticipant &participant);
|
||||
|
||||
Publisher *getPublisher() { return m_publisher; }
|
||||
dds::pub::Publisher getDdsPublisher() const;
|
||||
void setPublisher(Publisher *publisher);
|
||||
void enableBatchedWriter(bool bEnable);
|
||||
|
||||
Subscriber *getSubscriber() { return m_subscriber; }
|
||||
dds::sub::Subscriber getDdsSubscriber() const;
|
||||
void setSubscriber(Subscriber *subscriber);
|
||||
DDSGeneric::SubscriberListener *getSubscriberListener();
|
||||
|
||||
const DDSGeneric::WaitSetHandler *getWaitSetHandler() const { return m_wsHandler; }
|
||||
|
||||
int getDomainId() const { return m_domainId; }
|
||||
IDL::PartitionType getPartitionType() const { return m_defaultPartitionType; }
|
||||
int getPartitionId() const { return m_partitionId; }
|
||||
int getExerciseId() const { return m_exerciseId; }
|
||||
std::string getDefaultPartition() const;
|
||||
QUrl getTcpConnectionUrl() const;
|
||||
static std::string getPartitionString(IDL::PartitionType pt, int partitionId = -1);
|
||||
|
||||
|
||||
|
||||
void initPublisherSubscriber();
|
||||
void initPublisher();
|
||||
void initSubscriber();
|
||||
void initDomainParticipant();
|
||||
|
||||
template<typename T>
|
||||
T *createDTS(const std::string &topicName = DDS::Helper::getShortTopicName(DDS::topic_type_name<typename T::TopicType>::value()))
|
||||
{
|
||||
if (getDomainParticipant() == nullptr ||
|
||||
getDdsPublisher() == nullptr)
|
||||
return nullptr;
|
||||
T *dts = new T(getDomainParticipant(),
|
||||
getDdsPublisher(),
|
||||
topicName);
|
||||
return dts;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *createDTC(const std::string &topicName = DDS::Helper::getShortTopicName(DDS::topic_type_name<typename T::TopicType>::value()))
|
||||
{
|
||||
if (getDomainParticipant() == nullptr ||
|
||||
getDdsSubscriber() == nullptr)
|
||||
return nullptr;
|
||||
T *dtc = new T(getDomainParticipant(),
|
||||
getDdsSubscriber(),
|
||||
getSubscriberListener(),
|
||||
topicName);
|
||||
return dtc;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *createDTC(const dds::topic::Filter &filter,
|
||||
const std::string &topicName = DDS::Helper::getShortTopicName(DDS::topic_type_name<typename T::TopicType>::value()))
|
||||
{
|
||||
if (getDomainParticipant() == nullptr ||
|
||||
getDdsSubscriber() == nullptr)
|
||||
return nullptr;
|
||||
T *dtc = new T(getDomainParticipant(),
|
||||
getDdsSubscriber(),
|
||||
getSubscriberListener(),
|
||||
filter,
|
||||
topicName);
|
||||
return dtc;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *createFTS()
|
||||
{
|
||||
if (getDomainParticipant() == nullptr ||
|
||||
getDdsPublisher() == nullptr ||
|
||||
getDdsSubscriber() == nullptr)
|
||||
return nullptr;
|
||||
createWaitSetHandler();
|
||||
T *fts = new T(getDomainParticipant(),
|
||||
getDdsPublisher(),
|
||||
getDdsSubscriber(),
|
||||
m_wsHandler);
|
||||
return fts;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *createFTC(QObject *objToInform)
|
||||
{
|
||||
if (getDomainParticipant() == nullptr ||
|
||||
getDdsPublisher() == nullptr ||
|
||||
getDdsSubscriber() == nullptr)
|
||||
return nullptr;
|
||||
T *ftc = new T(getDomainParticipant(),
|
||||
getDdsPublisher(),
|
||||
getDdsSubscriber(),
|
||||
objToInform,
|
||||
IDL::toStdString(m_defaultPartitionType),
|
||||
m_partitionId);
|
||||
return ftc;
|
||||
}
|
||||
|
||||
private:
|
||||
void initInternal();
|
||||
void createWaitSetHandler();
|
||||
private:
|
||||
int m_domainId;
|
||||
IDL::PartitionType m_defaultPartitionType;
|
||||
int m_partitionId;
|
||||
int m_exerciseId;
|
||||
|
||||
QUrl m_tcpConnectionUrl;
|
||||
DDS::QosProfile::ConnectionType m_connectionType;
|
||||
dds::domain::DomainParticipant m_domainParticipant;
|
||||
DDSConnector::Publisher *m_publisher;
|
||||
DDSConnector::Subscriber *m_subscriber;
|
||||
DDSGeneric::WaitSetHandler *m_wsHandler;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CoherentSet
|
||||
{
|
||||
public:
|
||||
explicit CoherentSet(const std::shared_ptr<DdsConnector> &connector)
|
||||
: m_coherentSet(connector->getDdsPublisher())
|
||||
{}
|
||||
private:
|
||||
dds::pub::CoherentSet m_coherentSet;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
|
||||
#include "pub_sub_base.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
PubSubBase::PubSubBase(const dds::domain::DomainParticipant &domainParticipant, const std::string &partition, const char *loggerName)
|
||||
: m_domainParticipant(domainParticipant)
|
||||
, m_partitions({partition})
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(loggerName))
|
||||
{}
|
||||
}
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <DDS/DDSConnector/ddsconnector_export.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <dds/domain/DomainParticipant.hpp>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
class DDSCONNECTOR_EXPORT PubSubBase
|
||||
{
|
||||
protected:
|
||||
PubSubBase(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const std::string &partition,
|
||||
const char *loggerName);
|
||||
public:
|
||||
virtual ~PubSubBase() = default;
|
||||
|
||||
void setDomainParticipant(const dds::domain::DomainParticipant &domainParticipant) { m_domainParticipant = domainParticipant; }
|
||||
const dds::domain::DomainParticipant &getDomainParticipant() const { return m_domainParticipant; }
|
||||
|
||||
protected:
|
||||
template<typename P>
|
||||
void updatePartitionQoS(P &pubSub);
|
||||
|
||||
protected:
|
||||
dds::domain::DomainParticipant m_domainParticipant;
|
||||
std::vector<std::string> m_partitions;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
}
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
template<typename P>
|
||||
void PubSubBase::updatePartitionQoS(P &pubSub)
|
||||
{
|
||||
if (pubSub == nullptr)
|
||||
return;
|
||||
|
||||
auto publisherQos = pubSub.qos();
|
||||
|
||||
|
||||
dds::core::policy::Partition partitionQos;
|
||||
partitionQos.name( m_partitions );
|
||||
publisherQos << partitionQos;
|
||||
|
||||
pubSub.qos(publisherQos);
|
||||
}
|
||||
}
|
||||
Executable
+106
@@ -0,0 +1,106 @@
|
||||
|
||||
|
||||
#include "publisher.h"
|
||||
#include "pub_sub_base_p.h"
|
||||
|
||||
#include <dds/pub/AnyDataWriter.hpp>
|
||||
#include <DDS/QosProfile/DDS_DefaultQos.h>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
|
||||
Publisher::Publisher(const dds::domain::DomainParticipant &domainParticipant, const std::string &partition)
|
||||
: PubSubBase(domainParticipant, partition, "DDSConnector.Publisher")
|
||||
, m_ddsPublisher(nullptr)
|
||||
{
|
||||
Q_ASSERT(domainParticipant != nullptr);
|
||||
initDDS();
|
||||
}
|
||||
|
||||
|
||||
Publisher::~Publisher()
|
||||
{
|
||||
try
|
||||
{
|
||||
deleteDDS();
|
||||
} catch (...)
|
||||
{}
|
||||
}
|
||||
|
||||
void Publisher::initDDS()
|
||||
{
|
||||
if (m_partitions.empty())
|
||||
return;
|
||||
|
||||
|
||||
if (m_logger.isDebugEnabled()) {
|
||||
DEBUG_LOG(m_logger) << "Publisher (DataWriter) Qos: ";
|
||||
dds::core::QosProvider qosProvider = dds::core::QosProvider::Default();
|
||||
DDS::QosProfile::print(qosProvider.datawriter_qos(), m_logger);
|
||||
}
|
||||
|
||||
dds::pub::qos::PublisherQos publisherQos = m_domainParticipant.default_publisher_qos();
|
||||
|
||||
|
||||
rti::core::policy::EntityName entityName;
|
||||
entityName.name(std::string("Pub ") + m_partitions.front());
|
||||
publisherQos << entityName;
|
||||
|
||||
DEBUG_LOG(m_logger) << "Publish data on partition " << m_partitions.front();
|
||||
|
||||
m_ddsPublisher = dds::pub::Publisher(m_domainParticipant, publisherQos);
|
||||
|
||||
enableBatchedWriter(false);
|
||||
|
||||
updatePartitionQoS(m_ddsPublisher);
|
||||
}
|
||||
|
||||
void Publisher::enableBatchedWriter(bool bEnable)
|
||||
{
|
||||
dds::pub::qos::DataWriterQos dwQos = m_ddsPublisher.default_datawriter_qos();
|
||||
auto batch = (bEnable ? rti::core::policy::Batch::Enabled() : rti::core::policy::Batch::Disabled());
|
||||
if (bEnable)
|
||||
{
|
||||
batch.max_data_bytes(64 * 1024);
|
||||
batch.max_flush_delay(dds::core::Duration::from_millisecs(10));
|
||||
}
|
||||
dwQos << batch;
|
||||
m_ddsPublisher.default_datawriter_qos(dwQos);
|
||||
}
|
||||
|
||||
void Publisher::deleteDDS()
|
||||
{
|
||||
qDeleteAll(m_dtsMap);
|
||||
qDeleteAll(m_dynamicTypeMap);
|
||||
m_ddsPublisher = nullptr;
|
||||
}
|
||||
|
||||
bool Publisher::write(const dds::core::xtypes::DynamicData &data, const std::string &topicName)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Publish dynamic data for topic (" << topicName << ")";
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
dds::pub::AnyDataWriter* anyWriter = m_dynamicTypeMap.value(topicName);
|
||||
if (anyWriter == nullptr)
|
||||
{
|
||||
dds::topic::Topic<dds::core::xtypes::DynamicData> topic(m_domainParticipant, topicName, data.type());
|
||||
auto writer = dds::pub::DataWriter<dds::core::xtypes::DynamicData>(m_ddsPublisher, topic);
|
||||
anyWriter = new dds::pub::AnyDataWriter(writer);
|
||||
m_dynamicTypeMap.insert(topicName, anyWriter);
|
||||
}
|
||||
anyWriter->get<dds::core::xtypes::DynamicData>().write(data);
|
||||
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Failed to publish data for topic (" << topicName << ")";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QMap>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include <DDS/DTS/generic_dts_impl.h>
|
||||
#include <DDS/Generic/generic_data_writer.h>
|
||||
|
||||
#include "pub_sub_base.h"
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
class DDSCONNECTOR_EXPORT Publisher : public PubSubBase
|
||||
{
|
||||
public:
|
||||
Publisher(const dds::domain::DomainParticipant &domainParticipant, const std::string &partition);
|
||||
~Publisher();
|
||||
|
||||
void enableBatchedWriter(bool bEnable);
|
||||
dds::pub::Publisher getDdsPublisher() const { return m_ddsPublisher; }
|
||||
|
||||
|
||||
template <typename T>
|
||||
bool write(const T &data)
|
||||
{
|
||||
using myDWO = DDSGeneric::GenericDataWriter<T>;
|
||||
using myDTS = DTS::GenericDtsImpl< myDWO >;
|
||||
|
||||
std::string dwoType = DDS::topic_type_name<T>::value();
|
||||
DEBUG_LOG(m_logger) << "dwoType = " << dwoType;
|
||||
|
||||
myDTS *dts = static_cast<myDTS *>(m_dtsMap.value(dwoType));
|
||||
if (dts == nullptr)
|
||||
{
|
||||
dts = new myDTS(m_domainParticipant, m_ddsPublisher);
|
||||
m_dtsMap.insert(dwoType, dts);
|
||||
}
|
||||
|
||||
return dts->write(data);
|
||||
}
|
||||
|
||||
|
||||
bool write(const dds::core::xtypes::DynamicData &data)
|
||||
{
|
||||
return write(data, data.type().name());
|
||||
}
|
||||
|
||||
bool write(const dds::core::xtypes::DynamicData &data, const std::string &topicName);
|
||||
|
||||
|
||||
protected:
|
||||
void initDDS();
|
||||
void deleteDDS();
|
||||
|
||||
protected:
|
||||
dds::pub::Publisher m_ddsPublisher;
|
||||
|
||||
|
||||
QMap<std::string, DTS::Base*> m_dtsMap;
|
||||
|
||||
|
||||
|
||||
QMap<std::string, dds::pub::AnyDataWriter*> m_dynamicTypeMap;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
|
||||
|
||||
|
||||
#include "subscriber.h"
|
||||
#include "pub_sub_base_p.h"
|
||||
|
||||
#include <DDS/QosProfile/DDS_DefaultQos.h>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
Subscriber::Subscriber(const dds::domain::DomainParticipant &domainParticipant, const std::string &partition, bool bUseSubscriberListener)
|
||||
: PubSubBase(domainParticipant, partition, "DDSConnector.Subscriber")
|
||||
, m_ddsSubscriber(nullptr)
|
||||
, m_subscriberListener(bUseSubscriberListener ? new DDSGeneric::SubscriberListener : nullptr)
|
||||
, m_subscriberListenerBinder(nullptr)
|
||||
{
|
||||
Q_ASSERT(domainParticipant != nullptr);
|
||||
initDDS();
|
||||
}
|
||||
|
||||
Subscriber::~Subscriber()
|
||||
{
|
||||
try
|
||||
{
|
||||
deleteDDS();
|
||||
} catch (...)
|
||||
{}
|
||||
}
|
||||
|
||||
void Subscriber::initDDS()
|
||||
{
|
||||
if (m_partitions.empty())
|
||||
return;
|
||||
|
||||
|
||||
if (m_logger.isDebugEnabled()) {
|
||||
DEBUG_LOG(m_logger) << "Subscriber (DataReader) Qos: ";
|
||||
dds::core::QosProvider qosProvider = dds::core::QosProvider::Default();
|
||||
DDS::QosProfile::print(qosProvider.datareader_qos(), m_logger);
|
||||
}
|
||||
|
||||
dds::sub::qos::SubscriberQos subscriberQos = m_domainParticipant.default_subscriber_qos();
|
||||
|
||||
|
||||
rti::core::policy::EntityName entityName;
|
||||
entityName.name(std::string("Sub ") + m_partitions.front());
|
||||
subscriberQos << entityName;
|
||||
|
||||
DEBUG_LOG(m_logger) << "Subscribe on partition " << m_partitions.front();
|
||||
|
||||
m_ddsSubscriber = dds::sub::Subscriber(m_domainParticipant,
|
||||
subscriberQos);
|
||||
|
||||
if (m_subscriberListener)
|
||||
m_subscriberListenerBinder = rti::core::bind_and_manage_listener(m_ddsSubscriber,
|
||||
m_subscriberListener,
|
||||
DDSGeneric::SubscriberListener::mask());
|
||||
|
||||
updatePartitionQoS(m_ddsSubscriber);
|
||||
}
|
||||
|
||||
void Subscriber::deleteDDS()
|
||||
{
|
||||
if (m_subscriberListener)
|
||||
{
|
||||
m_subscriberListener->prepareShutdown();
|
||||
m_subscriberListener = nullptr;
|
||||
}
|
||||
m_subscriberListenerBinder = nullptr;
|
||||
m_ddsSubscriber = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+71
@@ -0,0 +1,71 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pub_sub_base.h"
|
||||
|
||||
#include <DDS/Generic/dynamic_data_reader.h>
|
||||
#include <DDS/DTC/generic_dtc_impl.h>
|
||||
#include <DDS/Generic/topic_description.h>
|
||||
#include <rti/core/ListenerBinder.hpp>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
class DDSCONNECTOR_EXPORT Subscriber : public PubSubBase
|
||||
{
|
||||
|
||||
public:
|
||||
Subscriber(const dds::domain::DomainParticipant &domainParticipant, const std::string &partition, bool bUseSubscriberListener = true);
|
||||
virtual ~Subscriber();
|
||||
|
||||
dds::sub::Subscriber getDdsSubscriber() const { return m_ddsSubscriber; }
|
||||
DDSGeneric::SubscriberListener *getSubscriberListener() { return m_subscriberListener; }
|
||||
|
||||
template <typename DTC_TYPE>
|
||||
DTC_TYPE *create(std::function<void(const typename DTC_TYPE::TopicTypeRead &, DataModification modType)> cb)
|
||||
{
|
||||
DTC_TYPE* dtc = new DTC_TYPE;
|
||||
if (!dtc->create(m_domainParticipant, m_ddsSubscriber, m_subscriberListener))
|
||||
{
|
||||
delete dtc;
|
||||
return nullptr;
|
||||
}
|
||||
dtc->setSubscriberCallback(cb);
|
||||
return dtc;
|
||||
}
|
||||
|
||||
|
||||
DTC::Ddtc<DDSGeneric::DynamicDataReader> *create(const std::function<void(const DDSGeneric::DynamicData &, const std::string &, DataModification modType)> &cb,
|
||||
const dds::core::xtypes::StructType &structType,
|
||||
DDSGeneric::SubscriberListener *listener = nullptr,
|
||||
const std::string &specialTopicName = std::string())
|
||||
{
|
||||
if (!listener)
|
||||
listener = getSubscriberListener();
|
||||
|
||||
DTC::DdtcImpl<DDSGeneric::DynamicDataReader> *ddtc =
|
||||
new DTC::DdtcImpl<DDSGeneric::DynamicDataReader>(getDomainParticipant(),
|
||||
getDdsSubscriber(),
|
||||
listener,
|
||||
structType,
|
||||
specialTopicName);
|
||||
ddtc->setSubscriberCallback(cb);
|
||||
return ddtc;
|
||||
}
|
||||
|
||||
protected:
|
||||
void initDDS();
|
||||
void deleteDDS();
|
||||
|
||||
protected:
|
||||
dds::sub::Subscriber m_ddsSubscriber;
|
||||
DDSGeneric::SubscriberListener *m_subscriberListener;
|
||||
rti::core::ListenerBinder< dds::sub::Subscriber, dds::sub::SubscriberListener> m_subscriberListenerBinder;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
ADD_LIBRARY(ddsdtc INTERFACE)
|
||||
TARGET_INCLUDE_DIRECTORIES(ddsdtc
|
||||
INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../
|
||||
)
|
||||
TARGET_LINK_LIBRARIES(ddsdtc
|
||||
INTERFACE
|
||||
Qt5::Core
|
||||
ddsgen
|
||||
NGLLogger
|
||||
LtsUtilsCoreHeaderOnly
|
||||
)
|
||||
SET(SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/generic_dtc.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/generic_dtc_impl.h
|
||||
)
|
||||
TARGET_SOURCES(ddsdtc INTERFACE ${SRCS})
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <LtsUtilsCore/data_modification_enum.h>
|
||||
|
||||
#include <dds/sub/LoanedSamples.hpp>
|
||||
|
||||
namespace NGLLogger
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace DTC
|
||||
{
|
||||
class SubscriberListener;
|
||||
|
||||
template <typename DRO_OBJECT, typename SC>
|
||||
class GenericDtcSC
|
||||
{
|
||||
public:
|
||||
using DataReaderObject = DRO_OBJECT;
|
||||
using SubscriberCallback = SC;
|
||||
using TopicType = typename DRO_OBJECT::TopicType;
|
||||
|
||||
GenericDtcSC() = default;
|
||||
virtual ~GenericDtcSC() = default;
|
||||
|
||||
virtual bool setSubscriberCallback(const SubscriberCallback &subscriberCallback) = 0;
|
||||
virtual dds::sub::LoanedSamples<TopicType> readAlive() const = 0;
|
||||
virtual dds::sub::LoanedSamples<TopicType> takeAlive() const = 0;
|
||||
virtual dds::sub::LoanedSamples<TopicType> takeDisposed() const = 0;
|
||||
virtual bool hasSamples() const = 0;
|
||||
virtual DataReaderObject *dataReader() const = 0;
|
||||
virtual bool waitForHistoricalData(int timeoutMSec) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename DRO_OBJECT>
|
||||
class GenericDtc
|
||||
: public GenericDtcSC<DRO_OBJECT,
|
||||
std::function<void(const typename DRO_OBJECT::TopicType &, DataModification)> >
|
||||
{};
|
||||
|
||||
|
||||
template<typename DRO_OBJECT>
|
||||
class GenericDtcSampleInfo
|
||||
: public GenericDtcSC<DRO_OBJECT,
|
||||
std::function<void(const typename DRO_OBJECT::TopicType &, const dds::sub::SampleInfo &)> >
|
||||
{};
|
||||
|
||||
|
||||
template<typename DRO_OBJECT>
|
||||
class Ddtc
|
||||
: public GenericDtcSC<DRO_OBJECT,
|
||||
std::function<void(const typename DRO_OBJECT::TopicType &, const std::string &, DataModification)> >
|
||||
{};
|
||||
|
||||
|
||||
template<typename DRO_OBJECT>
|
||||
class DdtcSampleInfo
|
||||
: public GenericDtcSC<DRO_OBJECT,
|
||||
std::function<void(const typename DRO_OBJECT::TopicType &, const std::string &, const dds::sub::SampleInfo &)> >
|
||||
{};
|
||||
}
|
||||
Executable
+313
@@ -0,0 +1,313 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <DDS/Generic/generic_data_reader.h>
|
||||
#include <DDS/Generic/subscriber_listener.h>
|
||||
#include <QTimer>
|
||||
|
||||
#include <dds/core/cond/StatusCondition.hpp>
|
||||
#include <dds/core/cond/WaitSet.hpp>
|
||||
|
||||
#include "generic_dtc.h"
|
||||
|
||||
|
||||
namespace DDSGeneric
|
||||
{
|
||||
class DynamicDataReader;
|
||||
}
|
||||
namespace DTC
|
||||
{
|
||||
|
||||
template<typename SuperClass>
|
||||
class GenericDtcImplSubscriberTemplate : public DDSGeneric::SubscriberListenerCallback, public SuperClass
|
||||
{
|
||||
public:
|
||||
using DataReaderObject = typename SuperClass::DataReaderObject;
|
||||
using SubscriberCallback = typename SuperClass::SubscriberCallback;
|
||||
using TopicTypeRead = typename DataReaderObject::TopicType;
|
||||
using TopicTypeSubscribe = typename DataReaderObject::TopicType;
|
||||
using DataReader = typename DataReaderObject::DataReader;
|
||||
|
||||
GenericDtcImplSubscriberTemplate(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const dds::sub::Subscriber &ddsSubscriber,
|
||||
DDSGeneric::SubscriberListener *subscriberListener,
|
||||
const std::string &topicName = DDS::Helper::getShortTopicName(DDS::topic_type_name<TopicTypeRead>::value()))
|
||||
: m_subscriberListener(subscriberListener)
|
||||
, m_dataReaderObject(new DataReaderObject)
|
||||
, m_subscriberCallback(nullptr)
|
||||
, m_writerState(0)
|
||||
, m_logger(DDS::getLogger(DDS::topic_type_name<TopicTypeRead>::value(), DDS::DataType::DATA_TOPIC_CLIENT))
|
||||
{
|
||||
createInternal(m_dataReaderObject->create(domainParticipant, ddsSubscriber, topicName));
|
||||
}
|
||||
|
||||
GenericDtcImplSubscriberTemplate(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const dds::sub::Subscriber &ddsSubscriber,
|
||||
DDSGeneric::SubscriberListener *subscriberListener,
|
||||
const dds::topic::Filter &filter,
|
||||
const std::string &topicName)
|
||||
: m_subscriberListener(subscriberListener)
|
||||
, m_dataReaderObject(new DataReaderObject)
|
||||
, m_subscriberCallback(nullptr)
|
||||
, m_writerState(0)
|
||||
, m_logger(DDS::getLogger(DDS::topic_type_name<TopicTypeRead>::value(), DDS::DataType::DATA_TOPIC_CLIENT))
|
||||
{
|
||||
createInternal(m_dataReaderObject->create(domainParticipant, ddsSubscriber, topicName, filter));
|
||||
}
|
||||
|
||||
GenericDtcImplSubscriberTemplate(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const dds::sub::Subscriber &ddsSubscriber,
|
||||
DDSGeneric::SubscriberListener *subscriberListener,
|
||||
const dds::core::xtypes::StructType &structType,
|
||||
const std::string &topicName)
|
||||
: m_subscriberListener(subscriberListener)
|
||||
, m_dataReaderObject(new DataReaderObject)
|
||||
, m_subscriberCallback(nullptr)
|
||||
, m_logger(DDS::getLogger(DDS::topic_type_name<TopicTypeRead>::value(), DDS::DataType::DATA_TOPIC_CLIENT))
|
||||
{
|
||||
createInternal(m_dataReaderObject->create(domainParticipant, ddsSubscriber, topicName, structType));
|
||||
}
|
||||
|
||||
~GenericDtcImplSubscriberTemplate()
|
||||
{
|
||||
if (m_dataReaderObject && m_subscriberCallback)
|
||||
m_subscriberListener->removeCallback(m_dataReaderObject->getDataReader(), this);
|
||||
delete m_dataReaderObject;
|
||||
}
|
||||
|
||||
bool setSubscriberCallback(const SubscriberCallback &subscriberCallback) override final
|
||||
{
|
||||
if (m_subscriberCallback)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Can not set subscriber callback - already set ...";
|
||||
return false;
|
||||
}
|
||||
if (!m_subscriberListener)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Can not set subscriber callback - no subscriber listener ...";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_subscriberCallback = subscriberCallback;
|
||||
|
||||
if (!m_dataReaderObject)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Can not set subscriber callback - no data reader object ...";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dataReaderObject->setHaveSubscriberCallback(m_subscriberCallback != nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
dds::sub::LoanedSamples<TopicTypeRead> readAlive() const override final
|
||||
{
|
||||
if (!m_dataReaderObject)
|
||||
return dds::sub::LoanedSamples<TopicTypeRead>();
|
||||
|
||||
bool bSeenUnreadSamples = false;
|
||||
auto ret = m_dataReaderObject->readAlive(bSeenUnreadSamples);
|
||||
if (bSeenUnreadSamples)
|
||||
QTimer::singleShot(0, this, &GenericDtcImplSubscriberTemplate::unreadSamplesDuringRead);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
dds::sub::LoanedSamples<TopicTypeRead> takeAlive() const override final
|
||||
{
|
||||
if (!m_dataReaderObject)
|
||||
return dds::sub::LoanedSamples<TopicTypeRead>();
|
||||
|
||||
bool bSeenUnreadSamples = false;
|
||||
auto ret = m_dataReaderObject->takeAlive(bSeenUnreadSamples);
|
||||
if (bSeenUnreadSamples)
|
||||
QTimer::singleShot(0, this, &GenericDtcImplSubscriberTemplate::unreadSamplesDuringRead);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
dds::sub::LoanedSamples<TopicTypeRead> takeDisposed() const override final
|
||||
{
|
||||
if (!m_dataReaderObject)
|
||||
return dds::sub::LoanedSamples<TopicTypeRead>();
|
||||
|
||||
return m_dataReaderObject->takeDisposed();
|
||||
}
|
||||
|
||||
bool hasSamples() const override final
|
||||
{
|
||||
if (!m_dataReaderObject)
|
||||
return false;
|
||||
|
||||
return m_dataReaderObject->hasSamples();
|
||||
}
|
||||
|
||||
DataReaderObject *dataReader() const override final
|
||||
{
|
||||
return m_dataReaderObject;
|
||||
}
|
||||
|
||||
bool waitForHistoricalData(int timeoutMSec) override
|
||||
{
|
||||
if (m_dataReaderObject == nullptr ||
|
||||
m_dataReaderObject->getDataReader() == nullptr)
|
||||
return false;
|
||||
|
||||
const bool bWaitForever = (timeoutMSec < 0);
|
||||
auto dr = m_dataReaderObject->getDataReader();
|
||||
try
|
||||
{
|
||||
if (dr.subscription_matched_status().current_count() == 0)
|
||||
{
|
||||
dds::core::cond::WaitSet ws;
|
||||
dds::core::cond::StatusCondition cond(dr);
|
||||
cond.enabled_statuses(dds::core::status::StatusMask::subscription_matched());
|
||||
ws.attach_condition(cond);
|
||||
const dds::core::Duration waitTimeout(bWaitForever ? dds::core::Duration::infinite() : dds::core::Duration::from_millisecs(timeoutMSec));
|
||||
const dds::core::cond::WaitSet::ConditionSeq seq(ws.wait(waitTimeout));
|
||||
|
||||
if (seq.empty())
|
||||
return false;
|
||||
}
|
||||
dr.wait_for_historical_data(dds::core::Duration(1));
|
||||
}
|
||||
catch (const dds::core::Exception &)
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
void unreadSamplesDuringRead()
|
||||
{
|
||||
droDataAvailable(DDS::topic_type_name<TopicTypeRead>::value(), m_dataReaderObject->topicName());
|
||||
}
|
||||
|
||||
void droDataAvailable(const std::string &topicType, const std::string &topicName) override final
|
||||
{
|
||||
Q_UNUSED(topicType);
|
||||
if (m_writerState.testAndSetRelaxed(1, 0))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Recreate Reader for topic " << topicName
|
||||
<< ", type name " << DDS::topic_type_name<TopicTypeRead>::value();
|
||||
if (m_subscriberListener)
|
||||
m_subscriberListener->removeCallback(m_dataReaderObject->getDataReader(), this);
|
||||
m_dataReaderObject->recreateReader();
|
||||
if (m_subscriberListener)
|
||||
m_subscriberListener->addCallback(m_dataReaderObject->getDataReader(), this);
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "IN droDataAvailable(), topic name: " << topicName
|
||||
<< ", type name " << DDS::topic_type_name<TopicTypeRead>::value()
|
||||
<< ", this: " << this
|
||||
<< ", m_dataReaderObject: " << m_dataReaderObject
|
||||
<< ", hasSamples: " << (m_dataReaderObject ? m_dataReaderObject->hasSamples() : -1);
|
||||
|
||||
if (m_subscriberListener)
|
||||
m_subscriberListener->enableCallback(m_dataReaderObject->getDataReader(), this);
|
||||
|
||||
if (!m_dataReaderObject || !m_subscriberCallback)
|
||||
return;
|
||||
|
||||
m_dataReaderObject->read(m_subscriberCallback);
|
||||
}
|
||||
|
||||
void noWriterAnymore(const std::string &topicType, const std::string &topicName) override
|
||||
{
|
||||
Q_UNUSED(topicType);
|
||||
Q_UNUSED(topicName);
|
||||
m_writerState = -1;
|
||||
}
|
||||
|
||||
void writerAvailable(const std::string &topicType, const std::string &topicName) override
|
||||
{
|
||||
Q_UNUSED(topicType);
|
||||
Q_UNUSED(topicName);
|
||||
m_writerState.testAndSetRelaxed(-1, 1);
|
||||
}
|
||||
|
||||
bool createInternal(bool droInitOk)
|
||||
{
|
||||
if (!droInitOk)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error initializing DRO for type " << DDS::topic_type_name<TopicTypeRead>::value();
|
||||
delete m_dataReaderObject;
|
||||
m_dataReaderObject = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_subscriberListener)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "subscriberListener exists, adding type "
|
||||
<< DDS::topic_type_name<TopicTypeRead>::value();
|
||||
m_subscriberListener->addCallback(m_dataReaderObject->getDataReader(), this);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Cannot add DTC to subscriberListener callback - type: "
|
||||
<< DDS::topic_type_name<TopicTypeRead>::value();
|
||||
}
|
||||
m_dataReaderObject->setHaveSubscriberCallback(m_subscriberListener != nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
DDSGeneric::SubscriberListener *m_subscriberListener;
|
||||
|
||||
|
||||
DataReaderObject *m_dataReaderObject;
|
||||
|
||||
|
||||
SubscriberCallback m_subscriberCallback;
|
||||
|
||||
QAtomicInt m_writerState;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
|
||||
template<typename DRO_OBJECT>
|
||||
class GenericDtcImpl final
|
||||
: public GenericDtcImplSubscriberTemplate<GenericDtc<DRO_OBJECT>>
|
||||
{
|
||||
using BaseClass = GenericDtcImplSubscriberTemplate<GenericDtc<DRO_OBJECT>>;
|
||||
public:
|
||||
using BaseClass::BaseClass;
|
||||
};
|
||||
|
||||
|
||||
template<typename DRO_OBJECT>
|
||||
class GenericDtcImplSampleInfo final
|
||||
: public GenericDtcImplSubscriberTemplate<GenericDtcSampleInfo<DRO_OBJECT>>
|
||||
{
|
||||
using BaseClass = GenericDtcImplSubscriberTemplate<GenericDtcSampleInfo<DRO_OBJECT>>;
|
||||
public:
|
||||
using BaseClass::BaseClass;
|
||||
};
|
||||
|
||||
|
||||
template<typename DRO_OBJECT>
|
||||
class DdtcImpl final
|
||||
: public GenericDtcImplSubscriberTemplate<Ddtc<DRO_OBJECT>>
|
||||
{
|
||||
using BaseClass = GenericDtcImplSubscriberTemplate<Ddtc<DRO_OBJECT>>;
|
||||
public:
|
||||
using BaseClass::BaseClass;
|
||||
};
|
||||
|
||||
|
||||
template<typename DRO_OBJECT>
|
||||
class DdtcImplSampleInfo final
|
||||
: public GenericDtcImplSubscriberTemplate<DdtcSampleInfo<DRO_OBJECT>>
|
||||
{
|
||||
using BaseClass = GenericDtcImplSubscriberTemplate<DdtcSampleInfo<DRO_OBJECT>>;
|
||||
public:
|
||||
using BaseClass::BaseClass;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
ADD_LIBRARY(ddsdts INTERFACE)
|
||||
TARGET_INCLUDE_DIRECTORIES(ddsdts
|
||||
INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../
|
||||
)
|
||||
TARGET_LINK_LIBRARIES(ddsdts
|
||||
INTERFACE
|
||||
Qt5::Core
|
||||
ddsgen
|
||||
NGLLogger
|
||||
)
|
||||
SET(SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/generic_dts.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/generic_dts_impl.h
|
||||
)
|
||||
TARGET_SOURCES(ddsdts INTERFACE ${SRCS})
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dds/domain/DomainParticipant.hpp>
|
||||
#include <dds/pub/Publisher.hpp>
|
||||
#include <dds/sub/SampleInfo.hpp>
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
class Base
|
||||
{
|
||||
public:
|
||||
Base() = default;
|
||||
virtual ~Base() = default;
|
||||
};
|
||||
|
||||
template <typename TopicType>
|
||||
class GenericDts : public Base
|
||||
{
|
||||
public:
|
||||
GenericDts() = default;
|
||||
virtual ~GenericDts() = default;
|
||||
|
||||
virtual bool write(const TopicType &data,
|
||||
const dds::core::optional<dds::sub::SampleInfo> &si = dds::core::optional<dds::sub::SampleInfo>()) = 0;
|
||||
virtual bool remove(const TopicType &data, bool errorMsgIfNotExists = true) = 0;
|
||||
};
|
||||
}
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "generic_dts.h"
|
||||
#include <DDS/Generic/generic_data_writer.h>
|
||||
#include <DDS/Generic/generic_logger.h>
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
template<typename DWO_OBJECT>
|
||||
class GenericDtsImpl final : public GenericDts<typename DWO_OBJECT::TopicType>
|
||||
{
|
||||
public:
|
||||
using DataWriterObject = DWO_OBJECT;
|
||||
using TopicType = typename DWO_OBJECT::TopicType;
|
||||
using DataWriter = typename dds::pub::DataWriter<TopicType>;
|
||||
|
||||
GenericDtsImpl(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const dds::pub::Publisher &ddsPublisher,
|
||||
const std::string &topicName = DDS::Helper::getShortTopicName(DDS::topic_type_name<TopicType>::value()))
|
||||
: m_dataWriterObject(new DataWriterObject())
|
||||
, m_logger(DDS::getLogger(DDS::topic_type_name<TopicType>::value(), DDS::DataType::DATA_TOPIC_SERVER))
|
||||
{
|
||||
Q_ASSERT(domainParticipant != nullptr);
|
||||
Q_ASSERT(ddsPublisher != nullptr);
|
||||
|
||||
m_dataWriterObject->create(domainParticipant, ddsPublisher, topicName);
|
||||
}
|
||||
|
||||
~GenericDtsImpl()
|
||||
{
|
||||
delete m_dataWriterObject;
|
||||
}
|
||||
|
||||
bool write(const TopicType &data,
|
||||
const dds::core::optional<dds::sub::SampleInfo> &si = dds::core::optional<dds::sub::SampleInfo>()) override final
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN write ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< ")";
|
||||
|
||||
if (!m_dataWriterObject)
|
||||
return false;
|
||||
|
||||
if (!m_dataWriterObject->write(data, si))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error writing for "
|
||||
<< DDS::topic_type_name<TopicType>::value();
|
||||
return false;
|
||||
}
|
||||
DEBUG_LOG(m_logger) << "TopicData ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< ") written";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(const TopicType &data, bool errorMsgIfNotExists = true) override final
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN remove ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< ")";
|
||||
|
||||
if (!m_dataWriterObject)
|
||||
return false;
|
||||
|
||||
if (!m_dataWriterObject->remove(data, errorMsgIfNotExists))
|
||||
{
|
||||
if (errorMsgIfNotExists)
|
||||
ERROR_LOG(m_logger) << "Error removing for "
|
||||
<< DDS::topic_type_name<TopicType>::value();
|
||||
return false;
|
||||
}
|
||||
DEBUG_LOG(m_logger) << "TopicData ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< ") removed";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
DataWriterObject *m_dataWriterObject;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+129
@@ -0,0 +1,129 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
IF(BUILD_TESTING AND TARGET ddsgen AND TARGET ddsdtc AND TARGET ddsfts AND TARGET ddsQoS)
|
||||
|
||||
SET(xml_FILE ddsut.xml)
|
||||
|
||||
SET(SRCS_common
|
||||
ddsut_export.h
|
||||
)
|
||||
# Generates ddsut.idl / ddsut_enums.idl
|
||||
CODEGEN_IDL_SVC(SRCS_common ${xml_FILE})
|
||||
# Generate *.cpp/*.hpp from ddsut_enums.idl / ddsut.idl
|
||||
RTIDDSGEN(SRCS_common ddsut_enums.idl)
|
||||
RTIDDSGEN(SRCS_common ddsut.idl)
|
||||
# Generate ddsut_dt[s,c]_types.h, ddsut_ft[s,c]_types.h
|
||||
CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
|
||||
# generate ddsut_[d|f]tc.h
|
||||
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE})
|
||||
|
||||
NGL_ADD_LIBRARY(ALL ddsut_common SHARED ${SRCS_common})
|
||||
NGL_TARGET_LINK_LIBRARIES(ddsut_common
|
||||
PRIVATE
|
||||
NGLLogger
|
||||
PUBLIC
|
||||
LtsUtilsCoreHeaderOnly
|
||||
Qt5::Core
|
||||
RTI::nddsc
|
||||
RTI::nddscore
|
||||
RTI::nddscpp2
|
||||
ddscore
|
||||
)
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(ddsut_common
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Testing DataWriter/DataReader without DTS/DTC
|
||||
# -----------------------------------------------------------------------------------
|
||||
SET(SRCS_TEST1
|
||||
generic_data_tests.cpp
|
||||
generic_data_tests.h
|
||||
initialisation.cpp
|
||||
initialisation.h
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Testing DataTopic Functionality (DTS/DTC)
|
||||
# -----------------------------------------------------------------------------------
|
||||
SET(SRCS_TEST2
|
||||
data_topic_tests_main.cpp
|
||||
data_topic_tests_main.h
|
||||
data_topic_tests.cpp
|
||||
data_topic_tests.h
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------------------
|
||||
# Testing FunctionTopic Functionality (FTS/FTC)
|
||||
# -----------------------------------------------------------------------------------
|
||||
SET(SRCS_TEST3
|
||||
function_topic_tests.cpp
|
||||
function_topic_tests.h
|
||||
)
|
||||
|
||||
|
||||
# Testing DataWriter/DataReader without DTS/DTC
|
||||
ADD_EXECUTABLE(ddsGenericDataTests ${SRCS_TEST1})
|
||||
TARGET_LINK_LIBRARIES(ddsGenericDataTests
|
||||
ddsgen
|
||||
ddsdtc
|
||||
ddsQoS
|
||||
nglcoreapplication
|
||||
NGLLogger
|
||||
ProjectConfig
|
||||
Qt5::Core
|
||||
Qt5::Test
|
||||
ddsut_common
|
||||
)
|
||||
ADD_TEST(NAME ddsGenericDataTests COMMAND $<TARGET_FILE:ddsGenericDataTests>)
|
||||
|
||||
# Testing DataTopic Functionality (DTS/DTC)
|
||||
ADD_EXECUTABLE(ddsDataTopicTests ${SRCS_TEST2})
|
||||
TARGET_LINK_LIBRARIES(ddsDataTopicTests
|
||||
nglcoreapplication
|
||||
NGLLogger
|
||||
RTI::nddscpp2
|
||||
ddsgen
|
||||
ddsdts
|
||||
ddsdtc
|
||||
ddsQoS
|
||||
ProjectConfig
|
||||
Qt5::Core
|
||||
Qt5::Test
|
||||
ddsut_common
|
||||
)
|
||||
ADD_TEST(NAME ddsDataTopicTests COMMAND $<TARGET_FILE:ddsDataTopicTests>)
|
||||
|
||||
# Testing FunctionTopic Functionality (FTS/FTC)
|
||||
ADD_EXECUTABLE(ddsFunctionTopicTests ${SRCS_TEST3})
|
||||
TARGET_LINK_LIBRARIES(ddsFunctionTopicTests
|
||||
ddsgen
|
||||
ddsfts
|
||||
ddsftc
|
||||
ddsQoS
|
||||
ddsconnector
|
||||
nglcoreapplication
|
||||
NGLLogger
|
||||
ngltools
|
||||
Qt5::Core
|
||||
Qt5::Test
|
||||
ddsut_common
|
||||
)
|
||||
ADD_TEST(NAME ddsFunctionTopicTests COMMAND $<TARGET_FILE:ddsFunctionTopicTests>)
|
||||
|
||||
IF(NOT BUILD_RPM)
|
||||
NGL_INSTALL(AAR SV TARGETS ddsut_common DESTINATION bin/test)
|
||||
NGL_INSTALL(AAR SV TARGETS ddsGenericDataTests DESTINATION bin/test)
|
||||
NGL_INSTALL(AAR SV TARGETS ddsDataTopicTests DESTINATION bin/test)
|
||||
NGL_INSTALL(AAR SV TARGETS ddsFunctionTopicTests DESTINATION bin/test)
|
||||
|
||||
NGL_INSTALL(AAR SV FILES log4cxx_ddsUnitTests.props DESTINATION bin/test)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
Executable
+155
@@ -0,0 +1,155 @@
|
||||
|
||||
|
||||
|
||||
#include "data_topic_tests.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
#include <DDS/QosProfile/DDS_DefaultQos.h>
|
||||
#include <DDS/DTS/generic_dts_impl.h>
|
||||
#include <DDS/DTC/generic_dtc_impl.h>
|
||||
#include <Common/ProjectConfig/project_defines.h>
|
||||
|
||||
#include <QtTest/QTest>
|
||||
#include <QVariant>
|
||||
#include <QThread>
|
||||
|
||||
|
||||
DataTopicTests::DataTopicTests()
|
||||
: m_domainParticipantPub(nullptr)
|
||||
, m_domainParticipantSub(nullptr)
|
||||
, m_domainId(0)
|
||||
, m_partitionId(1)
|
||||
, m_ddsPublisher(nullptr)
|
||||
, m_testPart(nullptr)
|
||||
, m_ddsSubscriber(nullptr)
|
||||
, m_subscriberListener(nullptr)
|
||||
, m_dtcTestPart(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()
|
||||
{
|
||||
|
||||
delete m_testPart;
|
||||
|
||||
|
||||
delete m_dtcTestPart;
|
||||
|
||||
|
||||
|
||||
delete m_subscriberListener;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool DataTopicTests::initPublisher()
|
||||
{
|
||||
m_domainParticipantPub =
|
||||
dds::domain::DomainParticipant(m_domainId,
|
||||
DDS::QosProfile::domainParticipantShm());
|
||||
|
||||
dds::pub::qos::PublisherQos publisherQos = m_domainParticipantPub.default_publisher_qos();
|
||||
|
||||
|
||||
QString partition = QString::fromStdString(IDL::exercisePartition(m_partitionId));
|
||||
|
||||
dds::core::policy::Partition partitionQos;
|
||||
partitionQos.name( { partition.toStdString() } );
|
||||
publisherQos << partitionQos;
|
||||
|
||||
DEBUG_LOG(m_logger) << "Publish on partition " << partition;
|
||||
|
||||
m_ddsPublisher = dds::pub::Publisher(m_domainParticipantPub, publisherQos);
|
||||
m_testPart = new DTS::TestParticipantImpl(m_domainParticipantPub, m_ddsPublisher);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DataTopicTests::write(const DdsUnitTests::TestParticipant &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Publish topic TestParticipant ";
|
||||
DEBUG_LOG(m_logger) << "ParticipantId: " << data.location().participantId()
|
||||
<< ", equipNo = " << data.location().equipNo();
|
||||
|
||||
return m_testPart->write(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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();
|
||||
|
||||
|
||||
dds::core::StringSeq partitions;
|
||||
partitions.reserve(1);
|
||||
for(int partitionId : { 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_subscriberListener,
|
||||
DDSGeneric::SubscriberListener::mask());
|
||||
|
||||
|
||||
m_dtcTestPart = new DTC::SampleInfo::TestParticipantImpl(m_domainParticipantSub,
|
||||
m_ddsSubscriber,
|
||||
m_subscriberListener);
|
||||
|
||||
m_dtcTestPart->setSubscriberCallback([this](const DdsUnitTests::TestParticipant &data, const dds::sub::SampleInfo &si) { processData(data, si); } );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataTopicTests::deleteDTC()
|
||||
{
|
||||
delete m_dtcTestPart;
|
||||
m_dtcTestPart = nullptr;
|
||||
}
|
||||
|
||||
void DataTopicTests::deleteSubscriber()
|
||||
{
|
||||
m_ddsSubscriber = nullptr;
|
||||
delete m_subscriberListener;
|
||||
m_subscriberListener = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DataTopicTests::processData(const DdsUnitTests::TestParticipant &instance, const dds::sub::SampleInfo &si)
|
||||
{
|
||||
INFO_LOG(m_logger) << "process data in topic TestParticipant, Sampleinfo: " << si.source_timestamp().sec();
|
||||
|
||||
DEBUG_LOG(m_logger) << "participantId: " << instance.location().participantId();
|
||||
DEBUG_LOG(m_logger) << "equipNo: " << instance.location().equipNo();
|
||||
|
||||
m_receivedParticipantData = instance;
|
||||
|
||||
QCOMPARE(m_mainThreadId, QThread::currentThreadId());
|
||||
|
||||
emit testParticipantDataArrived();
|
||||
}
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <DDS/Generic/subscriber_listener.h>
|
||||
|
||||
#include <DdsUnitTests/ddsut_dtc_types.h>
|
||||
#include <DdsUnitTests/ddsut_dts_types.h>
|
||||
|
||||
|
||||
namespace NGL {
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
class DataTopicTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataTopicTests();
|
||||
virtual ~DataTopicTests();
|
||||
|
||||
|
||||
bool initPublisher();
|
||||
bool initSubscriber();
|
||||
|
||||
|
||||
bool write(const DdsUnitTests::TestParticipant &data);
|
||||
|
||||
|
||||
DdsUnitTests::TestParticipant getReceivedParticipantData() const { return m_receivedParticipantData; }
|
||||
void deleteDTC();
|
||||
void deleteSubscriber();
|
||||
|
||||
protected:
|
||||
|
||||
void processData(const DdsUnitTests::TestParticipant &instance, const dds::sub::SampleInfo &si);
|
||||
|
||||
Q_SIGNALS:
|
||||
void testParticipantDataArrived();
|
||||
|
||||
private:
|
||||
dds::domain::DomainParticipant m_domainParticipantPub;
|
||||
dds::domain::DomainParticipant m_domainParticipantSub;
|
||||
int m_domainId;
|
||||
int m_partitionId;
|
||||
|
||||
|
||||
dds::pub::Publisher m_ddsPublisher;
|
||||
DTS::TestParticipant *m_testPart;
|
||||
|
||||
|
||||
dds::sub::Subscriber m_ddsSubscriber;
|
||||
DDSGeneric::SubscriberListener *m_subscriberListener;
|
||||
|
||||
DTC::SampleInfo::TestParticipant *m_dtcTestPart;
|
||||
|
||||
Qt::HANDLE m_mainThreadId;
|
||||
|
||||
DdsUnitTests::TestParticipant m_receivedParticipantData;
|
||||
|
||||
NGL::INGLLogger& m_logger;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
|
||||
|
||||
|
||||
#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_ddsUnitTests.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_participantDataArrivedSpy = new QSignalSpy(m_dtTest, &DataTopicTests::testParticipantDataArrived);
|
||||
}
|
||||
|
||||
|
||||
void DataTopicTestsMain::publishTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<"DataTopicTestsMain: publish TestParticipant";
|
||||
|
||||
m_sentParticipantData.location().participantId(100);
|
||||
m_sentParticipantData.location().equipNo(1010);
|
||||
m_sentParticipantData.testId(100);
|
||||
QVERIFY2(m_dtTest->write(m_sentParticipantData), "Publishing Topic TestParticipant failed");
|
||||
|
||||
DEBUG_LOG(m_logger) <<"DataTopicTestsMain: publish ready";
|
||||
}
|
||||
|
||||
|
||||
void DataTopicTestsMain::subscribeTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN subscribeTestCase";
|
||||
|
||||
QTRY_COMPARE(m_participantDataArrivedSpy->count(), 1);
|
||||
|
||||
DdsUnitTests::TestParticipant recvPartiticpant = m_dtTest->getReceivedParticipantData();
|
||||
|
||||
QCOMPARE(recvPartiticpant.location().participantId(), m_sentParticipantData.location().participantId());
|
||||
QCOMPARE(recvPartiticpant.location().equipNo(), m_sentParticipantData.location().equipNo());
|
||||
}
|
||||
|
||||
|
||||
void DataTopicTestsMain::deleteDataReaderTestCase()
|
||||
{
|
||||
DdsUnitTests::TestParticipant data;
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
m_sentParticipantData.id(i);
|
||||
m_dtTest->write(m_sentParticipantData);
|
||||
}
|
||||
m_dtTest->deleteDTC();
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
m_sentParticipantData.id(i);
|
||||
m_dtTest->write(m_sentParticipantData);
|
||||
}
|
||||
m_dtTest->deleteSubscriber();
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
m_sentParticipantData.id(i);
|
||||
m_dtTest->write(m_sentParticipantData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DataTopicTestsMain::cleanupTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<"DataTopicTestsMain: cleanupTestCase";
|
||||
delete m_dtTest; m_dtTest = nullptr;
|
||||
delete m_participantDataArrivedSpy; m_participantDataArrivedSpy = nullptr;
|
||||
}
|
||||
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QSignalSpy>
|
||||
#include <DdsUnitTests/ddsut_dtc_types.h>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
class DataTopicTests;
|
||||
|
||||
|
||||
class DataTopicTestsMain : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DataTopicTestsMain();
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void publishTestCase();
|
||||
void subscribeTestCase();
|
||||
void deleteDataReaderTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
DdsUnitTests::TestParticipant m_sentParticipantData;
|
||||
QSignalSpy *m_participantDataArrivedSpy = nullptr;
|
||||
DataTopicTests *m_dtTest = nullptr;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
Executable
+123
@@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<service namespace="DdsUnitTests" abbreviation="DDSUT" procNames="">
|
||||
<rcs>
|
||||
<Id Id="$Id: core.xml 38132 2018-01-10 07:52:50Z ehrlicher $"/>
|
||||
<HeadURL HeadURL="$HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/DDS/Core/core.xml $"/>
|
||||
<Author Author="$Author: ehrlicher $"/>
|
||||
</rcs>
|
||||
<tables/>
|
||||
<views/>
|
||||
<topicTypes>
|
||||
<topicType name="BaseEntity">
|
||||
<column name="id" idlType="long" isKey="1"/>
|
||||
<column name="name" idlType="string"/>
|
||||
<column name="isDeleted" idlType="boolean"/>
|
||||
<column name="isUsed" idlType="boolean" comment="Defines if it is used by others (i.e. foreign key constraint)"/>
|
||||
<column name="isBlockedForEdit" idlType="boolean"/>
|
||||
</topicType>
|
||||
<topicType name="BaseEntityTranslatable">
|
||||
<column name="id" idlType="long" isKey="1"/>
|
||||
<column name="name" idlType="string" isTranslatable="1"/>
|
||||
<column name="isDeleted" idlType="boolean"/>
|
||||
<column name="isUsed" idlType="boolean" comment="Defines if it is used by others (i.e. foreign key constraint)"/>
|
||||
<column name="isBlockedForEdit" idlType="boolean"/>
|
||||
</topicType>
|
||||
<topicType name="BaseEntityWithSampleTime" extensibility="EXTENSIBLE" baseValueType="BaseEntity">
|
||||
<column name="sampleTime" idlType="long long" comment="Timestamp"/>
|
||||
</topicType>
|
||||
<topicType name="CurrentRunningState" isDataObject="1" routeToClient="0" routeInReplay="0">
|
||||
<column name="pid" idlType="long long" isKey="1"/>
|
||||
<column name="runningState" idlType="Core::RunningState"/>
|
||||
<column name="ports" idlType="sequence<PortInformation>" isOptional="1"/>
|
||||
<column name="errorString" idlType="string" isOptional="1"/>
|
||||
</topicType>
|
||||
<topicType name="Entity" extensibility="EXTENSIBLE" baseValueType="BaseEntityWithSampleTime">
|
||||
<column name="location" idlType="SpatialData"/>
|
||||
<column name="symbolId" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="FunctionReply">
|
||||
<column name="id" idlType="long"/>
|
||||
<column name="errorString" idlType="string"/>
|
||||
<column name="errorParams" idlType="sequence<string>"/>
|
||||
</topicType>
|
||||
<topicType name="FunctionReplyWithState" baseValueType="FunctionReply">
|
||||
<column name="replyState" idlType="Core::FtReplyState"/>
|
||||
</topicType>
|
||||
<topicType name="FunctionRequest">
|
||||
<column name="id" idlType="long"/>
|
||||
<column name="value" idlType="double"/>
|
||||
<column name="info" idlType="string" isOptional="1"/>
|
||||
</topicType>
|
||||
<topicType name="FunctionRequestWithState" baseValueType="FunctionRequest">
|
||||
<column name="status" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="GeoPosition" extensibility="FINAL">
|
||||
<column name="lon" idlType="double"/>
|
||||
<column name="lat" idlType="double"/>
|
||||
<column name="z" idlType="double" comment="height above ground"/>
|
||||
</topicType>
|
||||
<topicType name="GeoPosition2D" extensibility="FINAL">
|
||||
<column name="lon" idlType="double" comment="[m]"/>
|
||||
<column name="lat" idlType="double" comment="[m]"/>
|
||||
</topicType>
|
||||
<topicType name="Orientation" extensibility="FINAL">
|
||||
<column name="heave" idlType="double"/>
|
||||
<column name="pitch" idlType="double"/>
|
||||
<column name="roll" idlType="double"/>
|
||||
</topicType>
|
||||
<topicType name="PartitionTime" isDataObject="1" routeToClient="1" routeInReplay="0">
|
||||
<column name="timeStamp" idlType="long long"/>
|
||||
</topicType>
|
||||
<topicType name="PortInformation">
|
||||
<column name="number" idlType="long"/>
|
||||
<column name="type" idlType="PortType"/>
|
||||
</topicType>
|
||||
<topicType name="RelativePosition">
|
||||
<column name="offsetX" idlType="double" comment="in meter"/>
|
||||
<column name="offsetY" idlType="double" comment="in meter"/>
|
||||
<column name="offsetZ" idlType="double" comment="in meter"/>
|
||||
</topicType>
|
||||
<topicType name="RemoveElementRequest">
|
||||
<column name="id" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="ReplayPartition" isDataObject="1" routeToClient="1" routeInReplay="0">
|
||||
<column name="id" idlType="long" isKey="1"/>
|
||||
<column name="name" idlType="string"/>
|
||||
<column name="exerciseId" idlType="long"/>
|
||||
<column name="workstationId" idlType="long"/>
|
||||
<column name="operatorId" idlType="long"/>
|
||||
<column name="replayType" idlType="Core::ReplayType"/>
|
||||
<column name="domainId" idlType="long"/>
|
||||
<column name="tcpConnectionUrl" idlType="string"/>
|
||||
</topicType>
|
||||
<topicType name="SpatialData" isDataObject="1" routeToClient="0" routeInReplay="0">
|
||||
<column name="changedAt" idlType="long"/>
|
||||
<column name="equipNo" idlType="long"/>
|
||||
<column name="gpsPosition" idlType="GeoPosition"/>
|
||||
<column name="orientation" idlType="Orientation"/>
|
||||
<column name="participantId" idlType="long"/>
|
||||
<column name="velocity" idlType="Velocity"/>
|
||||
</topicType>
|
||||
<topicType name="TestParticipant" isDataObject="1" routeToClient="0" routeInReplay="0" recordTopic="1" baseValueType="Entity">
|
||||
<column name="designator" idlType="string<20>"/>
|
||||
<column name="testId" idlType="long"/>
|
||||
<column name="gpsQuality" idlType="short"/>
|
||||
<column name="equipmentNumber" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="Velocity">
|
||||
<column name="x" idlType="double"/>
|
||||
<column name="y" idlType="double"/>
|
||||
<column name="z" idlType="double"/>
|
||||
</topicType>
|
||||
</topicTypes>
|
||||
<functionTopics>
|
||||
<functionTopic name="FunctionTopicTest" requestTopicType="FunctionRequest" replyTopicType="FunctionReply" requestTopicName="FunctionTopicTestRequest" replyTopicName="FunctionTopicTestReply" routeInReplay="0"/>
|
||||
<functionTopic name="FunctionTopicTestWithState" requestTopicType="FunctionRequestWithState" replyTopicType="FunctionReplyWithState" requestTopicName="FunctionTopicTestWithStateRequest" replyTopicName="FunctionTopicTestWithStateReply" routeInReplay="0"/>
|
||||
</functionTopics>
|
||||
<enumerations>
|
||||
<enumeration name="PortType">
|
||||
<attribute key="Unknown" value="0"/>
|
||||
<attribute key="WebSocketServer" value="1"/>
|
||||
</enumeration>
|
||||
</enumerations>
|
||||
</service>
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#ifndef DDSUT_COMMON_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define DDSUT_COMMON_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_DDSUT_COMMON_LIB)
|
||||
# define DDSUT_COMMON_EXPORT Q_DECL_EXPORT
|
||||
# ifndef WIN32
|
||||
# ifdef NDDSUSERDllExport
|
||||
# undef NDDSUSERDllExport
|
||||
# endif
|
||||
# define NDDSUSERDllExport Q_DECL_EXPORT
|
||||
# endif
|
||||
# else
|
||||
# define DDSUT_COMMON_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
|
||||
|
||||
|
||||
#include "function_topic_tests.h"
|
||||
#include <Common/ProjectConfig/project_defines.h>
|
||||
#include <DDS/DDSConnector/subscriber.h>
|
||||
#include <DDS/DDSConnector/publisher.h>
|
||||
|
||||
#include <QtTest/QTest>
|
||||
|
||||
#include <nglapplication/nglcoreapplication.h>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
NGLCoreApplication app(argc, argv);
|
||||
app.createLogger("UnitTests", "log4cxx_ddsUnitTests.props");
|
||||
|
||||
FunctionTopicTests *tc = new FunctionTopicTests;
|
||||
|
||||
int retCode = QTest::qExec(tc, argc, argv);
|
||||
|
||||
delete tc;
|
||||
|
||||
return retCode;
|
||||
}
|
||||
|
||||
|
||||
FunctionTopicTests::FunctionTopicTests()
|
||||
: m_mainThreadId(QThread::currentThreadId())
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTests.FunctionTopicTests"))
|
||||
{
|
||||
qRegisterMetaType<DdsUnitTests::FunctionReply>("DdsUnitTests::FunctionReply");
|
||||
qRegisterMetaType<DdsUnitTests::FunctionReplyWithState>("DdsUnitTests::FunctionReplyWithState");
|
||||
}
|
||||
|
||||
|
||||
void FunctionTopicTests::initTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<"FunctionTopicTests: initTestCase";
|
||||
|
||||
const int domainId = 0;
|
||||
|
||||
m_ddsConnectorClient = std::make_shared<DDSConnector::DdsConnector>(domainId, IDL::PartitionType::Global);
|
||||
QVERIFY2(m_ddsConnectorClient->init(), "Initialisation of ddsconnector (Client) failed");
|
||||
m_ddsConnectorClient->initSubscriber();
|
||||
QVERIFY2(m_ddsConnectorClient->getSubscriber(), "Initialisation of Subscriber (DdsConnectorClient) failed");
|
||||
m_ddsConnectorClient->initPublisher();
|
||||
QVERIFY2(m_ddsConnectorClient->getPublisher(), "Initialisation of Publisher (DdsConnectorClient) failed");
|
||||
|
||||
m_ddsConnectorServer = std::make_shared<DDSConnector::DdsConnector>(domainId, IDL::PartitionType::Global);
|
||||
QVERIFY2(m_ddsConnectorServer->init(), "Initialisation of ddsconnector (Server) failed");
|
||||
m_ddsConnectorServer->initSubscriber();
|
||||
QVERIFY2(m_ddsConnectorServer->getSubscriber(), "Initialisation of Subscriber (DdsConnectorServer) failed");
|
||||
m_ddsConnectorServer->initPublisher();
|
||||
QVERIFY2(m_ddsConnectorServer->getPublisher(), "Initialisation of Publisher (DdsConnectorServer) failed");
|
||||
}
|
||||
|
||||
|
||||
void FunctionTopicTests::initFTSTestCase()
|
||||
{
|
||||
m_fts.reset(m_ddsConnectorServer->createFTS<FTS::FunctionTopicTest>());
|
||||
QVERIFY2(m_fts.get(), "Initialisation of FTS Replier failed");
|
||||
m_fts->setSubscriberCallback([this](const rti::core::SampleIdentity &identity, const DdsUnitTests::FunctionRequest &request) { this->onFunctionTopicRequest(identity, request);});
|
||||
|
||||
m_ftsWS.reset(m_ddsConnectorServer->createFTS<FTS::FunctionTopicTestWithState>());
|
||||
QVERIFY2(m_ftsWS.get(), "Initialisation of FTS Replier failed");
|
||||
m_ftsWS->setSubscriberCallback([this](const rti::core::SampleIdentity &identity, const DdsUnitTests::FunctionRequestWithState &request) { this->onFunctionTopicRequest(identity, request);});
|
||||
|
||||
}
|
||||
|
||||
void FunctionTopicTests::initFTCTestCase()
|
||||
{
|
||||
m_ftc.reset(m_ddsConnectorClient->createFTC<FTC::FunctionTopicTest>(this));
|
||||
QVERIFY2(m_ftc.get(), "Initialisation of FTC Requester failed");
|
||||
m_ftcWS.reset(m_ddsConnectorClient->createFTC<FTC::FunctionTopicTestWithState>(this));
|
||||
QVERIFY2(m_ftc.get(), "Initialisation of FTC Requester failed");
|
||||
}
|
||||
|
||||
|
||||
void FunctionTopicTests::requestTestCase1()
|
||||
{
|
||||
|
||||
requestWithState();
|
||||
|
||||
QCOMPARE(m_ddsConnectorServer->getWaitSetHandler()->attachedConditionCount(), 3);
|
||||
requestWithState();
|
||||
|
||||
QCOMPARE(m_ddsConnectorServer->getWaitSetHandler()->attachedConditionCount(), 3);
|
||||
}
|
||||
|
||||
|
||||
void FunctionTopicTests::requestWithState()
|
||||
{
|
||||
|
||||
QSignalSpy spyRequest(this, &FunctionTopicTests::functionTopicRequestFinished);
|
||||
QSignalSpy spyReply(this, &FunctionTopicTests::functionTopicReply);
|
||||
|
||||
DEBUG_LOG(m_logger) <<"FunctionTopicTests: request";
|
||||
|
||||
|
||||
DdsUnitTests::FunctionRequestWithState data;
|
||||
|
||||
data.id(1);
|
||||
data.info(std::string("Test request"));
|
||||
data.value(1000.0);
|
||||
|
||||
int uniqueId = 0;
|
||||
bool bRet = m_ftcWS->requestDataClassic(data,
|
||||
SLOT(slotRequestedData(DdsUnitTests::FunctionTopicTestWithStateReply,int,bool)),
|
||||
SLOT(slotRequestedDataError(int,int,int)),
|
||||
uniqueId);
|
||||
|
||||
QVERIFY2(bRet, "Could not request data!");
|
||||
|
||||
QTRY_COMPARE(spyRequest.count(), 1);
|
||||
QTRY_COMPARE(spyReply.count(), 2);
|
||||
}
|
||||
|
||||
|
||||
void FunctionTopicTests::requestTestCase2()
|
||||
{
|
||||
|
||||
QSignalSpy spyRequest(this, &FunctionTopicTests::functionTopicRequestFinished);
|
||||
QSignalSpy spyReply(this, &FunctionTopicTests::functionTopicReply);
|
||||
|
||||
DEBUG_LOG(m_logger) <<"FunctionTopicTests: request";
|
||||
|
||||
|
||||
DdsUnitTests::FunctionRequest data;
|
||||
|
||||
data.id(1);
|
||||
data.info(std::string("Test request"));
|
||||
data.value(1000.0);
|
||||
|
||||
int uniqueId = 0;
|
||||
bool bRet = m_ftc->requestDataClassic(data,
|
||||
SLOT(slotRequestedData(DdsUnitTests::FunctionTopicTestReply,int,bool)),
|
||||
SLOT(slotRequestedDataError(int,int,int)),
|
||||
uniqueId);
|
||||
|
||||
QVERIFY2(bRet, "Could not request data!");
|
||||
|
||||
QTRY_COMPARE(spyRequest.count(), 1);
|
||||
QTRY_COMPARE(spyReply.count(), 1);
|
||||
}
|
||||
|
||||
|
||||
void FunctionTopicTests::cleanupTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<"FunctionTopicTests: cleanupTestCase";
|
||||
|
||||
m_fts = nullptr;
|
||||
m_ftc = nullptr;
|
||||
m_ftsWS = nullptr;
|
||||
m_ftcWS = nullptr;
|
||||
m_ddsConnectorServer = nullptr;
|
||||
m_ddsConnectorClient = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void FunctionTopicTests::onFunctionTopicRequest(const rti::core::SampleIdentity &sampleId,
|
||||
const DdsUnitTests::FunctionRequestWithState &request)
|
||||
{
|
||||
QCOMPARE(m_mainThreadId, QThread::currentThreadId());
|
||||
|
||||
INFO_LOG(m_logger) << "Got function topic request (with status member)";
|
||||
m_requestTopicWithState = request;
|
||||
QCOMPARE(m_requestTopicWithState.id(), 1);
|
||||
QCOMPARE(m_requestTopicWithState.info().get(), std::string("Test request"));
|
||||
QCOMPARE(m_requestTopicWithState.value(), 1000.0);
|
||||
|
||||
DdsUnitTests::FunctionReplyWithState reply;
|
||||
|
||||
DEBUG_LOG(m_logger) << "Send sample with status REPLY_COMPLETED";
|
||||
reply.id(1);
|
||||
reply.errorString("Hello");
|
||||
reply.replyState(Core::FtReplyState::Completed);
|
||||
|
||||
bool bRet = m_ftsWS->sendReply(sampleId, reply);
|
||||
QVERIFY2(bRet, "Sending Reply failed");
|
||||
|
||||
emit functionTopicRequestFinished();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FunctionTopicTests::onFunctionTopicRequest(const rti::core::SampleIdentity &sampleId,
|
||||
const DdsUnitTests::FunctionRequest &request)
|
||||
{
|
||||
QCOMPARE(m_mainThreadId, QThread::currentThreadId());
|
||||
|
||||
INFO_LOG(m_logger) << "Got function topic request (without status member)";
|
||||
|
||||
m_requestTopic = request;
|
||||
|
||||
DdsUnitTests::FunctionReply reply;
|
||||
|
||||
reply.id(2);
|
||||
reply.errorString("What?");
|
||||
|
||||
bool bRet = m_fts->sendReply(sampleId, reply);
|
||||
QVERIFY2(bRet, "Sending Reply failed");
|
||||
|
||||
emit functionTopicRequestFinished();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FunctionTopicTests::slotRequestedData(const DdsUnitTests::FunctionTopicTestWithStateReply &reply,
|
||||
int seqNo, bool bFinished)
|
||||
{
|
||||
Q_UNUSED(seqNo);
|
||||
QCOMPARE(m_mainThreadId, QThread::currentThreadId());
|
||||
|
||||
INFO_LOG(m_logger) << "Got reply from FTS: " << reply.replyState();
|
||||
|
||||
m_replyTopicWithState = reply;
|
||||
|
||||
if (m_replyTopicWithState.replyState() == Core::FtReplyState::InProgress)
|
||||
{
|
||||
QVERIFY(!bFinished);
|
||||
QCOMPARE(m_replyTopicWithState.id(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
QVERIFY(bFinished);
|
||||
QCOMPARE(m_replyTopicWithState.id(), 1);
|
||||
QCOMPARE(m_replyTopicWithState.errorString(), std::string("Hello"));
|
||||
QCOMPARE(m_replyTopicWithState.replyState().underlying(), Core::FtReplyState::Completed);
|
||||
}
|
||||
emit functionTopicReply();
|
||||
}
|
||||
|
||||
void FunctionTopicTests::slotRequestedData(const DdsUnitTests::FunctionTopicTestReply &reply,
|
||||
int seqNo, bool bFinished)
|
||||
{
|
||||
Q_UNUSED(seqNo);
|
||||
QVERIFY(bFinished);
|
||||
QCOMPARE(m_mainThreadId, QThread::currentThreadId());
|
||||
|
||||
m_replyTopic = reply;
|
||||
QCOMPARE(m_replyTopic.id(), 2);
|
||||
QCOMPARE(m_replyTopic.errorString(), std::string("What?"));
|
||||
|
||||
emit functionTopicReply();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FunctionTopicTests::slotRequestedDataError(int reason, int seqNo, int requestId)
|
||||
{
|
||||
Q_UNUSED(seqNo);
|
||||
Q_UNUSED(requestId);
|
||||
QFAIL(qPrintable(QStringLiteral("Error when trying to request data: %1").arg(reason)));
|
||||
}
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QSignalSpy>
|
||||
#include <QThread>
|
||||
|
||||
#include <DdsUnitTests/ddsut.hpp>
|
||||
#include <DdsUnitTests/ddsut_ftc_types.h>
|
||||
#include <DdsUnitTests/ddsut_fts_types.h>
|
||||
#include <DDS/DDSConnector/dds_connector.h>
|
||||
#include <DDS/FTC/generic_ftc_impl.h>
|
||||
#include <DDS/FTS/generic_fts_impl.h>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
class FunctionTopicTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FunctionTopicTests();
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void initFTSTestCase();
|
||||
void initFTCTestCase();
|
||||
void requestTestCase1();
|
||||
void requestTestCase2();
|
||||
void cleanupTestCase();
|
||||
|
||||
Q_SIGNALS:
|
||||
void functionTopicRequestFinished();
|
||||
void functionTopicReply();
|
||||
protected Q_SLOTS:
|
||||
|
||||
void slotRequestedData(const DdsUnitTests::FunctionTopicTestReply &reply, int seqNo, bool bFinished);
|
||||
void slotRequestedData(const DdsUnitTests::FunctionTopicTestWithStateReply &reply, int seqNo, bool bFinished);
|
||||
void slotRequestedDataError(int reason, int seqNo, int requestId);
|
||||
|
||||
void onFunctionTopicRequest(const rti::core::SampleIdentity &sampleId,
|
||||
const DdsUnitTests::FunctionRequest &request);
|
||||
void onFunctionTopicRequest(const rti::core::SampleIdentity &sampleId,
|
||||
const DdsUnitTests::FunctionRequestWithState &request);
|
||||
|
||||
private:
|
||||
void requestWithState();
|
||||
private:
|
||||
|
||||
std::unique_ptr<FTS::FunctionTopicTest> m_fts;
|
||||
std::unique_ptr<FTS::FunctionTopicTestWithState> m_ftsWS;
|
||||
|
||||
std::unique_ptr<FTC::FunctionTopicTest> m_ftc;
|
||||
std::unique_ptr<FTC::FunctionTopicTestWithState> m_ftcWS;
|
||||
|
||||
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnectorClient;
|
||||
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnectorServer;
|
||||
|
||||
|
||||
DdsUnitTests::FunctionRequest m_requestTopic;
|
||||
DdsUnitTests::FunctionReply m_replyTopic;
|
||||
|
||||
DdsUnitTests::FunctionRequestWithState m_requestTopicWithState;
|
||||
DdsUnitTests::FunctionReplyWithState m_replyTopicWithState;
|
||||
|
||||
Qt::HANDLE m_mainThreadId;
|
||||
|
||||
NGL::INGLLogger& m_logger;
|
||||
};
|
||||
Executable
+226
@@ -0,0 +1,226 @@
|
||||
|
||||
|
||||
|
||||
#include "generic_data_tests.h"
|
||||
#include <Common/ProjectConfig/project_defines.h>
|
||||
#include <DDS/Generic/generic_data_reader.h>
|
||||
#include <DDS/Generic/generic_data_writer.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_ddsUnitTests.props");
|
||||
|
||||
GenericDataTests *tc = new GenericDataTests;
|
||||
|
||||
int retCode = QTest::qExec(tc, argc, argv);
|
||||
|
||||
delete tc;
|
||||
|
||||
return retCode;
|
||||
}
|
||||
|
||||
GenericDataTests::GenericDataTests()
|
||||
: m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTests.GenericDataTests"))
|
||||
{
|
||||
m_spatialData.equipNo(0);
|
||||
m_testPart.location().equipNo(0);
|
||||
m_testPart.location().participantId(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GenericDataTests::initTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<"GenericDataTests: initTestCase";
|
||||
|
||||
m_init = new Initialisation();
|
||||
|
||||
int domainId = 0;
|
||||
int exerciseId = 2;
|
||||
|
||||
QVERIFY2(m_init, "Initialisation failed");
|
||||
QVERIFY2(m_init->initDDS(domainId, exerciseId, Initialisation::USE_PUBLISHER), "Initialisation (Publisher) failed");
|
||||
QVERIFY2(m_init->initDDS(domainId, exerciseId, Initialisation::USE_SUBSCRIBER), "Initialisation (Subscriber) failed");
|
||||
|
||||
|
||||
QVERIFY2(m_init->getSubscriberListener(), "Initialisation SubscriberListener failed");
|
||||
}
|
||||
|
||||
void GenericDataTests::initDRTestCase()
|
||||
{
|
||||
m_dataReader = new DTC::DroTestParticipant;
|
||||
QVERIFY2(m_dataReader, "Initialisation of GenericDataReader(TestParticipant) failed");
|
||||
QVERIFY2(m_dataReader->create(m_init->getDomainParticipantSub(), m_init->getDDSSubscriber()), "Creating DataReader (TestParticipant) failed");
|
||||
m_init->getSubscriberListener()->addCallback(m_dataReader->getDataReader(), this);
|
||||
|
||||
m_dataReaderSd = new DTC::DroSpatialData;
|
||||
QVERIFY2(m_dataReaderSd, "Initialisation of GenericDataReader(SpatialData) failed");
|
||||
QVERIFY2(m_dataReaderSd->create(m_init->getDomainParticipantSub(), m_init->getDDSSubscriber()), "Creating DataReader (SpatialData) failed");
|
||||
m_init->getSubscriberListener()->addCallback(m_dataReaderSd->getDataReader(), this);
|
||||
}
|
||||
|
||||
void GenericDataTests::initDWTestCase()
|
||||
{
|
||||
m_dataWriter = new DTS::DwoTestParticipant();
|
||||
QVERIFY2(m_dataWriter, "Initialisation of GenericDataWriter(TestParticipant) failed");
|
||||
QVERIFY2(m_dataWriter->create(m_init->getDomainParticipant(), m_init->getDDSPublisher()), "Creating DataWriter (TestParticipant) failed");
|
||||
|
||||
m_dataWriterSd = new DTS::DwoSpatialData();
|
||||
QVERIFY2(m_dataWriterSd, "Initialisation of GenericDataWriter(SpatialData) failed");
|
||||
QVERIFY2(m_dataWriterSd->create(m_init->getDomainParticipant(), m_init->getDDSPublisher()), "Creating DataWriter (SpatialData) failed");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GenericDataTests::dataWriterTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<"GenericDataTests: dataWriter";
|
||||
|
||||
|
||||
DdsUnitTests::SpatialData data;
|
||||
|
||||
data.participantId(100);
|
||||
data.equipNo(999);
|
||||
data.gpsPosition().lon(70000);
|
||||
data.gpsPosition().lat(90000);
|
||||
data.gpsPosition().z(10);
|
||||
|
||||
data.orientation().heave(10);
|
||||
data.orientation().pitch(10);
|
||||
data.orientation().roll(10);
|
||||
|
||||
data.velocity().x(10);
|
||||
data.velocity().y(2);
|
||||
data.velocity().z(1);
|
||||
|
||||
data.changedAt(11111);
|
||||
|
||||
QVERIFY2(m_dataWriterSd->write(data), "Publishing Topic (SpatialData) failed");
|
||||
|
||||
DdsUnitTests::TestParticipant allData;
|
||||
|
||||
allData.name("************************ Name of TestParticpant ***********************");
|
||||
allData.location() = data;
|
||||
allData.id(10000);
|
||||
QVERIFY2(m_dataWriter->write(allData), "Publishing Topic (TestParticipant) failed");
|
||||
allData.id(11000);
|
||||
QVERIFY2(m_dataWriter->write(allData), "Publishing Topic (TestParticipant) failed");
|
||||
allData.id(11100);
|
||||
QVERIFY2(m_dataWriter->write(allData), "Publishing Topic (TestParticipant) failed");
|
||||
allData.id(11110);
|
||||
QVERIFY2(m_dataWriter->write(allData), "Publishing Topic (TestParticipant) failed");
|
||||
|
||||
allData.id(11111);
|
||||
|
||||
QVERIFY2(m_dataWriter->write(allData), "Publishing Topic (TestParticipant) failed");
|
||||
}
|
||||
|
||||
|
||||
void GenericDataTests::dataReaderTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "GenericDataTests: dataReader";
|
||||
|
||||
|
||||
QTRY_COMPARE(m_spatialData.equipNo(), 999);
|
||||
QCOMPARE(m_spatialData.changedAt(), 11111);
|
||||
|
||||
QTRY_COMPARE(m_testPart.location().equipNo(), 999);
|
||||
QCOMPARE(m_testPart.location().participantId(), 100);
|
||||
|
||||
bool bSeenUnreadSamples = false;
|
||||
dds::sub::LoanedSamples<DdsUnitTests::TestParticipant> vecTp = m_dataReader->readAlive(bSeenUnreadSamples);
|
||||
INFO_LOG(m_logger) <<"vecTp size: " << vecTp.length();
|
||||
QCOMPARE(vecTp.length(), static_cast<decltype(vecTp.length())>(5));
|
||||
QCOMPARE(vecTp[0].data().id(), 10000);
|
||||
QCOMPARE(vecTp[1].data().id(), 11000);
|
||||
QCOMPARE(vecTp[2].data().id(), 11100);
|
||||
QCOMPARE(vecTp[3].data().id(), 11110);
|
||||
QCOMPARE(vecTp[4].data().id(), 11111);
|
||||
|
||||
dds::sub::LoanedSamples<DdsUnitTests::TestParticipant> vecTpLoaned = m_dataReader->takeAlive(bSeenUnreadSamples);
|
||||
INFO_LOG(m_logger) <<"vecTpLoaned size: " << vecTp.length();
|
||||
QCOMPARE(vecTpLoaned.length(), static_cast<decltype(vecTpLoaned.length())>(5));
|
||||
QCOMPARE(vecTpLoaned[0].data().id(), 10000);
|
||||
QCOMPARE(vecTpLoaned[1].data().id(), 11000);
|
||||
QCOMPARE(vecTpLoaned[2].data().id(), 11100);
|
||||
QCOMPARE(vecTpLoaned[3].data().id(), 11110);
|
||||
QCOMPARE(vecTpLoaned[4].data().id(), 11111);
|
||||
}
|
||||
|
||||
|
||||
void GenericDataTests::deleteDataReaderTestCase()
|
||||
{
|
||||
DdsUnitTests::TestParticipant data;
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
data.id(i);
|
||||
m_dataWriter->write(data);
|
||||
}
|
||||
m_init->getSubscriberListener()->removeCallback(m_dataReader->getDataReader(), this);
|
||||
delete m_dataReader;
|
||||
m_dataReader = nullptr;
|
||||
for(int i = 0; i < 1000; ++i)
|
||||
{
|
||||
data.id(i);
|
||||
m_dataWriter->write(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GenericDataTests::cleanupTestCase()
|
||||
{
|
||||
DEBUG_LOG(m_logger) <<"GenericDataTests: cleanupTestCase";
|
||||
if (m_dataReader)
|
||||
{
|
||||
m_init->getSubscriberListener()->removeCallback(m_dataReader->getDataReader(), this);
|
||||
delete m_dataReader;
|
||||
}
|
||||
if (m_dataReaderSd)
|
||||
{
|
||||
m_init->getSubscriberListener()->removeCallback(m_dataReaderSd->getDataReader(), this);
|
||||
delete m_dataReaderSd;
|
||||
}
|
||||
m_init->cleanupSubscriberListener();
|
||||
delete m_dataWriter;
|
||||
delete m_dataWriterSd;
|
||||
delete m_init;
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
|
||||
void GenericDataTests::processData(const DdsUnitTests::SpatialData &data, DataModification modType)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "SpatialData: equipNo = " << data.equipNo() << " modType: " << modType;
|
||||
m_spatialData = data;
|
||||
}
|
||||
|
||||
void GenericDataTests::processData(const DdsUnitTests::TestParticipant &data, const dds::sub::SampleInfo &si)
|
||||
{
|
||||
Q_UNUSED(si);
|
||||
DEBUG_LOG(m_logger) << "TestParticipant: equipNo = " << data.location().equipNo();
|
||||
m_testPart = data;
|
||||
}
|
||||
|
||||
void GenericDataTests::droDataAvailable(const std::string &topicType, const std::string &topicName)
|
||||
{
|
||||
Q_UNUSED(topicName)
|
||||
DEBUG_LOG(m_logger) << "IN droDataAvailable: type = " << topicType;
|
||||
if (topicType == "DdsUnitTests::TestParticipant")
|
||||
{
|
||||
m_init->getSubscriberListener()->enableCallback(m_dataReader->getDataReader(), this);
|
||||
m_dataReader->read([this](const DdsUnitTests::TestParticipant &data, const dds::sub::SampleInfo &si) { processData(data, si); });
|
||||
}
|
||||
else if(topicType == "DdsUnitTests::SpatialData")
|
||||
{
|
||||
m_init->getSubscriberListener()->enableCallback(m_dataReaderSd->getDataReader(), this);
|
||||
m_dataReaderSd->read([this](const DdsUnitTests::SpatialData &data, DataModification modType) { processData(data, modType); } );
|
||||
}
|
||||
}
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "initialisation.h"
|
||||
|
||||
#include <DdsUnitTests/ddsut_dtc_types.h>
|
||||
#include <DdsUnitTests/ddsut_dts_types.h>
|
||||
|
||||
#include <DDS/Generic/subscriber_listener.h>
|
||||
#include <LtsUtilsCore/data_modification_enum.h>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
class GenericDataTests : public DDSGeneric::SubscriberListenerCallback
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GenericDataTests();
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void initDRTestCase();
|
||||
void initDWTestCase();
|
||||
void dataWriterTestCase();
|
||||
void dataReaderTestCase();
|
||||
void deleteDataReaderTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
protected:
|
||||
void droDataAvailable(const std::string &topicType, const std::string &topicName) override;
|
||||
void noWriterAnymore(const std::string &topicType, const std::string &topicName) override
|
||||
{
|
||||
Q_UNUSED(topicType);
|
||||
Q_UNUSED(topicName);
|
||||
}
|
||||
|
||||
void writerAvailable(const std::string &topicType, const std::string &topicName) override
|
||||
{
|
||||
Q_UNUSED(topicType);
|
||||
Q_UNUSED(topicName);
|
||||
}
|
||||
|
||||
void processData(const DdsUnitTests::TestParticipant &data, const dds::sub::SampleInfo &si);
|
||||
void processData(const DdsUnitTests::SpatialData &data, DataModification modType);
|
||||
|
||||
private:
|
||||
Initialisation *m_init = nullptr;
|
||||
|
||||
DTS::DwoTestParticipant *m_dataWriter = nullptr;
|
||||
DTC::DroTestParticipant *m_dataReader = nullptr;
|
||||
|
||||
DTS::DwoSpatialData *m_dataWriterSd = nullptr;
|
||||
DTC::DroSpatialData *m_dataReaderSd = nullptr;
|
||||
|
||||
DdsUnitTests::SpatialData m_spatialData;
|
||||
DdsUnitTests::TestParticipant m_testPart;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
|
||||
|
||||
#include "initialisation.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
#include <DDS/QosProfile/DDS_DefaultQos.h>
|
||||
#include <Common/ProjectConfig/project_defines.h>
|
||||
|
||||
|
||||
Initialisation::Initialisation()
|
||||
: m_domainParticipant(nullptr)
|
||||
, m_domainParticipantSub(nullptr)
|
||||
, m_domainId(0)
|
||||
, m_exerciseId(-1)
|
||||
, m_ddsPublisher(nullptr)
|
||||
, m_ddsSubscriber(nullptr)
|
||||
, m_subListener(new DDSGeneric::SubscriberListener)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTests.Initialisation"))
|
||||
{
|
||||
if (!DDS::QosProfile::setDefault())
|
||||
ERROR_LOG(m_logger) << "Error setting default QoS from xml-file ";
|
||||
|
||||
}
|
||||
|
||||
Initialisation::~Initialisation()
|
||||
{
|
||||
cleanupSubscriberListener();
|
||||
m_ddsSubscriber = nullptr;
|
||||
m_ddsPublisher = nullptr;
|
||||
}
|
||||
|
||||
void Initialisation::cleanupSubscriberListener()
|
||||
{
|
||||
if (m_ddsSubscriber != nullptr && m_subListener != nullptr)
|
||||
{
|
||||
m_ddsSubscriber.listener(nullptr, dds::core::status::StatusMask::none());
|
||||
delete m_subListener;
|
||||
m_subListener = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Initialisation::initDDS(int domainId, int exerciseId, PubSub pubsub)
|
||||
{
|
||||
if (m_domainParticipant == nullptr)
|
||||
m_domainParticipant = dds::domain::DomainParticipant(domainId, DDS::QosProfile::domainParticipantShm());
|
||||
|
||||
|
||||
|
||||
if (pubsub == USE_PUBLISHER || pubsub == USE_PUB_SUB)
|
||||
{
|
||||
dds::pub::qos::PublisherQos publisherQos = m_domainParticipant.default_publisher_qos();
|
||||
|
||||
DEBUG_LOG(m_logger) << "Publish on partition " << IDL::exercisePartition(exerciseId);
|
||||
|
||||
|
||||
dds::core::policy::Partition partitionQos;
|
||||
partitionQos.name( { IDL::exercisePartition(exerciseId) } );
|
||||
publisherQos << partitionQos;
|
||||
|
||||
m_ddsPublisher = dds::pub::Publisher(m_domainParticipant,
|
||||
publisherQos);
|
||||
}
|
||||
|
||||
if (pubsub == USE_SUBSCRIBER || pubsub == USE_PUB_SUB)
|
||||
{
|
||||
dds::domain::DomainParticipant domainPart = m_domainParticipant;
|
||||
if ( domainPart == nullptr || m_domainParticipantSub == nullptr)
|
||||
{
|
||||
m_domainParticipantSub = dds::domain::DomainParticipant(domainId, DDS::QosProfile::domainParticipantShm());
|
||||
domainPart = m_domainParticipantSub;
|
||||
}
|
||||
|
||||
dds::sub::qos::SubscriberQos subscriberQos = domainPart.default_subscriber_qos();
|
||||
|
||||
DEBUG_LOG(m_logger) << "Subsribing on partition " << IDL::exercisePartition(exerciseId);
|
||||
|
||||
|
||||
dds::core::policy::Partition partitionQos;
|
||||
partitionQos.name( { IDL::exercisePartition(exerciseId) } );
|
||||
subscriberQos << partitionQos;
|
||||
|
||||
m_ddsSubscriber = dds::sub::Subscriber(domainPart,
|
||||
subscriberQos,
|
||||
m_subListener,
|
||||
m_subListener->mask());
|
||||
}
|
||||
|
||||
if (m_domainId == 0)
|
||||
m_domainId = domainId;
|
||||
|
||||
if (m_exerciseId < 0)
|
||||
m_exerciseId = exerciseId;
|
||||
|
||||
return true;
|
||||
}
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <DDS/Generic/generic_base.h>
|
||||
#include <DDS/Generic/subscriber_listener.h>
|
||||
|
||||
namespace NGL {
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
class Initialisation
|
||||
{
|
||||
public:
|
||||
Initialisation();
|
||||
virtual ~Initialisation();
|
||||
|
||||
enum PubSub
|
||||
{
|
||||
USE_PUBLISHER = 1,
|
||||
USE_SUBSCRIBER = 2,
|
||||
USE_PUB_SUB = 4
|
||||
};
|
||||
|
||||
|
||||
bool initDDS(int domainId, int exerciseId, PubSub pubsub = USE_PUBLISHER);
|
||||
void cleanupSubscriberListener();
|
||||
|
||||
dds::domain::DomainParticipant getDomainParticipant() { return m_domainParticipant; }
|
||||
dds::domain::DomainParticipant getDomainParticipantSub() { return m_domainParticipantSub != nullptr ? m_domainParticipantSub : m_domainParticipant; }
|
||||
dds::pub::Publisher getDDSPublisher() { return m_ddsPublisher; }
|
||||
dds::sub::Subscriber getDDSSubscriber() { return m_ddsSubscriber; }
|
||||
DDSGeneric::SubscriberListener *getSubscriberListener() { return m_subListener; }
|
||||
|
||||
private:
|
||||
dds::domain::DomainParticipant m_domainParticipant;
|
||||
dds::domain::DomainParticipant m_domainParticipantSub;
|
||||
int m_domainId;
|
||||
int m_exerciseId;
|
||||
dds::pub::Publisher m_ddsPublisher;
|
||||
dds::sub::Subscriber m_ddsSubscriber;
|
||||
DDSGeneric::SubscriberListener *m_subListener;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
log4j.reset=true
|
||||
log4j.rootLogger=WARN, file
|
||||
|
||||
# Service: PlayerManagement
|
||||
###########################################################
|
||||
|
||||
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.FunctionTopicTests=WARN
|
||||
|
||||
# GenericTopicSqlTests
|
||||
log4j.logger.UnitTests.GenericTopicSqlTests=WARN
|
||||
|
||||
# Qos Infos
|
||||
log4j.logger.DDS.DefaultQos=WARN
|
||||
|
||||
# Generic
|
||||
#log4j.logger.GenericBase=DEBUG
|
||||
#log4j.logger.Generic.DataWriter=DEBUG
|
||||
#log4j.logger.Generic.DataReader=DEBUG
|
||||
#log4j.logger.Generic.DroTypeInfo=DEBUG
|
||||
|
||||
log4j.logger.GenericBase=WARN
|
||||
log4j.logger.Generic.Subscriber=WARN
|
||||
log4j.logger.Generic.Publisher=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.Exercise=WARN
|
||||
log4j.logger.DTC.StatusDRWatcher=WARN
|
||||
|
||||
# FTSs / FTCs
|
||||
log4j.logger.FTS.Replier=WARN
|
||||
log4j.logger.FTC.Requester=WARN
|
||||
log4j.logger.Generic.FTS=WARN
|
||||
log4j.logger.Generic.FTC=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:
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
|
||||
# ddsftc
|
||||
ADD_LIBRARY(ddsftc INTERFACE)
|
||||
TARGET_INCLUDE_DIRECTORIES(ddsftc
|
||||
INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../
|
||||
)
|
||||
TARGET_LINK_LIBRARIES(ddsftc
|
||||
INTERFACE
|
||||
Qt5::Core
|
||||
ddsfts_connext
|
||||
ddscore # due to the usage of Core::FtReplyState inside generic_ftc_impl.h, only on core.hpp - fixme :/
|
||||
ddsgen
|
||||
ddsQoS
|
||||
RTI::nddsc
|
||||
RTI::nddscpp2
|
||||
ddsftccore
|
||||
LtsUtilsCoreHeaderOnly
|
||||
)
|
||||
SET(SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/generic_ftc.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/generic_ftc.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/generic_ftc_impl.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/generic_ftc_threading.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/generic_ftc_threading.cpp
|
||||
)
|
||||
|
||||
TARGET_SOURCES(ddsftc INTERFACE ${SRCS})
|
||||
|
||||
|
||||
# ddsftccore
|
||||
NGL_ADD_LIBRARY2(WS SV HLA ddsftccore SHARED abstract_requester.h abstract_requester.cpp)
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(ddsftccore
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../ # 'DDS/FTC/...' reads better than only FTC
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(ddsftccore
|
||||
PUBLIC
|
||||
Qt5::Core
|
||||
ddscore
|
||||
)
|
||||
NGL_INSTALL_TARGET(ddsftccore)
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
#include "abstract_requester.h"
|
||||
|
||||
|
||||
Request2::Request2():QObject(){}
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <DDS/FTC/ddsftccore_export.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
|
||||
class DDSFTCCORE_EXPORT Request2 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Request2();
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
};
|
||||
|
||||
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#include "generic_ftc.h"
|
||||
|
||||
namespace FTC
|
||||
{
|
||||
}
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <DDS/Core/core.hpp>
|
||||
|
||||
#include <future>
|
||||
|
||||
class Request2;
|
||||
|
||||
namespace FTC
|
||||
{
|
||||
|
||||
template <typename RequestTopicType, typename ReplyTopicType>
|
||||
class GenericFtc
|
||||
{
|
||||
public:
|
||||
GenericFtc() = default;
|
||||
virtual ~GenericFtc() = default;
|
||||
|
||||
virtual bool requestDataClassic(const RequestTopicType &data,
|
||||
const char *replySlot,
|
||||
const char *errorSlot,
|
||||
int &uniqueId) = 0;
|
||||
virtual std::future<ReplyTopicType> requestDataModern(const RequestTopicType &requestTopic, Request2 *requestWatcher) = 0;
|
||||
};
|
||||
}
|
||||
Executable
+567
@@ -0,0 +1,567 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#include "generic_ftc.h"
|
||||
#include "abstract_requester.h"
|
||||
#include "generic_ftc_threading.h"
|
||||
|
||||
#include <DDS/Core/core.hpp>
|
||||
#include <DDS/FTS/connext.h>
|
||||
#include <DDS/Generic/generic_logger.h>
|
||||
#include <DDS/Generic/generic_base.h>
|
||||
#include <DDS/QosProfile/DDS_DefaultQos.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QMetaMethod>
|
||||
#include <QTimer>
|
||||
|
||||
#include <queue>
|
||||
#include <future>
|
||||
|
||||
|
||||
namespace FTC
|
||||
{
|
||||
template <class C>
|
||||
class HasStatusFunction
|
||||
{
|
||||
template <class T>
|
||||
static std::true_type testSignature(void (T::*)(const Core::FtReplyState &));
|
||||
|
||||
template <class T>
|
||||
static decltype(testSignature(&T::replyState)) test(std::nullptr_t);
|
||||
|
||||
template <class T>
|
||||
static std::false_type test(...);
|
||||
|
||||
public:
|
||||
using type = decltype(test<C>(nullptr));
|
||||
static const bool value = type::value;
|
||||
};
|
||||
|
||||
template <class C>
|
||||
class HasRequestId
|
||||
{
|
||||
template <class T>
|
||||
static std::true_type testSignature(const int32_t &(T::*)() const);
|
||||
template <class T>
|
||||
static std::true_type testSignature(int32_t (T::*)() const);
|
||||
|
||||
template <class T>
|
||||
static decltype(testSignature(&T::requestId)) test(std::nullptr_t);
|
||||
|
||||
template <class T>
|
||||
static std::false_type test(...);
|
||||
|
||||
public:
|
||||
using type = decltype(test<C>(nullptr));
|
||||
static const bool value = type::value;
|
||||
};
|
||||
|
||||
template<typename GENERIC_FTC>
|
||||
struct TopicNames
|
||||
{};
|
||||
|
||||
template<typename TOPICTYPE_REQUEST, typename TOPICTYPE_REPLY, typename DUMMY>
|
||||
class GenericFtcImpl final : public GenericFtc<TOPICTYPE_REQUEST, TOPICTYPE_REPLY>, public Threading
|
||||
{
|
||||
public:
|
||||
using RequestTopic = TOPICTYPE_REQUEST;
|
||||
using ReplyTopic = TOPICTYPE_REPLY;
|
||||
using TopicNameHelper = TopicNames<GenericFtcImpl<RequestTopic, ReplyTopic, DUMMY>>;
|
||||
|
||||
GenericFtcImpl(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const dds::pub::Publisher &ddsPublisher,
|
||||
const dds::sub::Subscriber &ddsSubscriber,
|
||||
QObject *objToInform,
|
||||
const std::string &partitionType,
|
||||
int partitionId)
|
||||
: GenericFtc<RequestTopic, ReplyTopic>()
|
||||
, m_objToInform(objToInform)
|
||||
, m_requesterParams(domainParticipant)
|
||||
, m_partitionType(partitionType)
|
||||
, m_partitionId(partitionId)
|
||||
, m_logger(DDS::getLogger(TopicNameHelper::functionTopicName(), DDS::DataType::FUNCTION_TOPIC_CLIENT))
|
||||
{
|
||||
|
||||
|
||||
Q_ASSERT(domainParticipant != nullptr);
|
||||
Q_ASSERT(ddsPublisher != nullptr);
|
||||
Q_ASSERT(ddsSubscriber != nullptr);
|
||||
Q_ASSERT(objToInform != nullptr);
|
||||
|
||||
|
||||
m_requesterParams.request_topic_name(TopicNameHelper::requestTopicName());
|
||||
m_requesterParams.reply_topic_name(TopicNameHelper::replyTopicName());
|
||||
m_requesterParams.publisher(ddsPublisher);
|
||||
m_requesterParams.subscriber(ddsSubscriber);
|
||||
m_requesterParams.datareader_qos(DDS::QosProfile::dataReaderDefault(DDS::QosProfile::TopicType::FunctionTopic));
|
||||
m_requesterParams.datawriter_qos(DDS::QosProfile::dataWriterDefault(DDS::QosProfile::TopicType::FunctionTopic));
|
||||
DDS::QosProfile::print(m_requesterParams.datareader_qos(), m_logger);
|
||||
DDS::QosProfile::print(m_requesterParams.datawriter_qos(), m_logger);
|
||||
|
||||
DEBUG_LOG(m_logger) << "=> Static request using request/reply topic names: "
|
||||
<< TopicNameHelper::requestTopicName()
|
||||
<< ", reply: "
|
||||
<< TopicNameHelper::replyTopicName();
|
||||
|
||||
qRegisterMetaType<ReplyTopic>(TopicNameHelper::replyTopicTypeName());
|
||||
preInit();
|
||||
}
|
||||
|
||||
|
||||
~GenericFtcImpl()
|
||||
{
|
||||
Q_ASSERT(!m_dying);
|
||||
m_dying = true;
|
||||
|
||||
decltype(m_modernRequests) modernRequests;
|
||||
decltype(m_classicRequests) classicRequests;
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_conditionMutex);
|
||||
setWorkAvailable(false);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> l(m_requestMutex);
|
||||
Q_ASSERT(m_classicRequests.empty());
|
||||
Q_ASSERT(m_modernRequests.empty());
|
||||
|
||||
classicRequests.swap(m_classicRequests);
|
||||
modernRequests.swap(m_modernRequests);
|
||||
}
|
||||
|
||||
for (; !modernRequests.empty(); modernRequests.pop())
|
||||
{
|
||||
ModernRequest<ReplyTopic> &request = modernRequests.front();
|
||||
fprintf(stderr, "[!] GenericFtcImpl::dtor: there was a modern request in the queue while the Ftc is being destroyed. Throwing!\n");
|
||||
Q_ASSERT(false);
|
||||
request.promise.set_exception(std::make_exception_ptr(std::runtime_error("Session aborted.")));
|
||||
emit request.requestWatcher->finished();
|
||||
}
|
||||
|
||||
for (; !classicRequests.empty(); classicRequests.pop())
|
||||
{
|
||||
ClassicRequest &request = classicRequests.front();
|
||||
fprintf(stderr, "[!] GenericFtcImpl::dtor: there was a classic request in the queue while the Ftc is being destroyed. Throwing!\n");
|
||||
Q_ASSERT(false);
|
||||
sendError(request, 0, true );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct ClassicRequest
|
||||
{
|
||||
connext::WriteSample<RequestTopic> sample;
|
||||
QMetaMethod replySlot;
|
||||
QMetaMethod errorSlot;
|
||||
int uniqueId = 0;
|
||||
};
|
||||
|
||||
|
||||
bool requestDataClassic(const RequestTopic &requestTopic,
|
||||
const char *replySlot,
|
||||
const char *errorSlot,
|
||||
int &uniqueId) override
|
||||
{
|
||||
Q_ASSERT(!m_dying);
|
||||
|
||||
ClassicRequest request;
|
||||
if (!checkSlots(request, replySlot, errorSlot))
|
||||
return false;
|
||||
request.sample.set_data(requestTopic);
|
||||
request.uniqueId = ++m_uniqueId;
|
||||
uniqueId = request.uniqueId;
|
||||
|
||||
createThread();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_requestMutex);
|
||||
m_classicRequests.push(request);
|
||||
}
|
||||
|
||||
runThread();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename ReplyTopic>
|
||||
struct ModernRequest
|
||||
{
|
||||
std::promise<ReplyTopic> promise;
|
||||
connext::WriteSample<RequestTopic> sample;
|
||||
Request2 *requestWatcher;
|
||||
};
|
||||
|
||||
|
||||
std::future<ReplyTopic> requestDataModern(const RequestTopic &requestTopic, Request2 *requestWatcher) override
|
||||
{
|
||||
Q_ASSERT(!m_dying);
|
||||
|
||||
std::promise<ReplyTopic> promise;
|
||||
std::future<ReplyTopic> future = promise.get_future();
|
||||
|
||||
connext::WriteSample<RequestTopic> sample;
|
||||
sample.set_data(requestTopic);
|
||||
|
||||
createThread();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_requestMutex);
|
||||
m_modernRequests.push({ std::move(promise), sample, requestWatcher });
|
||||
}
|
||||
|
||||
runThread();
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
QString functionTopicName() const override
|
||||
{
|
||||
return QString::fromUtf8(TopicNameHelper::functionTopicName());
|
||||
}
|
||||
|
||||
|
||||
int partitionId() const override
|
||||
{
|
||||
return m_partitionId;
|
||||
}
|
||||
|
||||
|
||||
QString partitionType() const override
|
||||
{
|
||||
return QString::fromStdString(m_partitionType);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<connext::Requester<RequestTopic, ReplyTopic> > requester;
|
||||
|
||||
|
||||
void cleanupThread() override
|
||||
{
|
||||
|
||||
requester.reset();
|
||||
}
|
||||
|
||||
|
||||
void threadDone() override
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for (; !m_modernRequests.empty(); m_modernRequests.pop())
|
||||
{
|
||||
ModernRequest<ReplyTopic> &request = m_modernRequests.front();
|
||||
fprintf(stderr, "[!] GenericFtcImpl::threadDone: there was a modern request in the queue while the thread already ended. Throwing!\n");
|
||||
Q_ASSERT(false);
|
||||
request.promise.set_exception(std::make_exception_ptr(std::runtime_error("Worker thread timed out.")));
|
||||
}
|
||||
|
||||
for (; !m_classicRequests.empty(); m_classicRequests.pop())
|
||||
{
|
||||
ClassicRequest &request = m_classicRequests.front();
|
||||
fprintf(stderr, "[!] GenericFtcImpl::dtor: there was a classic request in the queue while the thread already ended. Throwing!\n");
|
||||
Q_ASSERT(false);
|
||||
sendError(request, 0, true );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void work() override
|
||||
{
|
||||
try
|
||||
{
|
||||
decltype(m_classicRequests) classicRequests;
|
||||
decltype(m_modernRequests) modernRequests;
|
||||
|
||||
Q_ASSERT(isWorkAvailable());
|
||||
if (!isWorkAvailable())
|
||||
{
|
||||
fprintf(stderr, "[!] GenericFtc::work: called while marked as no work available! run? %d (%s)\n", isRun(), threadName().toStdString().c_str());
|
||||
}
|
||||
setWorkAvailable(false);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_requestMutex);
|
||||
|
||||
if (!m_classicRequests.empty())
|
||||
{
|
||||
m_classicRequests.swap(classicRequests);
|
||||
}
|
||||
if (!m_modernRequests.empty())
|
||||
{
|
||||
m_modernRequests.swap(modernRequests);
|
||||
}
|
||||
}
|
||||
|
||||
cancellationPoint();
|
||||
|
||||
if (classicRequests.empty() && modernRequests.empty())
|
||||
{
|
||||
fprintf(stderr, "[!] GenericFtc::work: called without having any requests! (%s)\n", threadName().toStdString().c_str());
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!requester)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
requester.reset(new connext::Requester<RequestTopic, ReplyTopic>(m_requesterParams));
|
||||
}
|
||||
|
||||
for (; !classicRequests.empty(); classicRequests.pop())
|
||||
{
|
||||
cancellationPoint();
|
||||
ClassicRequest &request = classicRequests.front();
|
||||
requester->send_request(request.sample);
|
||||
cancellationPoint();
|
||||
getRequestedDataClassic(*requester.get(), request);
|
||||
cancellationPoint();
|
||||
}
|
||||
|
||||
for (; !modernRequests.empty(); modernRequests.pop())
|
||||
{
|
||||
cancellationPoint();
|
||||
ModernRequest<ReplyTopic> &request = modernRequests.front();
|
||||
requester->send_request(request.sample);
|
||||
cancellationPoint();
|
||||
try
|
||||
{
|
||||
getModernRequestedData(*requester.get(), request);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
request.promise.set_exception(std::current_exception());
|
||||
|
||||
QTimer::singleShot(0, request.requestWatcher, &Request2::finished);
|
||||
}
|
||||
cancellationPoint();
|
||||
}
|
||||
}
|
||||
catch (const dds::core::Exception &ex)
|
||||
{
|
||||
fprintf(stderr, "[!] GenericFtc::work: dds::core::exception: %s (%s)\n", ex.what(), threadName().toStdString().c_str());
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
fprintf(stderr, "[!] GenericFtc::work: exception: %s (%s)\n", ex.what(), threadName().toStdString().c_str());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
fprintf(stderr, "[!] GenericFtc::work: unknown exception occured !!! (%s)\n", threadName().toStdString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename std::enable_if<HasStatusFunction<T>::value, int>::type = 0>
|
||||
static inline bool isInProgress(const connext::Sample<T> &reply)
|
||||
{
|
||||
return (reply.data().replyState() == Core::FtReplyState::InProgress);
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename std::enable_if<!HasStatusFunction<T>::value, int>::type = 0>
|
||||
static inline bool isInProgress(const connext::Sample<T> &)
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void getRequestedDataClassic(connext::Requester<RequestTopic, ReplyTopic> &requester, ClassicRequest &request)
|
||||
{
|
||||
|
||||
#ifdef WIN32
|
||||
const dds::core::Duration MAX_WAIT(200);
|
||||
#else
|
||||
const dds::core::Duration MAX_WAIT(20);
|
||||
#endif
|
||||
|
||||
bool inProgress = true;
|
||||
while (inProgress && isRun())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Waiting for reply ";
|
||||
|
||||
connext::Sample<ReplyTopic> reply;
|
||||
|
||||
if (!requester.receive_reply(reply, MAX_WAIT))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Timed out waiting for reply";
|
||||
sendError(request, 3);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (reply.info().valid())
|
||||
{
|
||||
if (request.sample.info().identity() != reply.info()->related_original_publication_virtual_sample_identity())
|
||||
{
|
||||
|
||||
INFO_LOG(m_logger) << "#receiveReply - outdated. sample id: " << connext::DebugInfo::toReadable(request.sample.info().identity())
|
||||
<< ", virutal sample id: " << connext::DebugInfo::toReadable(reply.info()->related_original_publication_virtual_sample_identity());
|
||||
continue;
|
||||
}
|
||||
inProgress = isInProgress(reply);
|
||||
if (request.replySlot.isValid())
|
||||
{
|
||||
INFO_LOG(m_logger) << "#receiveReply for " << TopicNameHelper::replyTopicTypeName();
|
||||
|
||||
request.replySlot.invoke(m_objToInform, Qt::QueuedConnection,
|
||||
QArgument<ReplyTopic>(TopicNameHelper::replyTopicTypeName(), reply.data()),
|
||||
Q_ARG(int, request.uniqueId),
|
||||
Q_ARG(bool, !inProgress));
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG(m_logger) << "reply slot doesn't exist for " << dds::topic::topic_type_name<ReplyTopic>::value();
|
||||
}
|
||||
if (!inProgress)
|
||||
return;
|
||||
}
|
||||
else
|
||||
WARNING_LOG(m_logger) << "No valid data available ";
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void getModernRequestedData(connext::Requester<RequestTopic, ReplyTopic> &requester, ModernRequest<ReplyTopic> &request) noexcept(false)
|
||||
{
|
||||
|
||||
#ifdef WIN32
|
||||
const dds::core::Duration MAX_WAIT(200);
|
||||
#else
|
||||
const dds::core::Duration MAX_WAIT(20);
|
||||
#endif
|
||||
|
||||
while (isRun())
|
||||
{
|
||||
connext::Sample<ReplyTopic> reply;
|
||||
|
||||
if (!requester.receive_reply(reply, MAX_WAIT))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Timeout while waiting for reply!";
|
||||
throw std::runtime_error("Timeout while waiting for reply!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!reply.info().valid())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "No valid data available!";
|
||||
|
||||
throw std::runtime_error("No valid data available!");
|
||||
}
|
||||
|
||||
if (request.sample.info().identity() != reply.info()->related_original_publication_virtual_sample_identity())
|
||||
{
|
||||
|
||||
INFO_LOG(m_logger) << "#receiveReply - outdated. sample id: " << connext::DebugInfo::toReadable(request.sample.info().identity())
|
||||
<< ", virutal sample id: " << connext::DebugInfo::toReadable(reply.info()->related_original_publication_virtual_sample_identity());
|
||||
continue;
|
||||
}
|
||||
|
||||
Q_ASSERT(!isInProgress(reply));
|
||||
if (isInProgress(reply))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Used ModernRequest for a request with FtReplyState! Your data will most likely be imcomplete!";
|
||||
}
|
||||
|
||||
INFO_LOG(m_logger) << "#receiveReply for " << TopicNameHelper::replyTopicTypeName();
|
||||
|
||||
request.promise.set_value(reply.data());
|
||||
|
||||
QTimer::singleShot(0, request.requestWatcher, &Request2::finished);
|
||||
|
||||
return;
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void sendErrorInternal(const QMetaMethod &errorSlot, int reason, int uniqueId, int requestId, bool directConnection = false)
|
||||
{
|
||||
if (errorSlot.isValid())
|
||||
{
|
||||
errorSlot.invoke(m_objToInform, directConnection ? Qt::DirectConnection : Qt::QueuedConnection,
|
||||
Q_ARG(int, reason),
|
||||
Q_ARG(int, uniqueId),
|
||||
Q_ARG(int, requestId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template < typename T = TOPICTYPE_REQUEST, typename std::enable_if < HasRequestId<T>::value, int >::type = 0 >
|
||||
void sendError(const ClassicRequest &request, int reason, bool directConnection = false)
|
||||
{
|
||||
sendErrorInternal(request.errorSlot, reason, request.uniqueId, request.sample.data().requestId(), directConnection);
|
||||
}
|
||||
|
||||
|
||||
template < typename T = TOPICTYPE_REQUEST, typename std::enable_if < !HasRequestId<T>::value, int >::type = 0 >
|
||||
void sendError(const ClassicRequest &request, int reason, bool directConnection = false)
|
||||
{
|
||||
sendErrorInternal(request.errorSlot, reason, request.uniqueId, -1, directConnection);
|
||||
}
|
||||
|
||||
|
||||
bool checkSlots(ClassicRequest &request, const char *replySlot, const char *errorSlot)
|
||||
{
|
||||
const QByteArray baReply = QByteArray::number(QSIGNAL_CODE) +
|
||||
"signalSignature(" +
|
||||
TopicNameHelper::replyTopicTypeName() +
|
||||
",int,bool)";
|
||||
request.replySlot = DDS::Helper::getMethod(m_logger, m_objToInform, replySlot, baReply.data());
|
||||
if (!request.replySlot.isValid())
|
||||
return false;
|
||||
|
||||
if (errorSlot)
|
||||
{
|
||||
const QByteArray baError = QByteArray::number(QSIGNAL_CODE) + "signalSignature(int,int,int)";
|
||||
request.errorSlot = DDS::Helper::getMethod(m_logger, m_objToInform, errorSlot, baError.data());
|
||||
if (!request.errorSlot.isValid())
|
||||
return false;
|
||||
}
|
||||
else
|
||||
request.errorSlot = QMetaMethod();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
QObject *m_objToInform;
|
||||
int m_uniqueId { 0 };
|
||||
|
||||
|
||||
std::queue<ModernRequest<ReplyTopic> > m_modernRequests;
|
||||
std::queue<ClassicRequest> m_classicRequests;
|
||||
|
||||
connext::RequesterParams m_requesterParams;
|
||||
bool m_dying { false };
|
||||
std::string m_partitionType;
|
||||
int m_partitionId;
|
||||
std::mutex m_requestMutex;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+330
@@ -0,0 +1,330 @@
|
||||
#include "generic_ftc_threading.h"
|
||||
|
||||
#include <QAtomicInt>
|
||||
#include <QTimer>
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Threading::~Threading()
|
||||
{
|
||||
if (m_threadLifetime != nullptr)
|
||||
{
|
||||
m_threadLifetime->stop();
|
||||
m_threadLifetime.reset();
|
||||
}
|
||||
|
||||
setCleanupNeeded(false);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_conditionMutex);
|
||||
setRun(false);
|
||||
}
|
||||
|
||||
if (!m_used)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
|
||||
m_condition.notify_all();
|
||||
m_thread.join();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Threading::preInit() const
|
||||
{
|
||||
m_partitionId = partitionId();
|
||||
m_partitionType = partitionType();
|
||||
|
||||
|
||||
|
||||
|
||||
const QString base(QStringLiteral("F%1%2").arg(m_partitionType.left(1)).arg(m_partitionId));
|
||||
|
||||
|
||||
const int baseLen = base.length();
|
||||
const int maxLen = 16 - 1 - baseLen;
|
||||
|
||||
|
||||
|
||||
|
||||
if (maxLen < 1)
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
m_threadName = functionTopicName();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString orgName(functionTopicName());
|
||||
QString name;
|
||||
|
||||
if (orgName.length() > maxLen)
|
||||
{
|
||||
QRegularExpression regexp(QStringLiteral("[A-Z][a-z]*"));
|
||||
auto matches = regexp.globalMatch(functionTopicName());
|
||||
|
||||
QStringList words;
|
||||
while (matches.hasNext())
|
||||
{
|
||||
const auto word = matches.next().capturedTexts().join(QString());
|
||||
|
||||
words += word;
|
||||
}
|
||||
|
||||
if (words.isEmpty())
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
m_threadName = functionTopicName();
|
||||
return;
|
||||
}
|
||||
|
||||
int wordsLeft = words.count();
|
||||
|
||||
|
||||
if (wordsLeft < 1)
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
m_threadName = functionTopicName();
|
||||
return;
|
||||
}
|
||||
|
||||
int bremse = 20;
|
||||
while (wordsLeft > 0)
|
||||
{
|
||||
if (bremse < 1)
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
bremse--;
|
||||
|
||||
const int lenRest = maxLen - name.count();
|
||||
if (lenRest < 1)
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
m_threadName = functionTopicName();
|
||||
return;
|
||||
}
|
||||
|
||||
const int maxWordLen = lenRest / wordsLeft;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (words.isEmpty())
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
name += words.takeFirst().leftRef(maxWordLen);
|
||||
|
||||
wordsLeft--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const int finalLeft = maxLen - name.count();
|
||||
if (finalLeft < 0 || (finalLeft > 0 && functionTopicName().length() > maxLen))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
name = orgName;
|
||||
}
|
||||
|
||||
m_threadName = QStringLiteral("%1%2[%3]").arg(base, name).arg(qrand(), 8, 16, QLatin1Char('0'));
|
||||
}
|
||||
|
||||
|
||||
const QString &Threading::threadName() const
|
||||
{
|
||||
if (m_threadName.isEmpty())
|
||||
{
|
||||
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
return m_threadName;
|
||||
}
|
||||
|
||||
|
||||
const char *Threading::posixThreadName() const
|
||||
{
|
||||
if (m_posixThreadName.empty())
|
||||
{
|
||||
m_posixThreadName = threadName().left(15).toStdString();
|
||||
}
|
||||
|
||||
return m_posixThreadName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void Threading::createThread()
|
||||
{
|
||||
if (!isRun())
|
||||
{
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
|
||||
m_condition.notify_all();
|
||||
m_thread.join();
|
||||
threadDone();
|
||||
|
||||
}
|
||||
|
||||
m_alreadyThreaded = 0;
|
||||
|
||||
|
||||
setRun(true);
|
||||
|
||||
m_thread = std::thread(&Threading::run, this, threadName());
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
pthread_setname_np(m_thread.native_handle(), posixThreadName());
|
||||
#endif
|
||||
}
|
||||
|
||||
m_createThreadCalledBefore = true;
|
||||
}
|
||||
|
||||
|
||||
void Threading::runThread()
|
||||
{
|
||||
m_used = true;
|
||||
|
||||
Q_ASSERT(m_createThreadCalledBefore);
|
||||
m_createThreadCalledBefore = false;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_conditionMutex);
|
||||
setWorkAvailable(true);
|
||||
}
|
||||
m_condition.notify_all();
|
||||
|
||||
if (!m_threadLifetime)
|
||||
{
|
||||
m_threadLifetime.reset(new QTimer);
|
||||
m_threadLifetime->setSingleShot(true);
|
||||
m_threadLifetime->setInterval(11*1000);
|
||||
QObject::connect(m_threadLifetime.get(), &QTimer::timeout, m_threadLifetime.get(),
|
||||
[this]
|
||||
{
|
||||
bool workAvailable = false;
|
||||
bool run = false;
|
||||
bool working = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_conditionMutex);
|
||||
workAvailable = isWorkAvailable();
|
||||
run = isRun();
|
||||
working = isWorking();
|
||||
}
|
||||
|
||||
if (workAvailable || working)
|
||||
{
|
||||
|
||||
m_threadLifetime->start();
|
||||
return;
|
||||
}
|
||||
|
||||
m_threadLifetime->deleteLater();
|
||||
m_threadLifetime = nullptr;
|
||||
|
||||
if (!run)
|
||||
{
|
||||
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_conditionMutex);
|
||||
setRun(false);
|
||||
}
|
||||
m_condition.notify_all();
|
||||
});
|
||||
}
|
||||
|
||||
m_threadLifetime->start();
|
||||
}
|
||||
|
||||
|
||||
void Threading::run(const QString &threadName)
|
||||
{
|
||||
Q_ASSERT(m_alreadyThreaded == 0);
|
||||
m_alreadyThreaded = 1;
|
||||
|
||||
const std::string _tn = threadName.toStdString();
|
||||
const char *tn = _tn.c_str();
|
||||
|
||||
while (isRun())
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> conditionLock(m_conditionMutex);
|
||||
|
||||
|
||||
m_condition.wait(conditionLock, [this]{ return (isWorkAvailable() || !isRun()); });
|
||||
|
||||
}
|
||||
|
||||
cancellationPoint();
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_conditionMutex);
|
||||
setWorking(true);
|
||||
}
|
||||
work();
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_conditionMutex);
|
||||
setWorking(false);
|
||||
}
|
||||
cancellationPoint();
|
||||
}
|
||||
catch (const CancelationPointReached &ex)
|
||||
{
|
||||
|
||||
break;
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
fprintf(stderr, "[!] Threading::run: exception: %s (%s)\n", ex.what(), tn);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
fprintf(stderr, "[!] Threading::run: unknown exception occured (%s) !!!\n", tn);
|
||||
}
|
||||
}
|
||||
|
||||
if (isCleanupNeeded())
|
||||
{
|
||||
|
||||
cleanupThread();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#include <QObject>
|
||||
#include <QAtomicInt>
|
||||
#include <QTimer>
|
||||
|
||||
#include <thread>
|
||||
#include <exception>
|
||||
#include <condition_variable>
|
||||
|
||||
|
||||
class CancelationPointReached : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CancelationPointReached()
|
||||
: std::runtime_error("CancelationPointReached")
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
class Threading
|
||||
{
|
||||
|
||||
public:
|
||||
Threading() = default;
|
||||
~Threading();
|
||||
|
||||
protected:
|
||||
void preInit() const;
|
||||
|
||||
|
||||
void createThread();
|
||||
void runThread();
|
||||
|
||||
|
||||
inline bool isRun() const { return (m_run != 0); }
|
||||
inline void setRun(bool run) { m_run = (run ? 1 : 0); }
|
||||
|
||||
|
||||
inline void setWorkAvailable(bool workAvailable) { m_workAvailable = (workAvailable ? 1 : 0); }
|
||||
inline bool isWorkAvailable() const { return (m_workAvailable != 0); }
|
||||
|
||||
|
||||
inline void setWorking(bool working) { m_isWorking = (working ? 1 : 0); }
|
||||
inline bool isWorking() const { return (m_isWorking != 0); }
|
||||
|
||||
const QString &threadName() const;
|
||||
inline const char *posixThreadName() const;
|
||||
|
||||
virtual void work() = 0;
|
||||
virtual QString functionTopicName() const = 0;
|
||||
virtual int partitionId() const = 0;
|
||||
virtual QString partitionType() const = 0;
|
||||
virtual void cleanupThread() = 0;
|
||||
virtual void threadDone() = 0;
|
||||
|
||||
inline void cancellationPoint() const noexcept(false)
|
||||
{
|
||||
if (!isRun())
|
||||
{
|
||||
|
||||
throw CancelationPointReached();
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex m_conditionMutex;
|
||||
|
||||
private:
|
||||
inline bool isCleanupNeeded() const { return (m_cleanupNeeded != 0); }
|
||||
inline void setCleanupNeeded(bool cleanupNeeded) { m_cleanupNeeded = (cleanupNeeded ? 1 : 0); }
|
||||
void run(const QString &threadName);
|
||||
|
||||
|
||||
std::unique_ptr<QTimer> m_threadLifetime;
|
||||
QAtomicInt m_run { 0 };
|
||||
QAtomicInt m_cleanupNeeded { 1 };
|
||||
std::thread m_thread;
|
||||
std::condition_variable m_condition;
|
||||
QAtomicInt m_workAvailable { 0 };
|
||||
QAtomicInt m_isWorking { 0 };
|
||||
mutable QString m_threadName;
|
||||
mutable std::string m_posixThreadName;
|
||||
QAtomicInt m_alreadyThreaded;
|
||||
mutable int m_partitionId { -1 };
|
||||
mutable QString m_partitionType;
|
||||
bool m_used { false };
|
||||
bool m_createThreadCalledBefore { false };
|
||||
};
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
ADD_LIBRARY(ddsfts_connext INTERFACE)
|
||||
TARGET_INCLUDE_DIRECTORIES(ddsfts_connext
|
||||
INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../
|
||||
)
|
||||
TARGET_LINK_LIBRARIES(ddsfts_connext
|
||||
INTERFACE
|
||||
RTI::nddscpp2
|
||||
)
|
||||
SET(SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/connext.h
|
||||
)
|
||||
TARGET_SOURCES(ddsfts_connext INTERFACE ${SRCS})
|
||||
|
||||
|
||||
SET(SRCS
|
||||
generic_fts.h ;# Interface
|
||||
generic_fts_impl.h ;# Implementation, using internal class GenericFTCInternal
|
||||
generic_fts_internal.h
|
||||
generic_fts_internal.cpp
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(WS AAR SV HLA ddsfts SHARED ${SRCS})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(
|
||||
INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(ddsfts
|
||||
PRIVATE
|
||||
RTI::nddscore
|
||||
ProjectConfig
|
||||
ddsQoS
|
||||
PUBLIC
|
||||
Qt5::Core
|
||||
RTI::nddsc
|
||||
RTI::nddscpp2
|
||||
ddsgen
|
||||
ddsfts_connext
|
||||
)
|
||||
Executable
+502
@@ -0,0 +1,502 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dds/core/cond/GuardCondition.hpp>
|
||||
#include <dds/core/cond/StatusCondition.hpp>
|
||||
#include <dds/core/cond/WaitSet.hpp>
|
||||
#include <dds/domain/DomainParticipant.hpp>
|
||||
#include <dds/pub/DataWriter.hpp>
|
||||
#include <dds/pub/Publisher.hpp>
|
||||
#include <dds/sub/DataReader.hpp>
|
||||
#include <dds/sub/SampleInfo.hpp>
|
||||
#include <dds/sub/Subscriber.hpp>
|
||||
#include <dds/topic/ContentFilteredTopic.hpp>
|
||||
#include <dds/topic/find.hpp>
|
||||
#include <rti/pub/WriteParams.hpp>
|
||||
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace connext
|
||||
{
|
||||
|
||||
namespace DebugInfo
|
||||
{
|
||||
inline QByteArray toReadable(const rti::core::Guid &guid)
|
||||
{
|
||||
const QByteArray str(reinterpret_cast<const char *>(&guid[0]), 16);
|
||||
return str.toHex();
|
||||
}
|
||||
inline QByteArray toReadable(const rti::core::SequenceNumber &seqNo,
|
||||
const rti::core::Guid &guid)
|
||||
{
|
||||
return "SeqNo: " + QByteArray::number(seqNo.value()) +
|
||||
", Guid-DW: " + toReadable(guid);
|
||||
}
|
||||
inline QByteArray toReadable(const rti::core::SampleIdentity &identiy)
|
||||
{
|
||||
return toReadable(identiy.sequence_number(), identiy.writer_guid());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
dds::topic::Topic<T> getTopic(const dds::domain::DomainParticipant &participant,
|
||||
const std::string &topicName)
|
||||
{
|
||||
using DataTopic = dds::topic::Topic<T>;
|
||||
auto ret = dds::topic::find<DataTopic>(participant, topicName);
|
||||
if (ret != nullptr)
|
||||
return ret;
|
||||
return DataTopic(participant, topicName);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
class SampleBase
|
||||
{
|
||||
protected:
|
||||
SampleBase() = default;
|
||||
virtual ~SampleBase() = default;
|
||||
|
||||
public:
|
||||
void set_data(const T &data)
|
||||
{
|
||||
m_data = data;
|
||||
}
|
||||
T &data()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
const T &data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
protected:
|
||||
T m_data;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class WriteSample : public SampleBase<T>
|
||||
{
|
||||
public:
|
||||
WriteSample() = default;
|
||||
|
||||
void set_info(const rti::pub::WriteParams &info)
|
||||
{
|
||||
m_info = info;
|
||||
}
|
||||
rti::pub::WriteParams &info()
|
||||
{
|
||||
return m_info;
|
||||
}
|
||||
const rti::pub::WriteParams &info() const
|
||||
{
|
||||
return m_info;
|
||||
}
|
||||
|
||||
private:
|
||||
rti::pub::WriteParams m_info;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class WriteSampleRef
|
||||
{
|
||||
public:
|
||||
WriteSampleRef()
|
||||
: m_data_ptr(nullptr)
|
||||
, m_info_ptr(nullptr)
|
||||
{}
|
||||
|
||||
void set_data(T *data_ptr)
|
||||
{
|
||||
m_data_ptr = data_ptr;
|
||||
}
|
||||
|
||||
T &data()
|
||||
{
|
||||
return *m_data_ptr;
|
||||
}
|
||||
|
||||
void set_info(rti::pub::WriteParams *info_ptr)
|
||||
{
|
||||
m_info_ptr = info_ptr;
|
||||
}
|
||||
|
||||
rti::pub::WriteParams &info()
|
||||
{
|
||||
return *m_info_ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T *m_data_ptr;
|
||||
rti::pub::WriteParams *m_info_ptr;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Sample : public SampleBase<T>
|
||||
{
|
||||
public:
|
||||
Sample() = default;
|
||||
|
||||
void set_info(const dds::sub::SampleInfo &info)
|
||||
{
|
||||
m_info = info;
|
||||
}
|
||||
dds::sub::SampleInfo &info()
|
||||
{
|
||||
return m_info;
|
||||
}
|
||||
const dds::sub::SampleInfo &info() const
|
||||
{
|
||||
return m_info;
|
||||
}
|
||||
|
||||
void set_identity(const rti::core::SampleIdentity &identity)
|
||||
{
|
||||
m_identity = identity;
|
||||
}
|
||||
|
||||
const rti::core::SampleIdentity &identity() const
|
||||
{
|
||||
return m_identity;
|
||||
}
|
||||
|
||||
private:
|
||||
dds::sub::SampleInfo m_info;
|
||||
rti::core::SampleIdentity m_identity;
|
||||
};
|
||||
|
||||
class RequesterParams
|
||||
{
|
||||
public:
|
||||
explicit RequesterParams(const dds::domain::DomainParticipant &participant)
|
||||
: m_participant(participant)
|
||||
, m_publisher(nullptr)
|
||||
, m_subscriber(nullptr)
|
||||
{}
|
||||
virtual ~RequesterParams() = default;
|
||||
|
||||
const std::string &request_topic_name() const
|
||||
{
|
||||
return m_request_topic_name;
|
||||
}
|
||||
void request_topic_name(const std::string &request_topic_name)
|
||||
{
|
||||
m_request_topic_name = request_topic_name;
|
||||
}
|
||||
|
||||
const std::string &reply_topic_name() const
|
||||
{
|
||||
return m_reply_topic_name;
|
||||
}
|
||||
void reply_topic_name(const std::string &reply_topic_name)
|
||||
{
|
||||
m_reply_topic_name = reply_topic_name;
|
||||
}
|
||||
|
||||
const dds::domain::DomainParticipant &domainParticipant() const
|
||||
{
|
||||
return m_participant;
|
||||
}
|
||||
|
||||
const dds::pub::Publisher &publisher() const
|
||||
{
|
||||
return m_publisher;
|
||||
}
|
||||
void publisher(const dds::pub::Publisher &publisher)
|
||||
{
|
||||
m_publisher = publisher;
|
||||
}
|
||||
|
||||
const dds::sub::Subscriber &subscriber() const
|
||||
{
|
||||
return m_subscriber;
|
||||
}
|
||||
void subscriber(const dds::sub::Subscriber &subscriber)
|
||||
{
|
||||
m_subscriber = subscriber;
|
||||
}
|
||||
|
||||
const dds::pub::qos::DataWriterQos &datawriter_qos() const
|
||||
{
|
||||
return m_datawriter_qos;
|
||||
}
|
||||
void datawriter_qos(const dds::pub::qos::DataWriterQos &datawriter_qos)
|
||||
{
|
||||
m_datawriter_qos = datawriter_qos;
|
||||
}
|
||||
|
||||
const dds::sub::qos::DataReaderQos &datareader_qos() const
|
||||
{
|
||||
return m_datareader_qos;
|
||||
}
|
||||
void datareader_qos(const dds::sub::qos::DataReaderQos &datareader_qos)
|
||||
{
|
||||
m_datareader_qos = datareader_qos;
|
||||
}
|
||||
|
||||
protected:
|
||||
dds::domain::DomainParticipant m_participant;
|
||||
dds::pub::Publisher m_publisher;
|
||||
dds::sub::Subscriber m_subscriber;
|
||||
dds::pub::qos::DataWriterQos m_datawriter_qos;
|
||||
dds::sub::qos::DataReaderQos m_datareader_qos;
|
||||
std::string m_request_topic_name;
|
||||
std::string m_reply_topic_name;
|
||||
};
|
||||
|
||||
template <typename REQUEST, typename REPLY>
|
||||
class ReplierParams : public RequesterParams
|
||||
{
|
||||
public:
|
||||
using RequesterParams::RequesterParams;
|
||||
};
|
||||
|
||||
template<typename REQUEST, typename REPLY>
|
||||
class ReplyRequestBase
|
||||
{
|
||||
protected:
|
||||
explicit ReplyRequestBase(const connext::RequesterParams &requesterParams)
|
||||
: m_requestTopic(getTopic<REQUEST>(requesterParams.domainParticipant(),
|
||||
requesterParams.request_topic_name()))
|
||||
, m_replyTopic(getTopic<REPLY>(requesterParams.domainParticipant(),
|
||||
requesterParams.reply_topic_name()))
|
||||
{}
|
||||
virtual ~ReplyRequestBase() = default;
|
||||
|
||||
protected:
|
||||
dds::topic::Topic<REQUEST> m_requestTopic;
|
||||
dds::topic::Topic<REPLY> m_replyTopic;
|
||||
};
|
||||
|
||||
template<typename REQUEST, typename REPLY>
|
||||
class Requester final : public ReplyRequestBase<REQUEST, REPLY>
|
||||
{
|
||||
public:
|
||||
explicit Requester(const connext::RequesterParams &requesterParams)
|
||||
: ReplyRequestBase<REQUEST, REPLY>(requesterParams)
|
||||
, m_requesterParams(requesterParams)
|
||||
, m_dataWriter(nullptr)
|
||||
, m_dataReader(nullptr)
|
||||
, m_contentFilteredTopic(nullptr)
|
||||
, m_curInstanceHandle(dds::core::InstanceHandle::nil())
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("connext.requester"))
|
||||
{}
|
||||
|
||||
void create_contentfilter(bool bNewDataWriter)
|
||||
{
|
||||
|
||||
if (!bNewDataWriter && m_contentFilteredTopic != nullptr)
|
||||
return;
|
||||
|
||||
rti::core::policy::DataWriterProtocol dwp;
|
||||
m_dataWriter.qos() >> dwp;
|
||||
const rti::core::Guid dwGuid = dwp.virtual_guid();
|
||||
const QByteArray writerGuid = DebugInfo::toReadable(dwGuid);
|
||||
const QByteArray filterContent = "@related_sample_identity.writer_guid.value = &hex(" + writerGuid + ")";
|
||||
if (m_contentFilteredTopic == nullptr)
|
||||
{
|
||||
|
||||
const std::string rtName = ReplyRequestBase<REQUEST, REPLY>::m_replyTopic.name();
|
||||
const QByteArray filterName(QByteArray::fromRawData(rtName.c_str(), static_cast<int>(rtName.size())) +
|
||||
QByteArray(" ") + writerGuid);
|
||||
m_contentFilteredTopic = dds::topic::ContentFilteredTopic<REPLY>(ReplyRequestBase<REQUEST, REPLY>::m_replyTopic,
|
||||
filterName.data(),
|
||||
dds::topic::Filter(filterContent.data()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
m_contentFilteredTopic->filter(dds::topic::Filter(filterContent.data()));
|
||||
}
|
||||
DEBUG_LOG(m_logger) << "content filter for topic "
|
||||
<< ReplyRequestBase<REQUEST, REPLY>::m_requestTopic.name()
|
||||
<< " - related_sample_identity should be: "
|
||||
<< writerGuid;
|
||||
}
|
||||
|
||||
void send_request(connext::WriteSample<REQUEST> &request)
|
||||
{
|
||||
bool bNewDataWriter = false;
|
||||
if (m_dataWriter == nullptr)
|
||||
{
|
||||
m_dataWriter = dds::pub::DataWriter<REQUEST>(m_requesterParams.publisher(),
|
||||
ReplyRequestBase<REQUEST, REPLY>::m_requestTopic,
|
||||
m_requesterParams.datawriter_qos());
|
||||
bNewDataWriter = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
create_contentfilter(bNewDataWriter);
|
||||
|
||||
if (m_dataReader == nullptr)
|
||||
{
|
||||
|
||||
m_dataReader = dds::sub::DataReader<REPLY>(m_requesterParams.subscriber(),
|
||||
m_contentFilteredTopic,
|
||||
m_requesterParams.datareader_qos());
|
||||
}
|
||||
|
||||
m_curInstanceHandle = m_dataWriter.register_instance(request.data());
|
||||
request.info().replace_automatic_values(true);
|
||||
request.info().identity(rti::core::SampleIdentity::automatic());
|
||||
m_dataWriter->write(request.data(), request.info());
|
||||
|
||||
const rti::core::SampleIdentity currentSampleIdentity = request.info().identity();
|
||||
DEBUG_LOG(m_logger) << "send_request for topic "
|
||||
<< ReplyRequestBase<REQUEST, REPLY>::m_requestTopic.name()
|
||||
<< ", m_currentSampleIdentity: "
|
||||
<< DebugInfo::toReadable(currentSampleIdentity);
|
||||
}
|
||||
|
||||
bool receive_reply(connext::Sample<REPLY> &reply,
|
||||
const dds::core::Duration &duration)
|
||||
{
|
||||
auto condition(dds::sub::cond::ReadCondition(m_dataReader, dds::sub::status::SampleState::not_read()));
|
||||
|
||||
dds::core::cond::WaitSet waitset;
|
||||
waitset.attach_condition(condition);
|
||||
|
||||
bool bRet = false;
|
||||
while (true)
|
||||
{
|
||||
|
||||
if (m_dataReader->take(reply.data(), reply.info()) && reply.info().valid())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "receive_reply for topic "
|
||||
<< ReplyRequestBase<REQUEST, REPLY>::m_requestTopic.name()
|
||||
<< ", SampleIdentity - original publication virtual: "
|
||||
<< DebugInfo::toReadable(reply.info()->original_publication_virtual_sample_identity());
|
||||
|
||||
DEBUG_LOG(m_logger) << "receive_reply for topic "
|
||||
<< ReplyRequestBase<REQUEST, REPLY>::m_requestTopic.name()
|
||||
<< ", SampleIdentity - related original: "
|
||||
<< DebugInfo::toReadable(reply.info()->related_original_publication_virtual_sample_identity());
|
||||
|
||||
|
||||
bRet = true;
|
||||
break;
|
||||
}
|
||||
|
||||
dds::core::cond::WaitSet::ConditionSeq activeConditionsSeq = waitset.wait(duration);
|
||||
if (activeConditionsSeq.empty())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Bailing out!";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const rti::core::SampleFlag irs(rti::core::SampleFlag::intermediate_reply_sequence());
|
||||
if ((reply.info()->flag() & irs) == 0)
|
||||
{
|
||||
if (!m_curInstanceHandle.is_nil())
|
||||
{
|
||||
|
||||
DEBUG_LOG(m_logger) << "Keyed topic - disposing instance: "
|
||||
<< ReplyRequestBase<REQUEST, REPLY>::m_requestTopic.name();
|
||||
m_dataWriter.dispose_instance(m_curInstanceHandle);
|
||||
}
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
dds::pub::DataWriter<REQUEST> get_request_datawriter() const { return m_dataWriter; }
|
||||
dds::sub::DataReader<REPLY> get_request_datareader() const { return m_dataReader; }
|
||||
|
||||
private:
|
||||
connext::RequesterParams m_requesterParams;
|
||||
dds::pub::DataWriter<REQUEST> m_dataWriter;
|
||||
dds::sub::DataReader<REPLY> m_dataReader;
|
||||
dds::topic::ContentFilteredTopic<REPLY> m_contentFilteredTopic;
|
||||
dds::core::InstanceHandle m_curInstanceHandle;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
template<typename REQUEST, typename REPLY>
|
||||
class Replier final : public ReplyRequestBase<REQUEST, REPLY>
|
||||
{
|
||||
public:
|
||||
explicit Replier(const connext::ReplierParams<REQUEST, REPLY> &replierParams)
|
||||
: ReplyRequestBase<REQUEST, REPLY>(replierParams)
|
||||
, m_replierParams(replierParams)
|
||||
, m_dataWriter(dds::pub::DataWriter<REPLY>(replierParams.publisher(),
|
||||
ReplyRequestBase<REQUEST, REPLY>::m_replyTopic,
|
||||
replierParams.datawriter_qos()))
|
||||
, m_dataReader(dds::sub::DataReader<REQUEST>(replierParams.subscriber(),
|
||||
ReplyRequestBase<REQUEST, REPLY>::m_requestTopic,
|
||||
replierParams.datareader_qos()))
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("connext.replier"))
|
||||
{}
|
||||
|
||||
void send_reply(connext::WriteSample<REPLY> &reply,
|
||||
const rti::core::SampleIdentity &identity,
|
||||
bool bLastSample = true)
|
||||
{
|
||||
connext::WriteSampleRef<REPLY> replyRef;
|
||||
replyRef.set_data(&reply.data());
|
||||
replyRef.set_info(&reply.info());
|
||||
return send_reply(replyRef, identity, bLastSample);
|
||||
}
|
||||
|
||||
void send_reply(connext::WriteSampleRef<REPLY> &reply,
|
||||
const rti::core::SampleIdentity &identity,
|
||||
bool bLastSample = true)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "send_reply for topic "
|
||||
<< ReplyRequestBase<REQUEST, REPLY>::m_requestTopic.name()
|
||||
<< ", sampleIdentity: " << DebugInfo::toReadable(identity);
|
||||
|
||||
rti::core::SampleFlag sf = reply.info().flag();
|
||||
if (bLastSample)
|
||||
sf &= ~rti::core::SampleFlag::intermediate_reply_sequence();
|
||||
else
|
||||
sf |= rti::core::SampleFlag::intermediate_reply_sequence();
|
||||
reply.info().flag(sf);
|
||||
reply.info().related_sample_identity(identity);
|
||||
DEBUG_LOG(m_logger) << "send_reply for topic "
|
||||
<< ReplyRequestBase<REQUEST, REPLY>::m_requestTopic.name()
|
||||
<< ", related sampleIdentity: " << DebugInfo::toReadable(reply.info().related_sample_identity());
|
||||
|
||||
m_dataWriter->write(reply.data(), reply.info());
|
||||
}
|
||||
|
||||
bool receive_request(connext::Sample<REQUEST> &request)
|
||||
{
|
||||
if (!m_dataReader->take(request.data(), request.info()))
|
||||
return false;
|
||||
|
||||
if (request.info().valid())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "receive_request for topic "
|
||||
<< ReplyRequestBase<REQUEST, REPLY>::m_requestTopic.name()
|
||||
<< ", sampleIdentity publication_virtual_sample_identity: "
|
||||
<< DebugInfo::toReadable(request.info()->publication_virtual_sample_identity())
|
||||
<< ", original_publication_virtual_sample_identity: "
|
||||
<< DebugInfo::toReadable(request.info()->original_publication_virtual_sample_identity())
|
||||
<< ", related_original_publication_virtual_sample_identity: "
|
||||
<< DebugInfo::toReadable(request.info()->related_original_publication_virtual_sample_identity())
|
||||
<< " with timestamp_ " << request.info().source_timestamp().to_millisecs();
|
||||
request.set_identity(request.info()->original_publication_virtual_sample_identity());
|
||||
}
|
||||
|
||||
return request.info().valid();
|
||||
}
|
||||
|
||||
dds::pub::DataWriter<REPLY> get_request_datawriter() const { return m_dataWriter; }
|
||||
dds::sub::DataReader<REQUEST> get_request_datareader() const { return m_dataReader; }
|
||||
|
||||
private:
|
||||
connext::ReplierParams<REQUEST, REPLY> m_replierParams;
|
||||
dds::pub::DataWriter<REPLY> m_dataWriter;
|
||||
dds::sub::DataReader<REQUEST> m_dataReader;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <rti/core/SampleIdentity.hpp>
|
||||
#include <QVector>
|
||||
|
||||
|
||||
|
||||
|
||||
namespace FTS
|
||||
{
|
||||
|
||||
template <typename TOPICTYPE_REQUEST, typename TOPICTYPE_REPLY>
|
||||
class GenericFts
|
||||
{
|
||||
public:
|
||||
GenericFts() = default;
|
||||
virtual ~GenericFts() = default;
|
||||
|
||||
virtual bool sendReply(const rti::core::SampleIdentity &requestIdentity, const TOPICTYPE_REPLY &replyData) = 0;
|
||||
virtual bool sendReply(const rti::core::SampleIdentity &requestIdentity, const QVector<TOPICTYPE_REPLY> &replyData) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+287
@@ -0,0 +1,287 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <DDS/Core/core.hpp>
|
||||
|
||||
#include "generic_fts_internal.h"
|
||||
#include "connext.h"
|
||||
#include <DDS/Generic/wait_set_handler.h>
|
||||
#include <DDS/Generic/generic_base.h>
|
||||
#include <DDS/Generic/generic_logger.h>
|
||||
|
||||
#include <queue>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <sstream>
|
||||
|
||||
#ifndef tr_lts
|
||||
#define tr_lts(ctx, t) (t)
|
||||
#endif
|
||||
|
||||
namespace FTS
|
||||
{
|
||||
template <class C>
|
||||
class HasStatusFunction
|
||||
{
|
||||
template <class T>
|
||||
static std::true_type testSignature(void (T::*)(const Core::FtReplyState&));
|
||||
|
||||
template <class T>
|
||||
static decltype(testSignature(&T::replyState)) test(std::nullptr_t);
|
||||
|
||||
template <class T>
|
||||
static std::false_type test(...);
|
||||
|
||||
public:
|
||||
using type = decltype(test<C>(nullptr));
|
||||
static const bool value = type::value;
|
||||
};
|
||||
|
||||
template<typename GENERIC_FTS>
|
||||
struct TopicNames;
|
||||
|
||||
template<typename TOPICTYPE_REQUEST, typename TOPICTYPE_REPLY, typename DUMMY>
|
||||
class GenericFtsImpl final : public GenericFts<TOPICTYPE_REQUEST,TOPICTYPE_REPLY>
|
||||
, public GenericFtsInternal
|
||||
{
|
||||
public:
|
||||
using RequestTopic = TOPICTYPE_REQUEST;
|
||||
using ReplyTopic = TOPICTYPE_REPLY;
|
||||
using TopicNameHelper = TopicNames<GenericFtsImpl<RequestTopic, ReplyTopic, DUMMY>>;
|
||||
private:
|
||||
using SubscriberCallback = std::function<void(const rti::core::SampleIdentity&, const RequestTopic&)>;
|
||||
|
||||
template <typename T = ReplyTopic, typename std::enable_if<HasStatusFunction<T>::value, int>::type = 0>
|
||||
void sendReplyState(Core::FtReplyState replyState, const rti::core::SampleIdentity &requestSampleIdentity)
|
||||
{
|
||||
connext::WriteSample<ReplyTopic> reply;
|
||||
reply.data().replyState(replyState);
|
||||
|
||||
bool bLastTelegram = ((replyState != Core::FtReplyState_def::InProgress) &&
|
||||
(replyState != Core::FtReplyState_def::NotStarted));
|
||||
m_replier->send_reply(reply, requestSampleIdentity, bLastTelegram);
|
||||
}
|
||||
|
||||
template <typename T = ReplyTopic, typename std::enable_if<!HasStatusFunction<T>::value, int>::type = 0>
|
||||
void sendReplyState(Core::FtReplyState, const rti::core::SampleIdentity &)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<HasStatusFunction<T>::value, int>::type = 0>
|
||||
void setReply(T &data, Core::FtReplyState status)
|
||||
{
|
||||
data.replyState(status);
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<!HasStatusFunction<T>::value, int>::type = 0>
|
||||
void setReply(T &, Core::FtReplyState)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
GenericFtsImpl(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const dds::pub::Publisher &ddsPublisher,
|
||||
const dds::sub::Subscriber &ddsSubscriber,
|
||||
DDSGeneric::WaitSetHandler *wsHandler)
|
||||
: GenericFts<RequestTopic,ReplyTopic>()
|
||||
, GenericFtsInternal()
|
||||
, m_wsHandler(wsHandler)
|
||||
, m_subscriberCallback(nullptr)
|
||||
, m_logger(DDS::getLogger(TopicNameHelper::functionTopicName(), DDS::DataType::FUNCTION_TOPIC_SERVER))
|
||||
{
|
||||
Q_ASSERT(domainParticipant != nullptr);
|
||||
Q_ASSERT(ddsPublisher != nullptr);
|
||||
Q_ASSERT(ddsSubscriber != nullptr);
|
||||
Q_ASSERT(wsHandler != nullptr);
|
||||
|
||||
qRegisterMetaType<RequestTopic> (TopicNameHelper::requestTopicTypeName());
|
||||
|
||||
connext::ReplierParams<RequestTopic,ReplyTopic> replierParams(domainParticipant);
|
||||
|
||||
|
||||
|
||||
|
||||
replierParams.request_topic_name(TopicNameHelper::requestTopicName());
|
||||
replierParams.reply_topic_name(TopicNameHelper::replyTopicName());
|
||||
replierParams.publisher(ddsPublisher);
|
||||
replierParams.subscriber(ddsSubscriber);
|
||||
replierParams.datareader_qos(readerQos(m_logger));
|
||||
replierParams.datawriter_qos(writerQos(m_logger));
|
||||
|
||||
|
||||
|
||||
|
||||
m_replier = new connext::Replier<RequestTopic, ReplyTopic>(replierParams);
|
||||
m_readCondition = new dds::sub::cond::ReadCondition(m_replier->get_request_datareader(), dds::sub::status::SampleState::not_read());
|
||||
|
||||
m_wsHandler->attachWaitSet(*m_readCondition, this);
|
||||
}
|
||||
|
||||
~GenericFtsImpl()
|
||||
{
|
||||
m_wsHandler->detachWaitSet(*m_readCondition, this);
|
||||
delete m_readCondition;
|
||||
delete m_replier;
|
||||
}
|
||||
|
||||
bool setSubscriberCallback(const SubscriberCallback &subscriberCallback)
|
||||
{
|
||||
if (m_subscriberCallback)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Can not set subscriber callback - already set ...";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_subscriberCallback = subscriberCallback;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool sendReply(const rti::core::SampleIdentity &requestSampleIdentity, const ReplyTopic &replyData) override final
|
||||
{
|
||||
connext::WriteSampleRef<ReplyTopic> reply;
|
||||
rti::pub::WriteParams replyParams;
|
||||
|
||||
reply.set_data(const_cast<ReplyTopic*>(&replyData));
|
||||
reply.set_info(&replyParams);
|
||||
|
||||
m_replier->send_reply(reply, requestSampleIdentity);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool sendReply(const rti::core::SampleIdentity &requestIdentity, const QVector<ReplyTopic> &replyData) override final
|
||||
{
|
||||
DEBUG_LOG(m_logger) << TopicNameHelper::requestTopicName() << " - IN sendReply";
|
||||
|
||||
for(int i = 0; i < replyData.size(); ++i)
|
||||
{
|
||||
if (HasStatusFunction<ReplyTopic>::value)
|
||||
{
|
||||
ReplyTopic rt = replyData.at(i);
|
||||
setReply(rt, i == replyData.size() - 1 ? Core::FtReplyState::Completed : Core::FtReplyState::InProgress);
|
||||
if (!sendReply(requestIdentity, rt))
|
||||
return false;
|
||||
} else {
|
||||
if (!sendReply(requestIdentity, replyData.at(i)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void processRequest(const connext::Sample<RequestTopic> &request)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << TopicNameHelper::requestTopicName() << " - processRequest: "
|
||||
<< connext::DebugInfo::toReadable(request.identity());
|
||||
|
||||
qint64 age = qAbs(qint64(request.info().source_timestamp().to_secs()) - time(nullptr));
|
||||
|
||||
if (age > 10)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << request.data();
|
||||
std::string s = ss.str();
|
||||
int slen = s.length();
|
||||
|
||||
static int max_slen = 1024;
|
||||
if (slen > max_slen)
|
||||
{
|
||||
s.resize(max_slen);
|
||||
s.append("...");
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "received a request for topic "
|
||||
<< TopicNameHelper::requestTopicName()
|
||||
<< " but it was outdated - ignoring. "
|
||||
<< " local time: " << (time(nullptr) - 10) << "[s], source timestamp: "
|
||||
<< qint64(request.info().source_timestamp().to_secs())
|
||||
<< "[s], -> age: " << age
|
||||
<< "[s], data: " << s;
|
||||
ReplyTopic rt;
|
||||
rt.errorString(tr_lts("dds", "Topic %1 was outdated and therefore ignored. Does the client time match the server time?"));
|
||||
rt.errorParams({TopicNameHelper::requestTopicName()});
|
||||
sendReply(request.identity(), rt);
|
||||
return;
|
||||
}
|
||||
|
||||
sendReplyState(Core::FtReplyState::InProgress, request.identity());
|
||||
|
||||
m_subscriberCallback(request.identity(), request.data());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void processRequests() override
|
||||
{
|
||||
DEBUG_LOG(m_logger) << TopicNameHelper::requestTopicName() << " - processRequests";
|
||||
|
||||
while (true)
|
||||
{
|
||||
connext::Sample<RequestTopic> request;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_requestMutex);
|
||||
if (m_requests.empty())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "No more requests";
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(m_logger) << TopicNameHelper::requestTopicName() << " - read one request for ";
|
||||
request = m_requests.front();
|
||||
m_requests.pop();
|
||||
}
|
||||
if (!request.info().valid())
|
||||
{
|
||||
WARNING_LOG(m_logger) << TopicNameHelper::requestTopicName() << " - No valid data";
|
||||
continue;
|
||||
}
|
||||
processRequest(request);
|
||||
}
|
||||
DEBUG_LOG(m_logger) << TopicNameHelper::requestTopicName() << " - processRequests finished";
|
||||
}
|
||||
|
||||
bool enqueueRequests() override
|
||||
{
|
||||
bool bValidDataRead = false;
|
||||
connext::Sample<RequestTopic> request;
|
||||
while (m_replier->receive_request(request))
|
||||
{
|
||||
if (!request.info().valid())
|
||||
continue;
|
||||
bValidDataRead = true;
|
||||
std::lock_guard<std::mutex> l(m_requestMutex);
|
||||
m_requests.push(request);
|
||||
}
|
||||
return bValidDataRead;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
connext::Replier<RequestTopic, ReplyTopic> *m_replier;
|
||||
dds::sub::cond::ReadCondition *m_readCondition;
|
||||
|
||||
|
||||
DDSGeneric::WaitSetHandler *m_wsHandler;
|
||||
|
||||
std::queue<connext::Sample<RequestTopic>> m_requests;
|
||||
std::mutex m_requestMutex;
|
||||
|
||||
|
||||
SubscriberCallback m_subscriberCallback;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
|
||||
|
||||
|
||||
#include "generic_fts_internal.h"
|
||||
#include <DDS/QosProfile/DDS_DefaultQos.h>
|
||||
#include <QMetaMethod>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include <qglobal.h>
|
||||
|
||||
namespace FTS
|
||||
{
|
||||
|
||||
void GenericFtsInternal::printDefaultQos(NGL::INGLLogger &logger)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
DDS::QosProfile::print(dds::core::QosProvider::Default(), logger);
|
||||
}
|
||||
|
||||
dds::sub::qos::DataReaderQos GenericFtsInternal::readerQos(NGL::INGLLogger &logger)
|
||||
{
|
||||
dds::sub::qos::DataReaderQos qos = DDS::QosProfile::dataReaderDefault(DDS::QosProfile::TopicType::FunctionTopic);
|
||||
if (logger.isDebugEnabled())
|
||||
DDS::QosProfile::print(qos, logger);
|
||||
return qos;
|
||||
}
|
||||
|
||||
dds::pub::qos::DataWriterQos GenericFtsInternal::writerQos(NGL::INGLLogger &logger)
|
||||
{
|
||||
dds::pub::qos::DataWriterQos qos = DDS::QosProfile::dataWriterDefault(DDS::QosProfile::TopicType::FunctionTopic);
|
||||
if (logger.isDebugEnabled())
|
||||
DDS::QosProfile::print(qos, logger);
|
||||
return qos;
|
||||
}
|
||||
|
||||
void GenericFtsInternal::dataAvailable()
|
||||
{
|
||||
if (!enqueueRequests())
|
||||
return;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5,10,0)
|
||||
if (!QMetaObject::invokeMethod(this, "processRequests", Qt::QueuedConnection))
|
||||
abort();
|
||||
#else
|
||||
QMetaObject::invokeMethod(this, &GenericFtsInternal::processRequests, Qt::QueuedConnection);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "generic_fts.h"
|
||||
#include <DDS/FTS/ddsfts_export.h>
|
||||
#include <DDS/Generic/wait_set_handler.h>
|
||||
#include <dds/sub/qos/DataReaderQos.hpp>
|
||||
#include <dds/pub/qos/DataWriterQos.hpp>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace FTS
|
||||
{
|
||||
class DDSFTS_EXPORT GenericFtsInternal : public QObject
|
||||
, protected DDSGeneric::WaitSetHandler::CallbackInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
GenericFtsInternal() = default;
|
||||
|
||||
static void printDefaultQos(NGL::INGLLogger &logger);
|
||||
static dds::sub::qos::DataReaderQos readerQos(NGL::INGLLogger &logger);
|
||||
static dds::pub::qos::DataWriterQos writerQos(NGL::INGLLogger &logger);
|
||||
|
||||
protected:
|
||||
|
||||
void dataAvailable() override;
|
||||
virtual bool enqueueRequests() = 0;
|
||||
protected Q_SLOTS:
|
||||
virtual void processRequests() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
FIND_PACKAGE(Threads REQUIRED)
|
||||
|
||||
SET(ddsgen_SRCS
|
||||
dynamic_data_reader.h
|
||||
generic_base.cpp
|
||||
generic_base.h
|
||||
generic_data_reader.h
|
||||
generic_data_writer.h
|
||||
generic_logger.cpp
|
||||
generic_logger.h
|
||||
subscriber_listener.cpp
|
||||
subscriber_listener.h
|
||||
topic_description.h
|
||||
wait_set_handler.cpp
|
||||
wait_set_handler.h
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(AAR SV HLA WS ddsgen SHARED ${ddsgen_SRCS} ${ddsgen_HDRS})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(ddsgen
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(ddsgen
|
||||
PUBLIC
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
Qt5::Core
|
||||
RTI::nddscpp2
|
||||
NGLLogger
|
||||
LtsUtilsCoreHeaderOnly
|
||||
)
|
||||
|
||||
# topic_type_plugin
|
||||
ADD_LIBRARY(topic_type_plugin INTERFACE)
|
||||
TARGET_SOURCES(topic_type_plugin INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/topic_type_plugin.h)
|
||||
TARGET_INCLUDE_DIRECTORIES(topic_type_plugin INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/../../)
|
||||
TARGET_LINK_LIBRARIES(topic_type_plugin INTERFACE ddsgen Qt5::Core)
|
||||
Executable
+354
@@ -0,0 +1,354 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dds/core/xtypes/StructType.hpp>
|
||||
#include <dds/sub/DataReader.hpp>
|
||||
#include <dds/sub/LoanedSamples.hpp>
|
||||
#include <dds/sub/SampleInfo.hpp>
|
||||
#include <dds/topic/find.hpp>
|
||||
|
||||
#include <DDS/Generic/generic_base.h>
|
||||
#include <DDS/Generic/generic_logger.h>
|
||||
#include <LtsUtilsCore/data_modification_enum.h>
|
||||
#include <functional>
|
||||
|
||||
namespace DDSGeneric
|
||||
{
|
||||
using StructType = dds::core::xtypes::StructType;
|
||||
using DynamicData = dds::core::xtypes::DynamicData;
|
||||
|
||||
class DynamicDataReader final
|
||||
{
|
||||
public:
|
||||
|
||||
using TopicType = DynamicData;
|
||||
using DataReader = dds::sub::DataReader<DynamicData>;
|
||||
using DataTopic = dds::topic::Topic<TopicType>;
|
||||
|
||||
using SubscriberCallback = std::function<void(const DynamicData &, const std::string &, DataModification)>;
|
||||
using SubscriberCallbackWithSampleInfo = std::function<void(const DynamicData &, const std::string &, const dds::sub::SampleInfo &)>;
|
||||
|
||||
DynamicDataReader()
|
||||
: m_dataReader(nullptr)
|
||||
, m_structType(nullptr)
|
||||
, m_ddsTopic(nullptr)
|
||||
, m_bHaveSubscriberCallback(false)
|
||||
, m_logger(DDS::getLogger("DynamicDataReader", DDS::DataType::DYNAMIC_DATA_READER))
|
||||
{}
|
||||
|
||||
~DynamicDataReader()
|
||||
{
|
||||
m_ddsTopic = nullptr;
|
||||
m_dataReader = nullptr;
|
||||
delete m_structType;
|
||||
}
|
||||
|
||||
const DataReader &getDataReader() const { return m_dataReader; }
|
||||
const StructType &getStructType() const { return *m_structType; }
|
||||
|
||||
bool create(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const dds::sub::Subscriber &ddsSubscriber,
|
||||
const std::string &specialTopicName,
|
||||
const StructType &structType)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN DynamicDataReader::create ("
|
||||
<< m_structType->name()
|
||||
<< ")";
|
||||
|
||||
if (domainParticipant == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "DomainParticipant does not exist ";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_ddsTopic != nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Type '"
|
||||
<< m_structType->name()
|
||||
<< "' already created";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (structType.has_parent())
|
||||
m_structType = new dds::core::xtypes::StructType(structType.name(), structType.parent(), structType.members());
|
||||
else
|
||||
m_structType = new dds::core::xtypes::StructType(structType.name(), structType.members());
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
const std::string topicName = specialTopicName.empty() ?
|
||||
DDS::Helper::getShortTopicName(m_structType->name()) :
|
||||
specialTopicName;
|
||||
|
||||
DEBUG_LOG(m_logger) << "Creating topic " << topicName << " with type: "
|
||||
<< m_structType->name();
|
||||
|
||||
m_ddsTopic = dds::topic::find<dds::topic::Topic<DynamicData>>(domainParticipant, topicName);
|
||||
if (m_ddsTopic == nullptr)
|
||||
{
|
||||
rti::core::xtypes::DynamicDataTypeSerializationProperty serialization_property;
|
||||
serialization_property.min_size_serialized(0);
|
||||
dds::domain::DomainParticipant dp = domainParticipant;
|
||||
rti::domain::register_type(dp, structType.name(), structType, serialization_property);
|
||||
m_ddsTopic = dds::topic::Topic<DynamicData>(domainParticipant, topicName, structType);
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "m_ddsTopic created: " << m_ddsTopic.name();
|
||||
|
||||
if (m_logger.isDebugEnabled())
|
||||
{
|
||||
dds::sub::qos::SubscriberQos subscriberQoS = ddsSubscriber.qos();
|
||||
dds::core::policy::Partition partition = subscriberQoS.policy<dds::core::policy::Partition>();
|
||||
dds::core::StringSeq parititionNames = partition.name();
|
||||
DEBUG_LOG(m_logger) << "Subscribing partitions: " << parititionNames.size();
|
||||
for (const std::string &name : parititionNames)
|
||||
DEBUG_LOG(m_logger) << "SubscriberQoS: partition name = " << name;
|
||||
}
|
||||
|
||||
dds::sub::qos::DataReaderQos dataReaderQos = ddsSubscriber.default_datareader_qos();
|
||||
|
||||
QString typeName = QString::fromStdString(m_structType->name());
|
||||
if (typeName.endsWith(QLatin1String("Request")) || typeName.endsWith(QLatin1String("Reply")))
|
||||
{
|
||||
|
||||
dds::core::policy::Durability readerDurability;
|
||||
dataReaderQos >> readerDurability;
|
||||
readerDurability.kind(dds::core::policy::DurabilityKind_def::VOLATILE);
|
||||
dataReaderQos << readerDurability;
|
||||
}
|
||||
|
||||
m_dataReader = DataReader(ddsSubscriber,
|
||||
m_ddsTopic,
|
||||
dataReaderQos,
|
||||
nullptr ,
|
||||
dds::core::status::StatusMask::all());
|
||||
|
||||
if (m_dataReader == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Creating dataReader for '" << topicName << "' with type: '"
|
||||
<< m_structType->name()
|
||||
<< "' failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
catch (const dds::core::Exception &ex)
|
||||
{
|
||||
ERROR_LOG(m_logger) << ex.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "dataReader created ("
|
||||
<< m_structType->name()
|
||||
<< ")";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool hasSamples() const
|
||||
{
|
||||
qint64 sampleSize = m_dataReader->datareader_cache_status().sample_count();
|
||||
DEBUG_LOG(m_logger) << "sampleSize = " << sampleSize;
|
||||
return sampleSize > 0;
|
||||
}
|
||||
|
||||
|
||||
dds::sub::LoanedSamples<DynamicData> readAlive(bool &bSeenNewSamples)
|
||||
{
|
||||
dds::sub::LoanedSamples<DynamicData> samples =
|
||||
m_dataReader.select().state(dds::sub::status::InstanceState::alive()).read();
|
||||
TRACE_LOG(m_logger) << "readAlive for " << DDS::topic_type_name<TopicType>::value()
|
||||
<< ", count: " << samples.length()
|
||||
<< ", this: " << this << ", instance handle " << m_dataReader.instance_handle();
|
||||
|
||||
bSeenNewSamples = checkNewSamples(samples);
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
||||
dds::sub::LoanedSamples<DynamicData> takeAlive(bool &bSeenNewSamples)
|
||||
{
|
||||
dds::sub::LoanedSamples<DynamicData> samples =
|
||||
m_dataReader.select().state(dds::sub::status::InstanceState::alive()).take();
|
||||
TRACE_LOG(m_logger) << "takeAlive for " << DDS::topic_type_name<TopicType>::value()
|
||||
<< ", count: " << samples.length()
|
||||
<< ", this: " << this << ", instance handle " << m_dataReader.instance_handle();
|
||||
|
||||
bSeenNewSamples = checkNewSamples(samples);
|
||||
return samples;
|
||||
}
|
||||
|
||||
dds::sub::LoanedSamples<DynamicData> takeDisposed()
|
||||
{
|
||||
dds::sub::LoanedSamples<DynamicData> samples =
|
||||
m_dataReader.select().state(dds::sub::status::InstanceState::not_alive_disposed()).take();
|
||||
TRACE_LOG(m_logger) << "takeDisposed for " << DDS::topic_type_name<TopicType>::value()
|
||||
<< ", count: " << samples.length()
|
||||
<< ", this: " << this << ", instance handle " << m_dataReader.instance_handle();
|
||||
return samples;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
bool read(C callbackFunc)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN read ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< "), this: " << this << ", instance handle " << m_dataReader.instance_handle();
|
||||
|
||||
if (m_dataReader == nullptr || m_ddsTopic == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "No dataReader initialised ";
|
||||
return false;
|
||||
}
|
||||
if (!isValid(callbackFunc))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "No subscriber set for "
|
||||
<< DDS::topic_type_name<TopicType>::value();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds::sub::LoanedSamples<DynamicData> samples = m_dataReader.select().state(dds::sub::status::DataState::new_data()).read();
|
||||
|
||||
DEBUG_LOG(m_logger) << "got " << samples.length() + m_newSamplesDuringRead.size() << " samples from reader for "
|
||||
<< DDS::topic_type_name<TopicType>::value();
|
||||
|
||||
for (const auto &sample : samples)
|
||||
{
|
||||
if (sample.info().valid())
|
||||
readData(sample, m_dataReader->topic_name(), callbackFunc);
|
||||
}
|
||||
|
||||
auto copy = m_newSamplesDuringRead;
|
||||
|
||||
m_newSamplesDuringRead.clear();
|
||||
for (auto &sample : copy)
|
||||
{
|
||||
|
||||
readData(rti::sub::LoanedSample<DynamicData>(sample.sample, &sample.info), m_dataReader->topic_name(), callbackFunc);
|
||||
delete sample.sample;
|
||||
}
|
||||
|
||||
return checkDisposedInstances(callbackFunc);
|
||||
}
|
||||
|
||||
void setHaveSubscriberCallback(bool bHave)
|
||||
{
|
||||
m_bHaveSubscriberCallback = bHave;
|
||||
}
|
||||
std::string topicName() const
|
||||
{
|
||||
return m_ddsTopic.name();
|
||||
}
|
||||
|
||||
void recreateReader()
|
||||
{
|
||||
if (m_dataReader == nullptr)
|
||||
return;
|
||||
m_dataReader = DataReader(m_dataReader.subscriber(), m_ddsTopic);
|
||||
}
|
||||
|
||||
private:
|
||||
#if RTI_DDS_VERSION_MAJOR < 6
|
||||
inline bool checkNewSamples(dds::sub::LoanedSamples<DynamicData> &samples)
|
||||
#else
|
||||
inline bool checkNewSamples(const dds::sub::LoanedSamples<DynamicData> &samples)
|
||||
#endif
|
||||
{
|
||||
|
||||
if (!m_bHaveSubscriberCallback)
|
||||
return false;
|
||||
|
||||
bool bSeenNewSamples = false;
|
||||
for (const auto &sample : samples)
|
||||
{
|
||||
const dds::sub::status::DataState ds = sample.info()->state();
|
||||
if (ds.view_state() == dds::sub::status::ViewState::new_view() ||
|
||||
ds.sample_state() == dds::sub::status::SampleState::not_read())
|
||||
{
|
||||
SampleStruct ss;
|
||||
ss.sample = new DynamicData(sample.data());
|
||||
ss.info = sample.info();
|
||||
|
||||
m_newSamplesDuringRead.emplace_back(ss);
|
||||
|
||||
bSeenNewSamples = true;
|
||||
}
|
||||
}
|
||||
return bSeenNewSamples;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
bool checkDisposedInstances(C callbackFunc)
|
||||
{
|
||||
const dds::sub::status::DataState ds(dds::sub::status::SampleState::any(),
|
||||
dds::sub::status::ViewState::any(),
|
||||
dds::sub::status::InstanceState::not_alive_mask());
|
||||
|
||||
|
||||
dds::sub::LoanedSamples<DynamicData> samples = m_dataReader.select().state(ds).take();
|
||||
|
||||
if (samples.length() == 0)
|
||||
return false;
|
||||
|
||||
DEBUG_LOG(m_logger) << "got " << samples.length() << " disposed instances from reader for "
|
||||
<< m_ddsTopic.name();
|
||||
|
||||
for (const auto &sample : samples)
|
||||
{
|
||||
if (sample.info().valid())
|
||||
readData(sample, m_dataReader->topic_name(), callbackFunc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void readData(const rti::sub::LoanedSample<DynamicData> &sample, const std::string &topicName, const SubscriberCallback &callbackFunc)
|
||||
{
|
||||
DataModification modType = DataModification::eModified;
|
||||
const dds::sub::status::DataState ds = sample.info()->state();
|
||||
if (ds.view_state() == dds::sub::status::ViewState::new_view())
|
||||
modType = DataModification::eAdded;
|
||||
if (ds.instance_state() != dds::sub::status::InstanceState::alive())
|
||||
modType = DataModification::eRemoved;
|
||||
|
||||
|
||||
callbackFunc(sample.data(), topicName, modType);
|
||||
}
|
||||
|
||||
static void readData(const rti::sub::LoanedSample<DynamicData> &sample, const std::string &topicName, const SubscriberCallbackWithSampleInfo &callbackFunc)
|
||||
{
|
||||
callbackFunc(sample.data(), topicName, sample.info());
|
||||
}
|
||||
|
||||
static bool isValid(const SubscriberCallback &callbackFunc)
|
||||
{
|
||||
return callbackFunc != nullptr;
|
||||
}
|
||||
static bool isValid(const SubscriberCallbackWithSampleInfo &callbackFunc)
|
||||
{
|
||||
return callbackFunc != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
DataReader m_dataReader;
|
||||
StructType *m_structType;
|
||||
DataTopic m_ddsTopic;
|
||||
|
||||
struct SampleStruct
|
||||
{
|
||||
DynamicData *sample = nullptr;
|
||||
dds::sub::SampleInfo info;
|
||||
};
|
||||
std::vector<SampleStruct> m_newSamplesDuringRead;
|
||||
bool m_bHaveSubscriberCallback;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
|
||||
|
||||
|
||||
#include "generic_base.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
#include <QStringList>
|
||||
#include <QMetaMethod>
|
||||
|
||||
namespace DDS
|
||||
{
|
||||
namespace Helper
|
||||
{
|
||||
|
||||
static inline bool endsWith(std::string const &str, std::string const &end)
|
||||
{
|
||||
if (end.size() > str.size())
|
||||
return false;
|
||||
return std::equal(end.rbegin(), end.rend(), str.rbegin());
|
||||
}
|
||||
|
||||
static inline std::string getSimpleTopicName(const std::string &fqName)
|
||||
{
|
||||
const size_t idx = fqName.rfind("::");
|
||||
if (idx == std::string::npos)
|
||||
return fqName;
|
||||
return fqName.substr(idx + 2);
|
||||
}
|
||||
|
||||
std::string getFunctionTopicName(const std::string &fqName)
|
||||
{
|
||||
const std::string ftName = getSimpleTopicName(fqName);
|
||||
static const std::string request("Request");
|
||||
static const std::string reply("Reply");
|
||||
if (endsWith(ftName, request))
|
||||
return ftName.substr(0, ftName.size() - request.size());
|
||||
if (endsWith(ftName, reply))
|
||||
return ftName.substr(0, ftName.size() - reply.size());
|
||||
return ftName;
|
||||
}
|
||||
|
||||
std::string getShortTopicName(const std::string &fqName)
|
||||
{
|
||||
|
||||
|
||||
return std::string("LTS_") + getSimpleTopicName(fqName);
|
||||
}
|
||||
|
||||
QMetaMethod getMethod(NGL::INGLLogger &logger,
|
||||
QObject *objectToInform,
|
||||
const char *slotToExecute,
|
||||
const char *signalSignature,
|
||||
bool bWarnOnEmptySlot)
|
||||
{
|
||||
QMetaMethod ret;
|
||||
if(!slotToExecute || !objectToInform)
|
||||
{
|
||||
if(bWarnOnEmptySlot)
|
||||
WARNING_LOG(logger) << "invalid arguments to getMethod()";
|
||||
return ret;
|
||||
}
|
||||
|
||||
int slotCode = slotToExecute[0] - '0';
|
||||
|
||||
if(slotCode != QSLOT_CODE && slotCode != QSIGNAL_CODE)
|
||||
{
|
||||
ERROR_LOG(logger) << "Use the SLOT or SIGNAL macro for slot name!";
|
||||
return ret;
|
||||
}
|
||||
|
||||
slotToExecute++;
|
||||
const QMetaObject *mo = objectToInform->metaObject();
|
||||
const QByteArray slot = QMetaObject::normalizedSignature(slotToExecute);
|
||||
const int indexOfSlot = mo->indexOfSlot(slot.data());
|
||||
|
||||
if(indexOfSlot < 0)
|
||||
{
|
||||
WARNING_LOG(logger) << "No such slot " << mo->className() << "::" << slot;
|
||||
return ret;
|
||||
}
|
||||
const QByteArray signal = QMetaObject::normalizedSignature(signalSignature);
|
||||
if(!QMetaObject::checkConnectArgs(signal.data(), slot.data()))
|
||||
{
|
||||
WARNING_LOG(logger) << "Incompatible slot arguments " << mo->className() << "::"
|
||||
<< slot << ", must be: " << signal;
|
||||
return ret;
|
||||
}
|
||||
return mo->method(indexOfSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dds/core/xtypes/DynamicData.hpp>
|
||||
#include <dds/core/xtypes/DynamicType.hpp>
|
||||
#include <dds/topic/Topic.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <DDS/Generic/ddsgen_export.h>
|
||||
|
||||
class QObject;
|
||||
class QMetaMethod;
|
||||
|
||||
namespace NGL {
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace DDS
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct topic_type_name {
|
||||
static std::string value() {
|
||||
return dds::topic::topic_type_name<T>::value();
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct topic_type_name<dds::core::xtypes::DynamicData> {
|
||||
static std::string value() {
|
||||
return "dds::topic::DynamicData::value()";
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct topic_type_name<rti::core::xtypes::DynamicTypeImpl> {
|
||||
static std::string value() {
|
||||
return "dds::topic::DynamicTypeImpl::value()";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace Helper
|
||||
{
|
||||
DDSGEN_EXPORT std::string getFunctionTopicName(const std::string &fqName);
|
||||
DDSGEN_EXPORT std::string getShortTopicName(const std::string &fqName);
|
||||
|
||||
|
||||
|
||||
DDSGEN_EXPORT QMetaMethod getMethod(NGL::INGLLogger &logger,
|
||||
QObject *objectToInform,
|
||||
const char *slotToExecute,
|
||||
const char *signalSignature,
|
||||
bool bWarnOnEmptySlot = true);
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+382
@@ -0,0 +1,382 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dds/sub/DataReader.hpp>
|
||||
#include <dds/topic/find.hpp>
|
||||
#include <dds/topic/Topic.hpp>
|
||||
|
||||
#include <DDS/Generic/generic_base.h>
|
||||
#include <DDS/Generic/generic_logger.h>
|
||||
#include <LtsUtilsCore/data_modification_enum.h>
|
||||
#include <functional>
|
||||
|
||||
namespace DDSGeneric
|
||||
{
|
||||
|
||||
template <typename TOPICTYPE>
|
||||
class GenericDataReader
|
||||
{
|
||||
public:
|
||||
using TopicType = TOPICTYPE;
|
||||
using TopicTypeRead = TOPICTYPE;
|
||||
using DataReader = dds::sub::DataReader<TopicTypeRead>;
|
||||
using DataTopic = dds::topic::Topic<TopicTypeRead>;
|
||||
using DataReaderQos = dds::sub::qos::DataReaderQos;
|
||||
using SubscriberCallback = std::function<void(const TopicTypeRead &, DataModification)>;
|
||||
using SubscriberCallbackWithSampleInfo = std::function<void(const TopicTypeRead &, const dds::sub::SampleInfo &)>;
|
||||
|
||||
GenericDataReader()
|
||||
: m_dataReader(nullptr)
|
||||
, m_ddsTopic(nullptr)
|
||||
, m_bHaveSubscriberCallback(false)
|
||||
, m_logger(DDS::getLogger(DDS::topic_type_name<TopicType>::value(), DDS::DataType::DATA_READER))
|
||||
{}
|
||||
|
||||
virtual ~GenericDataReader()
|
||||
{
|
||||
m_ddsTopic = nullptr;
|
||||
m_dataReader = nullptr;
|
||||
}
|
||||
|
||||
DataReader getDataReader() const { return m_dataReader; }
|
||||
|
||||
bool create(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const dds::sub::Subscriber &ddsSubscriber,
|
||||
const std::string &topicName = DDS::Helper::getShortTopicName(DDS::topic_type_name<TopicType>::value()),
|
||||
bool bCreateDataReader = true)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN GenericDataReader::create ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< "), this: " << this;
|
||||
|
||||
if (domainParticipant == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "DomainParticipant does not exist ";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_ddsTopic != nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Type '"
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< "' already created";
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Creating read topic " << topicName << " with type: "
|
||||
<< DDS::topic_type_name<TopicType>::value();
|
||||
|
||||
m_ddsTopic = dds::topic::find<DataTopic>(domainParticipant, topicName);
|
||||
if (m_ddsTopic == nullptr)
|
||||
m_ddsTopic = DataTopic(domainParticipant, topicName);
|
||||
|
||||
if (m_logger.isDebugEnabled())
|
||||
{
|
||||
dds::sub::qos::SubscriberQos subscriberQoS = ddsSubscriber.qos();
|
||||
dds::core::policy::Partition partition = subscriberQoS.policy<dds::core::policy::Partition>();
|
||||
dds::core::StringSeq parititionNames = partition.name();
|
||||
DEBUG_LOG(m_logger) << "Subscribing partitions: " << parititionNames.size();
|
||||
for (const std::string &name : parititionNames)
|
||||
DEBUG_LOG(m_logger) << " partition name = " << name;
|
||||
}
|
||||
|
||||
if (bCreateDataReader)
|
||||
{
|
||||
dds::sub::qos::DataReaderQos drQos = domainParticipant->default_datareader_qos();
|
||||
m_dataReader = DataReader(ddsSubscriber, m_ddsTopic, drQos);
|
||||
if (m_dataReader == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Creating dataReader for '" << topicName << "' with type: '"
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< "' failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const dds::core::Exception &ex)
|
||||
{
|
||||
ERROR_LOG(m_logger) << ex.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "dataReader created ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< ")";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool hasSamples() const
|
||||
{
|
||||
qint64 sampleSize = m_dataReader->datareader_cache_status().sample_count();
|
||||
DEBUG_LOG(m_logger) << "sampleSize = " << sampleSize;
|
||||
return sampleSize > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds::sub::LoanedSamples<TopicTypeRead> readAlive(bool &bSeenNewSamples)
|
||||
{
|
||||
dds::sub::LoanedSamples<TopicTypeRead> samples =
|
||||
m_dataReader.select().state(dds::sub::status::InstanceState::alive()).read();
|
||||
TRACE_LOG(m_logger) << "readAlive for " << DDS::topic_type_name<TopicType>::value()
|
||||
<< ", count: " << samples.length()
|
||||
<< ", this: " << this << ", instance handle " << m_dataReader.instance_handle();
|
||||
|
||||
bSeenNewSamples = checkNewSamples(samples);
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dds::sub::LoanedSamples<TopicTypeRead> takeAlive(bool &bSeenNewSamples)
|
||||
{
|
||||
dds::sub::LoanedSamples<TopicTypeRead> samples =
|
||||
m_dataReader.select().state(dds::sub::status::InstanceState::alive()).take();
|
||||
TRACE_LOG(m_logger) << "takeAlive for " << DDS::topic_type_name<TopicType>::value()
|
||||
<< ", count: " << samples.length()
|
||||
<< ", this: " << this << ", instance handle " << m_dataReader.instance_handle();
|
||||
|
||||
bSeenNewSamples = checkNewSamples(samples);
|
||||
return samples;
|
||||
}
|
||||
|
||||
dds::sub::LoanedSamples<TopicTypeRead> takeDisposed()
|
||||
{
|
||||
dds::sub::LoanedSamples<TopicTypeRead> samples =
|
||||
m_dataReader.select().state(dds::sub::status::InstanceState::not_alive_disposed()).take();
|
||||
TRACE_LOG(m_logger) << "takeDisposed for " << DDS::topic_type_name<TopicType>::value()
|
||||
<< ", count: " << samples.length()
|
||||
<< ", this: " << this << ", instance handle " << m_dataReader.instance_handle();
|
||||
return samples;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
bool read(C callbackFunc)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN read ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< "), this: " << this << ", instance handle " << m_dataReader.instance_handle();
|
||||
|
||||
if (m_dataReader == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "No dataReader initialised ";
|
||||
return false;
|
||||
}
|
||||
if (!isValid(callbackFunc))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "No subscriber set for "
|
||||
<< DDS::topic_type_name<TopicType>::value();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<SampleStruct> samples;
|
||||
{
|
||||
dds::sub::LoanedSamples<TopicTypeRead> loanedSamples = m_dataReader.select().state(dds::sub::status::DataState::new_data()).read();
|
||||
samples.reserve(loanedSamples.length());
|
||||
for (const auto &sample : loanedSamples)
|
||||
{
|
||||
if (!sample.info().valid())
|
||||
continue;
|
||||
samples.emplace_back(sample.data(), sample.info());
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "got " << samples.size() + m_newSamplesDuringRead.size() << " samples from reader for "
|
||||
<< DDS::topic_type_name<TopicType>::value();
|
||||
|
||||
for (const SampleStruct &ss : samples)
|
||||
readData(ss.sample, ss.info, callbackFunc);
|
||||
|
||||
if (!m_newSamplesDuringRead.empty())
|
||||
{
|
||||
std::vector<SampleStruct> copy;
|
||||
std::swap(copy, m_newSamplesDuringRead);
|
||||
|
||||
|
||||
for (const SampleStruct &ss : copy)
|
||||
readData(ss.sample, ss.info, callbackFunc);
|
||||
}
|
||||
|
||||
return checkDisposedInstances(callbackFunc);
|
||||
}
|
||||
|
||||
void setHaveSubscriberCallback(bool bHave)
|
||||
{
|
||||
m_bHaveSubscriberCallback = bHave;
|
||||
}
|
||||
std::string topicName() const
|
||||
{
|
||||
return m_ddsTopic.name();
|
||||
}
|
||||
|
||||
DataReader dataReader() const { return m_dataReader; }
|
||||
|
||||
virtual void recreateReader()
|
||||
{
|
||||
if (m_dataReader == nullptr)
|
||||
return;
|
||||
m_dataReader = DataReader(m_dataReader.subscriber(), m_ddsTopic);
|
||||
}
|
||||
private:
|
||||
#if RTI_DDS_VERSION_MAJOR < 6
|
||||
inline bool checkNewSamples(dds::sub::LoanedSamples<TopicTypeRead> &samples)
|
||||
#else
|
||||
inline bool checkNewSamples(const dds::sub::LoanedSamples<TopicTypeRead> &samples)
|
||||
#endif
|
||||
{
|
||||
|
||||
if (!m_bHaveSubscriberCallback)
|
||||
return false;
|
||||
|
||||
bool bSeenNewSamples = false;
|
||||
for (const auto &sample : samples)
|
||||
{
|
||||
const dds::sub::status::DataState ds = sample.info()->state();
|
||||
if (ds.view_state() == dds::sub::status::ViewState::new_view() ||
|
||||
ds.sample_state() == dds::sub::status::SampleState::not_read())
|
||||
{
|
||||
|
||||
m_newSamplesDuringRead.emplace_back(sample.data(), sample.info());
|
||||
|
||||
bSeenNewSamples = true;
|
||||
}
|
||||
}
|
||||
return bSeenNewSamples;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
bool checkDisposedInstances(C callbackFunc)
|
||||
{
|
||||
const dds::sub::status::DataState ds(dds::sub::status::SampleState::any(),
|
||||
dds::sub::status::ViewState::any(),
|
||||
dds::sub::status::InstanceState::not_alive_mask());
|
||||
|
||||
|
||||
dds::sub::LoanedSamples<TopicTypeRead> samples = m_dataReader.select().state(ds).take();
|
||||
|
||||
if (samples.length() == 0)
|
||||
return false;
|
||||
|
||||
DEBUG_LOG(m_logger) << "got " << samples.length() << " disposed instances from reader for "
|
||||
<< DDS::topic_type_name<TopicType>::value();
|
||||
|
||||
for (const auto &sample : samples)
|
||||
{
|
||||
if (sample.info().valid())
|
||||
readData(sample.data(), sample.info(), callbackFunc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void readData(const TopicType &sample, const dds::sub::SampleInfo &info, const SubscriberCallback &callbackFunc)
|
||||
{
|
||||
DataModification modType = DataModification::eModified;
|
||||
const dds::sub::status::DataState ds = info.state();
|
||||
if (ds.view_state() == dds::sub::status::ViewState::new_view())
|
||||
modType = DataModification::eAdded;
|
||||
if (ds.instance_state() != dds::sub::status::InstanceState::alive())
|
||||
modType = DataModification::eRemoved;
|
||||
|
||||
|
||||
callbackFunc(sample, modType);
|
||||
}
|
||||
|
||||
static void readData(const TopicType &sample, const dds::sub::SampleInfo &info, const SubscriberCallbackWithSampleInfo &callbackFunc)
|
||||
{
|
||||
callbackFunc(sample, info);
|
||||
}
|
||||
|
||||
static bool isValid(const SubscriberCallback &callbackFunc)
|
||||
{
|
||||
return callbackFunc != nullptr;
|
||||
}
|
||||
static bool isValid(const SubscriberCallbackWithSampleInfo &callbackFunc)
|
||||
{
|
||||
return callbackFunc != nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
DataReader m_dataReader;
|
||||
DataReaderQos m_dataReaderQos;
|
||||
|
||||
DataTopic m_ddsTopic;
|
||||
struct SampleStruct
|
||||
{
|
||||
TopicTypeRead sample;
|
||||
dds::sub::SampleInfo info;
|
||||
|
||||
SampleStruct() = default;
|
||||
SampleStruct(const TopicTypeRead &s, const dds::sub::SampleInfo &i)
|
||||
: sample(s)
|
||||
, info(i)
|
||||
{}
|
||||
};
|
||||
std::vector<SampleStruct> m_newSamplesDuringRead;
|
||||
bool m_bHaveSubscriberCallback;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
template<typename TOPICTYPE>
|
||||
class GenericDataReaderContentFilter final
|
||||
: public GenericDataReader<TOPICTYPE>
|
||||
{
|
||||
using BaseClass = GenericDataReader<TOPICTYPE>;
|
||||
public:
|
||||
using TopicType = TOPICTYPE;
|
||||
using DataReader = dds::sub::DataReader<TopicType>;
|
||||
|
||||
using BaseClass::BaseClass;
|
||||
|
||||
bool create(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const dds::sub::Subscriber &ddsSubscriber,
|
||||
const std::string &specialTopicName,
|
||||
const dds::topic::Filter &filter)
|
||||
{
|
||||
if (!BaseClass::create(domainParticipant, ddsSubscriber, specialTopicName, false))
|
||||
return false;
|
||||
m_filter = filter;
|
||||
try
|
||||
{
|
||||
qint64 ptr = reinterpret_cast<qint64>(this);
|
||||
const QByteArray name = QByteArray(DDS::topic_type_name<TopicType>::value().c_str())
|
||||
+ "- content filter 0x"
|
||||
+ QByteArray::number(ptr, 16);
|
||||
m_filteredTopic = dds::topic::ContentFilteredTopic<TopicType>(BaseClass::m_ddsTopic, name.data(), m_filter);
|
||||
BaseClass::m_dataReader = DataReader(ddsSubscriber, m_filteredTopic);
|
||||
}
|
||||
catch (const dds::core::Exception &ex)
|
||||
{
|
||||
ERROR_LOG(BaseClass::m_logger) << ex.what();
|
||||
return false;
|
||||
}
|
||||
DEBUG_LOG(BaseClass::m_logger) << "dataReader with filter created ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< ")";
|
||||
return true;
|
||||
}
|
||||
|
||||
dds::topic::Filter &filter() { return m_filter; }
|
||||
const dds::topic::Filter &filter() const { return m_filter; }
|
||||
|
||||
virtual void recreateReader()
|
||||
{
|
||||
if (BaseClass::m_dataReader == nullptr)
|
||||
return;
|
||||
BaseClass::m_dataReader = DataReader(BaseClass::m_dataReader.subscriber(), m_filteredTopic);
|
||||
}
|
||||
|
||||
private:
|
||||
dds::topic::ContentFilteredTopic<TopicType> m_filteredTopic = dds::topic::ContentFilteredTopic<TopicType>(nullptr);
|
||||
dds::topic::Filter m_filter = dds::topic::Filter(std::string());
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+143
@@ -0,0 +1,143 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dds/pub/DataWriter.hpp>
|
||||
#include <dds/sub/SampleInfo.hpp>
|
||||
#include <dds/topic/find.hpp>
|
||||
#include <dds/topic/Topic.hpp>
|
||||
|
||||
#include <DDS/Generic/generic_logger.h>
|
||||
#include <DDS/Generic/generic_base.h>
|
||||
|
||||
namespace DDSGeneric
|
||||
{
|
||||
|
||||
template <typename TOPICTYPE>
|
||||
class GenericDataWriter final
|
||||
{
|
||||
public:
|
||||
using TopicType = TOPICTYPE;
|
||||
using DataWriter = dds::pub::DataWriter<TopicType>;
|
||||
using DataTopic = dds::topic::Topic<TopicType>;
|
||||
|
||||
GenericDataWriter()
|
||||
: m_dataWriter(nullptr)
|
||||
, m_ddsTopic(nullptr)
|
||||
, m_logger(DDS::getLogger(DDS::topic_type_name<TopicType>::value(), DDS::DataType::DATA_WRITER))
|
||||
{}
|
||||
|
||||
bool create(const dds::domain::DomainParticipant &participant,
|
||||
const dds::pub::Publisher &ddsPublisher,
|
||||
const std::string &topicName = DDS::Helper::getShortTopicName(DDS::topic_type_name<TopicType>::value()))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN GenericDataWriter::create ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< ")";
|
||||
|
||||
if (participant == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Participant not available, aborting. ";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_ddsTopic != nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Type '"
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< "' already created";
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
DEBUG_LOG(m_logger) << "Creating write topic " << topicName << " with type: "
|
||||
<< DDS::topic_type_name<TopicType>::value();
|
||||
|
||||
m_ddsTopic = dds::topic::find<DataTopic>(participant, topicName);
|
||||
if (m_ddsTopic == nullptr)
|
||||
m_ddsTopic = DataTopic(participant, topicName);
|
||||
|
||||
m_dataWriter = DataWriter(ddsPublisher,
|
||||
m_ddsTopic,
|
||||
ddsPublisher.default_datawriter_qos());
|
||||
|
||||
if (m_dataWriter == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Initialisation of "
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< " failed ";
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
catch ( const dds::core::Exception &ex )
|
||||
{
|
||||
ERROR_LOG(m_logger) << ex.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "dataWriter created ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< ")";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write(const TopicType &data,
|
||||
const dds::core::optional<dds::sub::SampleInfo> &si = dds::core::optional<dds::sub::SampleInfo>())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN write ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< ") ";
|
||||
|
||||
if (m_dataWriter == nullptr) {
|
||||
ERROR_LOG(m_logger) << "No dataWriter initialised ";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (si.is_set())
|
||||
{
|
||||
rti::pub::WriteParams writeParams;
|
||||
rti::core::SampleIdentity sampleIdentity(si.get()->source_guid(), si.get()->publication_sequence_number());
|
||||
writeParams.related_sample_identity(sampleIdentity);
|
||||
|
||||
m_dataWriter->write(data, writeParams);
|
||||
} else {
|
||||
m_dataWriter.write(data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(const TopicType &data, bool errorMsgIfNotExists = true)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN remove ("
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< ") ";
|
||||
|
||||
if (m_dataWriter == nullptr) {
|
||||
ERROR_LOG(m_logger) << "No dataWriter initialised ";
|
||||
return false;
|
||||
}
|
||||
const dds::core::InstanceHandle ih = m_dataWriter.lookup_instance(data);
|
||||
if (ih.is_nil()) {
|
||||
if (errorMsgIfNotExists)
|
||||
ERROR_LOG(m_logger) << "Could not find instance for topic "
|
||||
<< DDS::topic_type_name<TopicType>::value()
|
||||
<< " for unregistering!";
|
||||
return false;
|
||||
}
|
||||
m_dataWriter.dispose_instance(ih);
|
||||
return true;
|
||||
}
|
||||
|
||||
DataWriter dataWriter() const { return m_dataWriter; }
|
||||
|
||||
private:
|
||||
DataWriter m_dataWriter;
|
||||
DataTopic m_ddsTopic;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
|
||||
|
||||
|
||||
#include "generic_logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace DDS
|
||||
{
|
||||
static const char *genericLoggerName(DataType ddsDataType)
|
||||
{
|
||||
switch (ddsDataType)
|
||||
{
|
||||
case DataType::DATA_TYPE_UNKNOWN:
|
||||
return "Unknown.";
|
||||
case DataType::DATA_READER:
|
||||
return "DataReader.";
|
||||
case DataType::DATA_WRITER:
|
||||
return "DataWriter.";
|
||||
case DataType::DYNAMIC_DATA_WRITER:
|
||||
return "DynamicDataWriter.";
|
||||
case DataType::DYNAMIC_DATA_READER:
|
||||
return "DynamicDataReader.";
|
||||
case DataType::DATA_TOPIC_SERVER:
|
||||
return "DTS.";
|
||||
case DataType::DATA_TOPIC_CLIENT:
|
||||
return "DTC.";
|
||||
case DataType::FUNCTION_TOPIC_SERVER:
|
||||
return "FTS.";
|
||||
case DataType::FUNCTION_TOPIC_CLIENT:
|
||||
return "FTC.";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
LOGGER_T getLogger(const char *name, DataType dt)
|
||||
{
|
||||
static const char ns[] = "DDS.";
|
||||
const char *dtName = genericLoggerName(dt);
|
||||
const size_t dtNameLen = strlen(dtName);
|
||||
std::string loggerName;
|
||||
const size_t len = strlen(name);
|
||||
loggerName.reserve(sizeof(ns) + len + dtNameLen);
|
||||
loggerName.append(ns, sizeof(ns) - 1);
|
||||
loggerName.append(dtName, dtNameLen);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include <DDS/Generic/ddsgen_export.h>
|
||||
|
||||
namespace DDS
|
||||
{
|
||||
|
||||
enum class DataType
|
||||
{
|
||||
DATA_TYPE_UNKNOWN = 0,
|
||||
|
||||
DATA_WRITER = 1,
|
||||
DATA_READER = 2,
|
||||
|
||||
DYNAMIC_DATA_WRITER = 3,
|
||||
DYNAMIC_DATA_READER = 4,
|
||||
|
||||
DATA_TOPIC_SERVER = 8,
|
||||
DATA_TOPIC_CLIENT = 9,
|
||||
|
||||
FUNCTION_TOPIC_SERVER = 12,
|
||||
FUNCTION_TOPIC_CLIENT = 13
|
||||
};
|
||||
|
||||
using LOGGER_T = NGL::INGLLogger&;
|
||||
|
||||
DDSGEN_EXPORT LOGGER_T getLogger(const char *name, DataType dt);
|
||||
inline LOGGER_T getLogger(const std::string &loggerName, DataType dt)
|
||||
{
|
||||
return getLogger(loggerName.data(), dt);
|
||||
}
|
||||
}
|
||||
Executable
+191
@@ -0,0 +1,191 @@
|
||||
|
||||
|
||||
|
||||
#include "subscriber_listener.h"
|
||||
#include "generic_base.h"
|
||||
#include "generic_logger.h"
|
||||
|
||||
namespace DDSGeneric
|
||||
{
|
||||
|
||||
SubscriberListener::SubscriberListener()
|
||||
: m_bPrepareForShutdown(false)
|
||||
, m_logger(DDS::getLogger("SubscriberListener", DDS::DataType::DATA_TOPIC_CLIENT))
|
||||
{
|
||||
qRegisterMetaType<std::string>("std::string");
|
||||
DEBUG_LOG(m_logger) << "SubscriberListener initialized, this " << this;
|
||||
}
|
||||
|
||||
void SubscriberListener::addCallback(const std::string &topicName,
|
||||
const std::string &topicTypeName,
|
||||
const dds::sub::AnyDataReader &dataReader,
|
||||
SubscriberListenerCallback *cb)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "addSubscriberCallback for " << topicName << ", type " << topicTypeName
|
||||
<< ", cb " << cb << ", this " << this;
|
||||
QWriteLocker l(&m_accessLock);
|
||||
if (m_bPrepareForShutdown)
|
||||
return;
|
||||
m_droCallbacks.insertMulti(std::make_pair(topicName, topicTypeName), fillData(dataReader, cb));
|
||||
}
|
||||
|
||||
void SubscriberListener::removeCallback(const std::string &topicName,
|
||||
const std::string &topicTypeName,
|
||||
const dds::sub::AnyDataReader &dataReader,
|
||||
SubscriberListenerCallback *cb)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "IN removeSubscriberCallback for " << topicName << ", type " << topicTypeName
|
||||
<< ", cb " << cb << ", this " << this;
|
||||
QWriteLocker l(&m_accessLock);
|
||||
m_droCallbacks.remove(std::make_pair(topicName, topicTypeName), fillData(dataReader, cb));
|
||||
}
|
||||
|
||||
void SubscriberListener::enaDisaCallback(const std::string &topicName,
|
||||
const std::string &topicTypeName,
|
||||
const dds::sub::AnyDataReader &dataReader,
|
||||
SubscriberListenerCallback *cb,
|
||||
bool bEnable)
|
||||
{
|
||||
const std::pair<std::string, std::string> key(std::make_pair(topicName, topicTypeName));
|
||||
const cbData cbDataSearch(fillData(dataReader, cb));
|
||||
bool bFound = false;
|
||||
|
||||
{
|
||||
QWriteLocker l(&m_accessLock);
|
||||
if (m_bPrepareForShutdown)
|
||||
return;
|
||||
|
||||
QMultiMap<std::pair<std::string, std::string>, cbData>::iterator cbIt = m_droCallbacks.find(key);
|
||||
for( ; cbIt != m_droCallbacks.end() && cbIt.key() == key; ++cbIt)
|
||||
{
|
||||
const cbData &data = cbIt.value();
|
||||
if (data != cbDataSearch)
|
||||
continue;
|
||||
bFound = true;
|
||||
cbIt.value().enabled = bEnable;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFound)
|
||||
WARNING_LOG(m_logger) << "Can not " << (bEnable ? "enable" : "disable")
|
||||
<< " callback for " << topicName << ", type " << topicTypeName
|
||||
<< ", cb " << cb << ", this " << this << " - not found!";
|
||||
}
|
||||
|
||||
void SubscriberListener::prepareShutdown()
|
||||
{
|
||||
QWriteLocker l(&m_accessLock);
|
||||
m_droCallbacks.clear();
|
||||
m_bPrepareForShutdown = true;
|
||||
}
|
||||
|
||||
|
||||
void SubscriberListener::on_data_available(dds::sub::AnyDataReader &reader)
|
||||
{
|
||||
|
||||
const std::string &topicName = reader.topic_name();
|
||||
const std::string &topicTypeName = reader.type_name();
|
||||
const std::pair<std::string, std::string> key(std::make_pair(topicName, topicTypeName));
|
||||
|
||||
QReadLocker l(&m_accessLock);
|
||||
if (m_droCallbacks.isEmpty() || m_bPrepareForShutdown)
|
||||
return;
|
||||
DEBUG_LOG(m_logger) << "Data available for " << topicName << ", type " << topicTypeName << ", this " << this;
|
||||
QMultiMap<std::pair<std::string, std::string>, cbData>::iterator cbIt = m_droCallbacks.find(key);
|
||||
const QMultiMap<std::pair<std::string, std::string>, cbData>::iterator cbEnd = m_droCallbacks.end();
|
||||
for( ; cbIt != cbEnd && cbIt.key() == key; ++cbIt)
|
||||
{
|
||||
const cbData &data = cbIt.value();
|
||||
if (data.dataReader != reader)
|
||||
continue;
|
||||
|
||||
if (!data.enabled)
|
||||
continue;
|
||||
|
||||
if (data.methodToCall.isValid())
|
||||
{
|
||||
data.methodToCall.invoke(data.cb, Qt::QueuedConnection,
|
||||
Q_ARG(std::string, topicTypeName),
|
||||
Q_ARG(std::string, topicName));
|
||||
cbIt.value().enabled = false;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SubscriberListener::on_liveliness_changed(dds::sub::AnyDataReader &reader,
|
||||
const dds::core::status::LivelinessChangedStatus &livelinessStatus)
|
||||
{
|
||||
const std::string &topicName = reader.topic_name();
|
||||
const std::string &topicTypeName = reader.type_name();
|
||||
|
||||
DEBUG_LOG(m_logger) << "on_liveliness_changed: " << livelinessStatus.alive_count()
|
||||
<< ", " << livelinessStatus.not_alive_count()
|
||||
<< ", " << livelinessStatus.alive_count_change()
|
||||
<< ", " << livelinessStatus.not_alive_count_change()
|
||||
<< " for type " << topicTypeName;
|
||||
|
||||
bool bNewWriters = false;
|
||||
bool bNoWritersAnymore = false;
|
||||
if (livelinessStatus.alive_count() == 0 &&
|
||||
livelinessStatus.alive_count_change() < 0)
|
||||
bNoWritersAnymore = true;
|
||||
if (livelinessStatus.alive_count() != 0 &&
|
||||
livelinessStatus.alive_count_change() == livelinessStatus.alive_count())
|
||||
bNewWriters = true;
|
||||
if (!bNewWriters && !bNoWritersAnymore)
|
||||
return;
|
||||
|
||||
QReadLocker l(&m_accessLock);
|
||||
if (m_droCallbacks.isEmpty())
|
||||
return;
|
||||
|
||||
const std::pair<std::string, std::string> key(std::make_pair(topicName, topicTypeName));
|
||||
QMultiMap<std::pair<std::string, std::string>, cbData>::iterator cbIt = m_droCallbacks.find(key);
|
||||
const QMultiMap<std::pair<std::string, std::string>, cbData>::iterator cbEnd = m_droCallbacks.end();
|
||||
for( ; cbIt != cbEnd && cbIt.key() == key; ++cbIt)
|
||||
{
|
||||
const cbData &data = cbIt.value();
|
||||
if (data.dataReader != reader)
|
||||
continue;
|
||||
|
||||
if (bNoWritersAnymore)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "bNoWritersAnymore for " << topicName << ", type " << topicTypeName;
|
||||
data.cb->noWriterAnymore(topicTypeName, topicName);
|
||||
}
|
||||
else if (bNewWriters)
|
||||
{
|
||||
data.cb->writerAvailable(topicTypeName, topicName);
|
||||
if (!data.enabled)
|
||||
continue;
|
||||
if (data.methodToCall.isValid())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "methodcall is valid for " << topicName << ", type " << topicTypeName;
|
||||
data.methodToCall.invoke(data.cb, Qt::QueuedConnection,
|
||||
Q_ARG(std::string, topicTypeName),
|
||||
Q_ARG(std::string, topicName));
|
||||
cbIt.value().enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SubscriberListener::cbData SubscriberListener::fillData(const dds::sub::AnyDataReader &dataReader, SubscriberListenerCallback *cb) const
|
||||
{
|
||||
|
||||
const char *signal = SIGNAL(droDataAvailable(std::string,std::string));
|
||||
const char *slot = SLOT(droDataAvailable(std::string,std::string));
|
||||
QMetaMethod method = DDS::Helper::getMethod(m_logger,
|
||||
cb,
|
||||
slot,
|
||||
signal);
|
||||
|
||||
|
||||
return cbData(cb, dataReader, method);
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+140
@@ -0,0 +1,140 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <DDS/Generic/ddsgen_export.h>
|
||||
#include <dds/sub/SubscriberListener.hpp>
|
||||
#include <dds/sub/AnyDataReader.hpp>
|
||||
#include <dds/topic/Topic.hpp>
|
||||
#include <QMetaMethod>
|
||||
#include <QReadWriteLock>
|
||||
#include <QObject>
|
||||
|
||||
namespace NGL {
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace DDSGeneric
|
||||
{
|
||||
class DDSGEN_EXPORT SubscriberListenerCallback : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~SubscriberListenerCallback() = default;
|
||||
public Q_SLOTS:
|
||||
|
||||
virtual void droDataAvailable(const std::string &topicType, const std::string &topicName) = 0;
|
||||
|
||||
virtual void noWriterAnymore(const std::string &topicType, const std::string &topicName) = 0;
|
||||
virtual void writerAvailable(const std::string &topicType, const std::string &topicName) = 0;
|
||||
};
|
||||
|
||||
class DDSGEN_EXPORT SubscriberListener final : public dds::sub::SubscriberListener
|
||||
{
|
||||
public:
|
||||
SubscriberListener();
|
||||
|
||||
template<typename TopicType>
|
||||
void addCallback(const dds::sub::DataReader<TopicType> &dataReader,
|
||||
SubscriberListenerCallback *cb)
|
||||
{
|
||||
addCallback(dataReader->topic_name(), dataReader->type_name(), dds::sub::AnyDataReader(dataReader), cb);
|
||||
}
|
||||
template<typename TopicType>
|
||||
void removeCallback(const dds::sub::DataReader<TopicType> &dataReader,
|
||||
SubscriberListenerCallback *cb)
|
||||
{
|
||||
removeCallback(dataReader->topic_name(), dataReader->type_name(), dds::sub::AnyDataReader(dataReader), cb);
|
||||
}
|
||||
|
||||
template<typename TopicType>
|
||||
void enableCallback(const dds::sub::DataReader<TopicType> &dataReader,
|
||||
SubscriberListenerCallback *cb)
|
||||
{
|
||||
enaDisaCallback(dataReader->topic_name(), dataReader->type_name(), dds::sub::AnyDataReader(dataReader), cb, true);
|
||||
}
|
||||
template<typename TopicType>
|
||||
void disableCallback(const dds::sub::DataReader<TopicType> &dataReader,
|
||||
SubscriberListenerCallback *cb)
|
||||
{
|
||||
enaDisaCallback(dataReader->topic_name(), dataReader->type_name(), dds::sub::AnyDataReader(dataReader), cb, false);
|
||||
}
|
||||
|
||||
static dds::core::status::StatusMask mask()
|
||||
{
|
||||
return (dds::core::status::StatusMask::liveliness_changed() | dds::core::status::StatusMask::data_available());
|
||||
}
|
||||
|
||||
void prepareShutdown();
|
||||
|
||||
protected:
|
||||
void addCallback(const std::string &topicName,
|
||||
const std::string &topicTypeName,
|
||||
const dds::sub::AnyDataReader &dataReader,
|
||||
SubscriberListenerCallback *cb);
|
||||
void removeCallback(const std::string &topicName,
|
||||
const std::string &topicTypeName,
|
||||
const dds::sub::AnyDataReader &dataReader,
|
||||
SubscriberListenerCallback *cb);
|
||||
void enaDisaCallback(const std::string &topicName,
|
||||
const std::string &topicTypeName,
|
||||
const dds::sub::AnyDataReader &dataReader,
|
||||
SubscriberListenerCallback *cb,
|
||||
bool bEnable);
|
||||
|
||||
void on_data_on_readers(dds::sub::Subscriber &) override {}
|
||||
|
||||
void on_liveliness_changed(dds::sub::AnyDataReader &reader,
|
||||
const dds::core::status::LivelinessChangedStatus &livelinessStatus) override;
|
||||
|
||||
void on_requested_deadline_missed(dds::sub::AnyDataReader&,
|
||||
const dds::core::status::RequestedDeadlineMissedStatus&) override {}
|
||||
void on_requested_incompatible_qos(dds::sub::AnyDataReader&,
|
||||
const dds::core::status::RequestedIncompatibleQosStatus&) override {}
|
||||
void on_sample_rejected(dds::sub::AnyDataReader&,
|
||||
const dds::core::status::SampleRejectedStatus&) override {}
|
||||
void on_data_available(dds::sub::AnyDataReader &reader) override;
|
||||
void on_subscription_matched(dds::sub::AnyDataReader&,
|
||||
const dds::core::status::SubscriptionMatchedStatus&) override {}
|
||||
void on_sample_lost(dds::sub::AnyDataReader&,
|
||||
const dds::core::status::SampleLostStatus&) override {}
|
||||
|
||||
private:
|
||||
struct cbData
|
||||
{
|
||||
SubscriberListenerCallback *cb = nullptr;
|
||||
bool enabled = true;
|
||||
dds::sub::AnyDataReader dataReader;
|
||||
QMetaMethod methodToCall;
|
||||
|
||||
bool operator ==(const cbData &o) const
|
||||
{
|
||||
|
||||
return cb == o.cb && methodToCall == o.methodToCall && dataReader == o.dataReader;
|
||||
}
|
||||
bool operator !=(const cbData &o) const
|
||||
{
|
||||
return !operator ==(o);
|
||||
}
|
||||
|
||||
cbData(SubscriberListenerCallback *cb_,
|
||||
const dds::sub::AnyDataReader &dataReader_,
|
||||
const QMetaMethod &methodToCall_)
|
||||
: cb(cb_)
|
||||
, enabled(true)
|
||||
, dataReader(dataReader_)
|
||||
, methodToCall(methodToCall_)
|
||||
{}
|
||||
};
|
||||
cbData fillData(const dds::sub::AnyDataReader &dataReader, SubscriberListenerCallback *cb) const;
|
||||
|
||||
QMultiMap<std::pair<std::string, std::string>, cbData> m_droCallbacks;
|
||||
QReadWriteLock m_accessLock;
|
||||
bool m_bPrepareForShutdown;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dds/core/xtypes/StructType.hpp>
|
||||
|
||||
namespace DDSGeneric
|
||||
{
|
||||
struct TopicDescription
|
||||
{
|
||||
TopicDescription()
|
||||
: type(dds::core::xtypes::StructType("Unknown")) {}
|
||||
TopicDescription(const std::string &name_, const std::string &typeName_, const dds::core::xtypes::StructType &type_)
|
||||
: name(name_)
|
||||
, typeName(typeName_)
|
||||
, type(type_)
|
||||
{}
|
||||
|
||||
std::string name;
|
||||
std::string typeName;
|
||||
dds::core::xtypes::StructType type;
|
||||
|
||||
bool operator==(const TopicDescription &ti) const
|
||||
{
|
||||
return (name.compare(ti.name) == 0) &&
|
||||
(typeName.compare(ti.typeName) == 0) &&
|
||||
(type == ti.type);
|
||||
}
|
||||
bool isEmpty() const
|
||||
{
|
||||
return name.empty();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
#include <DDS/Generic/topic_description.h>
|
||||
#include <Common/ProjectConfig/project_defines.h>
|
||||
|
||||
namespace RoutingService
|
||||
{
|
||||
class TopicDispatcherInterface;
|
||||
}
|
||||
|
||||
namespace DDSGeneric
|
||||
{
|
||||
class TopicTypePlugin
|
||||
{
|
||||
public:
|
||||
virtual ~TopicTypePlugin() = default;
|
||||
|
||||
|
||||
|
||||
|
||||
virtual QMap<std::string, DDSGeneric::TopicDescription> getDataTopics() const = 0;
|
||||
virtual QMap<std::string, DDSGeneric::TopicDescription> getFunctionTopics() const = 0;
|
||||
|
||||
virtual std::vector<RoutingService::TopicDispatcherInterface *> topicsToRouteToWs(IDL::PartitionType partitionType) const = 0;
|
||||
virtual std::vector<RoutingService::TopicDispatcherInterface *> topicsToRouteToSv(IDL::PartitionType partitionType) const = 0;
|
||||
|
||||
virtual QString getServiceShortName() const = 0;
|
||||
};
|
||||
}
|
||||
Q_DECLARE_INTERFACE(DDSGeneric::TopicTypePlugin, "topic_type")
|
||||
Executable
+147
@@ -0,0 +1,147 @@
|
||||
|
||||
|
||||
|
||||
|
||||
#include "wait_set_handler.h"
|
||||
#include "generic_logger.h"
|
||||
#include "generic_base.h"
|
||||
|
||||
namespace DDSGeneric
|
||||
{
|
||||
|
||||
WaitSetHandler::WaitSetHandler()
|
||||
: m_logger(DDS::getLogger("WaitSetHandler", DDS::DataType::FUNCTION_TOPIC_SERVER))
|
||||
{
|
||||
m_waitSet.attach_condition(m_abortCondition);
|
||||
m_task = std::async(std::launch::async, [this]() { this->waitSetTask();});
|
||||
}
|
||||
|
||||
WaitSetHandler::~WaitSetHandler()
|
||||
{
|
||||
m_abortCondition.trigger_value(true);
|
||||
m_task.wait();
|
||||
}
|
||||
|
||||
bool WaitSetHandler::attachWaitSet(const dds::core::cond::Condition &cond, CallbackInterface *cb)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "AttachWaitSet with callback " << cb;
|
||||
|
||||
WaitSetData wsd(cond, cb);
|
||||
|
||||
std::lock_guard<std::mutex> l(m_accessMutex);
|
||||
|
||||
auto it = m_waitSetData.find(wsd.condition->native_condition());
|
||||
if (it != m_waitSetData.end())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Waitset " << cb << " already registered";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_waitSet.attach_condition(wsd.condition);
|
||||
wsd.enabled = true;
|
||||
wsd.needWaitSetAttach = false;
|
||||
|
||||
m_waitSetData.insert(std::make_pair(wsd.condition->native_condition(), wsd));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaitSetHandler::detachWaitSet(const dds::core::cond::Condition &cond, CallbackInterface *cb)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "DetachWaitSet with callback " << cb;
|
||||
|
||||
WaitSetData wsd(cond, cb);
|
||||
|
||||
std::lock_guard<std::mutex> l(m_accessMutex);
|
||||
|
||||
auto it = m_waitSetData.find(wsd.condition->native_condition());
|
||||
if (it == m_waitSetData.end())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Waitset for " << cb << " not registered";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_waitSet.detach_condition(wsd.condition);
|
||||
m_waitSetData.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaitSetHandler::enableWaitSet(const dds::core::cond::Condition &cond, CallbackInterface *cb)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "EnableWaitSet with callback " << cb;
|
||||
|
||||
WaitSetData wsd(cond, cb);
|
||||
|
||||
std::lock_guard<std::mutex> l(m_accessMutex);
|
||||
auto it = m_waitSetData.find(wsd.condition->native_condition());
|
||||
if (it == m_waitSetData.end())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not find WaitSetData - cannot enable waitSet for " << cb;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (it->second.needWaitSetAttach)
|
||||
m_waitSet.attach_condition(wsd.condition);
|
||||
it->second.enabled = true;
|
||||
it->second.needWaitSetAttach = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaitSetHandler::disableWaitSet(const dds::core::cond::Condition &cond, CallbackInterface *cb)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "DisableWaitSet with callback " << cb;
|
||||
|
||||
WaitSetData wsd(cond, cb);
|
||||
|
||||
std::lock_guard<std::mutex> l(m_accessMutex);
|
||||
auto it = m_waitSetData.find(wsd.condition->native_condition());
|
||||
if (it == m_waitSetData.end())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not find WaitSetData - cannot disable waitSet for " << cb;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_waitSet.detach_condition(wsd.condition);
|
||||
it->second.enabled = false;
|
||||
it->second.needWaitSetAttach = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int WaitSetHandler::attachedConditionCount() const
|
||||
{
|
||||
return m_waitSet.conditions().size();
|
||||
}
|
||||
|
||||
|
||||
void WaitSetHandler::waitSetTask()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
dds::core::cond::WaitSet::ConditionSeq activeConditionsSeq = m_waitSet.wait();
|
||||
for (const dds::core::cond::Condition &cond : activeConditionsSeq)
|
||||
{
|
||||
if (cond == m_abortCondition)
|
||||
{
|
||||
m_waitSet->detach_all();
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> l(m_accessMutex);
|
||||
const DDS_Condition *ddsCond = cond->native_condition();
|
||||
auto it = m_waitSetData.lower_bound(ddsCond);
|
||||
while (it != m_waitSetData.end())
|
||||
{
|
||||
WaitSetData &wsd = it->second;
|
||||
if (wsd.enabled)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Condition triggered for " << wsd.callback;
|
||||
wsd.callback->dataAvailable();
|
||||
}
|
||||
++it;
|
||||
if (it == m_waitSetData.end() || it->first != ddsCond)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dds/core/cond/Condition.hpp>
|
||||
#include <dds/core/cond/GuardCondition.hpp>
|
||||
#include <dds/core/cond/WaitSet.hpp>
|
||||
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <DDS/Generic/ddsgen_export.h>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace DDSGeneric
|
||||
{
|
||||
class DDSGEN_EXPORT WaitSetHandler final
|
||||
{
|
||||
public:
|
||||
class CallbackInterface
|
||||
{
|
||||
public:
|
||||
virtual ~CallbackInterface() = default;
|
||||
virtual void dataAvailable() = 0;
|
||||
};
|
||||
|
||||
WaitSetHandler();
|
||||
~WaitSetHandler();
|
||||
|
||||
bool attachWaitSet(const dds::core::cond::Condition &cond, CallbackInterface *cb);
|
||||
bool detachWaitSet(const dds::core::cond::Condition &cond, CallbackInterface *cb);
|
||||
bool enableWaitSet(const dds::core::cond::Condition &cond, CallbackInterface *cb);
|
||||
bool disableWaitSet(const dds::core::cond::Condition &cond, CallbackInterface *cb);
|
||||
|
||||
int attachedConditionCount() const;
|
||||
|
||||
private:
|
||||
struct WaitSetData final
|
||||
{
|
||||
dds::core::cond::Condition condition;
|
||||
CallbackInterface *callback = nullptr;
|
||||
bool enabled = true;
|
||||
bool needWaitSetAttach = false;
|
||||
|
||||
WaitSetData(const dds::core::cond::Condition &cond, CallbackInterface *cb)
|
||||
: condition(cond)
|
||||
, callback(cb)
|
||||
{}
|
||||
};
|
||||
private:
|
||||
void waitSetTask();
|
||||
private:
|
||||
|
||||
dds::core::cond::WaitSet m_waitSet;
|
||||
dds::core::cond::GuardCondition m_abortCondition;
|
||||
std::future<void> m_task;
|
||||
std::mutex m_accessMutex;
|
||||
std::map<const DDS_Condition *, WaitSetData> m_waitSetData;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
}
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
SET(SRCS
|
||||
DDS_DefaultQos.h
|
||||
DDS_DefaultQos.cpp
|
||||
)
|
||||
|
||||
IF(TABLET_MODE)
|
||||
NGL_INSTALL(WS AAR HLA SV FILES "default_profile_tablet.xml" DESTINATION "./share/Lts/DDS" RENAME "default_profile.xml")
|
||||
NGL_INSTALL(WS AAR HLA SV FILES "default_profile.xml" DESTINATION "./share/Lts/DDS" RENAME "default_profile_ws.xml")
|
||||
ELSE()
|
||||
INSTALL_SHARED_FILES(SRCS
|
||||
FILES "default_profile.xml"
|
||||
DESTINATION "DDS"
|
||||
SV WS AAR HLA
|
||||
)
|
||||
ENDIF()
|
||||
NGL_ADD_LIBRARY2(AAR SV HLA WS ddsQoS SHARED ${SRCS})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(ddsQoS
|
||||
INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(ddsQoS
|
||||
PRIVATE
|
||||
RTI::nddsc
|
||||
RTI::nddscpp2
|
||||
NGLLogger
|
||||
ngltools
|
||||
PUBLIC
|
||||
Qt5::Core
|
||||
)
|
||||
Executable
+254
@@ -0,0 +1,254 @@
|
||||
|
||||
|
||||
|
||||
#include "DDS_DefaultQos.h"
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
#include <ngltools/ngltools_filesystem.h>
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace DDS
|
||||
{
|
||||
namespace QosProfile
|
||||
{
|
||||
|
||||
static void printPolicyMap(const std::map<std::string, std::string> &policyMap, NGL::INGLLogger &logger);
|
||||
|
||||
QString defaultProfileXml()
|
||||
{
|
||||
return NGL::findFile(NGL::FileType::ConfigFile, QStringLiteral("default_profile.xml"));
|
||||
}
|
||||
|
||||
bool setDefault(const QStringList &qosFileList)
|
||||
{
|
||||
NGL::INGLLogger &logger = NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("DDS.DefaultQos");
|
||||
const QStringList qosStr = qosFileList.isEmpty() ?
|
||||
QStringList(defaultProfileXml()) :
|
||||
qosFileList;
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
DEBUG_LOG(logger) << "findFile - m_qosFileList = ";
|
||||
for (const QString &file : qosStr)
|
||||
DEBUG_LOG(logger) << "# profile: " << file;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
dds::core::StringSeq urls;
|
||||
urls.reserve(qosStr.size());
|
||||
for (const QString &file : qosStr)
|
||||
{
|
||||
const QString url = QUrl::fromLocalFile(file).toString();
|
||||
DEBUG_LOG(logger) << "Using qosFile: " << file << " - " << url;
|
||||
urls.emplace_back(url.toStdString());
|
||||
}
|
||||
dds::core::QosProvider defaultProvider = dds::core::QosProvider::Default();
|
||||
rti::core::QosProviderParams qosProviderParams = defaultProvider->default_provider_params();
|
||||
qosProviderParams.url_profile(urls);
|
||||
defaultProvider->default_provider_params(qosProviderParams);
|
||||
|
||||
print(defaultProvider, logger);
|
||||
|
||||
}
|
||||
catch (const dds::core::Exception &ex)
|
||||
{
|
||||
ERROR_LOG(logger) << ex.what();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static QString fixupSchemeName(const QUrl &url)
|
||||
{
|
||||
|
||||
QString str = url.toString();
|
||||
str = str.replace(QLatin1String("tcpv4lan"), QLatin1String("tcpv4_lan"));
|
||||
return QStringLiteral("0@") + str;
|
||||
}
|
||||
|
||||
dds::domain::qos::DomainParticipantQos domainParticipantShmUdp()
|
||||
{
|
||||
return domainParticipantDefault(ConnectionType::ShmUdp, QUrl());
|
||||
}
|
||||
|
||||
dds::domain::qos::DomainParticipantQos domainParticipantShmUdpLocalhost()
|
||||
{
|
||||
return domainParticipantDefault(ConnectionType::ShmUdpLocalhost, QUrl());
|
||||
}
|
||||
|
||||
dds::domain::qos::DomainParticipantQos domainParticipantShm()
|
||||
{
|
||||
return domainParticipantDefault(ConnectionType::Shm, QUrl());
|
||||
}
|
||||
|
||||
dds::domain::qos::DomainParticipantQos domainParticipantShmTcp(const QUrl &tcpConnectionUrl)
|
||||
{
|
||||
return domainParticipantDefault(ConnectionType::Tcp, tcpConnectionUrl);
|
||||
}
|
||||
|
||||
dds::domain::qos::DomainParticipantQos domainParticipantDefault(ConnectionType type, const QUrl &tcpConnectionUrl)
|
||||
{
|
||||
dds::core::QosProvider defaultProvider = dds::core::QosProvider::Default();
|
||||
dds::domain::qos::DomainParticipantQos dpQos = defaultProvider.participant_qos();
|
||||
switch (type)
|
||||
{
|
||||
case ConnectionType::Tcp:
|
||||
{
|
||||
dpQos = defaultProvider.participant_qos("LtsProfiles::DomainParticipant.Tcp");
|
||||
if (tcpConnectionUrl.scheme() == QLatin1String("tcpv4lan"))
|
||||
{
|
||||
|
||||
rti::core::policy::Discovery discovery;
|
||||
dpQos >> discovery;
|
||||
const dds::core::StringSeq seq({fixupSchemeName(tcpConnectionUrl).toStdString()});
|
||||
discovery.initial_peers(seq);
|
||||
dpQos << discovery;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
rti::core::policy::Property props;
|
||||
dpQos >> props;
|
||||
props.set({"dds.transport.TCPv4.tcp1.server_bind_port", std::to_string(tcpConnectionUrl.port(7400))});
|
||||
dpQos << props;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ConnectionType::Shm:
|
||||
dpQos = defaultProvider.participant_qos("LtsProfiles::DomainParticipant.Shm");
|
||||
break;
|
||||
case ConnectionType::ShmUdp:
|
||||
dpQos = defaultProvider.participant_qos("LtsProfiles::DomainParticipant.ShmUdp");
|
||||
break;
|
||||
case ConnectionType::ShmUdpLocalhost:
|
||||
dpQos = defaultProvider.participant_qos("LtsProfiles::DomainParticipant.ShmUdpLocalhost");
|
||||
break;
|
||||
case ConnectionType::Server:
|
||||
dpQos = defaultProvider.participant_qos("LtsProfiles::DomainParticipant.Server");
|
||||
break;
|
||||
case ConnectionType::Client:
|
||||
dpQos = defaultProvider.participant_qos("LtsProfiles::DomainParticipant.Client");
|
||||
break;
|
||||
case ConnectionType::Tablet:
|
||||
dpQos = defaultProvider.participant_qos("LtsProfiles::DomainParticipant.Tablet");
|
||||
break;
|
||||
}
|
||||
return dpQos;
|
||||
}
|
||||
|
||||
dds::pub::qos::DataWriterQos dataWriterDefault(TopicType type)
|
||||
{
|
||||
dds::core::QosProvider defaultProvider = dds::core::QosProvider::Default();
|
||||
dds::pub::qos::DataWriterQos dwQos = defaultProvider.datawriter_qos();
|
||||
switch (type)
|
||||
{
|
||||
case TopicType::DataTopic:
|
||||
break;
|
||||
case TopicType::FunctionTopic:
|
||||
dwQos = defaultProvider.datawriter_qos("LtsProfiles::DataReaderWriter.FT");
|
||||
break;
|
||||
}
|
||||
return dwQos;
|
||||
}
|
||||
|
||||
dds::sub::qos::DataReaderQos dataReaderDefault(TopicType type)
|
||||
{
|
||||
dds::core::QosProvider defaultProvider = dds::core::QosProvider::Default();
|
||||
dds::sub::qos::DataReaderQos drQos = defaultProvider.datareader_qos();
|
||||
switch (type)
|
||||
{
|
||||
case TopicType::DataTopic:
|
||||
break;
|
||||
case TopicType::FunctionTopic:
|
||||
drQos = defaultProvider.datareader_qos("LtsProfiles::DataReaderWriter.FT");
|
||||
break;
|
||||
}
|
||||
return drQos;
|
||||
}
|
||||
|
||||
dds::sub::qos::DataReaderQos dataReaderDefault( ConnectionType conType)
|
||||
{
|
||||
dds::core::QosProvider defaultProvider = dds::core::QosProvider::Default();
|
||||
dds::sub::qos::DataReaderQos drQos = defaultProvider.datareader_qos();
|
||||
switch (conType)
|
||||
{
|
||||
case ConnectionType::Shm:
|
||||
drQos = defaultProvider.datareader_qos("LtsProfiles::DomainParticipant.Shm");
|
||||
break;
|
||||
case ConnectionType::ShmUdp:
|
||||
drQos = defaultProvider.datareader_qos("LtsProfiles::DomainParticipant.ShmUdp");
|
||||
break;
|
||||
case ConnectionType::ShmUdpLocalhost:
|
||||
drQos = defaultProvider.datareader_qos("LtsProfiles::DomainParticipant.ShmUdpLocalhost");
|
||||
break;
|
||||
case ConnectionType::Server:
|
||||
drQos = defaultProvider.datareader_qos("LtsProfiles::DomainParticipant.Server");
|
||||
break;
|
||||
case ConnectionType::Client:
|
||||
drQos = defaultProvider.datareader_qos("LtsProfiles::DomainParticipant.Client");
|
||||
break;
|
||||
case ConnectionType::Tablet:
|
||||
drQos = defaultProvider.datareader_qos("LtsProfiles::DomainParticipant.Tablet");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return drQos;
|
||||
}
|
||||
|
||||
|
||||
void print(dds::core::QosProvider provider, NGL::INGLLogger &logger)
|
||||
{
|
||||
|
||||
|
||||
|
||||
print(provider.participant_qos(), logger);
|
||||
|
||||
|
||||
print(provider.datawriter_qos(), logger);
|
||||
|
||||
|
||||
print(provider.datareader_qos(), logger);
|
||||
}
|
||||
|
||||
void print(const dds::domain::qos::DomainParticipantQos &qos, NGL::INGLLogger &logger)
|
||||
{
|
||||
|
||||
DEBUG_LOG(logger) << "Qos for DomainParticipant: ";
|
||||
printPolicyMap(qos->property.get_all(), logger);
|
||||
}
|
||||
|
||||
void print(const dds::sub::qos::DataReaderQos &readerQos, NGL::INGLLogger &logger)
|
||||
{
|
||||
|
||||
DEBUG_LOG(logger) << "Qos for dataReader: ";
|
||||
DEBUG_LOG(logger) << "resourceLimit.max_samples " << readerQos->resource_limits.max_samples();
|
||||
printPolicyMap(readerQos->property.get_all(), logger);
|
||||
}
|
||||
|
||||
void print(const dds::pub::qos::DataWriterQos &writerQos, NGL::INGLLogger &logger)
|
||||
{
|
||||
|
||||
DEBUG_LOG(logger) << "Default Qos for dataWriter: ";
|
||||
DEBUG_LOG(logger) << "ReliabilityKind: " << writerQos->reliability->kind();
|
||||
DEBUG_LOG(logger) << "Publish mode: " << writerQos->publish_mode.kind();
|
||||
printPolicyMap(writerQos->property.get_all(), logger);
|
||||
}
|
||||
|
||||
void printPolicyMap(const std::map<std::string, std::string> &policyMap, NGL::INGLLogger &logger)
|
||||
{
|
||||
for (const auto &it : policyMap)
|
||||
{
|
||||
const std::string &policyName = it.first;
|
||||
const std::string &policyContent = it.second;
|
||||
|
||||
DEBUG_LOG(logger) << "policy " << policyName << ": " << policyContent ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <dds/core/QosProvider.hpp>
|
||||
#include <QosProfile/ddsqos_export.h>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
namespace DDS
|
||||
{
|
||||
namespace QosProfile
|
||||
{
|
||||
enum class ConnectionType
|
||||
{
|
||||
ShmUdp,
|
||||
ShmUdpLocalhost,
|
||||
Shm,
|
||||
Tcp,
|
||||
Server,
|
||||
Client,
|
||||
Tablet
|
||||
};
|
||||
enum class TopicType
|
||||
{
|
||||
DataTopic,
|
||||
FunctionTopic
|
||||
};
|
||||
|
||||
DDSQOS_EXPORT QString defaultProfileXml();
|
||||
DDSQOS_EXPORT bool setDefault(const QStringList &qosFileList = QStringList());
|
||||
DDSQOS_EXPORT dds::domain::qos::DomainParticipantQos domainParticipantDefault(ConnectionType type, const QUrl &tcpConnectionUrl);
|
||||
DDSQOS_EXPORT dds::pub::qos::DataWriterQos dataWriterDefault(TopicType type);
|
||||
DDSQOS_EXPORT dds::sub::qos::DataReaderQos dataReaderDefault(TopicType type);
|
||||
DDSQOS_EXPORT dds::sub::qos::DataReaderQos dataReaderDefault(ConnectionType conType);
|
||||
|
||||
|
||||
|
||||
DDSQOS_EXPORT dds::domain::qos::DomainParticipantQos domainParticipantShmUdp();
|
||||
DDSQOS_EXPORT dds::domain::qos::DomainParticipantQos domainParticipantShmUdpLocalhost();
|
||||
DDSQOS_EXPORT dds::domain::qos::DomainParticipantQos domainParticipantShm();
|
||||
DDSQOS_EXPORT dds::domain::qos::DomainParticipantQos domainParticipantShmTcp(const QUrl &tcpConnectionUrl);
|
||||
|
||||
DDSQOS_EXPORT void print(dds::core::QosProvider provider, NGL::INGLLogger &logger);
|
||||
DDSQOS_EXPORT void print(const dds::domain::qos::DomainParticipantQos &qos, NGL::INGLLogger &logger);
|
||||
DDSQOS_EXPORT void print(const dds::sub::qos::DataReaderQos &readerQos, NGL::INGLLogger &logger);
|
||||
DDSQOS_EXPORT void print(const dds::pub::qos::DataWriterQos &writerQos, NGL::INGLLogger &logger);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Executable
+571
@@ -0,0 +1,571 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Description
|
||||
XML QoS Profile for a project with DDS
|
||||
|
||||
For more information about XML QoS Profiles see Chapter 15 in the
|
||||
RTI Connext user manual.
|
||||
-->
|
||||
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="/opt/RTI/resource/qos_profiles_5.1.0/schema/rti_dds_qos_profiles.xsd">
|
||||
<!-- QoS Library containing the default QoS profile for the project
|
||||
|
||||
A QoS library is a named set of QoS profiles.
|
||||
-->
|
||||
<qos_library name="LtsProfiles">
|
||||
<!-- QoS profile used to configure reliable communication between the DataWriter and DataReader
|
||||
|
||||
A QoS profile groups a set of related QoS.
|
||||
-->
|
||||
|
||||
<qos_profile name="Base" base_name="BuiltinQosLibExp::Generic.StrictReliable" is_default_qos="true">
|
||||
<!-- Participent QoS -->
|
||||
<participant_qos>
|
||||
<property>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.shmem.builtin.receive_buffer_size</name>
|
||||
<value>20971520</value> <!-- 20MB -->
|
||||
</element>
|
||||
</value>
|
||||
<!--FRAN: allow IP fragmentation (30jun2020)-->
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.UDPv4.builtin.parent.message_size_max</name>
|
||||
<value>1450</value>
|
||||
</element>
|
||||
</value>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.UDPv4.builtin.multicast_ttl</name>
|
||||
<value>5</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.period.sec</name>
|
||||
<value>0</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.period.nanosec</name>
|
||||
<value>100000000</value>
|
||||
</element>
|
||||
<!-- UDP MTU -->
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.bytes_per_token</name>
|
||||
<value>3072</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.tokens_leaked_per_period</name>
|
||||
<value>0</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.tokens_added_per_period</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.max_tokens</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
<resource_limits>
|
||||
<!-- type_code is not needed for RTI 5.x and above-->
|
||||
<type_code_max_serialized_length>0</type_code_max_serialized_length>
|
||||
<!-- type_object is only needed if someone wants to dynamically decode the structures
|
||||
without haveing this info otherwise (e.g. programatically) which is not the case
|
||||
in our system-->
|
||||
<type_object_max_serialized_length>0</type_object_max_serialized_length>
|
||||
</resource_limits>
|
||||
<discovery_config>
|
||||
<!--FRAN: disable sending DATA(p) packets to domain 0, RTPX (30jun2020)-->
|
||||
<default_domain_announcement_period>
|
||||
<sec>DURATION_INFINITE_SEC</sec>
|
||||
<nanosec>DURATION_INFINITE_NSEC</nanosec>
|
||||
</default_domain_announcement_period>
|
||||
<publication_writer_publish_mode>
|
||||
<kind>ASYNCHRONOUS_PUBLISH_MODE_QOS</kind>
|
||||
<!-- <flow_controller_name>dds.flow_controller.token_bucket.my_2_5Mbps_fc</flow_controller_name> -->
|
||||
</publication_writer_publish_mode>
|
||||
<subscription_writer_publish_mode>
|
||||
<kind>ASYNCHRONOUS_PUBLISH_MODE_QOS</kind>
|
||||
<!-- <flow_controller_name>dds.flow_controller.token_bucket.my_2_5Mbps_fc</flow_controller_name> -->
|
||||
</subscription_writer_publish_mode>
|
||||
<participant_liveliness_assert_period>
|
||||
<sec>5</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</participant_liveliness_assert_period>
|
||||
<participant_liveliness_lease_duration>
|
||||
<sec>20</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</participant_liveliness_lease_duration>
|
||||
<max_liveliness_loss_detection_period>
|
||||
<sec>10</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</max_liveliness_loss_detection_period>
|
||||
<publication_writer>
|
||||
<enable_multicast_periodic_heartbeat>1</enable_multicast_periodic_heartbeat>
|
||||
<!--FRAN: why these values?-->
|
||||
<min_send_window_size>20</min_send_window_size>
|
||||
<max_send_window_size>400</max_send_window_size>
|
||||
<heartbeats_per_max_samples>50</heartbeats_per_max_samples>
|
||||
<!--FRAN: set to 500ms instead of 50ms (30jun2020)-->
|
||||
<fast_heartbeat_period>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</fast_heartbeat_period>
|
||||
<late_joiner_heartbeat_period>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</late_joiner_heartbeat_period>
|
||||
<max_nack_response_delay>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</max_nack_response_delay>
|
||||
</publication_writer>
|
||||
<subscription_writer>
|
||||
<enable_multicast_periodic_heartbeat>1</enable_multicast_periodic_heartbeat>
|
||||
<!--FRAN: set to 500ms instead of 50ms (30jun2020)-->
|
||||
<min_send_window_size>20</min_send_window_size>
|
||||
<max_send_window_size>400</max_send_window_size>
|
||||
<heartbeats_per_max_samples>50</heartbeats_per_max_samples>
|
||||
<fast_heartbeat_period>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</fast_heartbeat_period>
|
||||
<late_joiner_heartbeat_period>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</late_joiner_heartbeat_period>
|
||||
<max_nack_response_delay>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</max_nack_response_delay>
|
||||
</subscription_writer>
|
||||
</discovery_config>
|
||||
</participant_qos>
|
||||
<!-- Publisher QOS -->
|
||||
|
||||
|
||||
<!--Patric: we enabled coherent sets -->
|
||||
<!-- QoS used to configure the data writer -->
|
||||
<!--
|
||||
<publisher_qos>
|
||||
<presentation>
|
||||
<access_scope>TOPIC_PRESENTATION_QOS</access_scope>
|
||||
<coherent_access>true</coherent_access>
|
||||
</presentation>
|
||||
</publisher_qos>
|
||||
<subscriber_qos>
|
||||
<presentation>
|
||||
<access_scope>TOPIC_PRESENTATION_QOS</access_scope>
|
||||
<coherent_access>true</coherent_access>
|
||||
</presentation>
|
||||
</subscriber_qos>
|
||||
-->
|
||||
|
||||
|
||||
<!-- QoS used to configure the data writer -->
|
||||
<datawriter_qos>
|
||||
<publish_mode>
|
||||
<kind>ASYNCHRONOUS_PUBLISH_MODE_QOS</kind>
|
||||
</publish_mode>
|
||||
<property>
|
||||
<value>
|
||||
<!-- The next setting is used to force dynamic memory
|
||||
allocation for samples with a serialized size of
|
||||
larger than 2K. Without setting
|
||||
pool_buffer_max_size, all memory would be obtained
|
||||
from a pre-allocated pool, which would greatly
|
||||
increase the memory usage in an application which
|
||||
sends large data.
|
||||
-->
|
||||
<element>
|
||||
<name>dds.data_writer.history.memory_manager.fast_pool.pool_buffer_max_size</name>
|
||||
<value>2048</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
<reliability>
|
||||
<kind>RELIABLE_RELIABILITY_QOS</kind>
|
||||
<!--FRAN: why 60 seconds?-->
|
||||
<max_blocking_time>
|
||||
<sec>60</sec>
|
||||
</max_blocking_time>
|
||||
</reliability>
|
||||
<durability>
|
||||
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
|
||||
</durability>
|
||||
<history>
|
||||
<kind>KEEP_LAST_HISTORY_QOS</kind>
|
||||
<depth>1</depth>
|
||||
</history>
|
||||
<resource_limits>
|
||||
<!--<max_samples_per_instance>1</max_samples_per_instance>-->
|
||||
<max_samples>8000</max_samples>
|
||||
</resource_limits>
|
||||
<protocol>
|
||||
<rtps_reliable_writer>
|
||||
<!--FRAN: why these values?-->
|
||||
<min_send_window_size>20</min_send_window_size>
|
||||
<max_send_window_size>400</max_send_window_size>
|
||||
<heartbeats_per_max_samples>50</heartbeats_per_max_samples>
|
||||
</rtps_reliable_writer>
|
||||
</protocol>
|
||||
<writer_resource_limits>
|
||||
<autoregister_instances>true</autoregister_instances>
|
||||
</writer_resource_limits>
|
||||
</datawriter_qos>
|
||||
|
||||
|
||||
|
||||
<datareader_qos>
|
||||
<reliability>
|
||||
<kind>RELIABLE_RELIABILITY_QOS</kind>
|
||||
</reliability>
|
||||
<reader_data_lifecycle>
|
||||
<autopurge_nowriter_samples_delay>
|
||||
<sec>5</sec>
|
||||
<nanosec>DURATION_ZERO_NSEC</nanosec>
|
||||
</autopurge_nowriter_samples_delay>
|
||||
<autopurge_disposed_samples_delay>
|
||||
<sec>5</sec>
|
||||
<nanosec>DURATION_ZERO_NSEC</nanosec>
|
||||
</autopurge_disposed_samples_delay>
|
||||
</reader_data_lifecycle>
|
||||
<durability>
|
||||
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
|
||||
<!-- Set this field to 0 if you want brokered communication (all communication
|
||||
passes through the persistence service before being received by the datareader;
|
||||
i.e. datawriter and datareader cannot 'see' each other directly)
|
||||
-->
|
||||
<direct_communication>true</direct_communication>
|
||||
</durability>
|
||||
<history>
|
||||
<kind>KEEP_LAST_HISTORY_QOS</kind>
|
||||
<depth>1</depth>
|
||||
</history>
|
||||
<resource_limits>
|
||||
<!--<initial_samples>200</initial_samples> -->
|
||||
<!--<max_samples_per_instance>1</max_samples_per_instance> -->
|
||||
<!-- We limit max_samples on the reader so that if the
|
||||
writer is publishing samples faster than the reader is
|
||||
taking them then the reader will start sending NACKs
|
||||
for samples received after the number of samples in
|
||||
its queue reaches max_samples. This behavior will put
|
||||
pressure on the writer to throttle back its publishing
|
||||
speed.
|
||||
-->
|
||||
<max_samples>8000</max_samples>
|
||||
</resource_limits>
|
||||
<reader_resource_limits>
|
||||
<!-- Determines whether the DataReader pre-allocates
|
||||
storage for storing fragmented samples. This setting
|
||||
can be used to limit upfront memory allocation costs
|
||||
in applications which are dealing with large data
|
||||
-->
|
||||
<dynamically_allocate_fragmented_samples>true</dynamically_allocate_fragmented_samples>
|
||||
<max_samples_per_read>8000</max_samples_per_read>
|
||||
</reader_resource_limits>
|
||||
<property>
|
||||
<value>
|
||||
<!-- The next setting is used to force dynamic memory
|
||||
allocation for samples with a serialized size of
|
||||
larger than 2K. Without setting
|
||||
pool_buffer_max_size, all memory would be obtained
|
||||
from a pre-allocated pool, which would greatly
|
||||
increase the memory usage in an application which
|
||||
sends large data.
|
||||
-->
|
||||
<element>
|
||||
<name>dds.data_reader.history.memory_manager.fast_pool.pool_buffer_max_size</name>
|
||||
<value>2048</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
</datareader_qos>
|
||||
</qos_profile>
|
||||
|
||||
<!--FRAN: who uses this profile?-->
|
||||
<!--Function topic. tablets->server-->
|
||||
<qos_profile name="DataReaderWriter.FT" base_name="LtsProfiles::Base">
|
||||
<datareader_qos>
|
||||
<durability>
|
||||
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
|
||||
</durability>
|
||||
<history>
|
||||
<kind>KEEP_ALL_HISTORY_QOS</kind>
|
||||
</history>
|
||||
</datareader_qos>
|
||||
<datawriter_qos>
|
||||
<publish_mode>
|
||||
<kind>ASYNCHRONOUS_PUBLISH_MODE_QOS</kind>
|
||||
</publish_mode>
|
||||
<durability>
|
||||
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
|
||||
</durability>
|
||||
<history>
|
||||
<kind>KEEP_LAST_HISTORY_QOS</kind>
|
||||
<depth>1</depth>
|
||||
</history>
|
||||
</datawriter_qos>
|
||||
</qos_profile>
|
||||
<!--Mostly used by the tablets as a special mode-->
|
||||
<qos_profile name="DomainParticipant.Shm" base_name="LtsProfiles::Base">
|
||||
<participant_qos>
|
||||
<transport_builtin>
|
||||
<mask>SHMEM</mask>
|
||||
</transport_builtin>
|
||||
<discovery>
|
||||
<multicast_receive_addresses/>
|
||||
<accept_unknown_peers>false</accept_unknown_peers>
|
||||
<initial_peers>
|
||||
<element>30@builtin.shmem://</element>
|
||||
</initial_peers>
|
||||
</discovery>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
|
||||
<!--Used by both tablets and server-->
|
||||
<qos_profile name="DomainParticipant.ShmUdp" base_name="LtsProfiles::Base">
|
||||
<participant_qos>
|
||||
<transport_builtin>
|
||||
<mask>UDPv4 | SHMEM</mask>
|
||||
</transport_builtin>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
|
||||
<!-- Both server and clients. Maybe not used-->
|
||||
<qos_profile name="DomainParticipant.ShmUdpLocalhost" base_name="DomainParticipant.ShmUdp">
|
||||
<participant_qos>
|
||||
<discovery>
|
||||
<multicast_receive_addresses/>
|
||||
<accept_unknown_peers>false</accept_unknown_peers>
|
||||
<initial_peers>
|
||||
<element>builtin.shmem://</element>
|
||||
<element>builtin.udpv4://127.0.0.1</element>
|
||||
</initial_peers>
|
||||
</discovery>
|
||||
<property>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.UDPv4.builtin.multicast_enabled</name>
|
||||
<value>0</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.UDPv4.builtin.parent.allow_interfaces</name>
|
||||
<value>127.0.0.1</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
<!-- configured for tcp client -> listenport = -1 -->
|
||||
<qos_profile name="DomainParticipant.Tcp" base_name="LtsProfiles::Base">
|
||||
<participant_qos>
|
||||
<transport_builtin>
|
||||
<mask>MASK_NONE</mask>
|
||||
</transport_builtin>
|
||||
<property>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.load_plugins</name>
|
||||
<value>dds.transport.TCPv4.tcp1</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.library</name>
|
||||
<value>nddstransporttcp</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.create_function</name>
|
||||
<value>NDDS_Transport_TCPv4_create</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.parent.classid</name>
|
||||
<value>NDDS_TRANSPORT_CLASSID_TCPV4_LAN</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.server_bind_port</name>
|
||||
<value>-1</value>
|
||||
</element>
|
||||
<!--
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.logging_verbosity_bitmap</name>
|
||||
<value>159</value>
|
||||
</element>
|
||||
-->
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.disable_nagle</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
<!--
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.send_max_wait_sec</name>
|
||||
<value>-1</value>
|
||||
</element>
|
||||
-->
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.max_packet_size</name>
|
||||
<value>63020</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.parent.message_size_max</name>
|
||||
<value>63020</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.recv_socket_buffer_size</name>
|
||||
<value>8388608</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.send_socket_buffer_size</name>
|
||||
<value>8388608</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.connection_liveliness.enable</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.connection_liveliness.lease_duration</name>
|
||||
<value>5</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.connection_liveliness.assertions_per_lease_duration</name>
|
||||
<value>2</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.outstanding_connection_cookies</name>
|
||||
<value>100</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.outstanding_connection_cookies_life_span</name>
|
||||
<value>5</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.force_asynchronous_send</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
<discovery_config>
|
||||
<participant_liveliness_assert_period>
|
||||
<sec>2</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</participant_liveliness_assert_period>
|
||||
<participant_liveliness_lease_duration>
|
||||
<sec>5</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</participant_liveliness_lease_duration>
|
||||
<max_liveliness_loss_detection_period>
|
||||
<sec>2</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</max_liveliness_loss_detection_period>
|
||||
</discovery_config>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
|
||||
|
||||
<!--FRAN: 3 new profiles for each kind of host (30jun2020)-->
|
||||
<!--This implements asymmetric discovery-->
|
||||
<qos_profile name="DomainParticipant.Client" base_name="DomainParticipant.ShmUdp">
|
||||
<participant_qos>
|
||||
<discovery>
|
||||
<initial_peers>
|
||||
<element>239.255.0.2</element>
|
||||
</initial_peers>
|
||||
<multicast_receive_addresses>
|
||||
<element>239.255.0.3</element>
|
||||
</multicast_receive_addresses>
|
||||
</discovery>
|
||||
</participant_qos>
|
||||
<!-- QoS used to configure the data reader -->
|
||||
<!--Patric: reader want all our LTS Topics via multicast now -->
|
||||
<datareader_qos>
|
||||
<multicast>
|
||||
<value>
|
||||
<element>
|
||||
<receive_address>239.255.1.7</receive_address>
|
||||
</element>
|
||||
</value>
|
||||
</multicast>
|
||||
<reliability>
|
||||
<kind>RELIABLE_RELIABILITY_QOS</kind>
|
||||
</reliability>
|
||||
<reader_data_lifecycle>
|
||||
<autopurge_nowriter_samples_delay>
|
||||
<sec>5</sec>
|
||||
<nanosec>DURATION_ZERO_NSEC</nanosec>
|
||||
</autopurge_nowriter_samples_delay>
|
||||
<autopurge_disposed_samples_delay>
|
||||
<sec>5</sec>
|
||||
<nanosec>DURATION_ZERO_NSEC</nanosec>
|
||||
</autopurge_disposed_samples_delay>
|
||||
</reader_data_lifecycle>
|
||||
<durability>
|
||||
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
|
||||
<!-- Set this field to 0 if you want brokered communication (all communication
|
||||
passes through the persistence service before being received by the datareader;
|
||||
i.e. datawriter and datareader cannot 'see' each other directly)
|
||||
-->
|
||||
<direct_communication>true</direct_communication>
|
||||
</durability>
|
||||
<history>
|
||||
<kind>KEEP_LAST_HISTORY_QOS</kind>
|
||||
<depth>1</depth>
|
||||
</history>
|
||||
<resource_limits>
|
||||
<!--<initial_samples>200</initial_samples> -->
|
||||
<!--<max_samples_per_instance>1</max_samples_per_instance> -->
|
||||
<!-- We limit max_samples on the reader so that if the
|
||||
writer is publishing samples faster than the reader is
|
||||
taking them then the reader will start sending NACKs
|
||||
for samples received after the number of samples in
|
||||
its queue reaches max_samples. This behavior will put
|
||||
pressure on the writer to throttle back its publishing
|
||||
speed.
|
||||
-->
|
||||
<max_samples>8000</max_samples>
|
||||
</resource_limits>
|
||||
<reader_resource_limits>
|
||||
<!-- Determines whether the DataReader pre-allocates
|
||||
storage for storing fragmented samples. This setting
|
||||
can be used to limit upfront memory allocation costs
|
||||
in applications which are dealing with large data
|
||||
-->
|
||||
<dynamically_allocate_fragmented_samples>true</dynamically_allocate_fragmented_samples>
|
||||
<max_samples_per_read>8000</max_samples_per_read>
|
||||
</reader_resource_limits>
|
||||
</datareader_qos>
|
||||
|
||||
|
||||
|
||||
</qos_profile>
|
||||
|
||||
<!--FRAN: 3 new profiles for each kind of host (30jun2020)-->
|
||||
<!--This implements asymmetric discovery-->
|
||||
<qos_profile name="DomainParticipant.Server" base_name="DomainParticipant.ShmUdp">
|
||||
<participant_qos>
|
||||
<discovery>
|
||||
<initial_peers>
|
||||
<element>239.255.0.1</element>
|
||||
<element>239.255.0.3</element>
|
||||
<element>127.0.0.1</element>
|
||||
</initial_peers>
|
||||
<multicast_receive_addresses>
|
||||
<element>239.255.0.2</element>
|
||||
</multicast_receive_addresses>
|
||||
</discovery>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
<!--FRAN: 3 new profiles for each kind of host (30jun2020)-->
|
||||
<!--This implements asymmetric discovery-->
|
||||
<qos_profile name="DomainParticipant.Tablet" base_name="DomainParticipant.ShmUdp">
|
||||
<participant_qos>
|
||||
<discovery>
|
||||
<initial_peers>
|
||||
<element>239.255.0.2</element>
|
||||
</initial_peers>
|
||||
<multicast_receive_addresses>
|
||||
<element>239.255.0.1</element>
|
||||
</multicast_receive_addresses>
|
||||
</discovery>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
</qos_library>
|
||||
</dds>
|
||||
+544
@@ -0,0 +1,544 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Description
|
||||
XML QoS Profile for a project with DDS
|
||||
|
||||
For more information about XML QoS Profiles see Chapter 15 in the
|
||||
RTI Connext user manual.
|
||||
-->
|
||||
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="/opt/RTI/resource/qos_profiles_5.1.0/schema/rti_dds_qos_profiles.xsd">
|
||||
<!-- QoS Library containing the default QoS profile for the project
|
||||
|
||||
A QoS library is a named set of QoS profiles.
|
||||
-->
|
||||
<qos_library name="LtsProfiles">
|
||||
<!-- QoS profile used to configure reliable communication between the DataWriter and DataReader
|
||||
|
||||
A QoS profile groups a set of related QoS.
|
||||
-->
|
||||
|
||||
<qos_profile name="Base" base_name="BuiltinQosLibExp::Generic.StrictReliable" is_default_qos="true">
|
||||
<!-- Participent QoS -->
|
||||
<participant_qos>
|
||||
<property>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.shmem.builtin.receive_buffer_size</name>
|
||||
<value>20971520</value> <!-- 20MB -->
|
||||
</element>
|
||||
</value>
|
||||
<!--FRAN: allow IP fragmentation (30jun2020)-->
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.UDPv4.builtin.parent.message_size_max</name>
|
||||
<value>1450</value>
|
||||
</element>
|
||||
</value>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.UDPv4.builtin.multicast_ttl</name>
|
||||
<value>5</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.period.sec</name>
|
||||
<value>0</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.period.nanosec</name>
|
||||
<value>100000000</value>
|
||||
</element>
|
||||
<!-- UDP MTU -->
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.bytes_per_token</name>
|
||||
<value>3072</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.tokens_leaked_per_period</name>
|
||||
<value>0</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.tokens_added_per_period</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.flow_controller.token_bucket.my_2_5Mbps_fc.token_bucket.max_tokens</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
<resource_limits>
|
||||
<!-- type_code is not needed for RTI 5.x and above-->
|
||||
<type_code_max_serialized_length>0</type_code_max_serialized_length>
|
||||
<!-- type_object is only needed if someone wants to dynamically decode the structures
|
||||
without haveing this info otherwise (e.g. programatically) which is not the case
|
||||
in our system-->
|
||||
<type_object_max_serialized_length>0</type_object_max_serialized_length>
|
||||
</resource_limits>
|
||||
<discovery_config>
|
||||
<!--FRAN: disable sending DATA(p) packets to domain 0, RTPX (30jun2020)-->
|
||||
<default_domain_announcement_period>
|
||||
<sec>DURATION_INFINITE_SEC</sec>
|
||||
<nanosec>DURATION_INFINITE_NSEC</nanosec>
|
||||
</default_domain_announcement_period>
|
||||
<publication_writer_publish_mode>
|
||||
<kind>ASYNCHRONOUS_PUBLISH_MODE_QOS</kind>
|
||||
<!-- <flow_controller_name>dds.flow_controller.token_bucket.my_2_5Mbps_fc</flow_controller_name> -->
|
||||
</publication_writer_publish_mode>
|
||||
<subscription_writer_publish_mode>
|
||||
<kind>ASYNCHRONOUS_PUBLISH_MODE_QOS</kind>
|
||||
<!-- <flow_controller_name>dds.flow_controller.token_bucket.my_2_5Mbps_fc</flow_controller_name> -->
|
||||
</subscription_writer_publish_mode>
|
||||
<participant_liveliness_assert_period>
|
||||
<sec>5</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</participant_liveliness_assert_period>
|
||||
<participant_liveliness_lease_duration>
|
||||
<sec>20</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</participant_liveliness_lease_duration>
|
||||
<max_liveliness_loss_detection_period>
|
||||
<sec>10</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</max_liveliness_loss_detection_period>
|
||||
<publication_writer>
|
||||
<enable_multicast_periodic_heartbeat>1</enable_multicast_periodic_heartbeat>
|
||||
<!--FRAN: why these values?-->
|
||||
<min_send_window_size>20</min_send_window_size>
|
||||
<max_send_window_size>400</max_send_window_size>
|
||||
<heartbeats_per_max_samples>50</heartbeats_per_max_samples>
|
||||
<!--FRAN: set to 500ms instead of 50ms (30jun2020)-->
|
||||
<fast_heartbeat_period>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</fast_heartbeat_period>
|
||||
<late_joiner_heartbeat_period>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</late_joiner_heartbeat_period>
|
||||
<max_nack_response_delay>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</max_nack_response_delay>
|
||||
</publication_writer>
|
||||
<subscription_writer>
|
||||
<enable_multicast_periodic_heartbeat>1</enable_multicast_periodic_heartbeat>
|
||||
<!--FRAN: set to 500ms instead of 50ms (30jun2020)-->
|
||||
<min_send_window_size>20</min_send_window_size>
|
||||
<max_send_window_size>400</max_send_window_size>
|
||||
<heartbeats_per_max_samples>50</heartbeats_per_max_samples>
|
||||
<fast_heartbeat_period>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</fast_heartbeat_period>
|
||||
<late_joiner_heartbeat_period>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</late_joiner_heartbeat_period>
|
||||
<max_nack_response_delay>
|
||||
<sec>0</sec>
|
||||
<nanosec>500000000</nanosec>
|
||||
</max_nack_response_delay>
|
||||
</subscription_writer>
|
||||
</discovery_config>
|
||||
</participant_qos>
|
||||
<!-- Publisher QOS -->
|
||||
|
||||
|
||||
<!--Patric: we enabled coherent sets -->
|
||||
<!-- QoS used to configure the data writer -->
|
||||
<!--
|
||||
<publisher_qos>
|
||||
<presentation>
|
||||
<access_scope>TOPIC_PRESENTATION_QOS</access_scope>
|
||||
<coherent_access>true</coherent_access>
|
||||
</presentation>
|
||||
</publisher_qos>
|
||||
<subscriber_qos>
|
||||
<presentation>
|
||||
<access_scope>TOPIC_PRESENTATION_QOS</access_scope>
|
||||
<coherent_access>true</coherent_access>
|
||||
</presentation>
|
||||
</subscriber_qos>
|
||||
-->
|
||||
|
||||
|
||||
<!-- QoS used to configure the data writer -->
|
||||
<datawriter_qos>
|
||||
<publish_mode>
|
||||
<kind>ASYNCHRONOUS_PUBLISH_MODE_QOS</kind>
|
||||
</publish_mode>
|
||||
<property>
|
||||
<value>
|
||||
<!-- The next setting is used to force dynamic memory
|
||||
allocation for samples with a serialized size of
|
||||
larger than 2K. Without setting
|
||||
pool_buffer_max_size, all memory would be obtained
|
||||
from a pre-allocated pool, which would greatly
|
||||
increase the memory usage in an application which
|
||||
sends large data.
|
||||
-->
|
||||
<element>
|
||||
<name>dds.data_writer.history.memory_manager.fast_pool.pool_buffer_max_size</name>
|
||||
<value>2048</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
<reliability>
|
||||
<kind>RELIABLE_RELIABILITY_QOS</kind>
|
||||
<!--FRAN: why 60 seconds?-->
|
||||
<max_blocking_time>
|
||||
<sec>60</sec>
|
||||
</max_blocking_time>
|
||||
</reliability>
|
||||
<durability>
|
||||
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
|
||||
</durability>
|
||||
<history>
|
||||
<kind>KEEP_LAST_HISTORY_QOS</kind>
|
||||
<depth>1</depth>
|
||||
</history>
|
||||
<resource_limits>
|
||||
<!--<max_samples_per_instance>1</max_samples_per_instance>-->
|
||||
<max_samples>8000</max_samples>
|
||||
</resource_limits>
|
||||
<protocol>
|
||||
<rtps_reliable_writer>
|
||||
<!--FRAN: why these values?-->
|
||||
<min_send_window_size>20</min_send_window_size>
|
||||
<max_send_window_size>400</max_send_window_size>
|
||||
<heartbeats_per_max_samples>50</heartbeats_per_max_samples>
|
||||
</rtps_reliable_writer>
|
||||
</protocol>
|
||||
<writer_resource_limits>
|
||||
<autoregister_instances>true</autoregister_instances>
|
||||
</writer_resource_limits>
|
||||
</datawriter_qos>
|
||||
|
||||
<!-- QoS used to configure the data reader -->
|
||||
<!--Patric: reader want all our LTS Topics via multicast now -->
|
||||
|
||||
<datareader_qos>
|
||||
<reliability>
|
||||
<kind>RELIABLE_RELIABILITY_QOS</kind>
|
||||
</reliability>
|
||||
<reader_data_lifecycle>
|
||||
<autopurge_nowriter_samples_delay>
|
||||
<sec>5</sec>
|
||||
<nanosec>DURATION_ZERO_NSEC</nanosec>
|
||||
</autopurge_nowriter_samples_delay>
|
||||
<autopurge_disposed_samples_delay>
|
||||
<sec>5</sec>
|
||||
<nanosec>DURATION_ZERO_NSEC</nanosec>
|
||||
</autopurge_disposed_samples_delay>
|
||||
</reader_data_lifecycle>
|
||||
<durability>
|
||||
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
|
||||
<!-- Set this field to 0 if you want brokered communication (all communication
|
||||
passes through the persistence service before being received by the datareader;
|
||||
i.e. datawriter and datareader cannot 'see' each other directly)
|
||||
-->
|
||||
<direct_communication>true</direct_communication>
|
||||
</durability>
|
||||
<history>
|
||||
<kind>KEEP_LAST_HISTORY_QOS</kind>
|
||||
<depth>1</depth>
|
||||
</history>
|
||||
<resource_limits>
|
||||
<!--<initial_samples>200</initial_samples> -->
|
||||
<!--<max_samples_per_instance>1</max_samples_per_instance> -->
|
||||
<!-- We limit max_samples on the reader so that if the
|
||||
writer is publishing samples faster than the reader is
|
||||
taking them then the reader will start sending NACKs
|
||||
for samples received after the number of samples in
|
||||
its queue reaches max_samples. This behavior will put
|
||||
pressure on the writer to throttle back its publishing
|
||||
speed.
|
||||
-->
|
||||
<max_samples>8000</max_samples>
|
||||
</resource_limits>
|
||||
<reader_resource_limits>
|
||||
<!-- Determines whether the DataReader pre-allocates
|
||||
storage for storing fragmented samples. This setting
|
||||
can be used to limit upfront memory allocation costs
|
||||
in applications which are dealing with large data
|
||||
-->
|
||||
<dynamically_allocate_fragmented_samples>true</dynamically_allocate_fragmented_samples>
|
||||
<max_samples_per_read>8000</max_samples_per_read>
|
||||
</reader_resource_limits>
|
||||
<property>
|
||||
<value>
|
||||
<!-- The next setting is used to force dynamic memory
|
||||
allocation for samples with a serialized size of
|
||||
larger than 2K. Without setting
|
||||
pool_buffer_max_size, all memory would be obtained
|
||||
from a pre-allocated pool, which would greatly
|
||||
increase the memory usage in an application which
|
||||
sends large data.
|
||||
-->
|
||||
<element>
|
||||
<name>dds.data_reader.history.memory_manager.fast_pool.pool_buffer_max_size</name>
|
||||
<value>2048</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
</datareader_qos>
|
||||
</qos_profile>
|
||||
|
||||
<!--FRAN: who uses this profile?-->
|
||||
<!--Function topic. tablets->server-->
|
||||
<qos_profile name="DataReaderWriter.FT" base_name="LtsProfiles::Base">
|
||||
<datareader_qos>
|
||||
<durability>
|
||||
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
|
||||
</durability>
|
||||
<history>
|
||||
<kind>KEEP_ALL_HISTORY_QOS</kind>
|
||||
</history>
|
||||
</datareader_qos>
|
||||
<datawriter_qos>
|
||||
<publish_mode>
|
||||
<kind>ASYNCHRONOUS_PUBLISH_MODE_QOS</kind>
|
||||
</publish_mode>
|
||||
<durability>
|
||||
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
|
||||
</durability>
|
||||
<history>
|
||||
<kind>KEEP_LAST_HISTORY_QOS</kind>
|
||||
<depth>10</depth>
|
||||
</history>
|
||||
</datawriter_qos>
|
||||
</qos_profile>
|
||||
<!--Mostly used by the tablets as a special mode-->
|
||||
<qos_profile name="DomainParticipant.Shm" base_name="LtsProfiles::Base">
|
||||
<participant_qos>
|
||||
<transport_builtin>
|
||||
<mask>SHMEM</mask>
|
||||
</transport_builtin>
|
||||
<discovery>
|
||||
<multicast_receive_addresses/>
|
||||
<accept_unknown_peers>false</accept_unknown_peers>
|
||||
<initial_peers>
|
||||
<element>60@builtin.shmem://</element>
|
||||
</initial_peers>
|
||||
</discovery>
|
||||
<property>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.shmem.builtin.host_id</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
|
||||
<!--Used by both tablets and server-->
|
||||
<qos_profile name="DomainParticipant.ShmUdp" base_name="LtsProfiles::Base">
|
||||
<participant_qos>
|
||||
<transport_builtin>
|
||||
<mask>UDPv4 | SHMEM</mask>
|
||||
</transport_builtin>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
|
||||
<!-- Both server and clients. Maybe not used-->
|
||||
<qos_profile name="DomainParticipant.ShmUdpLocalhost" base_name="DomainParticipant.ShmUdp">
|
||||
<participant_qos>
|
||||
<discovery>
|
||||
<multicast_receive_addresses/>
|
||||
<accept_unknown_peers>false</accept_unknown_peers>
|
||||
<initial_peers>
|
||||
<element>builtin.shmem://</element>
|
||||
<element>builtin.udpv4://127.0.0.1</element>
|
||||
</initial_peers>
|
||||
</discovery>
|
||||
<property>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.UDPv4.builtin.multicast_enabled</name>
|
||||
<value>0</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.UDPv4.builtin.parent.allow_interfaces</name>
|
||||
<value>127.0.0.1</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
<!-- configured for tcp client -> listenport = -1 -->
|
||||
<qos_profile name="DomainParticipant.Tcp" base_name="LtsProfiles::Base">
|
||||
<participant_qos>
|
||||
<transport_builtin>
|
||||
<mask>MASK_NONE</mask>
|
||||
</transport_builtin>
|
||||
<property>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.load_plugins</name>
|
||||
<value>dds.transport.TCPv4.tcp1</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.library</name>
|
||||
<value>nddstransporttcp</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.create_function</name>
|
||||
<value>NDDS_Transport_TCPv4_create</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.parent.classid</name>
|
||||
<value>NDDS_TRANSPORT_CLASSID_TCPV4_LAN</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.server_bind_port</name>
|
||||
<value>-1</value>
|
||||
</element>
|
||||
<!--
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.logging_verbosity_bitmap</name>
|
||||
<value>159</value>
|
||||
</element>
|
||||
-->
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.disable_nagle</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
<!--
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.send_max_wait_sec</name>
|
||||
<value>-1</value>
|
||||
</element>
|
||||
-->
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.max_packet_size</name>
|
||||
<value>63020</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.parent.message_size_max</name>
|
||||
<value>63020</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.recv_socket_buffer_size</name>
|
||||
<value>8388608</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.send_socket_buffer_size</name>
|
||||
<value>8388608</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.connection_liveliness.enable</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.connection_liveliness.lease_duration</name>
|
||||
<value>5</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.connection_liveliness.assertions_per_lease_duration</name>
|
||||
<value>2</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.outstanding_connection_cookies</name>
|
||||
<value>100</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.outstanding_connection_cookies_life_span</name>
|
||||
<value>5</value>
|
||||
</element>
|
||||
<element>
|
||||
<name>dds.transport.TCPv4.tcp1.force_asynchronous_send</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
<discovery_config>
|
||||
<participant_liveliness_assert_period>
|
||||
<sec>2</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</participant_liveliness_assert_period>
|
||||
<participant_liveliness_lease_duration>
|
||||
<sec>5</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</participant_liveliness_lease_duration>
|
||||
<max_liveliness_loss_detection_period>
|
||||
<sec>2</sec>
|
||||
<nanosec>0</nanosec>
|
||||
</max_liveliness_loss_detection_period>
|
||||
</discovery_config>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
|
||||
|
||||
<!--FRAN: 3 new profiles for each kind of host (30jun2020)-->
|
||||
<!--This implements asymmetric discovery-->
|
||||
<qos_profile name="DomainParticipant.Client" base_name="DomainParticipant.ShmUdp">
|
||||
<participant_qos>
|
||||
<transport_builtin>
|
||||
<mask>SHMEM</mask>
|
||||
</transport_builtin>
|
||||
<discovery>
|
||||
<accept_unknown_peers>false</accept_unknown_peers>
|
||||
<initial_peers>
|
||||
<element>60@builtin.shmem://</element>
|
||||
</initial_peers>
|
||||
</discovery>
|
||||
<property>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.shmem.builtin.host_id</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
|
||||
<!--FRAN: 3 new profiles for each kind of host (30jun2020)-->
|
||||
<!--This implements asymmetric discovery-->
|
||||
<qos_profile name="DomainParticipant.Server" base_name="DomainParticipant.ShmUdp">
|
||||
<participant_qos>
|
||||
<transport_builtin>
|
||||
<mask>SHMEM</mask>
|
||||
</transport_builtin>
|
||||
<discovery>
|
||||
<accept_unknown_peers>false</accept_unknown_peers>
|
||||
<initial_peers>
|
||||
<element>60@builtin.shmem://</element>
|
||||
</initial_peers>
|
||||
</discovery>
|
||||
<property>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.shmem.builtin.host_id</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
<!--FRAN: 3 new profiles for each kind of host (30jun2020)-->
|
||||
<!--This implements asymmetric discovery-->
|
||||
<qos_profile name="DomainParticipant.Tablet" base_name="DomainParticipant.ShmUdp">
|
||||
<participant_qos>
|
||||
<transport_builtin>
|
||||
<mask>SHMEM</mask>
|
||||
</transport_builtin>
|
||||
<discovery>
|
||||
<accept_unknown_peers>false</accept_unknown_peers>
|
||||
<initial_peers>
|
||||
<element>60@builtin.shmem://</element>
|
||||
</initial_peers>
|
||||
</discovery>
|
||||
<property>
|
||||
<value>
|
||||
<element>
|
||||
<name>dds.transport.shmem.builtin.host_id</name>
|
||||
<value>1</value>
|
||||
</element>
|
||||
</value>
|
||||
</property>
|
||||
</participant_qos>
|
||||
</qos_profile>
|
||||
</qos_library>
|
||||
</dds>
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
|
||||
DDS Definitions
|
||||
=========================================================================
|
||||
|
||||
Abbr | Full name
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
IDL | Interface Definition Language
|
||||
-------------------------------------------------------------------------
|
||||
DR | DataReader
|
||||
DW | DataWriter
|
||||
-------------------------------------------------------------------------
|
||||
DDR | DynamicDataReader
|
||||
DDW | DynamicDataWriter
|
||||
-------------------------------------------------------------------------
|
||||
DRO | DataReaderObject
|
||||
DWO | DataWriterObject
|
||||
-------------------------------------------------------------------------
|
||||
DDRO | DynamicDataReaderObject
|
||||
DDWO | DynamicDataWriterObject
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
Static (defined in idl)
|
||||
-------------------------------------------------------------------------
|
||||
DTC | DataTopicClient
|
||||
DTS | DataTopicServer
|
||||
-------------------------------------------------------------------------
|
||||
FTC | FunctionTopicClient
|
||||
FTS | FunctionTopicServer
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
Dynamic (Topic will be defined in source code)
|
||||
-------------------------------------------------------------------------
|
||||
DDTC | DynamicDataTopicClient
|
||||
DDTS | DynamicDataTopicServer
|
||||
-------------------------------------------------------------------------
|
||||
DFTC | DynamicFunctionTopicClient
|
||||
DFTS | DynamicFunctionTopicServer
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
Tool to publish & subscribe (to) data
|
||||
-------------------------------------------------------------------------
|
||||
DTCR | DataTopicContentReceiver
|
||||
DTCG | DataTopicContentGenerator
|
||||
-------------------------------------------------------------------------
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
ADD_INTERFACE_LIBRARY(
|
||||
routing_plugin
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/routing_plugin.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../..
|
||||
ddsgen
|
||||
ddsQoS
|
||||
NGLLogger
|
||||
)
|
||||
Executable
+363
@@ -0,0 +1,363 @@
|
||||
|
||||
|
||||
|
||||
#include <dds/pub/DataWriter.hpp>
|
||||
#include <dds/pub/discovery.hpp>
|
||||
#include <dds/sub/DataReader.hpp>
|
||||
#include <dds/sub/DataReaderListener.hpp>
|
||||
#include <dds/topic/find.hpp>
|
||||
#include <dds/topic/Topic.hpp>
|
||||
|
||||
#include <DDS/Generic/generic_base.h>
|
||||
#include <DDS/QosProfile/DDS_DefaultQos.h>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace DebugInfo
|
||||
{
|
||||
static inline QByteArray toReadable(const rti::core::Guid &guid)
|
||||
{
|
||||
return QByteArray(reinterpret_cast<const char *>(&guid[0]), 16).toHex();
|
||||
}
|
||||
static inline QByteArray toReadable(const rti::core::SequenceNumber &seqNo,
|
||||
const rti::core::Guid &guid)
|
||||
{
|
||||
return "SeqNo: " + QByteArray::number(seqNo.value()) + ", Guid-DW: " + toReadable(guid);
|
||||
}
|
||||
static inline QByteArray toReadable(const rti::core::SampleIdentity &identiy)
|
||||
{
|
||||
return toReadable(identiy.sequence_number(), identiy.writer_guid());
|
||||
}
|
||||
}
|
||||
|
||||
namespace FTS
|
||||
{
|
||||
template<typename GENERIC_FTS>
|
||||
struct TopicNames;
|
||||
}
|
||||
namespace FTC
|
||||
{
|
||||
template<typename GENERIC_FTC>
|
||||
struct TopicNames;
|
||||
}
|
||||
|
||||
namespace RoutingService
|
||||
{
|
||||
|
||||
|
||||
template<typename T>
|
||||
static dds::topic::Topic<T> getTopic(const dds::domain::DomainParticipant &participant,
|
||||
const std::string &topicName)
|
||||
{
|
||||
using DataTopic = dds::topic::Topic<T>;
|
||||
auto ret = dds::topic::find<DataTopic>(participant, topicName);
|
||||
if (ret != nullptr)
|
||||
return ret;
|
||||
return DataTopic(participant, topicName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class SampleWriterInterface
|
||||
{
|
||||
public:
|
||||
SampleWriterInterface() = default;
|
||||
virtual ~SampleWriterInterface() = default;
|
||||
|
||||
virtual void writeSamples() = 0;
|
||||
|
||||
virtual NGL::INGLLogger &logger() = 0;
|
||||
};
|
||||
|
||||
class DataWriterTaskInterface
|
||||
{
|
||||
public:
|
||||
DataWriterTaskInterface() = default;
|
||||
virtual ~DataWriterTaskInterface() = default;
|
||||
|
||||
virtual void enqueue(SampleWriterInterface *swi) = 0;
|
||||
};
|
||||
|
||||
class TopicDispatcherInterface
|
||||
{
|
||||
public:
|
||||
TopicDispatcherInterface() = default;
|
||||
virtual ~TopicDispatcherInterface() = default;
|
||||
|
||||
virtual bool init(DataWriterTaskInterface *dwt,
|
||||
const dds::domain::DomainParticipant &dpIn,
|
||||
const dds::sub::Subscriber &subIn,
|
||||
const dds::domain::DomainParticipant &dpOut,
|
||||
const dds::pub::Publisher &pubOut) = 0;
|
||||
virtual void shutdown() = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename TOPIC>
|
||||
class SampleWriter final : public SampleWriterInterface
|
||||
{
|
||||
public:
|
||||
SampleWriter(dds::sub::LoanedSamples<TOPIC> &&samples,
|
||||
const dds::pub::DataWriter<TOPIC> &dataWriter,
|
||||
bool bWithOriginalIdentity,
|
||||
NGL::INGLLogger &logger)
|
||||
: m_loanedSamples(std::move(samples))
|
||||
, m_dataWriter(dataWriter)
|
||||
, m_bWithOriginalIdentity(bWithOriginalIdentity)
|
||||
, m_logger(logger)
|
||||
{}
|
||||
|
||||
rti::core::SampleIdentity getIdentity(const rti::core::SampleIdentity &identity) const
|
||||
{
|
||||
|
||||
static const auto s_unknownSequenceNumber(rti::core::SequenceNumber::unknown());
|
||||
static const auto s_unknownGui(rti::core::Guid::unknown());
|
||||
static const auto s_unknownSampleIdentity(rti::core::SampleIdentity::unknown());
|
||||
|
||||
if (!m_bWithOriginalIdentity)
|
||||
return s_unknownSampleIdentity;
|
||||
|
||||
if (identity.sequence_number() == s_unknownSequenceNumber)
|
||||
return s_unknownSampleIdentity;
|
||||
if (identity.writer_guid() == s_unknownGui)
|
||||
return s_unknownSampleIdentity;
|
||||
return identity;
|
||||
}
|
||||
|
||||
void writeSamples() override
|
||||
{
|
||||
TRACE_LOG(m_logger) << "Writing samples for " << DDS::topic_type_name<TOPIC>::value()
|
||||
<< ", sample count: " << m_loanedSamples.length();
|
||||
rti::pub::WriteParams writeParams;
|
||||
for (const auto &sample : m_loanedSamples)
|
||||
{
|
||||
try
|
||||
{
|
||||
writeParams.identity(getIdentity(sample.info()->original_publication_virtual_sample_identity()));
|
||||
writeParams.related_sample_identity(getIdentity(sample.info()->related_original_publication_virtual_sample_identity()));
|
||||
|
||||
|
||||
writeParams.handle(sample.info().instance_handle());
|
||||
writeParams.flag(sample.info()->flag());
|
||||
|
||||
|
||||
|
||||
TRACE_LOG(m_logger) << "identity: " << DebugInfo::toReadable(writeParams.identity())
|
||||
<< ", related_sample_identity: " << DebugInfo::toReadable(writeParams.related_sample_identity());
|
||||
|
||||
const auto &state = sample.info().state();
|
||||
const dds::sub::status::InstanceState &instanceState = state.instance_state();
|
||||
if (instanceState == dds::sub::status::InstanceState::alive())
|
||||
{
|
||||
m_dataWriter->write(sample.data(), writeParams);
|
||||
}
|
||||
else if (instanceState == dds::sub::status::InstanceState::not_alive_disposed())
|
||||
{
|
||||
TRACE_LOG(m_logger) << " - is disposed";
|
||||
m_dataWriter->dispose_instance(writeParams);
|
||||
}
|
||||
else if (instanceState == dds::sub::status::InstanceState::not_alive_no_writers())
|
||||
{
|
||||
TRACE_LOG(m_logger) << " - is unregistered";
|
||||
m_dataWriter->unregister_instance(writeParams);
|
||||
}
|
||||
else
|
||||
WARNING_LOG(m_logger) << "Unknown instance state: " << instanceState;
|
||||
}
|
||||
catch (const dds::core::Exception &ex)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "writeSamples exception: " << ex.what();
|
||||
}
|
||||
}
|
||||
m_dataWriter->flush();
|
||||
}
|
||||
NGL::INGLLogger &logger() override { return m_logger; }
|
||||
private:
|
||||
dds::sub::LoanedSamples<TOPIC> m_loanedSamples;
|
||||
dds::pub::DataWriter<TOPIC> m_dataWriter;
|
||||
bool m_bWithOriginalIdentity;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class DataTopicListener final : public dds::sub::DataReaderListener<T>
|
||||
{
|
||||
public:
|
||||
using DataReader = dds::sub::DataReader<T>;
|
||||
|
||||
DataTopicListener(DataWriterTaskInterface *dwt,
|
||||
const std::string &topicName,
|
||||
DDS::QosProfile::TopicType topicType,
|
||||
NGL::INGLLogger &logger)
|
||||
: m_dwt(dwt)
|
||||
, m_dataWriter(nullptr)
|
||||
, m_topicName(topicName)
|
||||
, m_topicType(topicType)
|
||||
, m_logger(logger)
|
||||
{}
|
||||
|
||||
bool init(const dds::domain::DomainParticipant &dpOut,
|
||||
const dds::pub::Publisher &pubOut)
|
||||
{
|
||||
if (!m_dwt)
|
||||
return false;
|
||||
DEBUG_LOG(m_logger) << "Creating DataWriter for domain " << dpOut.domain_id();
|
||||
m_dataWriter = dds::pub::DataWriter<T>(pubOut,
|
||||
getTopic<T>(dpOut, m_topicName),
|
||||
DDS::QosProfile::dataWriterDefault(m_topicType));
|
||||
|
||||
|
||||
dds::domain::DomainParticipant &dpOutNonConst = const_cast<dds::domain::DomainParticipant &>(dpOut);
|
||||
dds::pub::ignore(dpOutNonConst, m_dataWriter.instance_handle());
|
||||
return true;
|
||||
}
|
||||
|
||||
void on_data_available(dds::sub::DataReader<T> &reader) override
|
||||
{
|
||||
|
||||
|
||||
TRACE_LOG(m_logger) << "Data available for " << DDS::topic_type_name<T>::value();
|
||||
auto ws = new SampleWriter<T>(reader.take(), m_dataWriter, m_topicType == DDS::QosProfile::TopicType::FunctionTopic, m_logger);
|
||||
m_dwt->enqueue(ws);
|
||||
}
|
||||
|
||||
void on_requested_deadline_missed(dds::sub::DataReader<T> &, const dds::core::status::RequestedDeadlineMissedStatus &) override {}
|
||||
void on_requested_incompatible_qos(dds::sub::DataReader<T> &,
|
||||
const dds::core::status::RequestedIncompatibleQosStatus &) override {}
|
||||
void on_sample_rejected(dds::sub::DataReader<T> &,
|
||||
const dds::core::status::SampleRejectedStatus &) override {}
|
||||
void on_liveliness_changed(dds::sub::DataReader<T> &,
|
||||
const dds::core::status::LivelinessChangedStatus &) override {}
|
||||
void on_subscription_matched(dds::sub::DataReader<T> &,
|
||||
const dds::core::status::SubscriptionMatchedStatus &) override {}
|
||||
void on_sample_lost(dds::sub::DataReader<T> &,
|
||||
const dds::core::status::SampleLostStatus &) override {}
|
||||
|
||||
static dds::core::status::StatusMask mask()
|
||||
{
|
||||
return (dds::core::status::StatusMask::data_available());
|
||||
}
|
||||
|
||||
protected:
|
||||
DataWriterTaskInterface *m_dwt;
|
||||
dds::pub::DataWriter<T> m_dataWriter;
|
||||
std::string m_topicName;
|
||||
DDS::QosProfile::TopicType m_topicType;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
template<typename TOPIC>
|
||||
class TopicDispatcher : public TopicDispatcherInterface
|
||||
{
|
||||
protected:
|
||||
explicit TopicDispatcher(NGL::INGLLogger &logger, const std::string &topicName, DDS::QosProfile::TopicType topicType)
|
||||
: m_dataReader(nullptr)
|
||||
, m_topicName(topicName)
|
||||
, m_topicType(topicType)
|
||||
, m_logger(logger)
|
||||
{
|
||||
}
|
||||
|
||||
~TopicDispatcher()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool init(DataWriterTaskInterface *dwt,
|
||||
const dds::domain::DomainParticipant &dpIn,
|
||||
const dds::sub::Subscriber &subIn,
|
||||
const dds::domain::DomainParticipant &dpOut,
|
||||
const dds::pub::Publisher &pubOut) override
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Initializing TopicDispatcher " << "(" << this << ")";
|
||||
if (m_dataReader != nullptr)
|
||||
return false;
|
||||
|
||||
m_dataReader = dds::sub::DataReader<TOPIC>(subIn,
|
||||
getTopic<TOPIC>(dpIn, m_topicName),
|
||||
DDS::QosProfile::dataReaderDefault(m_topicType));
|
||||
DEBUG_LOG(m_logger) << "Creating DataReader for domain " << dpIn.domain_id() << ", ptr: " << m_dataReader.delegate().get();
|
||||
|
||||
m_dtl.reset(new DataTopicListener<TOPIC>(dwt, m_topicName, m_topicType, m_logger));
|
||||
|
||||
if (!m_dtl->init(dpOut, pubOut))
|
||||
return false;
|
||||
m_dataReader.listener(m_dtl.get(), DataTopicListener<TOPIC>::mask());
|
||||
return true;
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Cleaning up TopicDispatcher " << "(" << this << ")";
|
||||
if (m_dataReader != nullptr)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "shutting down DataReader: " << m_dataReader.delegate().get();
|
||||
m_dataReader.listener(nullptr, dds::core::status::StatusMask::none());
|
||||
try
|
||||
{
|
||||
m_dataReader.close();
|
||||
}
|
||||
catch (const dds::core::Exception &ex)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "topic close ex: " << ex.what();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_dtl = nullptr;
|
||||
|
||||
m_dataReader = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
dds::sub::DataReader<TOPIC> m_dataReader;
|
||||
std::unique_ptr<DataTopicListener<TOPIC>> m_dtl;
|
||||
std::string m_topicName;
|
||||
DDS::QosProfile::TopicType m_topicType;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
|
||||
template<typename TOPIC>
|
||||
class DataTopicDispatcher final : public TopicDispatcher<TOPIC>
|
||||
{
|
||||
public:
|
||||
explicit DataTopicDispatcher(NGL::INGLLogger &logger)
|
||||
: TopicDispatcher<TOPIC>(logger,
|
||||
DDS::Helper::getShortTopicName(dds::topic::topic_type_name<TOPIC>::value()),
|
||||
DDS::QosProfile::TopicType::DataTopic)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
template<typename FTS_TOPIC>
|
||||
class FunctionTopicReplyDispatcher final : public TopicDispatcher<typename FTS_TOPIC::ReplyTopic>
|
||||
{
|
||||
public:
|
||||
explicit FunctionTopicReplyDispatcher(NGL::INGLLogger &logger)
|
||||
: TopicDispatcher<typename FTS_TOPIC::ReplyTopic>(logger,
|
||||
FTS::TopicNames<FTS_TOPIC>::replyTopicName(),
|
||||
DDS::QosProfile::TopicType::FunctionTopic)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
template<typename FTC_TOPIC>
|
||||
class FunctionTopicRequestDispatcher final : public TopicDispatcher<typename FTC_TOPIC::RequestTopic>
|
||||
{
|
||||
public:
|
||||
explicit FunctionTopicRequestDispatcher(NGL::INGLLogger &logger)
|
||||
: TopicDispatcher<typename FTC_TOPIC::RequestTopic>(logger,
|
||||
FTC::TopicNames<FTC_TOPIC>::requestTopicName(),
|
||||
DDS::QosProfile::TopicType::FunctionTopic)
|
||||
{}
|
||||
};
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user