#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace connext { namespace DebugInfo { inline QByteArray toReadable(const rti::core::Guid &guid) { const QByteArray str(reinterpret_cast(&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 dds::topic::Topic getTopic(const dds::domain::DomainParticipant &participant, const std::string &topicName) { using DataTopic = dds::topic::Topic; auto ret = dds::topic::find(participant, topicName); if (ret != nullptr) return ret; return DataTopic(participant, topicName); } template 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 class WriteSample : public SampleBase { 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 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 class Sample : public SampleBase { 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 class ReplierParams : public RequesterParams { public: using RequesterParams::RequesterParams; }; template class ReplyRequestBase { protected: explicit ReplyRequestBase(const connext::RequesterParams &requesterParams) : m_requestTopic(getTopic(requesterParams.domainParticipant(), requesterParams.request_topic_name())) , m_replyTopic(getTopic(requesterParams.domainParticipant(), requesterParams.reply_topic_name())) {} virtual ~ReplyRequestBase() = default; protected: dds::topic::Topic m_requestTopic; dds::topic::Topic m_replyTopic; }; template class Requester final : public ReplyRequestBase { public: explicit Requester(const connext::RequesterParams &requesterParams) : ReplyRequestBase(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::m_replyTopic.name(); const QByteArray filterName(QByteArray::fromRawData(rtName.c_str(), static_cast(rtName.size())) + QByteArray(" ") + writerGuid); m_contentFilteredTopic = dds::topic::ContentFilteredTopic(ReplyRequestBase::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::m_requestTopic.name() << " - related_sample_identity should be: " << writerGuid; } void send_request(connext::WriteSample &request) { bool bNewDataWriter = false; if (m_dataWriter == nullptr) { m_dataWriter = dds::pub::DataWriter(m_requesterParams.publisher(), ReplyRequestBase::m_requestTopic, m_requesterParams.datawriter_qos()); bNewDataWriter = true; } else { } create_contentfilter(bNewDataWriter); if (m_dataReader == nullptr) { m_dataReader = dds::sub::DataReader(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::m_requestTopic.name() << ", m_currentSampleIdentity: " << DebugInfo::toReadable(currentSampleIdentity); } bool receive_reply(connext::Sample &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::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::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::m_requestTopic.name(); m_dataWriter.dispose_instance(m_curInstanceHandle); } } return bRet; } dds::pub::DataWriter get_request_datawriter() const { return m_dataWriter; } dds::sub::DataReader get_request_datareader() const { return m_dataReader; } private: connext::RequesterParams m_requesterParams; dds::pub::DataWriter m_dataWriter; dds::sub::DataReader m_dataReader; dds::topic::ContentFilteredTopic m_contentFilteredTopic; dds::core::InstanceHandle m_curInstanceHandle; NGL::INGLLogger &m_logger; }; template class Replier final : public ReplyRequestBase { public: explicit Replier(const connext::ReplierParams &replierParams) : ReplyRequestBase(replierParams) , m_replierParams(replierParams) , m_dataWriter(dds::pub::DataWriter(replierParams.publisher(), ReplyRequestBase::m_replyTopic, replierParams.datawriter_qos())) , m_dataReader(dds::sub::DataReader(replierParams.subscriber(), ReplyRequestBase::m_requestTopic, replierParams.datareader_qos())) , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("connext.replier")) {} void send_reply(connext::WriteSample &reply, const rti::core::SampleIdentity &identity, bool bLastSample = true) { connext::WriteSampleRef replyRef; replyRef.set_data(&reply.data()); replyRef.set_info(&reply.info()); return send_reply(replyRef, identity, bLastSample); } void send_reply(connext::WriteSampleRef &reply, const rti::core::SampleIdentity &identity, bool bLastSample = true) { DEBUG_LOG(m_logger) << "send_reply for topic " << ReplyRequestBase::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::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) { if (!m_dataReader->take(request.data(), request.info())) return false; if (request.info().valid()) { DEBUG_LOG(m_logger) << "receive_request for topic " << ReplyRequestBase::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 get_request_datawriter() const { return m_dataWriter; } dds::sub::DataReader get_request_datareader() const { return m_dataReader; } private: connext::ReplierParams m_replierParams; dds::pub::DataWriter m_dataWriter; dds::sub::DataReader m_dataReader; NGL::INGLLogger &m_logger; }; }