Files
lab-rv32i-freertos-task-sta…/json/card_source.json
T
2026-07-19 17:18:13 +02:00

665 lines
60 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"$schema": "../../../tools/card-layouts/schemas/card-source.schema.json",
"schema": "esc-card-source.v1",
"card": {
"id": "mpabi-freertos-02-task-wrapper-crtp",
"series": "freertos-cpp",
"series_title": "FreeRTOS C++",
"number": "02",
"count": "16",
"slug": "task-wrapper-crtp",
"title": "C++ Task wrapper: OOP i cykl życia",
"topic": "Dziedziczenie CRTP, statyczny trampoline i wykonanie taska w FreeRTOS",
"project": "Freestanding C++ nad FreeRTOS",
"subject": "Informatyka",
"level": "Rok 2 · L02 · RV32I/Hazard3",
"revision_date": "2026-07-17T00:00:00+02:00",
"status": "Robocza",
"version": "v00.04",
"uuid": "3400a752-f4d6-579c-a033-73677fef4b74",
"author": "M. Pabiszczak",
"year": "2026"
},
"generated": {
"tex": "doc/generated/main.tex",
"html": "web/index.html",
"html_css": "web/style.css",
"html_tree_inspector": false,
"react_app": true
},
"render_dictionary": false,
"viewpoints": [
{"id": "A1", "label": "CONTEXT", "subtitle": "Aplikacja → wrapper → FreeRTOS → Hazard3", "status": "enabled", "target_label": "fig:a1-context"},
{"id": "A2", "label": "STRUCTURE", "subtitle": "Klasy, pola i kontrakt CRTP", "status": "enabled", "target_label": "fig:task-wrapper-class"},
{"id": "A3", "label": "DISPATCH", "subtitle": "Callback C → trampoline → bezpośrednie run()", "status": "enabled", "target_label": "fig:a3-dispatch"},
{"id": "A4", "label": "APPLICATION", "subtitle": "Dwie instancje SumTask i SupervisorTask", "status": "enabled", "target_label": "fig:a4-application"},
{"id": "A5", "label": "FLOW", "subtitle": "Deterministyczny przebieg E01E12", "status": "enabled", "target_label": "fig:task-wrapper-sequence-a"},
{"id": "A6", "label": "STATE", "subtitle": "Życie obiektu, stan wrappera i stan kernela", "status": "enabled", "target_label": "fig:a6-state"},
{"id": "A7", "label": "RUNTIME", "subtitle": "Obiekt, TCB, stos, rejestry, tick i PC", "status": "enabled", "target_label": "fig:a7-runtime"},
{"id": "A8", "label": "PATTERNS", "subtitle": "Role, decyzje i świadomie odrzucone mechanizmy", "status": "enabled", "target_label": "fig:a8-patterns"}
],
"debug_strategies": {
"code.context": {
"id": "code.context", "kind": "code", "action": "open",
"prerequisites": ["repozytorium K02", "zweryfikowany source blob"],
"layout": ["read-only source", "granica architektoniczna"],
"commands": ["rg -n '#include|extern|xTaskCreate'"],
"expected_observations": ["zależności prowadzą wyłącznie w dół stosu"],
"assertions": ["kernel i port pozostają w C", "aplikacja zna wyłącznie publiczną powierzchnię wrappera"],
"evidence_fields": ["code_ref", "source_git_blob", "zaznaczona relacja UML"]
},
"code.structure": {
"id": "code.structure", "kind": "code", "action": "open",
"prerequisites": ["freestanding C++17", "nagłówek include/freertos/task.hpp"],
"layout": ["source", "diagram klas"],
"commands": ["make check-contracts"],
"expected_observations": ["Task<Derived> przechowuje handle i stan", "friend udostępnia prywatne run() tylko bazie"],
"assertions": ["Derived dziedziczy Task<Derived>", "wrapper jest niekopiowalny i nieprzenoszalny"],
"evidence_fields": ["source blob", "pozytywny test kontraktu", "negatywny test kompilacji"]
},
"code.abi": {
"id": "code.abi", "kind": "code", "action": "open",
"prerequisites": ["zbudowany prog.elf"],
"layout": ["script ABI", "nm/readelf", "listing"],
"commands": ["make check-abi", "riscv64-unknown-elf-nm -C build/hazard3/task01_task_wrapper/prog.elf"],
"expected_observations": ["bez vtable, typeinfo, __cxa_* i _Unwind_*"],
"assertions": ["dispatch nie wymaga RTTI ani wywołania przez vptr"],
"evidence_fields": ["ELF sha256", "wynik check_abi.sh", "bezpośredni jal"]
},
"code.pattern": {
"id": "code.pattern", "kind": "code", "action": "open",
"prerequisites": ["A2 STRUCTURE", "A3 DISPATCH"],
"layout": ["mapa ról", "source"],
"commands": ["rg -n 'trampoline|start|TaskState|static_assert' include src"],
"expected_observations": ["każda nazwana rola wskazuje konkretny fragment kodu"],
"assertions": ["CRTP jest idiomem statycznego dispatchu", "karta nie nazywa wrappera RAII ani dynamicznym Template Method"],
"evidence_fields": ["rola", "uczestnik", "trade-off", "code_ref"]
},
"run.create": {
"id": "run.create", "kind": "run", "action": "replay",
"prerequisites": ["Hazard3 online", "czysta RAM", "aktualny ELF"],
"layout": ["source", "registers", "object memory", "heap_4"],
"commands": ["info registers a0 a1 sp", "print g_last_checkpoint", "print g_checkpoint_object"],
"expected_observations": ["ten sam obiekt przechodzi od constructed do ready", "handle powstaje w heap_4"],
"assertions": ["replay osiąga właściwy E-event", "warunek instancji wskazuje workerA"],
"evidence_fields": ["snapshot_ref", "event_id", "object address", "handle", "timestamp"]
},
"run.dispatch": {
"id": "run.dispatch", "kind": "run", "action": "replay",
"prerequisites": ["scheduler uruchomiony", "workerA READY"],
"layout": ["source trampoline", "assembly", "a0", "task stack"],
"commands": ["info registers a0 sp pc", "x/i $pc", "bt"],
"expected_observations": ["void* i Derived* mają ten sam adres", "wywołanie run() jest bezpośrednie"],
"assertions": ["SP należy do stosu taska", "brak odczytu vptr przed run()"],
"evidence_fields": ["context", "this", "SP", "PC", "instruction"]
},
"run.scheduler": {
"id": "run.scheduler", "kind": "run", "action": "replay",
"prerequisites": ["dwa workery o równym priorytecie", "time slicing włączony"],
"layout": ["source loop", "tick", "trace", "PC"],
"commands": ["print g_trace_task[0]@3", "print g_context_switches", "print g_elapsed_ticks"],
"expected_observations": ["ślad zaczyna się A → B → A", "obliczenie przekracza co najmniej dwa ticki"],
"assertions": ["context switches >= 2", "worker A trafia do E08"],
"evidence_fields": ["trace", "tick", "switch count", "timestamp"]
},
"run.state": {
"id": "run.state", "kind": "run", "action": "replay",
"prerequisites": ["kanoniczny checkpoint"],
"layout": ["wrapper fields", "public kernel state", "source"],
"commands": ["print g_last_checkpoint", "info registers sp pc"],
"expected_observations": ["stan diagnostyczny wrappera nie jest eTaskState", "completed poprzedza self-delete"],
"assertions": ["zmiana stanu odpowiada wybranemu zdarzeniu", "self-delete nie niszczy statycznego obiektu C++"],
"evidence_fields": ["wrapper state", "kernel state", "event_id", "PC"]
},
"run.runtime": {
"id": "run.runtime", "kind": "run", "action": "replay",
"prerequisites": ["Hazard3/GDB online", "symbole ELF"],
"layout": ["object", "TCB", "stack", "registers", "listing"],
"commands": ["info registers a0 sp pc", "print g_tcb[0]", "print g_stack_low[0]", "print g_stack_high[0]"],
"expected_observations": ["obiekt, TCB i stos są trzema różnymi obszarami", "SP mieści się w zakresie stosu taska"],
"assertions": ["TCB leży w heap_4", "PC odpowiada aktywnemu checkpointowi"],
"evidence_fields": ["object", "TCB", "stack range", "SP", "PC", "ELF hash"]
},
"run.pass": {
"id": "run.pass", "kind": "run", "action": "replay",
"prerequisites": ["oba workery zakończone", "supervisor uruchomiony"],
"layout": ["results", "states", "trace", "assertions"],
"commands": ["print g_results", "print g_iterations", "print g_task_wrapper_pass"],
"expected_observations": ["oba wyniki = 200010000", "oba obliczenia mają 20000 iteracji"],
"assertions": ["PASS = 1", "elapsed ticks >= 2", "context switches >= 2"],
"evidence_fields": ["results", "iterations", "PASS", "ticks", "switches", "timestamp"]
}
},
"template": "templates/karta-klasyczna.json",
"title_block": {
"category": "KARTA PRACY · INFORMATYKA",
"prepared_by": "M. Pabiszczak",
"prepared_on": "2026-07-17T00:00:00+02:00",
"title": "C++ Task wrapper: OOP i cykl życia",
"url": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/948ce9fd-3d0c-5721-a0ae-ef422008fec8",
"repository_url": "https://zsl-gitea.mpabi.pl/edu-freertos-cpp/lab-rv32i-freertos-task-stack-vector",
"url_host_uuid": "dce7fb9d-7b2f-5d49-96a2-3a30d3070b84",
"url_domain": "mpabi.pl",
"doc_uuid": "948ce9fd-3d0c-5721-a0ae-ef422008fec8",
"revision": "v00.04",
"issued_on": "2026-07-17T00:00:00+02:00",
"series": "FREERTOS-CPP-02",
"document_type": "karta pracy",
"tool": "card-layouts",
"show_qr": true,
"show_repository_qr": true,
"height_cm": 2.6,
"repeat_on_every_page": true,
"replace_front_matter": true
},
"front_page_scope": {
"title": "Cel karty",
"content_tex": "Uczeń buduje i uruchamia mały, header-only wrapper \\texttt{Task<Derived>}. Na UML klas rozpoznaje dziedziczenie CRTP, enkapsulację i statyczny polimorfizm. Na UML sekwencji przechodzi po rzeczywistym cyklu \\texttt{start -> xTaskCreate -> trampoline -> run -> vTaskDelete} i odtwarza każdy punkt w Hazard3/GDB.",
"scope_title": "Zakres karty",
"scope_content_tex": "Karta zawiera dokładnie jeden task C++. Dwie instancje \\texttt{SumTask} wykonują obliczenie dłuższe niż okno ticka, a \\texttt{SupervisorTask} sprawdza wynik i stany wrapperów. Kernel i port pozostają w C. Poza zakresem są peryferia, Vector, porównanie z C, wyjątki, RTTI oraz hosted libstdc++.",
"scope_table": {
"headers": ["Lekcja", "Task", "Najważniejsza idea", "Priorytet", "Status", "Version"],
"rows": [
{
"chapter": "L02",
"task": "Task01",
"idea_tex": "OOP \\texttt{Task<Derived>} i debugowanie cyklu życia",
"priority": "główny",
"status": "ready",
"version": "v00.03",
"key": true
}
]
}
},
"side_margin_tree_layout": {
"columns": [
{"id": "zawodowe", "label": "TECH", "side": "left", "tree": "WE -> EK -> KW", "description": "Dowody techniczne."},
{"id": "ogolne", "label": "OG", "side": "right", "tree": "WE -> EN -> KW", "description": "Model OOP i wnioski."}
]
},
"learning_effects": {
"K02.EN01": {
"bloom_level": "Analiza",
"label": "Model OOP wrappera",
"text": "Uczeń odczytuje dziedziczenie CRTP, widoczność składowych, relację friend oraz zależność od C API FreeRTOS.",
"assessment_criteria": ["K02.KW01"]
},
"K02.EK01": {
"bloom_level": "Zastosowanie",
"label": "Replay cyklu Task",
"text": "Uczeń wybiera strzałkę UML, odtwarza checkpoint i wiąże stan C++ z ramką, rejestrami i stanem kernela.",
"assessment_criteria": ["K02.KW01"]
}
},
"assessment_criteria": {
"K02.KW01": {
"text": "Program kończy się PASS; oba wyniki wynoszą 200010000; uczeń dowodzi przejść constructed, ready, running i completed oraz wskazuje bezpośrednie wywołanie Derived::run bez vtable.",
"learning_effects": ["K02.EN01", "K02.EK01"]
}
},
"educational_requirements": {
"K02.WE01": {
"text": "Zaprojektowanie i zbadanie pierwszego wrappera Task w freestanding C++17.",
"label": "C++ Task wrapper",
"learning_effects": ["K02.EN01", "K02.EK01"],
"learning_tree": {
"schema": "we-learning-tree.v1",
"policy": "Każda relacja z UML ma odpowiadać obserwowalnemu punktowi programu.",
"ogolne": [
{
"effect_ref": "K02.EN01",
"display": "EN LOCAL OOP.01",
"source": "LOCAL",
"official": "OOP",
"local": "01",
"kind": "EN",
"tree_id": "K02.WE01.OG.LOCAL.OOP.01",
"text": "Czyta model klas i odróżnia statyczny polimorfizm od virtual dispatch.",
"kw": [
{"criterion_ref": "K02.KW01", "display": "KW LOCAL OOP.01", "source": "LOCAL", "kind": "KW", "official": "OOP", "local": "01", "text": "Wskazuje dziedziczenie, friend, prywatne run i brak vtable."}
]
}
],
"zawodowe": [
{
"effect_ref": "K02.EK01",
"display": "EK LOCAL RTOS.01",
"source": "LOCAL",
"official": "RTOS",
"local": "01",
"kind": "EK",
"tree_id": "K02.WE01.TECH.LOCAL.RTOS.01",
"text": "Odtwarza cykl wrappera w Hazard3/GDB.",
"kw": [
{"criterion_ref": "K02.KW01", "display": "KW LOCAL RTOS.01", "source": "LOCAL", "kind": "KW", "official": "RTOS", "local": "01", "text": "Łączy checkpoint z konkretną strzałką UML i stanem obiektu."}
]
}
]
}
}
},
"debug_checkpoints": {
"schema": "stem-debug-checkpoints.v1",
"semantics": "deterministic-replay",
"artifact": {
"source": "src/tasks/task01_task_wrapper.cpp",
"source_git_blob": "a6a7366678f0282e4999ef2ee5770ffad96441a7",
"elf": "build/task01_task_wrapper/prog.elf",
"hazard3_image": "build/task01_task_wrapper/prog.bin",
"hazard3_image_sha256": "8fc3d08b0904ddd0a2cc1c01754e20457caaf21da7f5dbddef8c16772eb330cb"
},
"targets": {
"hazard3-sim": {
"adapter": "hazard3-reset-load-replay.v1",
"baseline": "restart-backend",
"clean_ram": true
}
},
"items": {
"task01.constructed": {"event_id": "E01", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 1 && $a1 == &'(anonymous namespace)::g_worker_a'"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_last_checkpoint'", "equals": 1}]}},
"task01.start-enter": {"event_id": "E02", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 2 && $a1 == &'(anonymous namespace)::g_worker_a' && '(anonymous namespace)::g_last_checkpoint' == 1"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_last_checkpoint'", "equals": 2}]}},
"task01.create-call": {"event_id": "E03", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 3 && $a1 == &'(anonymous namespace)::g_worker_a'"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_last_checkpoint'", "equals": 3}]}},
"task01.ready": {"event_id": "E04", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 4 && $a1 == &'(anonymous namespace)::g_worker_a'"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_last_checkpoint'", "equals": 4}]}},
"task01.scheduler": {"event_id": "E05", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 5"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_last_checkpoint'", "equals": 5}, {"expr": "'(anonymous namespace)::g_duplicate_start_rejected'", "equals": 1}, {"expr": "'(anonymous namespace)::g_worker_a_handle_after_start' != 0", "equals": 1}]}},
"task01.trampoline": {"event_id": "E06", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 6 && $a1 == &'(anonymous namespace)::g_worker_a'"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_last_checkpoint'", "equals": 6}]}},
"task01.run": {"event_id": "E07", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 7 && $a1 == &'(anonymous namespace)::g_worker_a'"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_last_checkpoint'", "equals": 7}]}},
"task01.timeslice": {"event_id": "E08", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 8 && $a1 == &'(anonymous namespace)::g_worker_a'"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_timeslice_checkpoint_seen'", "equals": 1}, {"expr": "'(anonymous namespace)::g_context_switches' >= 2", "equals": 1}]}},
"task01.run-return": {"event_id": "E09", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 9 && $a1 == &'(anonymous namespace)::g_worker_a'"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_results'[0]", "equals": 200010000}, {"expr": "'(anonymous namespace)::g_iterations'[0]", "equals": 20000}]}},
"task01.completed": {"event_id": "E10", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 10 && $a1 == &'(anonymous namespace)::g_worker_a'"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_last_checkpoint'", "equals": 10}, {"expr": "'(anonymous namespace)::g_results'[0]", "equals": 200010000}]}},
"task01.delete": {"event_id": "E11", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 11 && $a1 == &'(anonymous namespace)::g_worker_a'"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_last_checkpoint'", "equals": 11}, {"expr": "'(anonymous namespace)::g_results'[0]", "equals": 200010000}]}},
"task01.pass": {"event_id": "E12", "stop": {"symbol": "task_wrapper_debug_checkpoint", "offset": 0, "condition": "$a0 == 12 && $a1 == &'(anonymous namespace)::g_supervisor'"}, "verify": {"expressions": [{"expr": "'(anonymous namespace)::g_task_wrapper_pass'", "equals": 1}, {"expr": "'(anonymous namespace)::g_results'[0]", "equals": 200010000}, {"expr": "'(anonymous namespace)::g_results'[1]", "equals": 200010000}, {"expr": "'(anonymous namespace)::g_iterations'[0]", "equals": 20000}, {"expr": "'(anonymous namespace)::g_iterations'[1]", "equals": 20000}, {"expr": "'(anonymous namespace)::g_duplicate_start_rejected'", "equals": 1}, {"expr": "'(anonymous namespace)::g_elapsed_ticks' >= 2", "equals": 1}, {"expr": "'(anonymous namespace)::g_context_switches' >= 2", "equals": 1}]}}
}
},
"sections": [
{
"title": "A1 — Context",
"order": 10,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"content_tex": "Granice odpowiedzialności: aplikacja, wrapper, FreeRTOS i Hazard3.",
"assets": [
{
"path": "assets/a1-context.png",
"html_path": "assets/a1-context.svg",
"source_path": "assets/a1-context.puml",
"caption": "A1 CONTEXT — pięć granic od aplikacji do Hazard3.",
"label": "fig:a1-context",
"alt": "Kontekst aplikacji, wrappera C++, FreeRTOS C API, kernela i symulatora Hazard3.",
"kind": "diagram",
"width": 1.0,
"page_grid": {
"columns": 1,
"rows": 1,
"page_width": 660,
"page_height": 760,
"overview": true,
"step_tiles": {"application": 1, "wrapper": 1, "c-api": 1, "kernel-port": 1, "hazard3": 1}
},
"interactive": {
"kind": "uml-class",
"storage_key": "k02-a1-context",
"title": "A1 · granice systemu",
"task": {"id": "task01", "label": "Task01 · C++ Task wrapper"},
"block": {"id": "a1-context", "label": "A1 CONTEXT", "description": "Kierunek zależności i odpowiedzialności warstw; bez udawania wykonania runtime."},
"phases": [{
"id": "boundaries", "label": "BOUNDARIES", "description": "Od kodu aplikacji do CPU Hazard3.",
"steps": [
{"id": "application", "number": 1, "label": "Application tasks", "mode": "CODE", "strategy_ref": "code.context", "svg_label": "01", "svg_target": "Application", "code_ref": "src/tasks/task01_task_wrapper.cpp:72", "description": "SumTask i SupervisorTask są konkretną warstwą aplikacyjną.", "evidence": "Kod aplikacji dziedziczy publiczny wrapper i nie dotyka portu kernela."},
{"id": "wrapper", "number": 2, "label": "Header-only C++ wrapper", "mode": "CODE", "strategy_ref": "code.context", "svg_label": "02", "svg_target": "Wrapper", "code_ref": "include/freertos/task.hpp:45", "description": "Task<Derived> jest cienką warstwą freestanding C++17.", "evidence": "Cała implementacja publicznego wrappera znajduje się w .hpp."},
{"id": "c-api", "number": 3, "label": "FreeRTOS C API boundary", "mode": "CODE", "strategy_ref": "code.context", "svg_label": "03", "svg_target": "CAPI", "code_ref": "include/freertos/task.hpp:7", "description": "Nagłówki kernela są objęte extern C, a callback ma ABI C.", "evidence": "Granica obejmuje xTaskCreate, vTaskDelete i TaskFunction_t."},
{"id": "kernel-port", "number": 4, "label": "Kernel, scheduler and heap_4", "mode": "CODE", "strategy_ref": "code.context", "svg_label": "04", "svg_target": "Kernel", "code_ref": "include/FreeRTOSConfig.h:1", "description": "Scheduler, tick, TCB i alokacja pozostają odpowiedzialnością FreeRTOS.", "evidence": "Konfiguracja włącza time slicing i dynamiczne tworzenie tasków."},
{"id": "hazard3", "number": 5, "label": "Hazard3 execution target", "mode": "CODE", "strategy_ref": "code.context", "svg_label": "05", "svg_target": "Hazard3", "code_ref": "Makefile:1", "description": "Artefakt RV32I jest wykonywany w symulatorze Hazard3 i obserwowany przez GDB.", "evidence": "Profil budowania wskazuje freestanding RV32I/Hazard3."}
]
}]
}
}
]
},
{
"title": "A2 — Structure",
"order": 15,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"page_orientation": "landscape",
"content_tex": "Statyczna struktura wrappera i jego kontrakt ABI.",
"assets": [
{
"path": "assets/task-wrapper-class.png",
"html_path": "assets/task-wrapper-class.svg",
"source_path": "assets/task-wrapper-class.puml",
"caption": "UML klas — Task<Derived>, SumTask, SupervisorTask i zależność od FreeRTOS C API.",
"label": "fig:task-wrapper-class",
"alt": "Diagram klas wrappera Task pokazujący dziedziczenie CRTP i zależność od C API FreeRTOS.",
"kind": "diagram",
"width": 1.0,
"page_grid": {
"columns": 1,
"rows": 1,
"page_width": 940,
"page_height": 560,
"overview": true,
"step_tiles": {
"crtp-template": 1,
"sumtask-inheritance": 1,
"friend-private-run": 1,
"start-state": 1,
"static-trampoline": 1,
"freertos-api": 1,
"supervisor-observes": 1,
"abi-proof": 1
}
},
"interactive": {
"kind": "uml-class",
"storage_key": "k02-task-wrapper-class",
"title": "Konstrukcja OOP wrappera Task<Derived>",
"task": {"id": "task01", "label": "Task01 · C++ Task wrapper"},
"block": {"id": "a2-structure", "label": "A2 STRUCTURE", "description": "Struktura kompilacyjna, mapowanie do kodu i dowody ABI; nie jest to kolejność wykonania."},
"phases": [
{
"id": "structure",
"label": "STRUCTURE",
"description": "Od szablonu CRTP do dowodu braku dynamicznego polimorfizmu.",
"steps": [
{"id": "crtp-template", "number": 1, "label": "Task<Derived> — szablon CRTP", "mode": "CODE", "strategy_ref": "code.structure", "svg_label": "01", "svg_target": "TaskTemplate", "code_ref": "include/freertos/task.hpp:45", "progress_id": "K02-UML-C01", "description": "Baza przyjmuje typ klasy pochodnej jako parametr i udostępnia wspólny interfejs bez metod virtual.", "evidence": "Deklaracja template<class Derived> class Task i brak słowa virtual w publicznym wrapperze."},
{"id": "sumtask-inheritance", "number": 2, "label": "SumTask dziedziczy Task<SumTask>", "mode": "CODE", "strategy_ref": "code.structure", "svg_label": "02", "svg_target": "TaskTemplate<-SumTask", "code_ref": "src/tasks/task01_task_wrapper.cpp:72", "progress_id": "K02-UML-C02", "description": "Relacja podstawia Derived=SumTask i ustanawia statyczny kontrakt klasy pochodnej.", "evidence": "Nagłówek klasy final : public freertos::Task<SumTask> oraz bezpośrednia relacja dziedziczenia w UML."},
{"id": "friend-private-run", "number": 3, "label": "friend i prywatne SumTask::run()", "mode": "CODE", "strategy_ref": "code.structure", "svg_label": "-03", "code_ref": "src/tasks/task01_task_wrapper.cpp:74", "progress_id": "K02-UML-C03", "description": "Tylko wyspecjalizowana baza CRTP otrzymuje dostęp do prywatnego run().", "evidence": "friend class freertos::Task<SumTask>, prywatne run() i udany pozytywny test kontraktu."},
{"id": "start-state", "number": 4, "label": "start() i zmiana stanu", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.create", "svg_label": "+04", "code_ref": "include/freertos/task.hpp:64", "snapshot_ref": "task01.ready", "progress_id": "K02-UML-C04", "description": "start() pilnuje jednokrotnego startu, przechodzi przez starting/ready i zapisuje uchwyt kernela.", "evidence": "Replay zatrzymuje workerA w stanie ready z niepustym TaskHandle_t."},
{"id": "static-trampoline", "number": 5, "label": "static trampoline(void*)", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.dispatch", "svg_label": "-05", "code_ref": "include/freertos/task.hpp:115", "snapshot_ref": "task01.trampoline", "progress_id": "K02-UML-C05", "description": "Statyczny callback odzyskuje Derived* z kontekstu void* i wywołuje prywatne run().", "evidence": "Ten sam adres workerA jest widoczny jako this, void* context i Derived*."},
{"id": "freertos-api", "number": 6, "label": "zależność od FreeRTOS C API", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.create", "svg_label": "06", "svg_target": "TaskTemplate->FreeRTOS", "code_ref": "include/freertos/task.hpp:80", "snapshot_ref": "task01.create-call", "progress_id": "K02-UML-C06", "description": "Wrapper przekracza granicę językową przez xTaskCreate i statyczny callback o ABI C.", "evidence": "Replay przed xTaskCreate pokazuje context=&workerA, a kernel i port pozostają skompilowane jako C."},
{"id": "supervisor-observes", "number": 7, "label": "Supervisor obserwuje dwa SumTask", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "07", "svg_target": "SupervisorTask-SumTask", "code_ref": "src/tasks/task01_task_wrapper.cpp:126", "snapshot_ref": "task01.pass", "progress_id": "K02-UML-C07", "description": "Niższy priorytet uruchamia SupervisorTask dopiero po zakończeniu obu workerów i sprawdza ich publiczny stan.", "evidence": "Końcowy checkpoint potwierdza dwa wyniki 200010000, completed, puste uchwyty i PASS."},
{"id": "abi-proof", "number": 8, "label": "brak virtual, vtable i RTTI", "mode": "CODE", "strategy_ref": "code.abi", "svg_label": "08", "svg_target": "ABIProof", "code_ref": "scripts/check_abi.sh:15", "progress_id": "K02-UML-C08", "description": "Statyczny polimorfizm ma być widoczny również w artefakcie ELF, nie tylko w składni źródła.", "evidence": "nm/readelf nie znajdują vtable, typeinfo, __cxa_*, _Unwind_*, .init_array ani hosted libstdc++."}
]
}
]
}
}
]
},
{
"title": "A3 — Dispatch",
"order": 20,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"content_tex": "Callback C prowadzi przez trampoline do bezpośredniego wywołania C++.",
"assets": [
{
"path": "assets/a3-dispatch.png", "html_path": "assets/a3-dispatch.svg", "source_path": "assets/a3-dispatch.puml",
"caption": "A3 DISPATCH — callback C, statyczny trampoline i bezpośrednie run().", "label": "fig:a3-dispatch",
"alt": "Przejście od callbacku FreeRTOS przez trampoline i CRTP do SumTask run bez vtable.", "kind": "diagram", "width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 660, "page_height": 760, "overview": true, "step_tiles": {"callback-signature": 1, "context-identity": 1, "typed-recovery": 1, "direct-run": 1, "elf-proof": 1}},
"interactive": {
"kind": "uml-class", "storage_key": "k02-a3-dispatch", "title": "A3 · wiązanie C → C++",
"task": {"id": "task01", "label": "Task01 · C++ Task wrapper"},
"block": {"id": "a3-dispatch", "label": "A3 DISPATCH", "description": "Mechanizm callbacku, odzyskanie typu i dowód bezpośredniego wywołania."},
"phases": [{"id": "binding", "label": "BINDING", "steps": [
{"id": "callback-signature", "number": 1, "label": "TaskFunction_t callback", "mode": "CODE", "strategy_ref": "code.context", "svg_label": "01", "svg_target": "Callback", "code_ref": "include/freertos/task.hpp:80", "description": "xTaskCreate przyjmuje zwykły wskaźnik funkcji C i void* context.", "evidence": "Sygnatura callbacku nie zawiera niejawnego this."},
{"id": "context-identity", "number": 2, "label": "void* zachowuje adres workerA", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.dispatch", "svg_label": "02", "svg_target": "Trampoline", "code_ref": "include/freertos/task.hpp:115", "snapshot_ref": "task01.trampoline", "description": "Trampoline odbiera ten sam adres, który start() przekazało do xTaskCreate.", "evidence": "a1/context i &g_worker_a są identyczne przy E06."},
{"id": "typed-recovery", "number": 3, "label": "static_cast odzyskuje Derived*", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.dispatch", "svg_label": "03", "svg_target": "CRTP", "code_ref": "include/freertos/task.hpp:117", "snapshot_ref": "task01.run", "description": "CRTP zna Derived=SumTask bez RTTI.", "evidence": "Stan wrappera jest running przed bezpośrednim wywołaniem."},
{"id": "direct-run", "number": 4, "label": "bezpośrednie SumTask::run()", "mode": "CODE", "strategy_ref": "code.abi", "svg_label": "04", "svg_target": "Run", "code_ref": "include/freertos/task.hpp:124", "description": "Kompilator wiąże konkretną metodę na etapie kompilacji.", "evidence": "Listing pokazuje direct call/jal bez odczytu vptr."},
{"id": "elf-proof", "number": 5, "label": "ELF bez vtable i RTTI", "mode": "CODE", "strategy_ref": "code.abi", "svg_label": "05", "svg_target": "ELF", "code_ref": "scripts/check_abi.sh:15", "description": "Dowód negatywny pochodzi z symboli i sekcji artefaktu.", "evidence": "check_abi.sh odrzuca vtable, typeinfo, __cxa_* i _Unwind_*."}
]}]
}
}
]
},
{
"title": "A4 — Application",
"order": 25,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"content_tex": "Dwa workery, jeden supervisor i jawne warunki zaliczenia.",
"assets": [
{
"path": "assets/a4-application.png", "html_path": "assets/a4-application.svg", "source_path": "assets/a4-application.puml",
"caption": "A4 APPLICATION — dwie instancje SumTask, invariant startu i SupervisorTask.", "label": "fig:a4-application",
"alt": "Konkretne obiekty aplikacji FreeRTOS C++ i ich warunki zaliczenia.", "kind": "diagram", "width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 660, "page_height": 760, "overview": true, "step_tiles": {"workers": 1, "duplicate-start": 1, "equal-work": 1, "supervisor": 1}},
"interactive": {
"kind": "uml-class", "storage_key": "k02-a4-application", "title": "A4 · konkretny eksperyment",
"task": {"id": "task01", "label": "Task01 · C++ Task wrapper"},
"block": {"id": "a4-application", "label": "A4 APPLICATION", "description": "Dwie identyczne prace, jeden supervisor i jawne inwarianty."},
"phases": [{"id": "objects-invariants", "label": "OBJECTS / INVARIANTS", "steps": [
{"id": "workers", "number": 1, "label": "dwa równorzędne SumTask", "mode": "CODE", "strategy_ref": "code.structure", "svg_label": "01", "svg_target": "WorkerA", "code_ref": "src/tasks/task01_task_wrapper.cpp:196", "description": "workerA i workerB różnią się wyłącznie id i nazwą.", "evidence": "Oba mają priority=2, stack=256 i work_limit=20000."},
{"id": "duplicate-start", "number": 2, "label": "drugi start jest odrzucony", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.create", "svg_label": "02", "svg_target": "Guard", "code_ref": "src/tasks/task01_task_wrapper.cpp:219", "snapshot_ref": "task01.scheduler", "description": "Przed schedulerem potwierdzamy niezmieniony uchwyt i stan ready.", "evidence": "g_duplicate_start_rejected=1 i zapisany handle jest niezerowy."},
{"id": "equal-work", "number": 3, "label": "obliczenie przeżywa time slice", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.scheduler", "svg_label": "03", "svg_target": "Trace", "code_ref": "src/tasks/task01_task_wrapper.cpp:101", "snapshot_ref": "task01.timeslice", "description": "CPU-bound loop jest dłuższy niż okno ticka.", "evidence": "Ślad zaczyna się A→B→A, a licznik przełączeń jest co najmniej 2."},
{"id": "supervisor", "number": 4, "label": "SupervisorTask zatwierdza PASS", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "04", "svg_target": "Supervisor", "code_ref": "src/tasks/task01_task_wrapper.cpp:151", "snapshot_ref": "task01.pass", "description": "Niższy priorytet sprawdza wyniki, stany, uchwyty, stosy i ślad.", "evidence": "Dwa wyniki 200010000, dwie wartości 20000 iteracji i PASS=1."}
]}]
}
}
]
},
{
"title": "A5 — Flow",
"order": 30,
"content_kind": "prose",
"content_tex": "E01--E12: od obiektu C++ do zakończenia taska.",
"asset_page_mode": "one-per-page",
"page_orientation": "landscape",
"spread_group": "k02-task-wrapper-lifecycle",
"spread_label": "K02 · Task wrapper lifecycle",
"spread_columns": 2,
"assets": [
{
"path": "assets/task-wrapper-sequence-a.png",
"html_path": "assets/task-wrapper-sequence-a.svg",
"source_path": "assets/task-wrapper-sequence-a.puml",
"caption": "UML 2A/2 — obiekt, start(), xTaskCreate i przejście do READY.",
"label": "fig:task-wrapper-sequence-a",
"alt": "Pierwszy segment diagramu sekwencji wrappera Task.",
"kind": "diagram",
"width": 1.0,
"page_grid": {
"columns": 1,
"rows": 1,
"page_width": 940,
"page_height": 560,
"overview": true,
"step_tiles": {"objects-constructed": 1, "start-enter": 1, "create-call": 1, "ready": 1, "scheduler-start": 1}
},
"interactive": {
"kind": "uml-sequence",
"storage_key": "k02-task-wrapper-a",
"title": "część 1/2 · obiekt do READY",
"task": {"id": "task01", "label": "Task01 · C++ Task wrapper"},
"block": {"id": "a5-flow", "label": "A5 FLOW", "description": "Jeden blok obejmuje oba segmenty UML sekwencji i zdarzenia E01E12."},
"phases": [
{
"id": "construct-start",
"label": "CONSTRUCT / START",
"description": "Od stałego obiektu w .data do taska READY w kernelu.",
"steps": [
{"id": "objects-constructed", "number": 1, "label": "obiekty skonstruowane", "mode": "RUN", "svg_label": "01", "progress_id": "K02-UML-S01", "description": "Sprawdź adres workerA i stan constructed przed start().", "code_ref": "src/tasks/task01_task_wrapper.cpp:213", "snapshot_ref": "task01.constructed", "snapshot": {"pc": "main: after constant initialization", "registers": [{"name": "a0", "value": "1"}, {"name": "a1", "value": "&'(anonymous namespace)::g_worker_a'"}], "frame": {"name": "main", "sp": "startup stack", "fp": "main frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "g_worker_a", "address": ".data", "value": "state=constructed, handle=null"}]}, "description": "Obiekt istnieje przed utworzeniem TCB."}},
{"id": "start-enter", "number": 2, "label": "workerA.start()", "mode": "RUN", "svg_label": "02", "progress_id": "K02-UML-S02", "description": "Wejdź z metody klasy pochodnej do odziedziczonego interfejsu Task<SumTask>.", "code_ref": "include/freertos/task.hpp:64", "snapshot_ref": "task01.start-enter", "snapshot": {"pc": "Task<SumTask>::start", "registers": [{"name": "a0", "value": "2"}, {"name": "a1", "value": "this"}], "frame": {"name": "Task<SumTask>::start", "sp": "startup stack", "fp": "start frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "state_", "address": "workerA field", "value": "constructed"}]}, "description": "Publiczna metoda bazowa steruje cyklem życia."}},
{"id": "create-call", "number": 3, "label": "xTaskCreate(trampoline, &workerA)", "mode": "RUN", "svg_label": "03", "progress_id": "K02-UML-S03", "description": "Znajdź granicę C++/C: statyczna funkcja i wskaźnik void*.", "code_ref": "include/freertos/task.hpp:80", "snapshot_ref": "task01.create-call", "snapshot": {"pc": "Task<SumTask>::start before xTaskCreate", "registers": [{"name": "a0", "value": "3"}, {"name": "a1", "value": "&workerA"}], "frame": {"name": "Task<SumTask>::start", "sp": "startup stack", "fp": "start frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "state_", "address": "workerA field", "value": "starting"}]}, "description": "Callback nie jest metodą virtual."}},
{"id": "ready", "number": 4, "label": "handle != null; state = ready", "mode": "RUN", "svg_label": "04", "progress_id": "K02-UML-S04", "description": "Potwierdź uchwyt i odróżnij created/ready od running.", "code_ref": "include/freertos/task.hpp:95", "snapshot_ref": "task01.ready", "snapshot": {"pc": "Task<SumTask>::start after xTaskCreate", "registers": [{"name": "a0", "value": "4"}, {"name": "a1", "value": "&workerA"}], "frame": {"name": "Task<SumTask>::start", "sp": "startup stack", "fp": "start frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "handle_", "address": "workerA field", "value": "TCB in heap_4"}, {"name": "state_", "address": "workerA field", "value": "ready"}]}, "description": "TCB istnieje, lecz run() jeszcze się nie wykonało."}},
{"id": "scheduler-start", "number": 5, "label": "vTaskStartScheduler()", "mode": "RUN", "svg_label": "05", "progress_id": "K02-UML-S05", "description": "Zapisz ostatni stan na startup stack przed przejęciem CPU przez scheduler.", "code_ref": "src/tasks/task01_task_wrapper.cpp:234", "snapshot_ref": "task01.scheduler", "snapshot": {"pc": "main before scheduler", "registers": [{"name": "a0", "value": "5"}, {"name": "sp", "value": "startup stack"}], "frame": {"name": "main", "sp": "startup stack", "fp": "main frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "g_worker_a / g_worker_b", "address": ".data", "value": "ready"}]}, "description": "Następny krok wykona się już na stosie taska."}}
]
}
]
}
},
{
"path": "assets/task-wrapper-sequence-b.png",
"html_path": "assets/task-wrapper-sequence-b.svg",
"source_path": "assets/task-wrapper-sequence-b.puml",
"caption": "UML 2B/2 — trampoline, run(), time slicing, completed i vTaskDelete.",
"label": "fig:task-wrapper-sequence-b",
"alt": "Drugi segment diagramu sekwencji wrappera Task.",
"kind": "diagram",
"width": 1.0,
"page_grid": {
"columns": 1,
"rows": 1,
"page_width": 940,
"page_height": 560,
"overview": true,
"step_tiles": {"trampoline-entry": 1, "derived-run": 1, "timeslice": 1, "run-return": 1, "completed": 1, "delete-current": 1, "supervisor-pass": 1}
},
"interactive": {
"kind": "uml-sequence",
"storage_key": "k02-task-wrapper-b",
"title": "część 2/2 · RUNNING do usunięcia",
"task": {"id": "task01", "label": "Task01 · C++ Task wrapper"},
"block": {"id": "a5-flow", "label": "A5 FLOW", "description": "Kontynuacja tego samego bloku i obiektu."},
"phases": [
{
"id": "dispatch-finish",
"label": "DISPATCH / FINISH",
"description": "Od callbacku C do prywatnego run(), zakończenia i PASS.",
"steps": [
{"id": "trampoline-entry", "number": 6, "label": "trampoline(&workerA)", "mode": "RUN", "svg_label": "06", "progress_id": "K02-UML-S06", "description": "Porównaj void* w a1 z adresem tego samego obiektu z kroku 01.", "code_ref": "include/freertos/task.hpp:115", "snapshot_ref": "task01.trampoline", "snapshot": {"pc": "Task<SumTask>::trampoline", "registers": [{"name": "a0", "value": "6"}, {"name": "a1", "value": "&workerA"}, {"name": "sp", "value": "workerA stack"}], "frame": {"name": "Task<SumTask>::trampoline", "sp": "workerA stack", "fp": "trampoline frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "context", "address": "register/stack", "value": "&workerA"}]}, "description": "Statyczny callback odzyskuje typ Derived*."}},
{"id": "derived-run", "number": 7, "label": "state = running; SumTask::run()", "mode": "RUN", "svg_label": "07", "progress_id": "K02-UML-S07", "description": "W disassembly znajdź bezpośrednie wywołanie run(), bez odczytu vtable.", "code_ref": "include/freertos/task.hpp:124", "snapshot_ref": "task01.run", "snapshot": {"pc": "before direct Derived::run", "registers": [{"name": "a0", "value": "7"}, {"name": "sp", "value": "workerA stack"}], "frame": {"name": "Task<SumTask>::trampoline", "sp": "workerA stack", "fp": "trampoline frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "state_", "address": "workerA field", "value": "running"}]}, "description": "CRTP zamienia kontrakt run() w statyczne wywołanie."}},
{"id": "timeslice", "number": 8, "label": "tick: A -> B -> A", "mode": "RUN", "svg_label": "08", "progress_id": "K02-UML-S08", "description": "Potwierdź, że obliczenie przeżywa zmianę taska i wznawia własny kontekst.", "code_ref": "src/tasks/task01_task_wrapper.cpp:112", "snapshot_ref": "task01.timeslice", "snapshot": {"pc": "SumTask::run after tick change", "registers": [{"name": "a0", "value": "8"}, {"name": "sp", "value": "active worker stack"}], "frame": {"name": "SumTask::run", "sp": "worker stack", "fp": "run frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "g_trace_task", "address": ".bss", "value": "A, B, A, ..."}]}, "description": "To jest dowód zachowania taska, nie relacja dziedziczenia."}},
{"id": "run-return", "number": 9, "label": "run() returns; result stored", "mode": "RUN", "svg_label": "09", "progress_id": "K02-UML-S09", "description": "Zobacz powrót do ramki trampoline i wynik 200010000.", "code_ref": "include/freertos/task.hpp:126", "snapshot_ref": "task01.run-return", "snapshot": {"pc": "trampoline after Derived::run", "registers": [{"name": "a0", "value": "9"}, {"name": "sp", "value": "worker stack"}], "frame": {"name": "Task<SumTask>::trampoline", "sp": "worker stack", "fp": "trampoline frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "g_results[worker]", "address": ".bss", "value": "200010000"}]}, "description": "Metoda pochodna zakończyła pracę, ale task kernela jeszcze istnieje."}},
{"id": "completed", "number": 10, "label": "state = completed; handle = null", "mode": "RUN", "svg_label": "10", "progress_id": "K02-UML-S10", "description": "Oddziel zakończenie modelu C++ od fizycznego zwolnienia TCB przez kernel.", "code_ref": "include/freertos/task.hpp:127", "snapshot_ref": "task01.completed", "snapshot": {"pc": "trampoline before delete", "registers": [{"name": "a0", "value": "10"}, {"name": "a1", "value": "worker object"}], "frame": {"name": "Task<SumTask>::trampoline", "sp": "worker stack", "fp": "trampoline frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "state_", "address": "worker field", "value": "completed"}, {"name": "handle_", "address": "worker field", "value": "null"}]}, "description": "Stan obserwowalny jest zapisany przed self-delete."}},
{"id": "delete-current", "number": 11, "label": "vTaskDelete(nullptr)", "mode": "RUN", "svg_label": "11", "progress_id": "K02-UML-S11", "description": "Wyjaśnij, dlaczego nullptr oznacza bieżący task i dlaczego wywołanie nie wraca.", "code_ref": "include/freertos/task.hpp:131", "snapshot_ref": "task01.delete", "snapshot": {"pc": "before vTaskDelete(nullptr)", "registers": [{"name": "a0", "value": "11"}, {"name": "sp", "value": "worker stack"}], "frame": {"name": "Task<SumTask>::trampoline", "sp": "worker stack", "fp": "trampoline frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "TCB + stack", "address": "heap_4", "value": "pending kernel deletion"}]}, "description": "Kernel usuwa bieżący task; kod za vTaskDelete jest nieosiągalny."}},
{"id": "supervisor-pass", "number": 12, "label": "SupervisorTask verifies PASS", "mode": "RUN", "svg_label": "12", "progress_id": "K02-UML-S12", "description": "Sprawdź dwa wyniki, dwa stany completed i ślad przełączeń.", "code_ref": "src/tasks/task01_task_wrapper.cpp:170", "snapshot_ref": "task01.pass", "snapshot": {"pc": "SupervisorTask::run before PASS", "registers": [{"name": "a0", "value": "12"}, {"name": "sp", "value": "supervisor stack"}], "frame": {"name": "SupervisorTask::run", "sp": "supervisor stack", "fp": "supervisor frame", "size_bytes": 0, "fields": []}, "memory": {"items": [{"name": "g_results", "address": ".bss", "value": "200010000, 200010000"}, {"name": "'(anonymous namespace)::g_task_wrapper_pass'", "address": ".bss", "value": "1"}]}, "description": "Dowód końcowy łączy model klas z wykonaniem kernela."}}
]
}
]
}
}
]
},
{
"title": "A6 — State",
"order": 40,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"content_tex": "Trzy różne stany: obiekt C++, wrapper i task FreeRTOS.",
"assets": [{
"path": "assets/a6-state.png", "html_path": "assets/a6-state.svg", "source_path": "assets/a6-state.puml",
"caption": "A6 STATE — object lifetime, wrapper state i kernel task state.", "label": "fig:a6-state",
"alt": "Trzy osobne tory stanu: obiekt C++, wrapper TaskState i scheduler FreeRTOS.", "kind": "diagram", "width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 660, "page_height": 760, "overview": true, "step_tiles": {"object-lifetime": 1, "constructed-ready": 1, "running-completed": 1, "ready-running": 1, "time-slicing": 1, "self-delete": 1}},
"interactive": {
"kind": "uml-class", "storage_key": "k02-a6-state", "title": "A6 · stan bez mieszania pojęć",
"task": {"id": "task01", "label": "Task01 · C++ Task wrapper"},
"block": {"id": "a6-state", "label": "A6 STATE", "description": "Oddziel życie statycznego obiektu, stan diagnostyczny wrappera i eTaskState kernela."},
"phases": [
{"id": "object-wrapper", "label": "OBJECT / WRAPPER", "steps": [
{"id": "object-lifetime", "number": 1, "label": "statyczny czas życia obiektu", "mode": "CODE", "strategy_ref": "code.structure", "svg_label": "01", "code_ref": "src/tasks/task01_task_wrapper.cpp:196", "description": "g_worker_a istnieje od inicjalizacji statycznej do końca procesu.", "evidence": "vTaskDelete nie uruchamia destruktora statycznego SumTask."},
{"id": "constructed-ready", "number": 2, "label": "constructed → starting → ready", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.state", "svg_label": "02", "svg_target": "Starting->ReadyWrapper", "code_ref": "include/freertos/task.hpp:71", "snapshot_ref": "task01.ready", "description": "start() zapisuje diagnostyczny stan wrappera wokół xTaskCreate.", "evidence": "E04 osiąga ready po udanym utworzeniu taska."},
{"id": "running-completed", "number": 3, "label": "running → completed", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.state", "svg_label": "03", "svg_target": "RunningWrapper->CompletedWrapper", "code_ref": "include/freertos/task.hpp:121", "snapshot_ref": "task01.completed", "description": "Trampoline publikuje running, a po run() publikuje completed.", "evidence": "Wynik jest zapisany, stan completed, publiczny handle=null."}
]},
{"id": "kernel", "label": "KERNEL", "steps": [
{"id": "ready-running", "number": 4, "label": "kernel Ready ↔ Running", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.state", "svg_label": "04", "svg_target": "ReadyKernel->RunningKernel", "code_ref": "src/tasks/task01_task_wrapper.cpp:90", "snapshot_ref": "task01.run", "description": "Scheduler wybiera task niezależnie od diagnostycznego enum wrappera.", "evidence": "E07 wykonuje się już na stosie workerA."},
{"id": "time-slicing", "number": 5, "label": "equal-priority time slicing", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.scheduler", "svg_label": "05", "svg_target": "RunningKernel->TimeSlice", "code_ref": "src/tasks/task01_task_wrapper.cpp:104", "snapshot_ref": "task01.timeslice", "description": "Dwa taski o priorytecie 2 przeplatają wykonanie po ticku.", "evidence": "Trace A→B→A i co najmniej dwa przełączenia."},
{"id": "self-delete", "number": 6, "label": "self-delete ≠ destrukcja obiektu", "mode": "RUN", "event_id": "E11", "strategy_ref": "run.state", "svg_label": "06", "svg_target": "RunningKernel->DeletedKernel", "code_ref": "include/freertos/task.hpp:127", "snapshot_ref": "task01.delete", "description": "vTaskDelete(nullptr) usuwa bieżący task kernela i nie wraca.", "evidence": "Statyczny obiekt nadal istnieje; reclaim TCB/stosu następuje później."}
]}
]
}
}]
},
{
"title": "A7 — Runtime",
"order": 50,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"page_orientation": "landscape",
"content_tex": "Obiekt, TCB, stos i CPU połączone z dowodem GDB.",
"assets": [{
"path": "assets/a7-runtime.png", "html_path": "assets/a7-runtime.svg", "source_path": "assets/a7-runtime.puml",
"caption": "A7 RUNTIME — obiekt, TCB, stos, CPU, scheduler i łańcuch audytowy.", "label": "fig:a7-runtime",
"alt": "Mapa obiektu C++, TCB w heap4, stosu taska, rejestrów RV32I i dowodu wykonania.", "kind": "diagram", "width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"object-identity": 1, "tcb": 1, "stack": 1, "stack-switch": 1, "trace": 1, "audit": 1}},
"interactive": {
"kind": "uml-class", "storage_key": "k02-a7-runtime", "title": "A7 · dowód z Hazard3/GDB",
"task": {"id": "task01", "label": "Task01 · C++ Task wrapper"},
"block": {"id": "a7-runtime", "label": "A7 RUNTIME", "description": "Każdy krok wybiera minimalny zestaw paneli Termdebug potrzebny do danego pytania."},
"phases": [
{"id": "memory", "label": "MEMORY", "steps": [
{"id": "object-identity", "number": 1, "label": "this = context = Derived*", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.runtime", "svg_label": "01", "svg_target": "Object", "code_ref": "include/freertos/task.hpp:117", "snapshot_ref": "task01.trampoline", "description": "Wskaźnik przekracza granicę C bez zmiany tożsamości obiektu.", "evidence": "Adres workerA jest równy context i recovered Derived*."},
{"id": "tcb", "number": 2, "label": "handle wskazuje TCB w heap_4", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.runtime", "svg_label": "02", "svg_target": "TCB", "code_ref": "src/tasks/task01_task_wrapper.cpp:92", "snapshot_ref": "task01.pass", "description": "Zapisany uchwyt po starcie odpowiada TCB zaobserwowanemu przez task.", "evidence": "g_worker_a_handle_after_start = g_tcb[0] i adres należy do heap_4."},
{"id": "stack", "number": 3, "label": "SP należy do stosu workera", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.runtime", "svg_label": "03", "svg_target": "Stack", "code_ref": "src/tasks/task01_task_wrapper.cpp:93", "snapshot_ref": "task01.timeslice", "description": "Stos taska jest innym obszarem niż obiekt i TCB.", "evidence": "g_stack_low[0] ≤ SP ≤ g_stack_high[0]."}
]},
{"id": "cpu-scheduler", "label": "CPU / SCHEDULER", "steps": [
{"id": "stack-switch", "number": 4, "label": "startup SP → task SP", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.runtime", "svg_label": "04", "svg_target": "CPU", "code_ref": "include/freertos/task.hpp:115", "snapshot_ref": "task01.trampoline", "description": "Po vTaskStartScheduler CPU wykonuje trampoline na stosie taska.", "evidence": "SP różni się od startup stack z E05, PC wskazuje trampoline."},
{"id": "trace", "number": 5, "label": "tick i ślad przełączeń", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.scheduler", "svg_label": "05", "svg_target": "Scheduler", "code_ref": "src/tasks/task01_task_wrapper.cpp:52", "snapshot_ref": "task01.timeslice", "description": "Trace utrwala kolejność tasków tylko przy zmianie id.", "evidence": "A→B→A, switches≥2, obliczenie wciąż trwa."},
{"id": "audit", "number": 6, "label": "tożsamość i timestamp dowodu", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "06", "svg_target": "Evidence", "code_ref": "src/tasks/task01_task_wrapper.cpp:170", "snapshot_ref": "task01.pass", "description": "Końcowy dowód wiąże wynik z konkretnym źródłem, binarium i zdarzeniem.", "evidence": "event_id, snapshot_ref, source blob, ELF hash, target i timestamp."}
]}
]
}
}]
},
{
"title": "A8 — Patterns",
"order": 60,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"page_orientation": "landscape",
"content_tex": "Role projektowe i świadome ograniczenia wrappera.",
"assets": [{
"path": "assets/a8-patterns.png", "html_path": "assets/a8-patterns.svg", "source_path": "assets/a8-patterns.puml",
"caption": "A8 PATTERNS — role projektowe i jawne wyłączenia pierwszego wrappera.", "label": "fig:a8-patterns",
"alt": "Callback Adapter, statyczny dispatch CRTP, guarded start oraz decyzje czego wrapper jeszcze nie zapewnia.", "kind": "diagram", "width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"callback-adapter": 1, "static-dispatch": 1, "guarded-start": 1, "exclusions": 1}},
"interactive": {
"kind": "uml-class", "storage_key": "k02-a8-patterns", "title": "A8 · role i decyzje",
"task": {"id": "task01", "label": "Task01 · C++ Task wrapper"},
"block": {"id": "a8-patterns", "label": "A8 PATTERNS", "description": "Nazwy wzorców są dopuszczalne wyłącznie wtedy, gdy wskazują konkretną rolę w kodzie."},
"phases": [{"id": "roles", "label": "ROLE MAP", "steps": [
{"id": "callback-adapter", "number": 1, "label": "C callback Adapter", "mode": "CODE", "strategy_ref": "code.pattern", "svg_label": "01", "svg_target": "Adapter", "code_ref": "include/freertos/task.hpp:115", "description": "trampoline tłumaczy void* callback na obiekt konkretnego typu.", "evidence": "Jedna jawna granica C→C++ i brak capturującej lambdy."},
{"id": "static-dispatch", "number": 2, "label": "CRTP static-dispatch idiom", "mode": "CODE", "strategy_ref": "code.pattern", "svg_label": "02", "svg_target": "Dispatch", "code_ref": "include/freertos/task.hpp:45", "description": "Task<Derived> wiąże prywatne run() na etapie kompilacji.", "evidence": "static_cast i direct call zamiast vtable."},
{"id": "guarded-start", "number": 3, "label": "guarded start surface", "mode": "CODE", "strategy_ref": "code.pattern", "svg_label": "03", "svg_target": "Guard", "code_ref": "include/freertos/task.hpp:64", "description": "start() centralizuje inwariant jednokrotnego utworzenia taska.", "evidence": "Drugi start zwraca false bez zmiany handle/state."},
{"id": "exclusions", "number": 4, "label": "nie RAII, nie virtual Template Method", "mode": "CODE", "strategy_ref": "code.pattern", "svg_label": "04", "svg_target": "Exclusions", "code_ref": "doc/task01-a1-a8-plan.md:147", "description": "Pierwsza karta świadomie nie obiecuje ownership ani destruktora zwalniającego task.", "evidence": "Obiekt ma static lifetime, a task kernela self-delete; brak virtual."}
]}]
}
}]
},
{
"title": "Task01 — C++ Task wrapper",
"order": 70,
"content_kind": "tasks",
"task_refs": ["task01"]
}
],
"tasks": {
"task01": {
"title": "C++ Task wrapper — OOP i debugowanie cyklu życia",
"uuid": "948ce9fd-3d0c-5721-a0ae-ef422008fec8",
"prompt_tex": "Przejdź po UML klas, a następnie po strzałkach 01--12 UML sekwencji. Dla wybranych punktów użyj Enter lub F2 i udowodnij w Hazard3/GDB, że ten sam obiekt C++ przechodzi przez C API, statyczny trampoline, prywatne run() i self-delete.",
"criterion": "Program wypisuje PASS; wyniki obu SumTask wynoszą 200010000; raport pokazuje stany constructed, ready, running i completed oraz brak virtual dispatch.",
"conclusion_tex": "Wrapper porządkuje cykl życia i zachowuje typ po obu stronach callbacku void*. CRTP daje statyczny polimorfizm, a rzeczywiste planowanie, stos i TCB nadal należą do FreeRTOS.",
"flow": [
{
"kind": "block",
"id": "class-model",
"title": "A — UML klas i kontrakt OOP",
"content_tex": "Najpierw czytaj relacje klas bez uruchamiania programu.",
"steps": [
{"id": "read-inheritance", "title": "Odczytaj dziedziczenie CRTP.", "content_tex": "Wyjaśnij podstawienie Derived=SumTask oraz dostęp friend do prywatnego run()."},
{"id": "prove-static-dispatch", "title": "Wyklucz virtual dispatch.", "content_tex": "Znajdź bezpośrednie wywołanie run() i potwierdź brak vtable/RTTI w wyniku check-abi."}
]
},
{
"kind": "block",
"id": "sequence-debug",
"title": "B — UML sekwencji i strategia debugowania",
"content_tex": "Każda strzałka ma stabilny checkpoint odtwarzany od czystego startu.",
"steps": [
{"id": "follow-object", "title": "Śledź jeden adres obiektu.", "content_tex": "Porównaj \\texttt{\\&'(anonymous namespace)::g\\_worker\\_a'} przed start(), jako this oraz jako void* context w trampoline."},
{"id": "follow-state", "title": "Śledź stan wrappera.", "content_tex": "Zapisz momenty constructed, ready, running i completed; nie utożsamiaj completed ze zwolnieniem TCB."},
{"id": "follow-stack", "title": "Śledź zmianę stosu.", "content_tex": "Porównaj sp przed schedulerem, w SumTask::run oraz w SupervisorTask::run."}
]
},
{
"kind": "exercise",
"id": "wrapper-debug-report",
"title": "Ćwiczenie — ślad obiektu i taska",
"prompt_tex": "Wybierz checkpointy 01, 03, 06, 07, 10 i 12. Dla każdego zapisz timestamp, pc, sp, adres obiektu i stan. Dołącz fragment disassembly dowodzący statycznego wywołania SumTask::run().",
"evidence_tex": "Timestampowane zrzuty Hazard3/GDB powiązane z numerami strzałek UML oraz końcowy PASS.",
"criterion": "Raport zachowuje ten sam adres obiektu, poprawnie rozdziela stos startup/workera/supervisora i nie przypisuje schedulerowi relacji OOP.",
"based_on": ["class-model", "sequence-debug"]
}
],
"educational_requirement_refs": ["K02.WE01"],
"learning_effect_refs": ["K02.EN01", "K02.EK01"],
"assessment_criterion_ref": "K02.KW01"
}
},
"tasks_order": ["task01"]
}