# Task 3 Brief: TCM command builders ## Context Tasks 1 and 2 are complete. Existing TCM frame codec is in src/core/tcm_frame.* and C ABI frame utilities are in src/c_api.cpp. CMake is unavailable on PATH in this environment. Keep CMakeLists.txt correct, but use direct MinGW g++ verification if CMake cannot run. ## Global constraints - Use only original source code; do not link proprietary SDK binaries. - Current workspace is not a git repository; replace commit steps with verification plus updates to progress.md. - Keep public C ABI free of C++ types. - Use C++17. - libusb-1.0 is the only native runtime dependency for the core library. - Firmware update commands are not exposed as an easy accidental CLI action. - IR protocol gaps return MAG160C_ERR_PROTOCOL_UNKNOWN or MAG160C_ERR_UNSUPPORTED. ## Files - Create: src/core/transport.hpp - Create: src/core/tcm_device.hpp - Create: src/core/tcm_device.cpp - Create: tests/cpp/test_tcm_device.cpp - Modify: CMakeLists.txt - Modify: include/mag160c/mag160c.h - Modify: src/c_api.cpp - Update: progress.md with the checkpoint. ## Interfaces to produce - mag160c::core::Transport - mag160c::core::TcmCommandBuilder - mag160c_tcm_build_rotate_frame - mag160c_tcm_build_light_frame ## Required transport interface ```cpp class Transport { public: virtual ~Transport() = default; virtual mag160c_error_t write(const std::vector& bytes) = 0; virtual mag160c_error_t read(std::vector* out, int timeout_ms) = 0; }; ``` ## Required TCM command behavior - Frame id starts at 1. - Frame id increments after each encoded frame. - Frame id wraps to 1 before reaching 0x8000. - rotate clamps input to [-128, 128]. - rotate payload is [direction, magnitude], direction 0 for positive and 1 for negative. - light payload is [on, red, green, blue]. - red/green/blue/yellow channel value is 0xff. - off payload is [0, 0, 0, 0]. - light steady sub is 0x31, blink is 0x32, breath is 0x33. - all observed TCM main commands here use main 0x02. ## Required exact test vectors - rotate +5 with initial frame id 1: ```text 7e 00 07 85 02 77 00 01 00 05 7f ``` - after one prior command, rotate -3 with frame id 2: ```text 7e 00 07 85 02 77 00 02 01 03 7f ``` - green blink with initial frame id 1: ```text 7e 00 09 87 02 32 00 01 01 00 ff 00 35 ``` ## Required C ABI declarations Add to include/mag160c/mag160c.h: ```c typedef enum mag160c_tcm_light_color_t { MAG160C_TCM_LIGHT_OFF = 0, MAG160C_TCM_LIGHT_RED = 1, MAG160C_TCM_LIGHT_GREEN = 2, MAG160C_TCM_LIGHT_BLUE = 3, MAG160C_TCM_LIGHT_YELLOW = 4 } mag160c_tcm_light_color_t; typedef enum mag160c_tcm_light_mode_t { MAG160C_TCM_LIGHT_STEADY = 0, MAG160C_TCM_LIGHT_BLINK = 1, MAG160C_TCM_LIGHT_BREATH = 2 } mag160c_tcm_light_mode_t; MAG160C_API mag160c_error_t mag160c_tcm_build_rotate_frame( int angle, uint8_t* out_bytes, size_t out_capacity, size_t* out_size ); MAG160C_API mag160c_error_t mag160c_tcm_build_light_frame( mag160c_tcm_light_color_t color, mag160c_tcm_light_mode_t mode, uint8_t* out_bytes, size_t out_capacity, size_t* out_size ); ``` ## Required C ABI behavior - Build helpers are dry-run byte builders only; they do not talk to USB. - If out_size is null, return MAG160C_ERR_INVALID_ARGUMENT. - If output buffer is null or too small, set out_size to the required frame size and return MAG160C_ERR_INVALID_ARGUMENT. - On success, copy bytes, set out_size, clear last_error, return MAG160C_OK. - Invalid light color or mode returns MAG160C_ERR_INVALID_ARGUMENT and sets a useful last_error. ## Required tests Create tests/cpp/test_tcm_device.cpp with: - test_rotate_positive_payload exact vector. - test_rotate_negative_payload_and_frame_increment exact vector. - test_green_blink_payload exact vector. Also extend tests/cpp/test_c_api.cpp if convenient to check the new dry-run C ABI for rotate +5. ## CMake update - Add src/core/tcm_device.cpp to mag160c_core sources. - Add test_tcm_device executable linked to mag160c_core. - Give test_tcm_device access to src private headers. - Register test_tcm_device with CTest. ## Verification First try: ```powershell cmake --build build ctest --test-dir build --output-on-failure ``` If CMake is unavailable, run: ```powershell g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\c_api.cpp tests\cpp\test_c_api.cpp -o build_task3_c_api_test.exe .\build_task3_c_api_test.exe g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\c_api.cpp tests\cpp\test_tcm_frame.cpp -o build_task3_tcm_frame_test.exe .\build_task3_tcm_frame_test.exe g++ -std=c++17 -DMAG160C_STATIC -DMAG160C_HAS_LIBUSB=0 -Iinclude -Isrc src\core\error.cpp src\core\context.cpp src\core\tcm_frame.cpp src\core\tcm_device.cpp src\c_api.cpp tests\cpp\test_tcm_device.cpp -o build_task3_tcm_device_test.exe .\build_task3_tcm_device_test.exe ``` Expected: all executables exit 0. ## Progress checkpoint Append this line to progress.md after verification: ```markdown - Implemented Task 3 TCM command builder, dry-run C ABI helpers, and command payload tests. ``` ## Report contract Write a report to .superpowers/sdd/task-3-report.md with: - status: DONE, DONE_WITH_CONCERNS, NEEDS_CONTEXT, or BLOCKED - files created/modified - exact commands run - test results - concerns, if any - self-review notes