commit cf8fdabf0846c73c66b565de48fe4a454080fc38 Author: user Date: Sun Jul 19 17:18:13 2026 +0200 publish: K02 task wrapper v00.04 diff --git a/README.md b/README.md new file mode 100644 index 0000000..c49e947 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# K02 — C++ Task wrapper + +This repository is the publication package for the card. It intentionally contains only: + +- `json/card_source.json` — canonical card data; +- `pdf/*.pdf` — the rendered, printable artifact. + +The interactive React view is deployed separately at: + +`https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/948ce9fd-3d0c-5721-a0ae-ef422008fec8` + +The generator, schemas and layouts belong to the `edu tools` organization. The VPS renderer belongs to the private `teachers` organization. Generated HTML is not stored in this card repository. diff --git a/json/card_source.json b/json/card_source.json new file mode 100644 index 0000000..a8bbb84 --- /dev/null +++ b/json/card_source.json @@ -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 E01–E12", "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 przechowuje handle i stan", "friend udostępnia prywatne run() tylko bazie"], + "assertions": ["Derived dziedziczy Task", "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}. 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} 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 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, 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", + "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 — 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 Task i brak słowa virtual w publicznym wrapperze."}, + {"id": "sumtask-inheritance", "number": 2, "label": "SumTask dziedziczy Task", "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 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, 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 E01–E12."}, + "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.", "code_ref": "include/freertos/task.hpp:64", "snapshot_ref": "task01.start-enter", "snapshot": {"pc": "Task::start", "registers": [{"name": "a0", "value": "2"}, {"name": "a1", "value": "this"}], "frame": {"name": "Task::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::start before xTaskCreate", "registers": [{"name": "a0", "value": "3"}, {"name": "a1", "value": "&workerA"}], "frame": {"name": "Task::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::start after xTaskCreate", "registers": [{"name": "a0", "value": "4"}, {"name": "a1", "value": "&workerA"}], "frame": {"name": "Task::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::trampoline", "registers": [{"name": "a0", "value": "6"}, {"name": "a1", "value": "&workerA"}, {"name": "sp", "value": "workerA stack"}], "frame": {"name": "Task::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::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::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::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::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 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"] +} diff --git a/pdf/mpabi-inf-freertos-cpp-02-task-wrapper-crtp-v00.04-3400a752-f4d6-579c-a033-73677fef4b74.pdf b/pdf/mpabi-inf-freertos-cpp-02-task-wrapper-crtp-v00.04-3400a752-f4d6-579c-a033-73677fef4b74.pdf new file mode 100644 index 0000000..948e855 Binary files /dev/null and b/pdf/mpabi-inf-freertos-cpp-02-task-wrapper-crtp-v00.04-3400a752-f4d6-579c-a033-73677fef4b74.pdf differ