Files
2026-10-23 16:13:31 +00:00

172 lines
6.2 KiB
C++

#pragma once
#include "ddkit/access/DdsWrap.hpp"
#include "ddkit/access/Dispatcher.hpp"
#include "ddkit/access/Topic.hpp"
#include "ddkit/access/Listener.hpp"
#include "ddkit/access/ConnectState.hpp"
namespace ddkit {
namespace access {
#if defined(DDS_VENDOR_CONNEXT)
#define DDKIT_USE_KEY_VALUE ON
#ifdef _WIN32
#define DDKIT_COND_TYPE_UNUSED
#else
#define DDKIT_COND_TYPE_UNUSED dds::core::cond::Condition
#endif
#elif defined(DDS_VENDOR_CYCLONE)
#define DDKIT_COND_TYPE_UNUSED dds::core::cond::Condition
#elif defined(DDS_VENDOR_OPENSPLICE)
#define DDKIT_COND_TYPE_UNUSED dds::core::cond::Condition
#else
#error "DDS_VENDOR not set"
#endif
template <typename T>
class Reader final {
public:
Reader(
std::shared_ptr<access::Dispatcher> dispatcher,
std::shared_ptr<Topic<T>> topic,
dds::sub::qos::DataReaderQos dataReaderQos,
dds::sub::qos::SubscriberQos subQos,
std::function<void(T const&)> funcWrite,
std::function<void(T const&)> funcDispose)
: m_dispatcher(dispatcher)
, m_connectState(std::make_shared<ConnectState>(topic->topicName(), topic->topicNameModule(), topic->topicNameTopic()))
, m_listener(std::make_shared<ListenerRead<T> >(m_connectState))
, m_ddsReader(dataReader(topic, m_listener.get(), dataReaderQos, subQos))
, m_dataState(dds::sub::status::DataState::any())
, m_shutdownRequest(false)
, m_funcWrite(funcWrite)
, m_funcDispose(funcDispose)
, m_func([this](DDKIT_COND_TYPE_UNUSED) {
DDKIT_LOG_TRACE;
const dds::sub::LoanedSamples<T> samples = m_ddsReader.select().state(m_dataState).take();
for (const auto& sample : samples) {
if (m_shutdownRequest) return; // fast exit
const auto& sampleInfo = sample.info();
#ifdef DDKIT_USE_KEY_VALUE
const auto& instanceHandle = sampleInfo.instance_handle();
#endif
const auto& dataState = sampleInfo.state();
const auto& instanceState = dataState.instance_state();
using namespace dds::sub::status;
ddkit::log::trace() << "Sample alive:" << (instanceState == InstanceState::alive());
ddkit::log::trace() << "Sample disposed:" << (instanceState == InstanceState::not_alive_disposed());
ddkit::log::trace() << "Sample no_writers:" << (instanceState == InstanceState::not_alive_no_writers());
// How to get partition of sending writer
// only connext and cyclone > 0.10
// auto pubData = dds::sub::matched_publication_data(m_ddsReader, sampleInfo.publication_handle());
// for (auto& n : pubData.partition()->name()) {
// std::cout << "Partition: " << n << std::endl;
// }
if (sampleInfo.valid() && instanceState == InstanceState::alive()) {
const T& t = sample.data();
// callback
if (m_funcWrite) {
ddkit::log::debug() << "received: " << t << ". Callbacking user ...";
m_funcWrite(t);
ddkit::log::trace() << "... usercallback called.";
}
} else {
if (m_funcDispose) {
#ifdef DDKIT_USE_KEY_VALUE
// - 'key_value()' works perfectly for connext
// (and for cyclone+opensplice while explicitly disposing)
// - no (positive) effect on cross-vendor (see top of file)
// - so it is best to stay with native sample.data() for cyclone+opensplice but
// use simple key_value for connext
T t;
m_ddsReader.key_value(t, instanceHandle);
#else
// - vendors, that are able to fetch the key even on dispose-calls via sample.data()
// (they fill in default-ctor-data in the other attrs, because only the key has
// to be transferred via DDS for dispose - same definition on our API - fine).
const T& t = sample.data();
#endif
ddkit::log::debug() << "received for disposing: " << t << ". Callbacking user ...";
m_funcDispose(t);
ddkit::log::trace() << "... usercallback called.";
}
}
}
})
, m_readCondition(m_ddsReader, m_dataState, m_func) {
DDKIT_LOG_TRACE;
m_dispatcher->addReadCondition(m_readCondition);
}
~Reader() {
DDKIT_LOG_TRACE;
unload();
m_ddsReader.close();
}
void unload() {
if (!m_shutdownRequest) {
m_shutdownRequest = true;
m_dispatcher->removeReadCondition(m_readCondition);
}
}
IConnectCheckPtr getConnectCheck() {
return m_connectState;
}
private:
std::shared_ptr<Dispatcher> m_dispatcher;
ConnectStatePtr m_connectState;
std::shared_ptr<ListenerRead<T>> m_listener;
dds::sub::DataReader<T> m_ddsReader;
dds::sub::status::DataState m_dataState;
std::atomic<bool> m_shutdownRequest;
std::function<void(T const&)> m_funcWrite;
std::function<void(T const&)> m_funcDispose;
std::function<void(DDKIT_COND_TYPE_UNUSED)> m_func;
dds::sub::cond::ReadCondition m_readCondition;
/// Note on reader-listeners: see writer-listeners in Writer.hpp
static dds::sub::DataReader<T> dataReader(
std::shared_ptr<Topic<T>> topic,
ListenerRead<T>* listener,
dds::sub::qos::DataReaderQos dataReaderQos,
dds::sub::qos::SubscriberQos subQos) {
DDKIT_LOG_TRACE;
dds::sub::DataReader<T> ddsReader = dds::sub::DataReader<T>(
dds::sub::Subscriber(topic->domainParticipant(), subQos),
topic->ddsTopic(),
dataReaderQos,
listener,
dds::core::status::StatusMask::all());
return ddsReader;
}
// no copying
Reader(Reader const&) = delete;
Reader(Reader&&) = delete;
void operator=(Reader const&) = delete;
};
#undef DDKIT_USE_KEY_VALUE
#undef DDKIT_COND_TYPE_UNUSED
} // namespace access
} // namespace ddkit