/** * {{ namespace }} Function Topic Client implementation * $Id$ * $HeadURL$ * $Author$ */ {% autoescape off %} // filename: {{ namespace_abbr }}_ftc_impl.cpp #include "{{ namespace_abbr }}_ftc_impl.h" #include #include #include #include #include #include //using namespace std::literals::chrono_literals; namespace {{ namespace }} { namespace Interface { // initializer function Q_DECL_EXPORT std::shared_ptr Ftc::create(const std::shared_ptr &ddsConnector, bool forceCreate) { // share only for main thread if (qApp->thread() != QThread::currentThread() || forceCreate) return std::make_shared(ddsConnector); static std::unordered_map> 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(ddsConnector); s_availableConnectors.insert(std::make_pair(ddsConnector.get(), ret)); return std::move(ret); // c++11 defect } // FtcImpl FtcImpl::FtcImpl(const std::shared_ptr &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(this)); assert(m_ftc{{ ft.name }}.get()); } else { m_ftc{{ ft.name }} = nullptr; } return true; } {% endfor %} template static T buildErrorReason(int reason, int requestId) { std::string errString; std::vector 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 &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 %}