publish: K02 task wrapper v00.04

This commit is contained in:
user
2026-07-19 17:18:13 +02:00
commit cf8fdabf08
3 changed files with 676 additions and 0 deletions
+664
View File
@@ -0,0 +1,664 @@
{
"$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"]
}