| 132 | // RPC Processing |
| 133 | |
| 134 | fl::json Remote::processRpc(const fl::json& request) { |
| 135 | // Extract optional timestamp field (0 = immediate, >0 = scheduled) |
| 136 | u32 timestamp = 0; |
| 137 | if (request.contains("timestamp") && request["timestamp"].is_int()) { |
| 138 | timestamp = static_cast<u32>(request["timestamp"].as_int().value()); |
| 139 | } |
| 140 | |
| 141 | u32 receivedAt = fl::millis(); |
| 142 | |
| 143 | // Execute or schedule |
| 144 | if (timestamp == 0) { |
| 145 | // Store request ID BEFORE invoking function (needed for async functions) |
| 146 | // This allows sendAsyncResponse() to find the request ID while function is running |
| 147 | if (request.contains("id") && request.contains("method")) { |
| 148 | fl::string methodName = request["method"].as_string().value_or(""); |
| 149 | int requestId = request["id"].as_int().value_or(0); |
| 150 | mAsyncRequests[methodName] = {requestId, receivedAt}; |
| 151 | FL_DBG("Stored request ID for " << methodName.c_str() << " (id=" << requestId << ")"); |
| 152 | } |
| 153 | |
| 154 | // Immediate execution - pass directly to Rpc |
| 155 | fl::json response = mRpc.handle(request); |
| 156 | |
| 157 | // For async functions, response already sent via sendAsyncResponse() |
| 158 | if (response.contains("__async") && response["__async"].as_bool().value_or(false)) { |
| 159 | // Don't return response (ACK already sent by Rpc) |
| 160 | // Return null to prevent Server from queuing it |
| 161 | fl::json nullResponse = fl::json::object(); |
| 162 | nullResponse.set("__skip", true); // Marker to skip queueing |
| 163 | return nullResponse; |
| 164 | } |
| 165 | |
| 166 | // For sync functions, remove request ID (not needed) |
| 167 | if (request.contains("method")) { |
| 168 | fl::string methodName = request["method"].as_string().value_or(""); |
| 169 | mAsyncRequests.erase(methodName); |
| 170 | } |
| 171 | |
| 172 | // Record result if successful |
| 173 | if (response.contains("result") && request.contains("method")) { |
| 174 | fl::string funcName = request["method"].as_string().value_or(""); |
| 175 | recordResult(funcName, response["result"], 0, receivedAt, receivedAt, false); |
| 176 | } |
| 177 | |
| 178 | return response; |
| 179 | } else { |
| 180 | // Scheduled execution - result will be pushed to ResponseSink after execution |
| 181 | scheduleFunction(timestamp, receivedAt, request); |
| 182 | FL_DBG("RPC: Scheduled function - result will be pushed after execution"); |
| 183 | |
| 184 | // Return acknowledgment with null result and "scheduled" marker |
| 185 | fl::json response = fl::json::object(); |
| 186 | if (request.contains("id")) { |
| 187 | response.set("id", request["id"]); |
| 188 | } |
| 189 | response.set("result", fl::json(nullptr)); |
| 190 | response.set("scheduled", true); // Marker to not queue this response |
| 191 | return response; |
no test coverage detected