#include #include #include #include #include #include #include #include #include namespace DebugInfo { static inline QByteArray toReadable(const rti::core::Guid &guid) { return QByteArray(reinterpret_cast(&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 struct TopicNames; } namespace FTC { template struct TopicNames; } namespace RoutingService { template static 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); } 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 class SampleWriter final : public SampleWriterInterface { public: SampleWriter(dds::sub::LoanedSamples &&samples, const dds::pub::DataWriter &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::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 m_loanedSamples; dds::pub::DataWriter m_dataWriter; bool m_bWithOriginalIdentity; NGL::INGLLogger &m_logger; }; template class DataTopicListener final : public dds::sub::DataReaderListener { public: using DataReader = dds::sub::DataReader; 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(pubOut, getTopic(dpOut, m_topicName), DDS::QosProfile::dataWriterDefault(m_topicType)); dds::domain::DomainParticipant &dpOutNonConst = const_cast(dpOut); dds::pub::ignore(dpOutNonConst, m_dataWriter.instance_handle()); return true; } void on_data_available(dds::sub::DataReader &reader) override { TRACE_LOG(m_logger) << "Data available for " << DDS::topic_type_name::value(); auto ws = new SampleWriter(reader.take(), m_dataWriter, m_topicType == DDS::QosProfile::TopicType::FunctionTopic, m_logger); m_dwt->enqueue(ws); } void on_requested_deadline_missed(dds::sub::DataReader &, const dds::core::status::RequestedDeadlineMissedStatus &) override {} void on_requested_incompatible_qos(dds::sub::DataReader &, const dds::core::status::RequestedIncompatibleQosStatus &) override {} void on_sample_rejected(dds::sub::DataReader &, const dds::core::status::SampleRejectedStatus &) override {} void on_liveliness_changed(dds::sub::DataReader &, const dds::core::status::LivelinessChangedStatus &) override {} void on_subscription_matched(dds::sub::DataReader &, const dds::core::status::SubscriptionMatchedStatus &) override {} void on_sample_lost(dds::sub::DataReader &, 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 m_dataWriter; std::string m_topicName; DDS::QosProfile::TopicType m_topicType; NGL::INGLLogger &m_logger; }; template 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(subIn, getTopic(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(dwt, m_topicName, m_topicType, m_logger)); if (!m_dtl->init(dpOut, pubOut)) return false; m_dataReader.listener(m_dtl.get(), DataTopicListener::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 m_dataReader; std::unique_ptr> m_dtl; std::string m_topicName; DDS::QosProfile::TopicType m_topicType; NGL::INGLLogger &m_logger; }; template class DataTopicDispatcher final : public TopicDispatcher { public: explicit DataTopicDispatcher(NGL::INGLLogger &logger) : TopicDispatcher(logger, DDS::Helper::getShortTopicName(dds::topic::topic_type_name::value()), DDS::QosProfile::TopicType::DataTopic) {} }; template class FunctionTopicReplyDispatcher final : public TopicDispatcher { public: explicit FunctionTopicReplyDispatcher(NGL::INGLLogger &logger) : TopicDispatcher(logger, FTS::TopicNames::replyTopicName(), DDS::QosProfile::TopicType::FunctionTopic) {} }; template class FunctionTopicRequestDispatcher final : public TopicDispatcher { public: explicit FunctionTopicRequestDispatcher(NGL::INGLLogger &logger) : TopicDispatcher(logger, FTC::TopicNames::requestTopicName(), DDS::QosProfile::TopicType::FunctionTopic) {} }; }