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

117 lines
4.4 KiB
C++

#pragma once
#include "ddkit/access/DdsWrap.hpp"
#include "ddkit/access/Topic.hpp"
#include "ddkit/access/Listener.hpp"
#include "ddkit/access/ConnectState.hpp"
#include "ddkit/utils/Logger.hpp"
namespace ddkit {
namespace access {
template <typename T>
class Writer final {
public:
explicit Writer(std::shared_ptr<Topic<T>> topic, dds::pub::qos::DataWriterQos dataWriterQos, dds::pub::qos::PublisherQos pubQos)
: m_connectState(std::make_shared<ConnectState>(topic->topicName(), topic->topicNameModule(), topic->topicNameTopic()))
, m_listener(std::make_shared<ListenerWrite<T> >(m_connectState))
, m_writer(dataWriter(topic, m_listener.get(), dataWriterQos, pubQos)) {
DDKIT_LOG_TRACE;
}
~Writer() {
DDKIT_LOG_TRACE;
// ddkit::log::info() << "closing writer!";
m_writer.close();
}
bool isConnected() { return m_connectState->isConnected(); }
void waitUntilConnected() { m_connectState->waitUntilConnected(); }
IConnectCheckPtr getConnectCheck() { return m_connectState; }
/// @brief isOkUnconnected == true -> do not warn about write while not connected
/// - one could set this on a late-join-topic to avoid the warnings
/// isThrowUnconnected == true -> do throw momentarily
/// - one could set this on a 'checked' write ('Cmd')
void send(T const& sample, bool isOkUnconnected, bool isThrowUnconnected) {
ddkit::log::debug() << "send: " << DDKIT_LOG_VAR_VALUE(sample);
bool isConnected = m_connectState->isConnected();
// return immediately, if Service for command is not yet connected (and told to throw)
if (!isConnected && isThrowUnconnected) {
throw std::runtime_error("Service for Cmd is unavailable yet - try later.");
}
// todo: as monitor-sessions are not distinguished so far, this (direct) exception will not be thrown,
// if a monitor is running and connected
// warn about probably lost samples (if not told otherwise)
if (!isConnected && !isOkUnconnected) {
ddkit::log::info() << "Writer not connected while sending (" << dds::topic::topic_type_name<T>::value() << ").";
}
// do the write
m_writer.write(sample);
}
void dispose(T const& sample) {
DDKIT_LOG_TRACE;
ddkit::log::debug() << "dispose: " << DDKIT_LOG_VAR_VALUE(sample);
// Get handle of instance
auto handle = m_writer.lookup_instance(sample);
// Instance has to be known
if (handle.is_nil()) {
ddkit::log::error() << "Tried to remove object \"" << sample << "\", which does not exist in dds.";
return;
}
// We have to do both `dispose_instance` and `unregister_instance`
// to fully free the instance. If we would just dispose the instance
// the instance would remain stored by the reader and writer since it
// remains `active`. Only after the `unregister_instance` the instance
// has no associated writer and can be freed.
m_writer.dispose_instance(handle);
m_writer.unregister_instance(handle);
}
private:
ConnectStatePtr m_connectState;
std::shared_ptr<ListenerWrite<T>> m_listener;
dds::pub::DataWriter<T> m_writer;
/// Note on writer-listeners:
/// - listeners have to be set in the constructor of the writer, because
/// an event could be missing, when the listener is set afterwards
/// - all depending on how fast discovery etc is, see also here:
/// https://github.com/eclipse-cyclonedds/cyclonedds-cxx/issues/378#issuecomment-1463626212
static dds::pub::DataWriter<T> dataWriter(
std::shared_ptr<Topic<T>> topic,
ListenerWrite<T>* listener,
dds::pub::qos::DataWriterQos dataWriterQos,
dds::pub::qos::PublisherQos pubQos) {
DDKIT_LOG_TRACE;
dds::pub::DataWriter<T> ddsWriter = dds::pub::DataWriter<T>(
dds::pub::Publisher(topic->domainParticipant(), pubQos),
topic->ddsTopic(),
dataWriterQos,
listener,
dds::core::status::StatusMask::all());
return ddsWriter;
}
// no copying
Writer(Writer const&) = delete;
Writer(Writer&&) = delete;
void operator=(Writer const&) = delete;
};
} // namespace access
} // namespace ddkit