HLCC Documentation 2.2.0
Loading...
Searching...
No Matches
requestor.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2020-2025 European Southern Observatory (ESO)
2//
3// SPDX-License-Identifier: LGPL-3.0-only
4
13#ifndef HLCC_CPPUTIL_REQUESTOR_HPP
14#define HLCC_CPPUTIL_REQUESTOR_HPP
15
16
17#include <atomic>
18
19#include <mal/Cii.hpp>
20
21
22
23namespace hlcc::cpputil {
24
25
35template <typename INTERFACE_TYPE>
36class Requestor {
37public:
42 explicit Requestor(log4cplus::Logger& logger)
43 : m_mutex {},
44 m_logger{logger},
45 m_uri{},
46 m_client{}
47 {
48 }
49
57 explicit Requestor(log4cplus::Logger& logger, const elt::mal::Uri& uri,
58 const std::optional<elt::mal::Mal::Properties> mal_properties = {})
59 : Requestor(logger) {
60 SetConnectionInfo(uri, mal_properties);
61 }
62
69 void SetConnectionInfo(const elt::mal::Uri& uri,
70 const std::optional<elt::mal::Mal::Properties> mal_properties = {}) {
71
72 std::lock_guard lck {m_mutex};
73
74 if (uri != m_uri) {
75 // Close previous instance. Probably would be done automatically after being dereferenced.
76 if (m_client) {
77 LOG4CPLUS_DEBUG(m_logger, "SetConnectionInfo will close old client");
78 m_client->close();
79 }
80
81 try {
82 // Create the MAL client.
83 // Note that (at least with ZMQ MAL), client construction immediately triggers an async connection attempt.
84 m_client = elt::mal::CiiFactory::getInstance().getClient<INTERFACE_TYPE>(
85 uri, elt::mal::rr::qos::QoS::DEFAULT,
86 mal_properties ? *mal_properties : elt::mal::Mal::Properties());
87 m_uri = uri;
88 LOG4CPLUS_DEBUG(m_logger, "Created rr client for " << uri);
89
90 // Register connection listener that logs all later connections and disconnections.
91 // This registration itself is async, which means that if immediately afterwards we call Connect
92 // or connect implicitly by using the interface, then the new connection may not be logged yet.
93 // We copy-capture the uri instead of using m_uri because these two may diverge when switching to a new URI.
94 m_client->registerConnectionListener([this, uri](bool connected) {
95 m_connected.store(connected);
96 LOG4CPLUS_DEBUG(m_logger, "Connected with " << uri << ": " << connected);
97 });
98 } catch (const std::exception& ex) {
99 // This happens, for example, when Nomad does not resolve the address and gives us a URI such as "zpb.rr:///TrackCmds".
100 // We just log the error and leave m_client empty or closed. A following call to Connect will fail.
101 LOG4CPLUS_WARN(m_logger, "Failed to create rr client for " << uri << ": " << ex.what());
102 }
103 }
104 }
105
112 std::shared_ptr<INTERFACE_TYPE>& GetInterface() {
113 std::lock_guard lck {m_mutex};
114 if (!m_client) {
115 LOG4CPLUS_DEBUG(m_logger, "Requestor::GetInterface() called without having an interface client. The user must call SetConnectionInfo first.");
116 // still we return the empty pointer.
117 }
118 return m_client;
119 }
120
132 bool Connect(std::chrono::seconds conn_timeout) {
133 std::lock_guard lck {m_mutex};
134 if (m_client) {
135 // TODO should we skip this if m_connected == true?
136 // Currently we also connect redundantly, just to be able to probe it independently of the timing of the connection callback.
137
138 elt::mal::future<void> conn_future = m_client->asyncConnect();
139 ::boost::chrono::seconds conn_timeout_cii{conn_timeout.count()};
140 LOG4CPLUS_TRACE(m_logger, "Requestor::Connect() called asyncConnect() and will wait max " << conn_timeout_cii.count() << " s.");
141 auto future_status = conn_future.wait_for(conn_timeout_cii);
142 bool success = future_status == (boost::future_status::ready); // ready, timeout, deferred
143 LOG4CPLUS_TRACE(m_logger, "Requestor::Connect() returned from waiting for connection, success=" << success);
144 return success;
145 }
146 else {
147 LOG4CPLUS_DEBUG(m_logger, "Requestor::Connect() failed because of missing MAL client.");
148 return false;
149 }
150 }
151
152 Requestor(const Requestor&) = delete;
153 Requestor& operator=(const Requestor&) = delete;
154
155private:
156 mutable std::recursive_mutex m_mutex;
157 log4cplus::Logger& m_logger;
158 elt::mal::Uri m_uri;
159 std::shared_ptr<INTERFACE_TYPE> m_client; // Share pointer to MAL Client
160
164 std::atomic<bool> m_connected{false};
165};
166
167
168
169} // namespace hlcc::cpputil
170
171#endif // HLCC_CPPUTIL_REQUESTOR_HPP
Definition requestor.hpp:36
Requestor(const Requestor &)=delete
Requestor(log4cplus::Logger &logger, const elt::mal::Uri &uri, const std::optional< elt::mal::Mal::Properties > mal_properties={})
Definition requestor.hpp:57
Requestor(log4cplus::Logger &logger)
Definition requestor.hpp:42
Requestor & operator=(const Requestor &)=delete
bool Connect(std::chrono::seconds conn_timeout)
Definition requestor.hpp:132
std::shared_ptr< INTERFACE_TYPE > & GetInterface()
Definition requestor.hpp:112
void SetConnectionInfo(const elt::mal::Uri &uri, const std::optional< elt::mal::Mal::Properties > mal_properties={})
Definition requestor.hpp:69
Definition ciiTypesToString.cpp:7