* @brief Runs an apiCall on the GUI thread so handlers never touch GUI objects off-thread. * Fails fast once shutdown is requested: a call drained late in teardown must never * execute a command against modules that are already torn down. */
| 62 | * execute a command against modules that are already torn down. |
| 63 | */ |
| 64 | QVariantMap DataModel::ControlApiMarshaller::dispatch(const QString& method, |
| 65 | const QVariantMap& params) |
| 66 | { |
| 67 | static std::atomic<quint64> s_requestSeq{0}; |
| 68 | |
| 69 | if (s_shutdownRequested.load(std::memory_order_acquire)) { |
| 70 | QVariantMap out; |
| 71 | out.insert(QStringLiteral("ok"), false); |
| 72 | out.insert(QStringLiteral("error"), QStringLiteral("apiCall: application is shutting down")); |
| 73 | return out; |
| 74 | } |
| 75 | |
| 76 | API::CommandRequest request; |
| 77 | request.id = QStringLiteral("cs-%1").arg(s_requestSeq.fetch_add(1) + 1); |
| 78 | request.command = method; |
| 79 | request.params = QJsonObject::fromVariantMap(params); |
| 80 | |
| 81 | const auto response = API::CommandHandler::instance().processCommand(request); |
| 82 | |
| 83 | QVariantMap out; |
| 84 | out.insert(QStringLiteral("ok"), response.success); |
| 85 | if (response.success) { |
| 86 | if (!response.result.isEmpty()) |
| 87 | out.insert(QStringLiteral("result"), response.result.toVariantMap()); |
| 88 | } else { |
| 89 | out.insert(QStringLiteral("error"), response.errorMessage); |
| 90 | out.insert(QStringLiteral("errorCode"), response.errorCode); |
| 91 | if (!response.errorData.isEmpty()) |
| 92 | out.insert(QStringLiteral("errorData"), response.errorData.toVariantMap()); |
| 93 | } |
| 94 | return out; |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * @brief GUI-thread entry that arms reply capture and writes to the device in one step. |
nothing calls this directly
no test coverage detected