Files
qt5-legatus-legacy/Lts/DDS/FTS/generic_fts_impl.h
T
torstenandCopilot 3028302f7b Qt6 Migration: Merge latest Qt5 devel content and apply migration fixes
- Merged 292 changed files from dlbo snapshot
- Fixed IDL enum collisions (PortType, PdmEventType)
- Fixed Grantlee template for C++11 scoped enums
- Fixed CMake VERSION_GREATER syntax
- Added missing directories (THP, LtsRestClient, etc.)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-15 18:20:51 +00:00

316 lines
9.7 KiB
C++
Executable File

/**
* @RDE_HEADER@
* Generic Function Topic Server (FTS Replier)
* $Id$
* $HeadURL$
* $Author$
*/
#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 &)
{
// nothing to do here
}
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)
{
// nothing to do here
}
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);
//DEBUG_LOG(m_logger) << "m_domainParticipant: " << m_domainParticipant;
//requesterParams.service_name("FunctionTopic");
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));
//DEBUG_LOG(m_logger) << "=> Static request using request/reply topic names: "
// << requestTopicName << ", reply: " << replyTopicName;
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;
}
/**
* @brief
*
* Send a single reply back to the requester
*
* @param requestIdentity
* @param replyData
*/
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;
}
/**
* @brief
*
* Send a block of reply informations back to the requester
* The replies will have a correctly set replyState member (all will have InProgress, the last on Completed)
*
* @param requestIdentity
* @param replyData
*/
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:
// -------------------------------------------------------------------------------------------------------------------
// FunctionTopics => defined in *.idl
// -------------------------------------------------------------------------------------------------------------------
/**
* @brief
* Get the requested data and send them on to the object that will supply the data
*
* @param request topic data
*/
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) // sec
{
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());
}
/**
* @brief
*
* This method waits for requests from a FTC.
* When it receives a request topic, it will get the requested data
* and send it via DDS Replier back to the FTC
*
*/
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:
// Replier (with request and reply topic (see above)
connext::Replier<RequestTopic, ReplyTopic> *m_replier;
dds::sub::cond::ReadCondition *m_readCondition;
// no ownership
DDSGeneric::WaitSetHandler *m_wsHandler;
std::queue<connext::Sample<RequestTopic>> m_requests;
std::mutex m_requestMutex;
// Callback for data processing
SubscriberCallback m_subscriberCallback;
NGL::INGLLogger &m_logger;
};
} // namespace FTS