Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/Common/CodeGen/svc_ftc_impl.cpp.grantlee
T
2026-10-03 09:20:13 +02:00

255 lines
7.3 KiB
Plaintext
Executable File

/**
* {{ namespace }} Function Topic Client implementation
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_ftc_impl.cpp
#include "{{ namespace_abbr }}_ftc_impl.h"
#include <DDS/FTC/generic_ftc_impl.h>
#include <LtsUtilsCore/await.h>
#include <QCoreApplication>
#include <QThread>
#include <unordered_map>
#include <chrono>
//using namespace std::literals::chrono_literals;
namespace {{ namespace }}
{
namespace Interface
{
// initializer function
Q_DECL_EXPORT std::shared_ptr<Ftc> Ftc::create(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector, bool forceCreate)
{
// share only for main thread
if (qApp->thread() != QThread::currentThread() || forceCreate)
return std::make_shared<FtcImpl>(ddsConnector);
static std::unordered_map<DDSConnector::DdsConnector*, std::weak_ptr<Ftc>> s_availableConnectors;
auto it = s_availableConnectors.find(ddsConnector.get());
if (it != s_availableConnectors.end())
{
auto ret = it->second.lock();
if (ret)
return ret;
s_availableConnectors.erase(it);
}
auto ret = std::make_shared<FtcImpl>(ddsConnector);
s_availableConnectors.insert(std::make_pair(ddsConnector.get(), ret));
return ret; // moved implicitly (C++17; explicit std::move on a local triggers -Wredundant-move)
}
// FtcImpl
FtcImpl::FtcImpl(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
: Ftc()
, m_ddsConnector(ddsConnector)
, m_logger(getLogger("Ftc"))
{}
bool FtcImpl::enableAll(bool enable)
{ {% for ft in functionTopics.member %}
if (!enable{{ ft.name }}(enable))
{
ERROR_LOG(m_logger) << "Error initializing function topic {{ ft.name }}";
return false;
}
{% endfor %}
return true;
}
FtcImpl::~FtcImpl()
{
enableAll(false);
}
{% for ft in functionTopics.member %}
// Function Topic '{{ ft.name }}'
bool FtcImpl::enable{{ ft.name }}(bool enable)
{
if (enable)
{
if (m_ftc{{ ft.name }})
{
INFO_LOG(m_logger) << "{{ ft.name }} already enabled!";
return true;
}
m_ftc{{ ft.name }}.reset(
m_ddsConnector->createFTC<FTC::{{ ft.name }}>(this));
assert(m_ftc{{ ft.name }}.get());
}
else
{
m_ftc{{ ft.name }} = nullptr;
}
return true;
}
{% endfor %}
template<typename T>
static T buildErrorReason(int reason, int requestId)
{
std::string errString;
std::vector<std::string> errArgs;
switch (reason)
{
case 1:
errString = tr_lts("dds", "Service could not handle request");
break;
case 3:
errString = tr_lts("dds", "Timeout while waiting for answer from server");
break;
default:
errString = tr_lts("dds", "Unknown error reason: %1");
errArgs.push_back(std::to_string(reason));
break;
}
T reply;
reply.id = requestId;
reply.errorString = errString;
reply.errorParams = errArgs;
return reply;
}
{% for ft in functionTopics.member %}
//
// {{ ft.name }}
//
int64_t FtcImpl::{{ ft.requestFlc }}(const {{ ft.requestType }} &request, {{ ft.replyHandler }} *handler)
{
return {{ ft.requestFlc }}(request, [ handler ](const {{ ft.replyTypeFro }} &reply) { if (handler) handler->{{ ft.onReply }}(reply); });
}
int64_t FtcImpl::{{ ft.requestFlc }}(const {{ ft.requestType }} &request, const std::function<void(const {{ ft.replyTypeFro }}&)> &cb)
{
if (!m_ftc{{ ft.name }})
{
WARNING_LOG(m_logger) << "Error requesting function topic {{ ft.name }} - not enabled";
Q_ASSERT(false);
return 0;
}
static const char * const slotReply = SLOT({{ ft.onReply }}({{ namespace }}::{{ ft.reply }},int,bool));
static const char * const slotError = SLOT({{ ft.onError }}(int,int,int));
int uniqueId = 0;
if (!m_ftc{{ ft.name }}->requestDataClassic(request, slotReply, slotError, uniqueId))
return 0;
m_{{ ft.replyHandlerFlc }}[uniqueId] = cb;
// first 16 hex bytes from Sha3_512 hash for '{{ ft.name }}' converted to an int64
m_last{{ ft.request }}UniqueId = uniqueId + {{ ft.replyHandlerHash }};
return m_last{{ ft.request }}UniqueId;
}
// NOTE: do not use if you call an FTC which returns an id while also containing an error as "error doesn't mean that the service might not reply with an id - so always take the id"
{% if ft.isCoreFt %}int32_t{% else %}{{ ft.replyTypeFro }}{% endif %} FtcImpl::{{ ft.requestFlc }}(const {{ ft.requestType }} &request) noexcept(false)
{
if (!m_ftc{{ ft.name }})
{
Q_ASSERT(false);
throw std::runtime_error("Error requesting function topic {{ ft.name }} - not enabled");
}
auto *requestWatcher = new Request2();
auto future = m_ftc{{ ft.name }}->requestDataModern(request, requestWatcher);
m_currentlyOpen_{{ ft.requestFlc }}Requests++;
//fprintf(stderr, "{{ ft.requestFlc }}: waiting for reply... (currently open requests: %d)\n", m_currentlyOpen_{{ ft.requestFlc }}Requests);
LtsUtils::await(requestWatcher, &Request2::finished, true); // blocks until request has reply or failed
m_currentlyOpen_{{ ft.requestFlc }}Requests--;
//fprintf(stderr, "{{ ft.requestFlc }}: request done!\n");
// the generic ftc impl's 'requestDataModern' + 'exec()' guarantees that calling 'future.get()' will not result in a 'wait()' call by 'get()' - but we should still check!
switch (future.wait_for(std::chrono::seconds(1)))
{
case std::future_status::deferred:
{
Q_ASSERT(false);
// fprintf(stderr, "{{ ft.requestFlc }}: deferred...\n");
break;
}
case std::future_status::timeout:
{
Q_ASSERT(false);
// fprintf(stderr, "{{ ft.requestFlc }}: future timeout!\n");
throw std::runtime_error("Future timeout!");
}
case std::future_status::ready:
{
// fprintf(stderr, "{{ ft.requestFlc }}: ready\n");
break;
}
}
{% if ft.isCoreFt %}
const auto _reply = future.get(); // throws if exception set
Core::FunctionReplyObject fro(
{% for rp in ft.replyParams %} _reply.{{ rp.nameGetter }}{% if not forloop.last %},{% endif %} {% endfor %}
);
fro.setUniqueId(m_last{{ ft.request }}UniqueId);
if (fro.error())
{
ERROR_LOG(m_logger) << "FTC call failed: " << fro.translated();
throw std::runtime_error(fro.translated().toStdString().c_str());
}
const auto reply = fro.id();
{% else %}
const auto reply = future.get(); // throws if exception set
{% endif %}
return reply;
}
void FtcImpl::{{ ft.onReply }}(const {{ namespace }}::{{ ft.reply }} &reply, int uniqueId, bool bRequestFinished)
{
auto it = m_{{ ft.replyHandlerFlc }}.find(uniqueId);
if (it == m_{{ ft.replyHandlerFlc }}.end())
{
WARNING_LOG(m_logger) << "Error replying function topic {{ ft.name }} - no replyHandler";
Q_ASSERT(false);
return;
}
auto cb = it.value();
if (bRequestFinished)
m_{{ ft.replyHandlerFlc }}.erase(it);
if (!cb)
return;
m_last{{ ft.request }}UniqueId = uniqueId + {{ ft.replyHandlerHash }};
{% if ft.isCoreFt %}
Core::FunctionReplyObject fro(
{% for rp in ft.replyParams %}
reply.{{ rp.nameGetter }}{% if not forloop.last %},{% endif %} {% endfor %}
);
fro.setUniqueId(m_last{{ ft.request }}UniqueId);
cb(fro);
{% else %}
cb(reply);
{% endif %}
}
void FtcImpl::{{ ft.onError }}(int reason, int uniqueId, int requestId)
{
{{ ft.onReply }}(buildErrorReason<{{ namespace }}::{{ ft.reply }}>(reason, requestId), uniqueId, true);
}
{% endfor %}
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}