183 {
185#if AUTORESEARCH_LEGACY_OBSERVES_RMT4_FRAMES && FL_RMT4_ASYNC
186 using namespace legacy_async_errors;
187 const fl::string mode =
args[
"mode"].as_string().value_or(
"unavailable");
188 result.set("mode", mode);
189 const bool injected = mode == "task-allocation" ||
190 mode == "mutex-allocation" || mode == "install";
191 const bool recoveryMode = mode == "timeout" || mode == "invalid-batch";
192 if (mode != "unavailable" && mode != "setup" && !recoveryMode && !injected) {
193 result.set("success", false);
194 result.set("error", "Unknown legacy asynchronous failure fixture mode");
195 return result;
196 }
197 if (mode == "timeout" &&
198 (FASTLED_RMT4_TRANSMISSION_TIMEOUT_MS != 1 || !FL_RMT4_DIAGNOSTICS)) {
199 result.set("success", false);
200 result.set("error", "timeout fixture requires FASTLED_RMT4_TRANSMISSION_TIMEOUT_MS=1 and FL_RMT4_DIAGNOSTICS=1");
201 return result;
202 }
203 if (mode == "invalid-batch" && !FL_RMT4_DIAGNOSTICS) {
204 result.set(
"success",
false);
205 result.set(
"error",
"invalid-batch fixture requires FL_RMT4_DIAGNOSTICS=1");
207 }
209 result.set(
"success",
false);
210 result.set(
"error",
"Existing output did not drain before fixture");
212 }
213 if (injected) {
214#if defined(FL_RMT4_FAULT_TEST) && FL_RMT4_FAULT_TEST == 1
215 return injectedFailureProbe(mode);
216#else
217 result.set(
"success",
false);
218 result.set(
"error",
"Requires FL_RMT4_FAULT_TEST=1 and the documented SDK linker wrappers");
220#endif
221 }
222 auto &manager = fl::RmtMemoryManager4::instance();
223 const size_t baseline = manager.allocatedWords();
224 auto frame = makeFrame(mode == "timeout" ? 4000 : 1);
225 if (!frame) {
226 result.set(
"success",
false);
227 result.set(
"error",
"Fixture frame allocation failed");
229 }
230 Observation observed;
231 bool recovered = false;
232 unsigned reserved = 0;
233 fl::u32 reservationMask = 0;
234 if (mode == "unavailable") {
235 FreeBlockReservations reservations(manager);
236 reserved = reservations.count();
237 reservationMask = reservations.mask();
239 observed = submit(driver, frame);
240 } else if (mode == "setup") {
241
242
243 fl::Rmt4AsyncDriver driver{fl::shared_ptr<fl::detail::IRMT4Peripheral>()};
244 observed = submit(driver, frame);
245 } else {
247 if (mode == "invalid-batch") {
248
249
250 driver.enqueue(fl::ChannelDataPtr());
251 }
252 observed = submit(driver, frame);
253 recovered = recoverySucceeded(driver);
254 }
255 const size_t after = manager.allocatedWords();
256 bool reservationsRestored = true;
257 for (unsigned block = 0; block < 32; ++block) {
258 if (reservationMask & (fl::u32(1) << block)) {
259 const bool available = manager.tryReserveBlocks(
static_cast<int>(block), 1);
261 if (available) { manager.releaseBlocks(static_cast<int>(block), 1); }
262 }
263 }
264 appendObservation(result, observed);
265 result.set(
"reservedFreeBlocks",
static_cast<int>(reserved));
266 result.set(
"allocatedWordsBefore",
static_cast<int>(baseline));
267 result.set(
"allocatedWordsAfter",
static_cast<int>(after));
268 result.set(
"ledgerRestored", baseline == after);
269 result.set(
"fixtureReservationsRestored", reservationsRestored);
270 result.set(
"recoveryAttempted", recoveryMode);
271 result.set(
"recoveredOnSameDriver", recoveryMode && recovered);
272 result.set(
"success", observed.leasedAtSubmission &&
273 observed.releasedAutonomously && observed.errorReported &&
274 !observed.completionSucceeded && baseline == after &&
275 (mode != "timeout" ||
276 (observed.translatedBytes > 0 && observed.translatedBytes < frame->getSize())) &&
277 (mode != "invalid-batch" || observed.translatedBytes == 3) &&
278 reservationsRestored && (!recoveryMode || recovered));
279#else
281 result.set(
"success",
false);
282 result.set(
"error",
"Requires asynchronous classic ESP32 RMT4 output");
283#endif
285}
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
void wait() FL_NO_EXCEPT
Wait for all channel bus transmissions to complete.
static json object() FL_NO_EXCEPT
shared_ptr< T > make_shared(Args &&... args) FL_NO_EXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
int available() FL_NO_EXCEPT