diff --git a/AGENTS.md b/AGENTS.md index 70f3c9b..997d20a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -228,14 +228,14 @@ let ``Validation fails for duplicate message IDs`` () = 보고서 파일명은 다음 형식을 **반드시** 따른다: ``` -Reports/YYYYMMDD_HHMM_작업내용요약.md +Reports/YYYYMMDD-HHMM_작업내용요약.md ``` - `YYYYMMDD`: 작업 날짜 (예: 20260212) - `HHMM`: 작업 종료 시각 (24시간제, 예: 1430) - `작업내용요약`: 핵심 작업을 간결하게 (예: `Dbc_예외삼킴_수정`, `Core_테스트_구축`) -예시: `Reports/20260212_1430_Dbc_예외삼킴_수정.md` +예시: `Reports/20260212-1430_Dbc_예외삼킴_수정.md` ### 규칙 3: 보고서 필수 포함 항목 @@ -261,11 +261,21 @@ Reports/YYYYMMDD_HHMM_작업내용요약.md ### 규칙 6: 장기 실행 배치의 RUN_ID 규칙 (선택 적용) -- 기본 규칙은 위의 `Reports/YYYYMMDD_HHMM_작업내용요약.md` 단일 보고서 방식이다 +- 기본 규칙은 위의 `Reports/YYYYMMDD-HHMM_작업내용요약.md` 단일 보고서 방식이다 - 다만 작업이 여러 세션에 걸쳐 이어지는 **장기 실행 배치**라면, 필요 시 `RUN_ID`(`yyyymmdd-hhmm`, KST 기준)를 정하고 여러 보고서가 동일한 RUN_ID를 공유하도록 할 수 있다 - 같은 미종료 배치를 이어서 수행하는 경우에는 새 RUN_ID를 임의로 만들지 말고 기존 RUN_ID를 재사용한다 - 이 선택 규칙은 복구성과 추적성을 높이기 위한 보강 규칙이며, 현재 레포의 기본 단일 보고서 관행을 대체하지 않는다 +#### RUN_ID/파일명 운영 체크 (재발 방지) + +- 보고서 파일명 prefix(`YYYYMMDD-HHMM`)는 **해당 보고서의 RUN_ID**를 사용한다 +- 장기 실행 배치에서는 배치 시작 RUN_ID를 정한 뒤, 같은 배치의 후속 보고서도 같은 prefix를 사용한다 +- 배치 내 개별 단계 시각은 파일명 prefix가 아니라 본문 메타(예: `배치 내 단계 시각`)로만 기록한다 +- 기록 정정이 필요한 경우: + 1. 원본 보고서 제목/본문보다 파일명 prefix 정합성을 우선 맞춘다 + 2. 본문에 `[정정 이력 - YYYY-MM-DD]`로 변경 근거를 남긴다 + 3. 기존 이력을 지우지 않고 patch-forward 방식으로 누적 기록한다 + --- > 이 규칙들은 프로젝트의 **추적 가능성(traceability)**과 **재현 가능성(reproducibility)**을 보장하기 위한 것입니다. diff --git a/Plans/ORACLE_DECODER_STRATEGY.md b/Plans/ORACLE_DECODER_STRATEGY.md new file mode 100644 index 0000000..9f5ba9a --- /dev/null +++ b/Plans/ORACLE_DECODER_STRATEGY.md @@ -0,0 +1,103 @@ +# Oracle Reference Decoder Incompatibility Strategy + +## Problem Statement +Three vendor DBC files from the testing corpus are currently excluded from oracle verification because the primary reference decoder, `cantools` (v41.2.1), fails to parse them. These files were classified as Exception 4 in `tests/oracle/CATEGORY_C_EXCEPTIONS.md`. + +**Affected Files and Errors:** +1. **hyundai_kia_generic.dbc**: Malformed `CM_` records at lines 1655-1657. `cantools` fails to handle unexpected formatting in comment records. +2. **toyota_2017_ref_pt.dbc**: 29-bit extended IDs rejected at line 387. `cantools` throws a "Standard frame id is more than 11 bits" error, suggesting it incorrectly treats extended frames as standard frames or fails to recognize the bit-31 extended flag. +3. **vw_meb.dbc**: 29-bit extended IDs rejected at line 2074. Same root cause as the Toyota DBC. + +Signal-CANdy successfully parses these files and generates valid C99 code, but the lack of a reference decoder prevents signal-level oracle verification for these specific targets. + +## Current State and Impact +- **Exclusion Rate**: 3 significant vendor DBC files (Hyundai, Toyota, VW) are entirely excluded from the automated oracle pipeline. +- **Verification Gap**: Changes to core logic affecting extended IDs or specific parsing edge cases cannot be cross-validated against a gold standard for these files. +- **Classification**: These files are permanently assigned to `Category C` (reference\_decoder\_incompatible), which lowers the effective oracle coverage of the project. + +## Decision Context +The project needs to decide whether to invest in a more robust or alternative reference decoder to achieve 100% oracle coverage or maintain the current status quo where `cantools` remains the sole, albeit limited, source of truth. + +## Option A: Maintain cantools-only + Category C policy (Status Quo) +### Description +Continue using `cantools` v41.2.1 as the exclusive reference decoder. Files that fail to parse are documented as Category C exceptions and excluded from oracle runs. + +### Evidence/Rationale +`cantools` is the industry standard for Python-based CAN toolchains. Exception 4 documents that these failures are external to Signal-CANdy. + +### Pros +- Zero additional implementation or maintenance effort. +- Consistent environment (only one reference tool to manage). +- Clear boundary: if the industry standard cannot parse it, exclusion is defensible. + +### Cons +- Permanent verification gaps for high-value vendor DBCs. +- Reduced confidence in generated code for extended ID messages (J1939/CAN FD style). + +### Effort: None +### Recommendation Score: 6/10 + +## Option B: Replace cantools with an alternative reference decoder (e.g., canmatrix) +### Description +Migrate the `run_oracle.py` and `engine.py` logic to use `canmatrix` or a similar library as the primary reference. + +### Evidence/Rationale +Research indicates `canmatrix` is generally more lenient with DBC syntax anomalies (like the Hyundai `CM_` issue) and has a more flexible internal model for extended IDs. + +### Pros +- Potential to resolve all 3 current Exception 4 cases. +- `canmatrix` supports a wider range of formats (.arxml, .kcd), potentially expanding future test scope. + +### Cons +- High migration effort: `run_oracle.py` is tightly coupled with the `cantools` API. +- `canmatrix` is often cited as being slower and having a less "mature" high-level API than `cantools`. +- Risk of introducing new Category C exceptions for files `cantools` handled but `canmatrix` might struggle with. + +### Effort: High +### Recommendation Score: 3/10 + +## Option C: Hybrid approach (cantools with fallback or canmatrix-based patching) +### Description +Keep `cantools` as the primary engine but implement a secondary path for Exception 4 files. This could involve using `canmatrix` just for the problematic files or implementing a pre-processing script to "fix" the DBCs before feeding them to `cantools`. + +### Evidence/Rationale +The Toyota/VW errors ("Standard frame id is more than 11 bits") often stem from DBCs lacking the bit-31 flag that tools like `cantools` expect for extended IDs. A simple Python script could set these flags or clean malformed `CM_` records. + +### Pros +- Resolves the verification gap for the 3 current files. +- Retains the performance and stability of `cantools` for the bulk of the corpus. +- Incremental effort. + +### Cons +- Increases complexity of the test harness. +- "Fixing" DBCs for the reference tool might mask genuine parsing bugs if not handled carefully. + +### Effort: Medium +### Recommendation Score: 8/10 + +## Recommended Path +**Implement Option C (Hybrid/Pre-processing fallback).** + +Evidence suggests the current failures are solvable with targeted interventions: +1. **For Extended IDs (Toyota/VW)**: A pre-processing step can identify IDs > 0x7FF and ensure the extended bit is set (or use `canmatrix` to load and re-save the DBC in a "standardized" format). +2. **For Malformed CM_ (Hyundai)**: A simple regex-based "sanitizer" can fix the syntax anomalies before `cantools` reads the file. + +This approach provides the highest verification gain (resolving all 3 exclusions) with significantly less risk and effort than a full migration to a new decoder. + +## Decision Criteria +- **Coverage**: Does it resolve the 3 excluded files? +- **Stability**: Does it preserve existing verification for the rest of the corpus? +- **Maintenance**: How much extra code is added to the test suite? +- **Speed**: Does it impact CI run times? + +## Open Questions +- Can a simple `sed`/`regex` fix the Hyundai `CM_` records without changing signal data? +- Does `cantools` have an undocumented "lenient" mode that could be enabled via flags? +- Is the extended ID issue in Toyota/VW DBCs a bug in the DBC files themselves (missing flag) or a strictness issue in `cantools`? + +## References +- `tests/oracle/CATEGORY_C_EXCEPTIONS.md` Exception 4 +- `tests/oracle/ORACLE_RESULTS.md` Remaining Category C, Recommendation #4 +- `Plans/ROADMAP.md` Oracle decoder strategy item +- [cantools Issue #301: Standard frame id is more than 11 bits](https://github.com/cantools/cantools/issues/301) +- [canmatrix documentation: Lenient DBC parsing](https://canmatrix.readthedocs.io/en/latest/formats.html) diff --git a/Plans/ROADMAP.md b/Plans/ROADMAP.md index e421116..7721c63 100644 --- a/Plans/ROADMAP.md +++ b/Plans/ROADMAP.md @@ -12,13 +12,7 @@ ## 현재 이관된 후속 항목 -### 1. valid 비트마스크 `>64` 신호 fallback 방식 - -- 현 상태: `≤32 -> uint32_t`, `33–64 -> uint64_t`, `>64 -> UnsupportedFeature` -- 근거: `Plans/Archive/ROADMAP_202602_202603_Closed.md` L-3a close-out 문구, `tests/oracle/CATEGORY_C_EXCEPTIONS.md` Exception 3 -- 후속 판단 과제: 배열 기반 valid 표현을 실제로 도입할지, 아니면 `>64` 미지원을 장기 정책으로 유지할지 결정 - -### 2. Oracle reference decoder 비호환 DBC 대응 전략 +### 1. Oracle reference decoder 비호환 DBC 대응 전략 - 현 상태: 일부 벤더 DBC는 `cantools` 파서 비호환으로 Category C 처리 - 근거: `tests/oracle/CATEGORY_C_EXCEPTIONS.md` Exception 4, `tests/oracle/ORACLE_RESULTS.md` @@ -27,5 +21,5 @@ ## 다음 세션 진입점 1. 최신 close-out / verification 보고서를 먼저 확인한다. -2. 위 두 항목 중 실제 제품 우선순위가 생긴 것만 새 세대 계획으로 승격한다. +2. 위 항목의 실제 제품 우선순위가 생기면 새 세대 계획으로 승격한다. 3. 새 계획을 시작할 때는 archive 문서를 수정하지 않고, 이 문서 또는 `Plans/` 하위 successor roadmap에서 이어간다. diff --git a/README.ko.md b/README.ko.md index c6cda52..ceb853d 100644 --- a/README.ko.md +++ b/README.ko.md @@ -24,8 +24,8 @@ 설치: ```pwsh -dotnet add package SignalCandy.Core --version 0.3.2 -dotnet add package SignalCandy --version 0.3.2 +dotnet add package SignalCandy.Core --version 0.4.0-alpha.1 +dotnet add package SignalCandy --version 0.4.0-alpha.1 ``` ## ⚡ 빠른 시작 (5분) @@ -457,7 +457,7 @@ make -C gen build 참고 - 분기 선택은 스위치 신호의 원시 정수값 기준입니다(일반 DBC 관례). - 멀티플렉스가 아닌 기반 신호는 항상 디코드/인코드됩니다. -- 유효성 비트마스크 폭: 신호가 ≤32개인 메시지는 32비트 `valid` 필드(`uint32_t`)를 사용합니다; 33–64개 신호는 자동으로 64비트 필드(`uint64_t` + `1ULL` 시프트)를 사용합니다. 64개 초과 신호는 생성할 수 없으며 — 코드 생성은 `CodeGenError.UnsupportedFeature`를 보고합니다. +- 유효성 비트마스크 폭: 신호가 ≤32개인 메시지는 32비트 `valid` 필드(`uint32_t`)를 사용합니다; 33–64개 신호는 자동으로 64비트 필드(`uint64_t` + `1ULL` 시프트)를 사용합니다; 65–1024개 신호는 바이트 배열 필드(`uint8_t valid[(N+7)/8]`)와 `sc_valid_set/clear/test()` 헬퍼 함수(`sc_utils.h`)를 사용합니다. 1024개 초과 다중화 신호 메시지는 지원되지 않습니다(코드 생성 시 `CodeGenError.UnsupportedFeature` 보고). valid와 mux_active 사용 ```c @@ -482,6 +482,23 @@ void handle_mux(const uint8_t data[8]) { } ``` +sc_valid_test 사용 (>64개 신호 — 바이트 배열 valid) +```c +#include "mux65_msg.h" +#include "sc_utils.h" /* sc_valid_test 헬퍼 — file_prefix가 sc_인 경우 sc_utils.h로 생성 */ + +void handle_mux65(const uint8_t data[8]) { + MUX65_MSG_t m = {0}; + if (MUX65_MSG_decode(&m, data, 8)) { + if (m.mux_active == MUX65_MSG_MUX_5) { + if (sc_valid_test(m.valid, MUX65_MSG_VALID_BRANCH_5)) { + /* m.Branch_5 사용 — >64개 신호 메시지의 인덱스 기반 valid 검사 */ + } + } + } +} +``` + ### 값 테이블 (VAL_) `VAL_` 매핑이 있는 시그널은 C enum과 `to_string` 헬퍼가 생성됩니다: @@ -606,7 +623,7 @@ void compare_state(int v) { - 실제 CRC/Counter 생성 검증은 명시적 `crc_counter:` YAML 메타데이터가 필요하며, 현재 지원 알고리즘은 CRC-8만 해당합니다. - 현재 `crc_counter_check: true`는 지원되지 않는 경로를 조용히 통과시키지 않도록 하는 fail-fast 가드레일로 동작합니다. - 클래식 CAN(최대 8바이트)과 CAN FD(최대 64바이트) 페이로드를 모두 지원합니다 - - 32개 초과의 다중화(mux) 시그널이 있는 메시지는 `uint64_t` valid 비트마스크를 자동 사용합니다. 64개 초과 시그널 메시지는 코드 생성 시 `CodeGenError.UnsupportedFeature`를 반환합니다 +- ≤32개 다중화(mux) 시그널 메시지는 32비트 `valid` 비트마스크(`uint32_t`)를 사용합니다; 33–64개 신호는 64비트(`uint64_t`)를 사용합니다; 65–1024개 신호는 바이트 배열(`uint8_t valid[(N+7)/8]`)과 `sc_utils.h`의 `sc_valid_set/clear/test()` 헬퍼를 사용합니다. 1024개 초과 다중화 신호 메시지는 지원되지 않습니다(`CodeGenError.UnsupportedFeature`). ## 디스패치 모드와 레지스트리 (nanopb와의 관련성) diff --git a/README.md b/README.md index 258fc00..648dfbe 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ This project generates portable C99 parser modules (headers/sources) from a `.db Install: ```pwsh -dotnet add package SignalCandy.Core --version 0.3.2 -dotnet add package SignalCandy --version 0.3.2 +dotnet add package SignalCandy.Core --version 0.4.0-alpha.1 +dotnet add package SignalCandy --version 0.4.0-alpha.1 ``` ## ⚡ Quick Start (5 minutes) @@ -326,7 +326,7 @@ make -C gen build Notes - Branch selection uses the raw integer value of the switch signal (typical DBC semantics). - Base (non-multiplexed) signals are always decoded/encoded. - - Valid bitmask width: messages with ≤32 signals use a 32-bit `valid` field (`uint32_t`); messages with 33–64 signals automatically use a 64-bit field (`uint64_t` + `1ULL` shift). Messages with >64 signals cannot be generated — codegen reports `CodeGenError.UnsupportedFeature`. + - Valid bitmask width: messages with ≤32 signals use a 32-bit `valid` field (`uint32_t`); messages with 33–64 signals automatically use a 64-bit field (`uint64_t` + `1ULL` shift); messages with 65–1024 signals use a byte-array field (`uint8_t valid[(N+7)/8]`) with `sc_valid_set/clear/test()` helper functions from `sc_utils.h`. Messages with >1024 multiplexed signals are not supported (codegen reports `CodeGenError.UnsupportedFeature`). Using valid and mux_active ```c @@ -351,6 +351,23 @@ void handle_mux(const uint8_t data[8]) { } ``` +Using sc_valid_test for >64 signals (byte-array valid) +```c +#include "mux65_msg.h" +#include "sc_utils.h" /* sc_valid_test helper — emitted as sc_utils.h when file_prefix is sc_ */ + +void handle_mux65(const uint8_t data[8]) { + MUX65_MSG_t m = {0}; + if (MUX65_MSG_decode(&m, data, 8)) { + if (m.mux_active == MUX65_MSG_MUX_5) { + if (sc_valid_test(m.valid, MUX65_MSG_VALID_BRANCH_5)) { + /* use m.Branch_5 — index-based valid check for >64 signal messages */ + } + } + } +} +``` + ### Value tables (VAL_) Signals with `VAL_` mappings get C enums and to_string helpers: @@ -680,7 +697,8 @@ Details can be reproduced via the stress suite and bulk runner in `scripts/bulk_ - Generated CRC/Counter handling requires explicit `crc_counter:` YAML metadata; current supported algorithms are CRC-8 only. - When `crc_counter_check: true` is enabled without explicit metadata, code generation fails fast for inferred CRC/counter-like signals instead of silently accepting an unsupported path. - Supports both classic CAN (up to 8-byte) and CAN FD (up to 64-byte) payloads -- Messages with >32 multiplexed signals automatically use a 64-bit `valid` bitmask (`uint64_t`). Messages with >64 multiplexed signals are not supported (code generation reports `CodeGenError.UnsupportedFeature`). +- Messages with ≤32 multiplexed signals use a 32-bit `valid` bitmask (`uint32_t`); 33–64 signals use 64-bit (`uint64_t`); 65–1024 signals use a byte-array (`uint8_t valid[(N+7)/8]`) with `sc_valid_set/clear/test()` helpers from `sc_utils.h`. Messages with >1024 multiplexed signals are not supported (`CodeGenError.UnsupportedFeature`). + ## Dispatch modes, registry, and relation to nanopb diff --git "a/Reports/20260212_1310_C1_C2_Critical_\354\210\230\354\240\225.md" "b/Reports/20260212-1310_C1_C2_Critical_\354\210\230\354\240\225.md" similarity index 100% rename from "Reports/20260212_1310_C1_C2_Critical_\354\210\230\354\240\225.md" rename to "Reports/20260212-1310_C1_C2_Critical_\354\210\230\354\240\225.md" diff --git "a/Reports/20260212_1323_CodegenTests_Windows_\355\230\270\355\231\230\354\204\261_\354\210\230\354\240\225.md" "b/Reports/20260212-1323_CodegenTests_Windows_\355\230\270\355\231\230\354\204\261_\354\210\230\354\240\225.md" similarity index 100% rename from "Reports/20260212_1323_CodegenTests_Windows_\355\230\270\355\231\230\354\204\261_\354\210\230\354\240\225.md" rename to "Reports/20260212-1323_CodegenTests_Windows_\355\230\270\355\231\230\354\204\261_\354\210\230\354\240\225.md" diff --git "a/Reports/20260212_1352_Core_\355\205\214\354\212\244\355\212\270_H1c_H1g_H3c.md" "b/Reports/20260212-1352_Core_\355\205\214\354\212\244\355\212\270_H1c_H1g_H3c.md" similarity index 100% rename from "Reports/20260212_1352_Core_\355\205\214\354\212\244\355\212\270_H1c_H1g_H3c.md" rename to "Reports/20260212-1352_Core_\355\205\214\354\212\244\355\212\270_H1c_H1g_H3c.md" diff --git "a/Reports/20260212_1405_H2_Generator_Core_\355\206\265\355\225\251.md" "b/Reports/20260212-1405_H2_Generator_Core_\355\206\265\355\225\251.md" similarity index 100% rename from "Reports/20260212_1405_H2_Generator_Core_\355\206\265\355\225\251.md" rename to "Reports/20260212-1405_H2_Generator_Core_\355\206\265\355\225\251.md" diff --git "a/Reports/20260212_1418_M2_M3_M4_\354\275\224\353\223\234\355\222\210\354\247\210\352\260\234\354\204\240.md" "b/Reports/20260212-1418_M2_M3_M4_\354\275\224\353\223\234\355\222\210\354\247\210\352\260\234\354\204\240.md" similarity index 100% rename from "Reports/20260212_1418_M2_M3_M4_\354\275\224\353\223\234\355\222\210\354\247\210\352\260\234\354\204\240.md" rename to "Reports/20260212-1418_M2_M3_M4_\354\275\224\353\223\234\355\222\210\354\247\210\352\260\234\354\204\240.md" diff --git "a/Reports/20260212_1534_CAN_FD_\354\247\200\354\233\220_\354\266\224\352\260\200.md" "b/Reports/20260212-1534_CAN_FD_\354\247\200\354\233\220_\354\266\224\352\260\200.md" similarity index 100% rename from "Reports/20260212_1534_CAN_FD_\354\247\200\354\233\220_\354\266\224\352\260\200.md" rename to "Reports/20260212-1534_CAN_FD_\354\247\200\354\233\220_\354\266\224\352\260\200.md" diff --git a/Reports/20260212_1615_main_c_comprehensive_bitpattern_tests.md b/Reports/20260212-1615_main_c_comprehensive_bitpattern_tests.md similarity index 100% rename from Reports/20260212_1615_main_c_comprehensive_bitpattern_tests.md rename to Reports/20260212-1615_main_c_comprehensive_bitpattern_tests.md diff --git "a/Reports/20260212_1626_L4c_DLC\353\247\244\355\225\221_\354\242\205\355\225\251\355\205\214\354\212\244\355\212\270_signed\354\210\230\354\240\225.md" "b/Reports/20260212-1626_L4c_DLC\353\247\244\355\225\221_\354\242\205\355\225\251\355\205\214\354\212\244\355\212\270_signed\354\210\230\354\240\225.md" similarity index 100% rename from "Reports/20260212_1626_L4c_DLC\353\247\244\355\225\221_\354\242\205\355\225\251\355\205\214\354\212\244\355\212\270_signed\354\210\230\354\240\225.md" rename to "Reports/20260212-1626_L4c_DLC\353\247\244\355\225\221_\354\242\205\355\225\251\355\205\214\354\212\244\355\212\270_signed\354\210\230\354\240\225.md" diff --git "a/Reports/20260212_9999_\354\235\274\354\235\274\354\264\235\354\240\225\353\246\254_ROADMAP_\353\214\200\352\267\234\353\252\250\354\247\204\355\226\211.md" "b/Reports/20260212-9999_\354\235\274\354\235\274\354\264\235\354\240\225\353\246\254_ROADMAP_\353\214\200\352\267\234\353\252\250\354\247\204\355\226\211.md" similarity index 100% rename from "Reports/20260212_9999_\354\235\274\354\235\274\354\264\235\354\240\225\353\246\254_ROADMAP_\353\214\200\352\267\234\353\252\250\354\247\204\355\226\211.md" rename to "Reports/20260212-9999_\354\235\274\354\235\274\354\264\235\354\240\225\353\246\254_ROADMAP_\353\214\200\352\267\234\353\252\250\354\247\204\355\226\211.md" diff --git a/Reports/20260213_0000_Morning_Briefing.md b/Reports/20260213-0000_Morning_Briefing.md similarity index 100% rename from Reports/20260213_0000_Morning_Briefing.md rename to Reports/20260213-0000_Morning_Briefing.md diff --git a/Reports/20260213_0730_Git_Workflow_Setup.md b/Reports/20260213-0730_Git_Workflow_Setup.md similarity index 100% rename from Reports/20260213_0730_Git_Workflow_Setup.md rename to Reports/20260213-0730_Git_Workflow_Setup.md diff --git "a/Reports/20260213_1101_CI_\355\212\270\353\246\254\352\261\260_\354\240\225\353\246\254_SDK\352\263\240\354\240\225.md" "b/Reports/20260213-1101_CI_\355\212\270\353\246\254\352\261\260_\354\240\225\353\246\254_SDK\352\263\240\354\240\225.md" similarity index 100% rename from "Reports/20260213_1101_CI_\355\212\270\353\246\254\352\261\260_\354\240\225\353\246\254_SDK\352\263\240\354\240\225.md" rename to "Reports/20260213-1101_CI_\355\212\270\353\246\254\352\261\260_\354\240\225\353\246\254_SDK\352\263\240\354\240\225.md" diff --git "a/Reports/20260213_1300_v0.3.0_\353\246\264\353\246\254\354\212\244.md" "b/Reports/20260213-1300_v0.3.0_\353\246\264\353\246\254\354\212\244.md" similarity index 100% rename from "Reports/20260213_1300_v0.3.0_\353\246\264\353\246\254\354\212\244.md" rename to "Reports/20260213-1300_v0.3.0_\353\246\264\353\246\254\354\212\244.md" diff --git "a/Reports/20260213_1630_README_\353\262\204\354\240\204_\352\265\254\354\241\260_\354\227\205\353\215\260\354\235\264\355\212\270.md" "b/Reports/20260213-1630_README_\353\262\204\354\240\204_\352\265\254\354\241\260_\354\227\205\353\215\260\354\235\264\355\212\270.md" similarity index 100% rename from "Reports/20260213_1630_README_\353\262\204\354\240\204_\352\265\254\354\241\260_\354\227\205\353\215\260\354\235\264\355\212\270.md" rename to "Reports/20260213-1630_README_\353\262\204\354\240\204_\352\265\254\354\241\260_\354\227\205\353\215\260\354\235\264\355\212\270.md" diff --git "a/Reports/20260213_1720_Oracle_\354\213\244\355\214\250\355\225\264\352\262\260_\355\224\214\353\236\234\354\210\230\353\246\275.md" "b/Reports/20260213-1720_Oracle_\354\213\244\355\214\250\355\225\264\352\262\260_\355\224\214\353\236\234\354\210\230\353\246\275.md" similarity index 100% rename from "Reports/20260213_1720_Oracle_\354\213\244\355\214\250\355\225\264\352\262\260_\355\224\214\353\236\234\354\210\230\353\246\275.md" rename to "Reports/20260213-1720_Oracle_\354\213\244\355\214\250\355\225\264\352\262\260_\355\224\214\353\236\234\354\210\230\353\246\275.md" diff --git "a/Reports/20260213_1817_Oracle_\355\225\230\353\204\244\354\212\244_\355\205\234\355\224\214\353\246\277_\352\265\254\355\230\204.md" "b/Reports/20260213-1817_Oracle_\355\225\230\353\204\244\354\212\244_\355\205\234\355\224\214\353\246\277_\352\265\254\355\230\204.md" similarity index 100% rename from "Reports/20260213_1817_Oracle_\355\225\230\353\204\244\354\212\244_\355\205\234\355\224\214\353\246\277_\352\265\254\355\230\204.md" rename to "Reports/20260213-1817_Oracle_\355\225\230\353\204\244\354\212\244_\355\205\234\355\224\214\353\246\277_\352\265\254\355\230\204.md" diff --git "a/Reports/20260213_1838_Oracle_Core_Engine_\352\265\254\355\230\204.md" "b/Reports/20260213-1838_Oracle_Core_Engine_\352\265\254\355\230\204.md" similarity index 100% rename from "Reports/20260213_1838_Oracle_Core_Engine_\352\265\254\355\230\204.md" rename to "Reports/20260213-1838_Oracle_Core_Engine_\352\265\254\355\230\204.md" diff --git a/Reports/20260213_1848_Oracle_Tolerance_Metadata_Comparison.md b/Reports/20260213-1848_Oracle_Tolerance_Metadata_Comparison.md similarity index 100% rename from Reports/20260213_1848_Oracle_Tolerance_Metadata_Comparison.md rename to Reports/20260213-1848_Oracle_Tolerance_Metadata_Comparison.md diff --git "a/Reports/20260213_1850_Oracle_Core_Engine_\352\265\254\355\230\204.md" "b/Reports/20260213-1850_Oracle_Core_Engine_\352\265\254\355\230\204.md" similarity index 100% rename from "Reports/20260213_1850_Oracle_Core_Engine_\352\265\254\355\230\204.md" rename to "Reports/20260213-1850_Oracle_Core_Engine_\352\265\254\355\230\204.md" diff --git "a/Reports/20260213_1925_\355\205\214\354\212\244\355\212\270_\354\213\240\353\242\260\355\231\225\353\263\264_\354\235\270\355\224\204\353\235\274.md" "b/Reports/20260213-1925_\355\205\214\354\212\244\355\212\270_\354\213\240\353\242\260\355\231\225\353\263\264_\354\235\270\355\224\204\353\235\274.md" similarity index 100% rename from "Reports/20260213_1925_\355\205\214\354\212\244\355\212\270_\354\213\240\353\242\260\355\231\225\353\263\264_\354\235\270\355\224\204\353\235\274.md" rename to "Reports/20260213-1925_\355\205\214\354\212\244\355\212\270_\354\213\240\353\242\260\355\231\225\353\263\264_\354\235\270\355\224\204\353\235\274.md" diff --git "a/Reports/20260213_1937_Oracle_pytest_\354\212\244\354\234\204\355\212\270_\352\265\254\354\266\225.md" "b/Reports/20260213-1937_Oracle_pytest_\354\212\244\354\234\204\355\212\270_\352\265\254\354\266\225.md" similarity index 100% rename from "Reports/20260213_1937_Oracle_pytest_\354\212\244\354\234\204\355\212\270_\352\265\254\354\266\225.md" rename to "Reports/20260213-1937_Oracle_pytest_\354\212\244\354\234\204\355\212\270_\352\265\254\354\266\225.md" diff --git a/Reports/20260213_1952_Oracle_Integration_Result_Report.md b/Reports/20260213-1952_Oracle_Integration_Result_Report.md similarity index 100% rename from Reports/20260213_1952_Oracle_Integration_Result_Report.md rename to Reports/20260213-1952_Oracle_Integration_Result_Report.md diff --git "a/Reports/20260215_1549_\352\263\204\355\232\215\355\230\204\355\231\251_\353\266\204\354\204\235_\354\227\205\353\215\260\354\235\264\355\212\270.md" "b/Reports/20260215-1549_\352\263\204\355\232\215\355\230\204\355\231\251_\353\266\204\354\204\235_\354\227\205\353\215\260\354\235\264\355\212\270.md" similarity index 100% rename from "Reports/20260215_1549_\352\263\204\355\232\215\355\230\204\355\231\251_\353\266\204\354\204\235_\354\227\205\353\215\260\354\235\264\355\212\270.md" rename to "Reports/20260215-1549_\352\263\204\355\232\215\355\230\204\355\231\251_\353\266\204\354\204\235_\354\227\205\353\215\260\354\235\264\355\212\270.md" diff --git "a/Reports/20260312_1545_B-O1_raw_range_heuristic_\354\240\201\354\232\251.md" "b/Reports/20260312-1545_B-O1_raw_range_heuristic_\354\240\201\354\232\251.md" similarity index 100% rename from "Reports/20260312_1545_B-O1_raw_range_heuristic_\354\240\201\354\232\251.md" rename to "Reports/20260312-1545_B-O1_raw_range_heuristic_\354\240\201\354\232\251.md" diff --git "a/Reports/20260312_1552_Oracle_\354\275\224\355\215\274\354\212\244_\354\236\254\352\262\200\354\246\235.md" "b/Reports/20260312-1552_Oracle_\354\275\224\355\215\274\354\212\244_\354\236\254\352\262\200\354\246\235.md" similarity index 100% rename from "Reports/20260312_1552_Oracle_\354\275\224\355\215\274\354\212\244_\354\236\254\352\262\200\354\246\235.md" rename to "Reports/20260312-1552_Oracle_\354\275\224\355\215\274\354\212\244_\354\236\254\352\262\200\354\246\235.md" diff --git "a/Reports/20260312_1602_Boulder_\355\224\214\353\236\234\354\240\225\353\246\254_\353\260\217_\353\271\204\355\231\234\354\204\261\355\231\224.md" "b/Reports/20260312-1602_Boulder_\355\224\214\353\236\234\354\240\225\353\246\254_\353\260\217_\353\271\204\355\231\234\354\204\261\355\231\224.md" similarity index 100% rename from "Reports/20260312_1602_Boulder_\355\224\214\353\236\234\354\240\225\353\246\254_\353\260\217_\353\271\204\355\231\234\354\204\261\355\231\224.md" rename to "Reports/20260312-1602_Boulder_\355\224\214\353\236\234\354\240\225\353\246\254_\353\260\217_\353\271\204\355\231\234\354\204\261\355\231\224.md" diff --git "a/Reports/20260312_1620_B-O1_\354\231\204\353\243\214_\353\254\270\354\204\234_\354\240\225\353\246\254.md" "b/Reports/20260312-1620_B-O1_\354\231\204\353\243\214_\353\254\270\354\204\234_\354\240\225\353\246\254.md" similarity index 100% rename from "Reports/20260312_1620_B-O1_\354\231\204\353\243\214_\353\254\270\354\204\234_\354\240\225\353\246\254.md" rename to "Reports/20260312-1620_B-O1_\354\231\204\353\243\214_\353\254\270\354\204\234_\354\240\225\353\246\254.md" diff --git a/Reports/20260312_1705_Oracle_mux_assumptions_validation.md b/Reports/20260312-1705_Oracle_mux_assumptions_validation.md similarity index 100% rename from Reports/20260312_1705_Oracle_mux_assumptions_validation.md rename to Reports/20260312-1705_Oracle_mux_assumptions_validation.md diff --git "a/Reports/20260312_1707_Oracle_mux_unit_tests_\354\266\224\352\260\200.md" "b/Reports/20260312-1707_Oracle_mux_unit_tests_\354\266\224\352\260\200.md" similarity index 100% rename from "Reports/20260312_1707_Oracle_mux_unit_tests_\354\266\224\352\260\200.md" rename to "Reports/20260312-1707_Oracle_mux_unit_tests_\354\266\224\352\260\200.md" diff --git "a/Reports/20260312_1720_Oracle_mux_decode_\353\271\204\352\265\220\352\262\200\354\246\235.md" "b/Reports/20260312-1720_Oracle_mux_decode_\353\271\204\352\265\220\352\262\200\354\246\235.md" similarity index 100% rename from "Reports/20260312_1720_Oracle_mux_decode_\353\271\204\352\265\220\352\262\200\354\246\235.md" rename to "Reports/20260312-1720_Oracle_mux_decode_\353\271\204\352\265\220\352\262\200\354\246\235.md" diff --git a/Reports/20260312_1800_B-O2_Oracle_Multiplex_Mode.md b/Reports/20260312-1800_B-O2_Oracle_Multiplex_Mode.md similarity index 100% rename from Reports/20260312_1800_B-O2_Oracle_Multiplex_Mode.md rename to Reports/20260312-1800_B-O2_Oracle_Multiplex_Mode.md diff --git "a/Reports/20260312_1830_B-O2_Final_Wave_\354\231\204\353\243\214.md" "b/Reports/20260312-1830_B-O2_Final_Wave_\354\231\204\353\243\214.md" similarity index 100% rename from "Reports/20260312_1830_B-O2_Final_Wave_\354\231\204\353\243\214.md" rename to "Reports/20260312-1830_B-O2_Final_Wave_\354\231\204\353\243\214.md" diff --git "a/Reports/20260313_0050_\354\225\210\354\240\225\355\231\224_\353\262\204\354\240\204\353\262\224\355\224\204_PR\354\203\235\354\204\261.md" "b/Reports/20260313-0050_\354\225\210\354\240\225\355\231\224_\353\262\204\354\240\204\353\262\224\355\224\204_PR\354\203\235\354\204\261.md" similarity index 100% rename from "Reports/20260313_0050_\354\225\210\354\240\225\355\231\224_\353\262\204\354\240\204\353\262\224\355\224\204_PR\354\203\235\354\204\261.md" rename to "Reports/20260313-0050_\354\225\210\354\240\225\355\231\224_\353\262\204\354\240\204\353\262\224\355\224\204_PR\354\203\235\354\204\261.md" diff --git "a/Reports/20260313_1045_v0.3.1_\353\246\264\353\246\254\354\246\210\354\231\204\353\243\214_\352\260\200\354\235\264\353\223\234\354\240\220\352\262\200.md" "b/Reports/20260313-1045_v0.3.1_\353\246\264\353\246\254\354\246\210\354\231\204\353\243\214_\352\260\200\354\235\264\353\223\234\354\240\220\352\262\200.md" similarity index 100% rename from "Reports/20260313_1045_v0.3.1_\353\246\264\353\246\254\354\246\210\354\231\204\353\243\214_\352\260\200\354\235\264\353\223\234\354\240\220\352\262\200.md" rename to "Reports/20260313-1045_v0.3.1_\353\246\264\353\246\254\354\246\210\354\231\204\353\243\214_\352\260\200\354\235\264\353\223\234\354\240\220\352\262\200.md" diff --git "a/Reports/20260313_1049_pre-0.3.2_\353\240\210\355\217\254\354\247\204\353\213\250_\354\240\204\353\236\265\354\240\234\354\225\210.md" "b/Reports/20260313-1049_pre-0.3.2_\353\240\210\355\217\254\354\247\204\353\213\250_\354\240\204\353\236\265\354\240\234\354\225\210.md" similarity index 100% rename from "Reports/20260313_1049_pre-0.3.2_\353\240\210\355\217\254\354\247\204\353\213\250_\354\240\204\353\236\265\354\240\234\354\225\210.md" rename to "Reports/20260313-1049_pre-0.3.2_\353\240\210\355\217\254\354\247\204\353\213\250_\354\240\204\353\236\265\354\240\234\354\225\210.md" diff --git a/Reports/20260313_1200_update_CRC_fields.md b/Reports/20260313-1200_update_CRC_fields.md similarity index 100% rename from Reports/20260313_1200_update_CRC_fields.md rename to Reports/20260313-1200_update_CRC_fields.md diff --git "a/Reports/20260313_1248_valid_bitmask_RED_\355\205\214\354\212\244\355\212\270_\354\266\224\352\260\200.md" "b/Reports/20260313-1248_valid_bitmask_RED_\355\205\214\354\212\244\355\212\270_\354\266\224\352\260\200.md" similarity index 100% rename from "Reports/20260313_1248_valid_bitmask_RED_\355\205\214\354\212\244\355\212\270_\354\266\224\352\260\200.md" rename to "Reports/20260313-1248_valid_bitmask_RED_\355\205\214\354\212\244\355\212\270_\354\266\224\352\260\200.md" diff --git a/Reports/20260313_1258_B-O3_valid_bitmask_auto_widening.md b/Reports/20260313-1258_B-O3_valid_bitmask_auto_widening.md similarity index 100% rename from Reports/20260313_1258_B-O3_valid_bitmask_auto_widening.md rename to Reports/20260313-1258_B-O3_valid_bitmask_auto_widening.md diff --git "a/Reports/20260313_1322_v0.3.2_B-O3_\354\231\204\353\243\214.md" "b/Reports/20260313-1322_v0.3.2_B-O3_\354\231\204\353\243\214.md" similarity index 100% rename from "Reports/20260313_1322_v0.3.2_B-O3_\354\231\204\353\243\214.md" rename to "Reports/20260313-1322_v0.3.2_B-O3_\354\231\204\353\243\214.md" diff --git a/Reports/20260313_1410_v0.3.2_pre_release_readiness.md b/Reports/20260313-1410_v0.3.2_pre_release_readiness.md similarity index 100% rename from Reports/20260313_1410_v0.3.2_pre_release_readiness.md rename to Reports/20260313-1410_v0.3.2_pre_release_readiness.md diff --git a/Reports/20260313_1428_v0.3.2_release_blocker_fix_UnsupportedFeature.md b/Reports/20260313-1428_v0.3.2_release_blocker_fix_UnsupportedFeature.md similarity index 100% rename from Reports/20260313_1428_v0.3.2_release_blocker_fix_UnsupportedFeature.md rename to Reports/20260313-1428_v0.3.2_release_blocker_fix_UnsupportedFeature.md diff --git "a/Reports/20260313_1433_v0.3.2_PR_\354\202\254\354\240\204\354\240\225\355\225\251\354\204\261\354\240\220\352\262\200.md" "b/Reports/20260313-1433_v0.3.2_PR_\354\202\254\354\240\204\354\240\225\355\225\251\354\204\261\354\240\220\352\262\200.md" similarity index 100% rename from "Reports/20260313_1433_v0.3.2_PR_\354\202\254\354\240\204\354\240\225\355\225\251\354\204\261\354\240\220\352\262\200.md" rename to "Reports/20260313-1433_v0.3.2_PR_\354\202\254\354\240\204\354\240\225\355\225\251\354\204\261\354\240\220\352\262\200.md" diff --git "a/Reports/20260313_1437_CHANGELOG_\354\234\240\354\247\200\354\244\221\353\213\250_\352\262\260\354\240\225.md" "b/Reports/20260313-1437_CHANGELOG_\354\234\240\354\247\200\354\244\221\353\213\250_\352\262\260\354\240\225.md" similarity index 100% rename from "Reports/20260313_1437_CHANGELOG_\354\234\240\354\247\200\354\244\221\353\213\250_\352\262\260\354\240\225.md" rename to "Reports/20260313-1437_CHANGELOG_\354\234\240\354\247\200\354\244\221\353\213\250_\352\262\260\354\240\225.md" diff --git "a/Reports/20260313_1501_PR14_\354\275\224\353\251\230\355\212\270\353\260\230\354\230\201_CI\354\266\251\353\217\214\355\225\264\352\262\260.md" "b/Reports/20260313-1501_PR14_\354\275\224\353\251\230\355\212\270\353\260\230\354\230\201_CI\354\266\251\353\217\214\355\225\264\352\262\260.md" similarity index 100% rename from "Reports/20260313_1501_PR14_\354\275\224\353\251\230\355\212\270\353\260\230\354\230\201_CI\354\266\251\353\217\214\355\225\264\352\262\260.md" rename to "Reports/20260313-1501_PR14_\354\275\224\353\251\230\355\212\270\353\260\230\354\230\201_CI\354\266\251\353\217\214\355\225\264\352\262\260.md" diff --git "a/Reports/20260313_1510_v0.3.2_\354\265\234\354\242\205\354\240\220\352\262\200_\355\203\234\352\271\205.md" "b/Reports/20260313-1510_v0.3.2_\354\265\234\354\242\205\354\240\220\352\262\200_\355\203\234\352\271\205.md" similarity index 100% rename from "Reports/20260313_1510_v0.3.2_\354\265\234\354\242\205\354\240\220\352\262\200_\355\203\234\352\271\205.md" rename to "Reports/20260313-1510_v0.3.2_\354\265\234\354\242\205\354\240\220\352\262\200_\355\203\234\352\271\205.md" diff --git "a/Reports/20260313_1644_L1_Scriban_\354\231\204\353\243\214_L2_\352\260\200\353\223\234\353\240\210\354\235\274.md" "b/Reports/20260313-1644_L1_Scriban_\354\231\204\353\243\214_L2_\352\260\200\353\223\234\353\240\210\354\235\274.md" similarity index 100% rename from "Reports/20260313_1644_L1_Scriban_\354\231\204\353\243\214_L2_\352\260\200\353\223\234\353\240\210\354\235\274.md" rename to "Reports/20260313-1644_L1_Scriban_\354\231\204\353\243\214_L2_\352\260\200\353\223\234\353\240\210\354\235\274.md" diff --git "a/Reports/20260313_1804_FS0764_\355\205\214\354\212\244\355\212\270\353\240\210\354\275\224\353\223\234\355\225\204\353\223\234\354\210\230\354\240\225.md" "b/Reports/20260313-1804_FS0764_\355\205\214\354\212\244\355\212\270\353\240\210\354\275\224\353\223\234\355\225\204\353\223\234\354\210\230\354\240\225.md" similarity index 100% rename from "Reports/20260313_1804_FS0764_\355\205\214\354\212\244\355\212\270\353\240\210\354\275\224\353\223\234\355\225\204\353\223\234\354\210\230\354\240\225.md" rename to "Reports/20260313-1804_FS0764_\355\205\214\354\212\244\355\212\270\353\240\210\354\275\224\353\223\234\355\225\204\353\223\234\354\210\230\354\240\225.md" diff --git "a/Reports/20260313_1816_Config_CrcCounter_YAML_\355\214\214\354\213\261.md" "b/Reports/20260313-1816_Config_CrcCounter_YAML_\355\214\214\354\213\261.md" similarity index 100% rename from "Reports/20260313_1816_Config_CrcCounter_YAML_\355\214\214\354\213\261.md" rename to "Reports/20260313-1816_Config_CrcCounter_YAML_\355\214\214\354\213\261.md" diff --git "a/Reports/20260313_1822_Config_\352\262\200\354\246\235_\353\241\234\354\247\201.md" "b/Reports/20260313-1822_Config_\352\262\200\354\246\235_\353\241\234\354\247\201.md" similarity index 100% rename from "Reports/20260313_1822_Config_\352\262\200\354\246\235_\353\241\234\354\247\201.md" rename to "Reports/20260313-1822_Config_\352\262\200\354\246\235_\353\241\234\354\247\201.md" diff --git "a/Reports/20260313_1837_T7_CRC8_\354\234\240\355\213\270\355\205\234\355\224\214\353\246\277_\354\266\224\352\260\200.md" "b/Reports/20260313-1837_T7_CRC8_\354\234\240\355\213\270\355\205\234\355\224\214\353\246\277_\354\266\224\352\260\200.md" similarity index 100% rename from "Reports/20260313_1837_T7_CRC8_\354\234\240\355\213\270\355\205\234\355\224\214\353\246\277_\354\266\224\352\260\200.md" rename to "Reports/20260313-1837_T7_CRC8_\354\234\240\355\213\270\355\205\234\355\224\214\353\246\277_\354\266\224\352\260\200.md" diff --git a/Reports/20260313_1844_T8_Message_CRC_Counter_Template_Slots.md b/Reports/20260313-1844_T8_Message_CRC_Counter_Template_Slots.md similarity index 100% rename from Reports/20260313_1844_T8_Message_CRC_Counter_Template_Slots.md rename to Reports/20260313-1844_T8_Message_CRC_Counter_Template_Slots.md diff --git "a/Reports/20260313_1856_T9_Dbc_ConfigMetadata_IR\353\263\264\352\260\225.md" "b/Reports/20260313-1856_T9_Dbc_ConfigMetadata_IR\353\263\264\352\260\225.md" similarity index 100% rename from "Reports/20260313_1856_T9_Dbc_ConfigMetadata_IR\353\263\264\352\260\225.md" rename to "Reports/20260313-1856_T9_Dbc_ConfigMetadata_IR\353\263\264\352\260\225.md" diff --git a/Reports/20260313_1909_T10_Codegen_crc_counter_validate.md b/Reports/20260313-1909_T10_Codegen_crc_counter_validate.md similarity index 100% rename from Reports/20260313_1909_T10_Codegen_crc_counter_validate.md rename to Reports/20260313-1909_T10_Codegen_crc_counter_validate.md diff --git a/Reports/20260313_2018_T15_CRC_Counter_test_append.md b/Reports/20260313-2018_T15_CRC_Counter_test_append.md similarity index 100% rename from Reports/20260313_2018_T15_CRC_Counter_test_append.md rename to Reports/20260313-2018_T15_CRC_Counter_test_append.md diff --git a/Reports/20260313_2025_T16_crc_c_build_verification.md b/Reports/20260313-2025_T16_crc_c_build_verification.md similarity index 100% rename from Reports/20260313_2025_T16_crc_c_build_verification.md rename to Reports/20260313-2025_T16_crc_c_build_verification.md diff --git "a/Reports/20260313_2100_L2bc_CRC_Counter_\352\265\254\355\230\204.md" "b/Reports/20260313-2100_L2bc_CRC_Counter_\352\265\254\355\230\204.md" similarity index 100% rename from "Reports/20260313_2100_L2bc_CRC_Counter_\352\265\254\355\230\204.md" rename to "Reports/20260313-2100_L2bc_CRC_Counter_\352\265\254\355\230\204.md" diff --git a/Reports/20260313_2230_Dbc_T12_blankline_fix.md b/Reports/20260313-2230_Dbc_T12_blankline_fix.md similarity index 100% rename from Reports/20260313_2230_Dbc_T12_blankline_fix.md rename to Reports/20260313-2230_Dbc_T12_blankline_fix.md diff --git a/Reports/20260313_2230_L2bc_Final_Verification.md b/Reports/20260313-2230_L2bc_Final_Verification.md similarity index 100% rename from Reports/20260313_2230_L2bc_Final_Verification.md rename to Reports/20260313-2230_L2bc_Final_Verification.md diff --git "a/Reports/20260315_1645_ROADMAP_\354\240\225\355\225\251\354\204\261_\352\260\220\354\202\254.md" "b/Reports/20260315-1645_ROADMAP_\354\240\225\355\225\251\354\204\261_\352\260\220\354\202\254.md" similarity index 100% rename from "Reports/20260315_1645_ROADMAP_\354\240\225\355\225\251\354\204\261_\352\260\220\354\202\254.md" rename to "Reports/20260315-1645_ROADMAP_\354\240\225\355\225\251\354\204\261_\352\260\220\354\202\254.md" diff --git "a/Reports/20260315_1648_ROADMAP_\354\240\225\355\225\251\354\204\261_\354\203\201\354\204\270\352\260\220\354\202\254_\354\240\225\354\240\225\355\217\254\355\225\250.md" "b/Reports/20260315-1648_ROADMAP_\354\240\225\355\225\251\354\204\261_\354\203\201\354\204\270\352\260\220\354\202\254_\354\240\225\354\240\225\355\217\254\355\225\250.md" similarity index 100% rename from "Reports/20260315_1648_ROADMAP_\354\240\225\355\225\251\354\204\261_\354\203\201\354\204\270\352\260\220\354\202\254_\354\240\225\354\240\225\355\217\254\355\225\250.md" rename to "Reports/20260315-1648_ROADMAP_\354\240\225\355\225\251\354\204\261_\354\203\201\354\204\270\352\260\220\354\202\254_\354\240\225\354\240\225\355\217\254\355\225\250.md" diff --git "a/Reports/20260315_1656_ROADMAP_\353\254\270\354\204\234\354\240\225\355\225\251\354\204\261_\353\260\230\354\230\201.md" "b/Reports/20260315-1656_ROADMAP_\353\254\270\354\204\234\354\240\225\355\225\251\354\204\261_\353\260\230\354\230\201.md" similarity index 100% rename from "Reports/20260315_1656_ROADMAP_\353\254\270\354\204\234\354\240\225\355\225\251\354\204\261_\353\260\230\354\230\201.md" rename to "Reports/20260315-1656_ROADMAP_\353\254\270\354\204\234\354\240\225\355\225\251\354\204\261_\353\260\230\354\230\201.md" diff --git "a/Reports/20260315_1811_ROADMAP_\354\242\205\352\262\260_\353\260\217_\354\260\250\352\270\260\353\266\204\353\246\254.md" "b/Reports/20260315-1811_ROADMAP_\354\242\205\352\262\260_\353\260\217_\354\260\250\352\270\260\353\266\204\353\246\254.md" similarity index 100% rename from "Reports/20260315_1811_ROADMAP_\354\242\205\352\262\260_\353\260\217_\354\260\250\352\270\260\353\266\204\353\246\254.md" rename to "Reports/20260315-1811_ROADMAP_\354\242\205\352\262\260_\353\260\217_\354\260\250\352\270\260\353\266\204\353\246\254.md" diff --git "a/Reports/20260315_1818_ROADMAP_\354\242\205\352\262\260_\355\233\204\354\206\215\354\240\225\354\240\225.md" "b/Reports/20260315-1818_ROADMAP_\354\242\205\352\262\260_\355\233\204\354\206\215\354\240\225\354\240\225.md" similarity index 100% rename from "Reports/20260315_1818_ROADMAP_\354\242\205\352\262\260_\355\233\204\354\206\215\354\240\225\354\240\225.md" rename to "Reports/20260315-1818_ROADMAP_\354\242\205\352\262\260_\355\233\204\354\206\215\354\240\225\354\240\225.md" diff --git "a/Reports/20260315_1830_Reports_\353\210\204\353\235\275\354\240\220\352\262\200_\353\260\217_\353\240\210\355\217\254\355\214\205\354\231\204\353\243\214.md" "b/Reports/20260315-1830_Reports_\353\210\204\353\235\275\354\240\220\352\262\200_\353\260\217_\353\240\210\355\217\254\355\214\205\354\231\204\353\243\214.md" similarity index 100% rename from "Reports/20260315_1830_Reports_\353\210\204\353\235\275\354\240\220\352\262\200_\353\260\217_\353\240\210\355\217\254\355\214\205\354\231\204\353\243\214.md" rename to "Reports/20260315-1830_Reports_\353\210\204\353\235\275\354\240\220\352\262\200_\353\260\217_\353\240\210\355\217\254\355\214\205\354\231\204\353\243\214.md" diff --git "a/Reports/20260315_1844_Plans_\352\265\254\354\241\260\354\240\204\355\231\230_\354\240\225\355\225\251\354\204\261.md" "b/Reports/20260315-1844_Plans_\352\265\254\354\241\260\354\240\204\355\231\230_\354\240\225\355\225\251\354\204\261.md" similarity index 100% rename from "Reports/20260315_1844_Plans_\352\265\254\354\241\260\354\240\204\355\231\230_\354\240\225\355\225\251\354\204\261.md" rename to "Reports/20260315-1844_Plans_\352\265\254\354\241\260\354\240\204\355\231\230_\354\240\225\355\225\251\354\204\261.md" diff --git "a/Reports/20260315_2031_Valid_GT64_\354\204\244\352\263\204\353\254\270\354\204\234_\354\231\204\354\204\261.md" "b/Reports/20260315-2031_Valid_GT64_\354\204\244\352\263\204\353\254\270\354\204\234_\354\231\204\354\204\261.md" similarity index 100% rename from "Reports/20260315_2031_Valid_GT64_\354\204\244\352\263\204\353\254\270\354\204\234_\354\231\204\354\204\261.md" rename to "Reports/20260315-2031_Valid_GT64_\354\204\244\352\263\204\353\254\270\354\204\234_\354\231\204\354\204\261.md" diff --git a/Reports/20260316-0749_Codegen_ValidBitmask_RED_Tests.md b/Reports/20260316-0749_Codegen_ValidBitmask_RED_Tests.md new file mode 100644 index 0000000..3581fcd --- /dev/null +++ b/Reports/20260316-0749_Codegen_ValidBitmask_RED_Tests.md @@ -0,0 +1,54 @@ +# 작업 보고서: Codegen_ValidBitmask_RED_Tests + +**RUN_ID**: 20260316-0749 +**배치 내 단계 시각(원본 파일명 기준)**: 20260316_0749 + +## 📝 작업 요약 + +- `tests/Signal.CANdy.Core.Tests/CodegenTests.fs`에 `>64` valid bitmask byte-array 기능용 RED 단계 테스트(T1–T6)를 추가/보강했다. +- 기존 65-signal UnsupportedFeature 테스트를 성공 기대 테스트로 전환했고, 128/72/1024/1025/조건부 utils emission/비-mux 100-signal 시나리오를 추가해 현재 구현 한계를 명시적으로 깨뜨리도록 만들었다. +- 요청된 로컬 커밋 `e0e788a` (`test(codegen): add failing tests for >64 valid byte-array`)를 생성했다. + +## 🛠 변경 상세 + +- 수정 파일: `tests/Signal.CANdy.Core.Tests/CodegenTests.fs` + - 기존 `valid bitmask uses uint64_t for 64-signal mux message` 테스트에 `uint8_t` 배열이 나오지 않아야 함 / `1ULL` shift 사용 확인 assertion을 추가했다. + - 기존 65-signal mux valid bitmask 테스트를 `valid bitmask uses uint8_t byte array for 65-signal mux message`로 리팩터링했다. + - 실제 temp output directory 생성/정리 패턴으로 변경. + - header/source에 대해 `uint8_t valid[9]`, `VALID_BYTES`, plain index macro, `sc_utils.h`, `memset`, `sc_valid_set` 기반 동작을 기대하도록 작성. + - 신규 테스트 추가: + - `valid bitmask uses uint8_t byte array for 128-signal mux message` + - `utils header emits sc_valid helpers only when a message has more than 64 mux signals` + - `codegen fails with UnsupportedFeature for 1025-signal mux message` + - `valid bitmask uses uint8_t byte array for 72-signal mux message` + - `valid bitmask uses uint8_t byte array for 1024-signal mux message` + - 기존 non-mux 다신호 테스트를 `non-mux message with 100 signals has no valid field`로 확장했다. +- 추가 확인: + - `lsp_diagnostics` 기준 변경 파일 컴파일 진단 오류 없음. + +## ✅ 테스트 결과 + +- 실행 명령: `dotnet test --configuration Release -v minimal --nologo` +- 결과: + - 빌드/테스트 실행 자체는 성공적으로 시작되었고 테스트 프로젝트 컴파일 오류는 없었다. + - `Signal.CANdy.Core.Tests`에서 총 6개 실패, 124개 통과. + - 실패한 신규/변경 테스트: + - `valid bitmask uses uint8_t byte array for 65-signal mux message` + - `valid bitmask uses uint8_t byte array for 128-signal mux message` + - `utils header emits sc_valid helpers only when a message has more than 64 mux signals` + - `codegen fails with UnsupportedFeature for 1025-signal mux message` + - `valid bitmask uses uint8_t byte array for 72-signal mux message` + - `valid bitmask uses uint8_t byte array for 1024-signal mux message` + - 실패 원인 요약: + - 현재 구현이 `>64` mux valid bitmask를 여전히 `UnsupportedFeature`로 처리한다. + - 1025-signal guard 메시지도 새 기대치(`1024`)와 불일치한다. + - 이는 RED phase 기대 결과와 일치한다. +- `Generator.Tests`: 27개 통과. + +## ⏭ 다음 계획 + +- 활성 ROADMAP 항목: `Plans/ROADMAP.md`의 `1. valid 비트마스크 >64 신호 fallback 방식`. +- 다음 세션에서는 byte-array 기반 valid 표현(`uint8_t valid[N]`, helper emission, set/clear/test 호출, upper bound 정책)을 `src/Signal.CANdy.Core/Codegen.fs` 및 관련 템플릿에 구현하는 GREEN phase 작업이 필요하다. +- 선행 조건: + - 이번 RED 테스트 기대치를 구현 설계와 정렬할 것. + - 현재 작업에서 완료된 ROADMAP 체크박스는 없음. diff --git "a/Reports/20260316-0807_Valid_GT64_ByteArray_\352\265\254\355\230\204.md" "b/Reports/20260316-0807_Valid_GT64_ByteArray_\352\265\254\355\230\204.md" new file mode 100644 index 0000000..d0f858c --- /dev/null +++ "b/Reports/20260316-0807_Valid_GT64_ByteArray_\352\265\254\355\230\204.md" @@ -0,0 +1,62 @@ +# 작업 보고서: Valid_GT64_ByteArray_구현 + +**RUN_ID**: 20260316-0807 +**배치 내 단계 시각(원본 파일명 기준)**: 20260316_0807 + +## 📝 작업 요약 + +- `src/Signal.CANdy.Core/Codegen.fs`와 헤더 템플릿에 `>64` multiplexed signal valid bitmask byte-array fallback의 핵심 구현(Phase 2-4 + Phase 5 초안)을 적용했다. +- 정확한 3개 로컬 커밋을 생성했고, 각 커밋 직후 `dotnet build --configuration Release --nologo`는 모두 성공했다. +- 하지만 최종 GREEN 단계는 기존 테스트 기대치 2건이 새 설계와 불일치해 완료하지 못했다. + +## 🛠 변경 상세 + +- 수정 파일: `src/Signal.CANdy.Core/Codegen.fs` + - Phase 2: `useValidArray`, 3-tier valid width 선택, `validArraySize` 추가. + - Phase 3: `sc_valid_set` 기반 decode valid set, `memset` 초기화, `#define ..._VALID_BYTES`, `uint8_t valid[N]` 구조체 필드, 64-bit 주석 유지. + - Phase 4: overflow guard 상한을 `>64`에서 `>1024`로 상향하고 오류 메시지를 1024 기준으로 갱신. + - Phase 5 초안: `has_valid_array`, `needs_utils_include`, `utils_header_name` Scriban 모델 값 추가. +- 수정 파일: `templates/utils.h.scriban` + - `has_valid_array` 조건으로 `sc_valid_set`, `sc_valid_clear`, `sc_valid_test` inline helper block 추가. +- 수정 파일: `templates/message.h.scriban` + - `needs_utils_include` 조건으로 `#include "{{ utils_header_name }}"` 추가. +- 생성한 커밋: + - `ed2bf51` `feat(codegen): add 3-tier valid width selection for >64 signals` + - `63d62a7` `feat(codegen): emit byte-array macros, struct field, and decode body for >64` + - `fa45ce9` `feat(codegen): lift overflow guard from 64 to 1024 signals` +- 미커밋 상태로 남은 변경: + - `src/Signal.CANdy.Core/Codegen.fs` + - `templates/utils.h.scriban` + - `templates/message.h.scriban` + - `tests/Signal.CANdy.Core.Tests/CodegenTests.fs` + - `tests/Signal.CANdy.Core.Tests/FacadeTests.fs` + +## ✅ 테스트 결과 + +- `lsp_diagnostics` + - `src/Signal.CANdy.Core/Codegen.fs`: clean + - `tests/Signal.CANdy.Core.Tests/CodegenTests.fs`: clean + - `tests/Signal.CANdy.Core.Tests/FacadeTests.fs`: clean +- 빌드 + - 각 Phase 2/3/4 직후 `dotnet build --configuration Release --nologo` 성공 + - Phase 5 초안 후에도 `dotnet build --configuration Release --nologo` 성공 +- 전체 테스트 + - `dotnet test --configuration Release -v minimal --nologo` 결과: `Signal.CANdy.Core.Tests` 128 pass / 2 fail, `Generator.Tests` 27 pass / 0 fail + - 잔여 실패 2건: + 1. `FacadeTests.GenerateCode throws SignalCandyCodeGenException for UnsupportedFeature` + - 기존 테스트가 65-signal mux를 여전히 UnsupportedFeature로 기대함 + - 현재 구현/설계는 65~1024 허용, 1025만 UnsupportedFeature + 2. `CodegenTests.valid bitmask uses uint8_t byte array for 65-signal mux message` + - 기존 테스트가 `Branch_0=0`, `Branch_63=63` 기대 + - 실제 구현은 `List.iteri` 기준 전체 signal order 유지로 `MuxSel=0`, `Branch_0=1`, `Branch_63=64` +- 따라서 사용자 요구의 최종 기준인 `152+ pass, 0 fail`에는 도달하지 못했다. + +## ⏭ 다음 계획 + +- 활성 계획 항목: `Plans/ROADMAP.md`의 `1. valid 비트마스크 >64 신호 fallback 방식` +- 다음 세션에서 단일 작업으로 아래 둘 중 하나를 선택해야 한다: + 1. 테스트 기대치를 설계 문서와 구현 현실에 맞게 갱신한 뒤 Phase 5 커밋 완성 + 2. 테스트를 건드리지 않고 현재 테스트 기대치(`Branch_0=0`, 65-signal facade exception)를 만족하도록 설계를 재조정 +- 선행 조건: + - orchestrator가 위 두 방향 중 하나를 단일 원자 작업으로 지정할 것 + - 현재 미커밋 변경과 테스트 파일 변경 허용 여부를 명시할 것 diff --git "a/Reports/20260316-0844_\353\213\244\354\235\214\354\232\260\354\204\240\354\210\234\354\234\204_\352\266\214\352\263\240.md" "b/Reports/20260316-0844_\353\213\244\354\235\214\354\232\260\354\204\240\354\210\234\354\234\204_\352\266\214\352\263\240.md" new file mode 100644 index 0000000..f63e5c8 --- /dev/null +++ "b/Reports/20260316-0844_\353\213\244\354\235\214\354\232\260\354\204\240\354\210\234\354\234\204_\352\266\214\352\263\240.md" @@ -0,0 +1,63 @@ +# 작업 보고서: 다음우선순위_권고 + +**RUN_ID**: 20260316-0844 +**배치 내 단계 시각(원본 파일명 기준)**: 20260316_0844 + +> [정정 이력 - 2026-03-16] RUN_ID 배치 경계를 재정의했다. 본 문서는 사전 분석 세션으로 별도 배치이며 RUN_ID를 `20260316-0844`로 유지한다. 이후 실행 배치(1030~1500)와 분리한다. + +## 📝 작업 요약 + +`>64` multiplexed signal valid bitmask의 byte-array fallback 구현이 완료됨에 따라, 현재 코드 구현과 불일치하는 **문서 및 로드맵의 진실 정합성(Truth-alignment) 맞춤**을 최우선 과제로 선정하고 저장소 상태를 분석했다. 이번 세션은 추가적인 코드 변경 없이 현황 분석 및 차기 작업 우선순위 권고를 목적으로 수행했다. + +--- + +## 🛠 변경 상세 + +## 1. 저장소 현황 분석 (Implementation vs. Docs) +- **구현 상태**: `src/Signal.CANdy.Core/Codegen.fs` 및 관련 템플릿에 65~1024개 신호를 위한 `uint8_t valid[N]` 배열 방식과 `sc_valid_*` 헬퍼 함수가 이미 구현되어 있으며, `Reports/20260316-1030_Valid_GT64_ByteArray_구현_완료.md` 기준으로 전체 구현이 완료되었다. +- **검증 상태**: 최신 구현 보고서 기준 `dotnet build --configuration Release --nologo` 성공, `dotnet test --configuration Release -v minimal --nologo` 157 pass / 0 fail, `fantomas --check src/ tests/` clean 상태다. +- **문서 불일치 (Stale Documentation)**: + - `Plans/ROADMAP.md`: `>64` fallback 방식을 여전히 future-facing 후속 판단 과제로 남겨 두고 있다. + - `README.md`: Limitations 섹션에서 여전히 `>64` mux 메시지를 unsupported로 설명한다. + - `README.ko.md`: 동일하게 64개 초과 mux 시그널 메시지를 `CodeGenError.UnsupportedFeature`로 설명한다. + - `tests/oracle/CATEGORY_C_EXCEPTIONS.md`: Exception 3이 여전히 `>64` unsupported 기준에 머물러 있다. + +## 2. 우선순위 권고 및 근거 +현재 저장소의 live truth를 기준으로 다음과 같은 순서를 권고한다. + +| 우선순위 | 작업 항목 | 근거 및 기대 효과 | +| :--- | :--- | :--- | +| **1순위** | **문서 및 로드맵 정합성 정리** | 이미 구현된 기능과 active docs가 충돌하고 있다. 이 상태를 방치하면 사용자 혼란, 유지보수 오류, 예외 분류 왜곡이 생긴다. 가장 낮은 비용으로 가장 큰 정합성 이득을 얻는다. | +| **2순위** | **Oracle reference decoder 비호환 대응 전략 문서화** | `Plans/ROADMAP.md`에 남아 있는 유일한 실질적 future-facing 이슈다. 일부 vendor DBC가 `cantools`와 비호환이므로, 대체 reference decoder 도입 여부 또는 Category C 유지 정책을 명시할 필요가 있다. | +| **3순위** | **신규 구현 착수는 보류** | 현재는 구현보다 truth-alignment가 더 시급하다. 문서/계획이 정리되기 전에 새 구현을 시작하면 backlog와 현실의 어긋남이 더 커진다. | + +## 3. 추가 관찰 사항 +- `Plans/Archive/ROADMAP_202602_202603_Closed.md` 및 일부 과거 보고서에는 `NEXT_ROADMAP.md` 경로가 남아 있는데, 현재 active planning은 이미 `Plans/ROADMAP.md`로 이관된 상태다. +- 이는 역사 기록이라 직접 수정 대상은 아니지만, 현재 truth를 설명하는 최신 보고서에서 patch-forward 방식으로 정정 인식을 유지하는 것이 바람직하다. +- 즉, 과거 기록을 다시 쓰는 것보다 **현재 active 문서만 바로잡는 것**이 더 안전하고 프로젝트 원칙에도 맞다. + +--- + +## ✅ 테스트 결과 + +- 이번 세션은 **분석/권고/레포트 작성 세션**으로, 소스 코드 변경은 수행하지 않았다. +- 근거로 참조한 최신 구현 검증 상태: + - `dotnet build --configuration Release --nologo` ✅ 성공 + - `dotnet test --configuration Release -v minimal --nologo` ✅ 157 pass / 0 fail + - `fantomas --check src/ tests/` ✅ clean +- 또한 현재 작업 시작 시점의 워킹 트리는 깨끗한 상태였다. + +--- + +## ⏭ 다음 계획 + +1. **가장 먼저 할 일**: `Plans/ROADMAP.md`, `README.md`, `README.ko.md`, `tests/oracle/CATEGORY_C_EXCEPTIONS.md`를 현행 구현 기준으로 갱신한다. + - `>64 unsupported` → `65–1024 byte-array fallback 지원, >1024 unsupported`로 바로잡기 + - `ROADMAP.md`의 1번 항목은 future work가 아니라 완료/종결 또는 active backlog 제거 대상으로 정리하기 +2. **그 다음 할 일**: Oracle reference decoder incompatibility에 대한 짧은 전략 문서를 만든다. + - `cantools` 유지 + Category C 지속 + - 다른 reference decoder 도입 + - 혼합 전략(현재 유지 + 조사 계획) + 위 셋 중 무엇이 맞는지 evidence-first로 정리한다. +3. **보류할 일**: 새로운 구현 트랙은 위 1, 2가 끝난 뒤 시작한다. + - 지금은 기능 부족보다 문서-현실 불일치가 더 큰 리스크다. diff --git "a/Reports/20260316-1030_Atlas_\355\224\214\353\236\234\354\213\244\355\226\211_\354\265\234\354\242\205\354\231\204\353\243\214.md" "b/Reports/20260316-1030_Atlas_\355\224\214\353\236\234\354\213\244\355\226\211_\354\265\234\354\242\205\354\231\204\353\243\214.md" new file mode 100644 index 0000000..d1430a0 --- /dev/null +++ "b/Reports/20260316-1030_Atlas_\355\224\214\353\236\234\354\213\244\355\226\211_\354\265\234\354\242\205\354\231\204\353\243\214.md" @@ -0,0 +1,90 @@ +# 작업 보고서: Atlas_플랜실행_최종완료 + +## 📝 작업 요약 + +**RUN_ID**: 20260316-1030 +**배치 내 단계 시각(원본 파일명 기준)**: 20260316_1006 + +> [정정 이력 - 2026-03-16] 본 문서는 1030~1500 실행 배치 종료 후 누락 보강을 위해 작성된 후속 레포트다. 파일 접두 시각과 별개로 배치 RUN_ID는 `20260316-1030`을 따른다. + +- 요청사항: "아틀라스가 남기지 않은 이번 플랜 실행 레포트"를 patch-forward 방식으로 보강 작성. +- 대상 플랜: `.sisyphus/plans/truth-alignment-oracle-strategy.md` +- 최종 결과: + - 구현 태스크 8/8 완료 + - Final Verification Wave 4/4 (F1~F4) 모두 APPROVE + - 문서 정합성 이슈 중 실제 결함 1건(README.md 코드 펜스 구조) 수정 완료 +- 이번 보고서는 기존 이력을 수정하지 않고, 누락된 orchestration 결과를 현재 세션에서 보완 기록한다. + +### 플랜 실행 커밋 요약 + +1. `eedfed0` — docs: active 문서를 shipped >64 valid bitmask byte-array fallback 상태로 정렬 +2. `c922770` — docs: `Plans/ORACLE_DECODER_STRATEGY.md` 신규 작성 +3. `1de513e` — docs: `Reports/20260316-1030_문서정합성_Oracle전략.md` 작성 +4. `178f849` — docs: README.md multiplexed 예제 코드펜스 구조 수정 + +## 🛠 변경 상세 + +### RUN_ID 정정 메모 (2026-03-16) + +- 동일 배치 보고서 `20260316-1030_Oracle_비호환_대응_전략_수립.md`, `20260316-1030_README_ko_동기화.md`, `20260316-1030_문서정합성_Oracle전략.md`의 공통 RUN_ID를 `20260316-1030`으로 통일 정정했다. +- 기준 근거: 동일 배치의 첫 완료/종결 보고서(`Reports/20260316-1030_Valid_GT64_ByteArray_구현_완료.md`)를 배치 시작점으로 채택. +- 원칙: RUN_ID는 배치 시작 시각을 사용하며, 배치 내 개별 단계 시각은 RUN_ID가 아니라 보조 메타데이터로만 기록한다. + +### 플랜 산출물 기준 변경 파일 + +- `README.md` + - Multiplexed messages의 valid 비트마스크 설명을 3-tier(≤32 / 33–64 / 65–1024)로 정렬 + - `sc_valid_test()` 기반 >64 예제 문서화 + - Limitations를 >1024 `UnsupportedFeature` 기준으로 명확화 + - 추가로 코드 펜스 불균형(예제 블록 경계) 1건 수정 +- `README.ko.md` + - README.md 변경사항 한국어 미러 반영 (3-tier + `sc_valid_test` + 1024 제한) +- `Plans/ROADMAP.md` + - 완료된 항목(#1) 제거 및 후속 항목 구조 정리 +- `tests/oracle/CATEGORY_C_EXCEPTIONS.md` + - Exception 3에 RESOLVED annotation 추가(바이트 배열 fallback + >1024 경계 명시) +- `tests/oracle/ORACLE_RESULTS.md` + - Recommendation #3 COMPLETED 상태로 갱신 (byte-array extension 반영) +- `Plans/ORACLE_DECODER_STRATEGY.md` + - Oracle reference decoder 비호환 대응 3안 비교/권고 문서 추가 +- `.sisyphus/plans/truth-alignment-oracle-strategy.md` + - Final Verification(F1~F4) 체크 및 Definition of Done/Final Checklist 체크 완료 + +### 스코프/가드레일 준수 + +- `.fs`, `.scriban`, `.c`, `.h` 파일 변경 없음 +- 기존 `Reports/` 파일 수정 없음 (신규 기록만 추가) +- `Plans/Archive/` 변경 없음 + +## ✅ 테스트 결과 + +### 필수 검증 명령 실행 결과 + +1. `dotnet build --configuration Release --nologo` + - 결과: **PASS** (경고 0, 오류 0) + +2. `dotnet test --configuration Release -v minimal --nologo` + - 결과: **PASS** + - `Signal.CANdy.Core.Tests`: 130 passed + - `Generator.Tests`: 27 passed + - 총합: **157 passed, 0 failed** + +3. `fantomas --check src/ tests/` + - 결과: **PASS** (포맷 이슈 없음) + +### Final Verification Wave + +- F1 Plan Compliance Audit (oracle): **APPROVE** +- F2 Code Quality Review: **APPROVE** +- F3 Real Manual QA: **APPROVE** +- F4 Scope Fidelity Check: **APPROVE** + +## ⏭ 다음 계획 + +- 현재 활성 ROADMAP(`Plans/ROADMAP.md`)의 후속 과제는 아래 1건이다. + - **1. Oracle reference decoder 비호환 DBC 대응 전략** +- 다음 세션 선행 조건: + 1. 최신 close-out/verification 리포트 우선 확인 + 2. 제품 우선순위가 생길 경우 `Plans/ORACLE_DECODER_STRATEGY.md`의 권고안을 기준으로 새 세대 계획 승격 + +> 참고: 이번 세션은 `truth-alignment-oracle-strategy` 플랜의 종료 정합성/검증 보강이 목적이었으며, ROADMAP의 future-facing 항목을 구현 완료로 전환하는 범위는 아니었다. diff --git "a/Reports/20260316-1030_Oracle_\353\271\204\355\230\270\355\231\230_\353\214\200\354\235\221_\354\240\204\353\236\265_\354\210\230\353\246\275.md" "b/Reports/20260316-1030_Oracle_\353\271\204\355\230\270\355\231\230_\353\214\200\354\235\221_\354\240\204\353\236\265_\354\210\230\353\246\275.md" new file mode 100644 index 0000000..e1efba7 --- /dev/null +++ "b/Reports/20260316-1030_Oracle_\353\271\204\355\230\270\355\231\230_\353\214\200\354\235\221_\354\240\204\353\236\265_\354\210\230\353\246\275.md" @@ -0,0 +1,42 @@ +# 작업 보고서: Oracle 비호환 대응 전략 수립 + +**RUN_ID**: 20260316-1030 +**배치 내 단계 시각(원본 파일명 기준)**: 20260316_1300 + +> [정정 이력 - 2026-03-16] 동일 배치(20260316-1030~1500) 보고서의 RUN_ID가 문서별로 분리/불명확하게 기록된 문제를 수정했다. 배치 시작 시각 기준 RUN_ID(`20260316-1030`)로 통일한다. + +## 📝 작업 요약 + +Oracle reference decoder (`cantools`) 비호환 DBC(Exception 4) 대응을 위한 증거 기반 전략 문서 `Plans/ORACLE_DECODER_STRATEGY.md`를 생성했다. 이 문서는 3가지 옵션(현행 유지, 대체 decoder 전환, 하이브리드 대응)을 평가하고, 가장 현실적이고 효과적인 '하이브리드/전처리(Option C)' 방식을 권고한다. + +--- + +## 🛠 변경 상세 + +## 1. 신규 파일 생성 +- **`Plans/ORACLE_DECODER_STRATEGY.md`**: + - **문제 정의**: Hyundai, Toyota, VW DBC가 `cantools` v41.2.1에서 malformed CM_ 및 29-bit ID 거부로 인해 oracle 검증에서 제외된 현황 기술. + - **옵션 평가**: + - **Option A (Status Quo)**: `cantools` 유지 + Category C 예외 유지 (노력 0, 점수 6/10). + - **Option B (Replacement)**: `canmatrix`로 전면 교체 (노력 High, 점수 3/10). + - **Option C (Hybrid/Fallback)**: `cantools`를 주력으로 쓰되, 문제 파일은 전처리(regex/flag fix) 또는 `canmatrix`를 선별적으로 사용하여 검증 간극 해소 (노력 Medium, 점수 8/10). + - **권고 사항**: 구현 효율과 검증 범위 확대를 모두 잡을 수 있는 **Option C**를 권고함. + +--- + +## ✅ 테스트 결과 + +- **파일 존재 여부**: `Plans/ORACLE_DECODER_STRATEGY.md` 정상 생성됨. +- **내용 검증**: + - `Option A/B/C` 섹션 포함 여부 확인 (3개 이상) + - `Pros/Cons` 분석 포함 여부 확인 + - `Recommended Path` 명시 확인 + - `Exception 4`, `CATEGORY_C` 키워드 포함 확인 +- **빌드/테스트**: 문서 작업으로 인해 `dotnet build/test` 결과에 영향 없음 (이전 상태: 157 pass). + +--- + +## ⏭ 다음 계획 + +1. **문서 정합성 최종 정리**: 이전 세션 권고 사항에 따라 `README.md` 및 `CATEGORY_C_EXCEPTIONS.md`의 `>64` mux 관련 내용을 최신 구현(byte-array fallback)에 맞게 갱신한다. +2. **전략 실행**: `ORACLE_DECODER_STRATEGY.md`에서 권고된 하이브리드 방식(Option C)에 따라 `run_oracle.py` 또는 `engine.py`에 DBC 전처리 로직 도입을 검토한다. diff --git "a/Reports/20260316-1030_README_ko_\353\217\231\352\270\260\355\231\224.md" "b/Reports/20260316-1030_README_ko_\353\217\231\352\270\260\355\231\224.md" new file mode 100644 index 0000000..76c682a --- /dev/null +++ "b/Reports/20260316-1030_README_ko_\353\217\231\352\270\260\355\231\224.md" @@ -0,0 +1,25 @@ +# 작업 보고서: README.ko 동기화 + +**RUN_ID**: 20260316-1030 +**배치 내 단계 시각(원본 파일명 기준)**: 20260316_1445 + +> [정정 이력 - 2026-03-16] 동일 배치(20260316-1030~1500) 내 보고서 간 RUN_ID 불일치 문제를 수정했다. 본 보고서는 배치 시작 시각 RUN_ID(`20260316-1030`)를 사용한다. + +📝 **작업 요약** +`README.md`에 적용된 3단계 유효성 비트마스크(3-tier valid bitmask) 및 1024개 신호 제한 변경 사항을 `README.ko.md`에 미러링하여 한국어 문서를 최신화했습니다. + +🛠 **변경 상세** +- `README.ko.md`: + - 멀티플렉스 메시지 섹션의 참고 사항(L460 부근)을 3단계 구조(≤32, 33–64, 65–1024) 및 바이트 배열 지원 설명으로 업데이트. + - `sc_valid_test()` 사용법을 보여주는 64개 초과 신호 메시지용 한국어 코드 예제 블록 추가. + - 제한사항(Limitations) 섹션의 유효성 비트마스크 관련 항목을 3단계 구조와 1024개 제한으로 업데이트. + - CAN FD 64바이트 페이로드 등 관련 없는 수치는 유지. + +✅ **테스트 결과** +- `grep -c "sc_valid_test" README.ko.md` -> 3 (통과, 기대값 ≥1) +- `grep -c "1024" README.ko.md` -> 2 (통과, 기대값 ≥1) +- `git diff`를 통해 기존 코드 블록 및 다른 섹션의 보존 확인. + +⏭ **다음 계획** +- Wave 3: `plans/truth-alignment-oracle-strategy.md`에 따른 추가 작업 (Task 7: 빌드 검증 등) 착수. +- `Plans/ROADMAP.md` 업데이트 필요 (Task 2 완료 표시). diff --git "a/Reports/20260316-1030_Valid_GT64_ByteArray_\352\265\254\355\230\204_\354\231\204\353\243\214.md" "b/Reports/20260316-1030_Valid_GT64_ByteArray_\352\265\254\355\230\204_\354\231\204\353\243\214.md" new file mode 100644 index 0000000..a5064c9 --- /dev/null +++ "b/Reports/20260316-1030_Valid_GT64_ByteArray_\352\265\254\355\230\204_\354\231\204\353\243\214.md" @@ -0,0 +1,119 @@ +# 작업 보고서: Valid_GT64_ByteArray_구현_완료 + +**RUN_ID**: 20260316-1030 +**배치 내 단계 시각(원본 파일명 기준)**: 20260316_1030 + +> [정정 이력 - 2026-03-16] 실행 배치(1030~1500) RUN_ID 기준점 문서로 지정했다. 동일 배치 후속 보고서(1300/1445/1500/1006)는 RUN_ID `20260316-1030`을 공유한다. + +## 📝 작업 요약 + +`>64` multiplexed signal valid bitmask byte-array fallback 기능 구현 **전체 완료** (Phase 1–7). + +이번 세션에서는 이전 세션에서 완료한 Phase 1–6에 이어 Phase 7(fantomas 포맷 정리)을 마무리하고, 미커밋 보고서 파일 2개를 커밋하고, 워킹 트리를 완전히 정리했다. + +--- + +## 🛠 변경 상세 + +## 이번 세션에서 수행한 작업 + +### Phase 7: Fantomas 포맷 정리 +- `fantomas src/Signal.CANdy.Core/Codegen.fs tests/Signal.CANdy.Core.Tests/CodegenTests.fs` 실행 +- 두 파일 formatting 적용 (22 insertions, 6 deletions) +- `fantomas --check src/ tests/` → exit 0 확인 +- 커밋: `3b82c65 refactor(codegen): apply fantomas formatting` + +### 보고서 파일 커밋 +- `Reports/20260316-0749_Codegen_ValidBitmask_RED_Tests.md` (RED phase 보고서) +- `Reports/20260316-0807_Valid_GT64_ByteArray_구현.md` (Phase 2-5 구현 보고서) +- 커밋: `9b1b7bd docs(reports): add implementation session reports for valid >64 bitmask feature` + +### 워킹 트리 정리 +- 이전 세션에서 생긴 zero-byte 잡파일 삭제: `2026-02~2026-03`, `이`, `현행` +- 최종 git status: `working tree clean` + +## 전체 구현 커밋 이력 (Phase 1–7) + +| 커밋 | 메시지 | Phase | +|------|--------|-------| +| `e0e788a` | test(codegen): add failing tests for >64 valid byte-array | Phase 1 (RED) | +| `ed2bf51` | feat(codegen): add 3-tier valid width selection for >64 signals | Phase 2 (M1) | +| `63d62a7` | feat(codegen): emit byte-array macros, struct field, and decode body for >64 | Phase 3 (M2–M5) | +| `fa45ce9` | feat(codegen): lift overflow guard from 64 to 1024 signals | Phase 4 (M6) | +| `60064c9` | feat(codegen): add conditional sc_valid helpers and utils include | Phase 5 (M7+M8+templates) | +| `c8ad404` | test(codegen): add golden files for 65-signal mux message | Phase 6 (golden files) | +| `3b82c65` | refactor(codegen): apply fantomas formatting | Phase 7 (REFACTOR) | +| `9b1b7bd` | docs(reports): add implementation session reports for valid >64 bitmask feature | 보고서 커밋 | + +## 수정/생성된 파일 + +### 구현 파일 +- `src/Signal.CANdy.Core/Codegen.fs` — M1–M8: 3-tier valid width 선택, byte-array 매크로/struct/decode, overflow guard 1024 상향, Scriban 모델 값 추가 +- `templates/utils.h.scriban` — `{{ if has_valid_array }}` 블록: `sc_valid_set/clear/test` 3개 inline helper 추가 +- `templates/message.h.scriban` — `{{ if needs_utils_include }}` 블록: `#include "{{ utils_header_name }}"` 추가 + +### 테스트 파일 +- `tests/Signal.CANdy.Core.Tests/CodegenTests.fs` — T1–T6 테스트 추가 (RED→GREEN) +- `tests/Signal.CANdy.Core.Tests/FacadeTests.fs` — `mkUnsupportedMuxIr` 를 1025-signal 기준으로 변경, `">1024"` 단언 + +### Golden 파일 (신규 생성) +- `tests/Signal.CANdy.Core.Tests/golden/mux65_msg.h` — 226라인 +- `tests/Signal.CANdy.Core.Tests/golden/mux65_msg.c` — 799라인 + +--- + +## ✅ 테스트 결과 + +| 검증 항목 | 결과 | +|-----------|------| +| `dotnet build --configuration Release --nologo` | ✅ 0 warnings/errors | +| `dotnet test --configuration Release -v minimal --nologo` | ✅ 157 passed (130 Core + 27 Generator), 0 failed | +| `fantomas --check src/ tests/` | ✅ exit 0 (모든 파일 포맷 준수) | +| `git status` | ✅ working tree clean | + +### 테스트 증가 내역 +- 기존 베이스라인: 152 tests +- 이번 구현 후: 157 tests (+5 신규) +- 새로 추가된 5개 테스트: T1~T6 중 5개 (골든 파일 비교 포함) + +--- + +## ⏭ 다음 계획 + +- `Plans/ROADMAP.md`의 해당 항목 **완료** 처리 필요: + - `valid 비트마스크 >64 신호 fallback 방식` 체크박스 → `[x]` +- 다음 작업 후보: ROADMAP.md에서 다음 미완료 항목 확인 +- README.md의 Limitations 섹션 업데이트 권장: + - 현재: "Messages with >64 multiplexed signals are not supported (code generation reports `CodeGenError.UnsupportedFeature`)" + - 업데이트 필요: >64이지만 ≤1024인 경우 byte-array 방식으로 지원됨, >1024만 UnsupportedFeature + +--- + +## 🔖 구현 기술 요약 (다음 세션을 위한 인계) + +## valid 비트마스크 3단계 선택 로직 (Codegen.fs) + +``` +≤32 signals → uint32_t valid; (기존, 1U shift) +33–64 signals → uint64_t valid; (기존, 1ULL shift) +65–1024 signals → uint8_t valid[N]; (신규, sc_valid_set/test/clear 헬퍼) +>1024 signals → UnsupportedFeature 에러 (가드) +``` + +## sc_valid 헬퍼 (utils.h.scriban에서 조건부 emit) + +```c +static inline void sc_valid_set(uint8_t *v, int i) { v[i/8] |= (uint8_t)(1u << (i%8)); } +static inline void sc_valid_clear(uint8_t *v, int i) { v[i/8] &= ~(uint8_t)(1u << (i%8)); } +static inline int sc_valid_test(const uint8_t *v, int i) { return (v[i/8] >> (i%8)) & 1; } +``` + +## 신호 인덱스 순서 (중요 — 테스트 단언에 영향) + +`List.iteri`는 `message.Signals` 전체 순서를 따름 (mux switch 포함): +- idx=0: MuxSel (switch) +- idx=1: Branch_0 +- ... +- idx=64: Branch_63 + +따라서 `#define MUX65_MSG_VALID_MUXSEL 0`, `#define MUX65_MSG_VALID_BRANCH_0 1` 등. diff --git "a/Reports/20260316-1030_\353\254\270\354\204\234\354\240\225\355\225\251\354\204\261_Oracle\354\240\204\353\236\265.md" "b/Reports/20260316-1030_\353\254\270\354\204\234\354\240\225\355\225\251\354\204\261_Oracle\354\240\204\353\236\265.md" new file mode 100644 index 0000000..dc6cf4b --- /dev/null +++ "b/Reports/20260316-1030_\353\254\270\354\204\234\354\240\225\355\225\251\354\204\261_Oracle\354\240\204\353\236\265.md" @@ -0,0 +1,31 @@ +# 작업 보고서: 문서정합성_Oracle전략 + +**RUN_ID**: 20260316-1030 +**배치 내 단계 시각(원본 파일명 기준)**: 20260316_1500 + +> [정정 이력 - 2026-03-16] 동일 배치(20260316-1030~1500)에서 보고서별 시각을 사실상 RUN-ID처럼 사용하던 기록을 정정했다. 배치 공통 RUN_ID는 시작 시각 기준 `20260316-1030`으로 통일한다. + +📝 **작업 요약** +이번 세션에서는 이미 출시된(shipped) >64 valid 비트마스크 byte-array fallback 구현과 모든 활성 문서(5개 파일)를 정합시키고, Oracle reference decoder 비호환 DBC 대응을 위한 전략 문서를 신규 작성했다. 이전 세션에서 완성된 9커밋 구현(157개 테스트 통과) 이후, 문서들이 구버전(">64 신호는 UnsupportedFeature") 내용을 유지하고 있던 문제를 해결하여 모든 활성 문서를 실제 코드와 동일한 진실로 정합하고 Oracle 전략 문서 신규 생성을 완료했다. + +🛠 **변경 상세** + +**수정된 파일 (5개):** +1. `README.md` — 멀티플렉스 메시지 Notes bullet을 3-tier valid 구조(≤32 uint32_t, 33–64 uint64_t, 65–1024 byte-array)로 재작성; `sc_valid_test()` >64 사용 예시 코드 블록 신규 추가; Limitations bullet을 1024 한계로 업데이트 +2. `README.ko.md` — README.md 변경 사항의 한국어 미러: 3-tier 구조 설명, `sc_valid_test` 예시 코드, 제한사항 항목 업데이트 (10커밋에 포함되어 이미 커밋됨) +3. `Plans/ROADMAP.md` — ROADMAP L10 규칙("구현 완료로 닫힌 항목은 여기로 되돌려 적지 않는다")에 따라 item #1(">64 fallback") 완전 삭제; 이전 item #2 → item #1로 번호 재지정; "두 항목" → "위 항목" 텍스트 수정 +4. `tests/oracle/CATEGORY_C_EXCEPTIONS.md` — Exception 3에 `**RESOLVED**` 어노테이션 추가(byte-array fallback + 65–1024 범위 + >1024 새 한계 명시); 표의 Status/Backlog 항목 업데이트 +5. `tests/oracle/ORACLE_RESULTS.md` — Recommendation #3에 `**COMPLETED (B-O3 + byte-array extension, 2026-03-16)**` 어노테이션 추가; 65–1024 byte-array 및 >1024 UnsupportedFeature 상세 기술 + +**신규 생성 파일 (1개):** +6. `Plans/ORACLE_DECODER_STRATEGY.md` — Oracle reference decoder(`cantools`) 비호환 DBC(Exception 4) 대응 전략 문서. Option A(현행 유지, 6/10), Option B(canmatrix 전면 교체, 3/10), Option C(하이브리드/전처리, 8/10) 3가지 옵션 평가; **Option C 권고**. + +✅ **테스트 결과** +- `dotnet build --configuration Release --nologo` → **성공** (경고 0개, 오류 0개) +- `dotnet test --configuration Release -v minimal --nologo` → **통과** (통과: 157개, 실패: 0개 — Core 130 + Generator 27) +- 문서 변경만으로 코드에는 영향 없음; 테스트 기반 유지 + +⏭ **다음 계획** +- `Plans/ROADMAP.md` 현재 활성 항목: **### 1. Oracle reference decoder 비호환 DBC 대응 전략** + - `Plans/ORACLE_DECODER_STRATEGY.md`에서 권고된 **Option C(하이브리드/전처리)** 실행 여부 의사결정 필요 + - 구체적 선행 조건: Option C 실행 결정 후 `tests/oracle/engine.py` 또는 `tests/oracle/run_oracle.py`에 DBC 전처리 로직 도입 검토 diff --git "a/Reports/20260316-1121_\353\262\204\354\240\204\354\227\205PR_readiness_\354\240\220\352\262\200.md" "b/Reports/20260316-1121_\353\262\204\354\240\204\354\227\205PR_readiness_\354\240\220\352\262\200.md" new file mode 100644 index 0000000..32750af --- /dev/null +++ "b/Reports/20260316-1121_\353\262\204\354\240\204\354\227\205PR_readiness_\354\240\220\352\262\200.md" @@ -0,0 +1,44 @@ +## 📝 작업 요약 + +- 현재 저장소가 **버전업 PR을 올릴 준비가 되었는지** comprehensive하게 점검했다. +- 점검 범위는 AGENTS.md의 Pre-Release Checklist, 실제 버전 문자열 정합성, 로컬 빌드/테스트/포맷, 브랜치 상태, Oracle release readiness review까지 포함한다. +- 결론: **지금 당장 version-bump PR은 비권장(REJECT / 조건 미충족)**. 기술적 건강도는 양호하지만, `main`이 `origin/main`보다 15커밋 앞서 있어 버전업 PR의 베이스라인이 remote-reviewed 상태가 아니다. + +## 🛠 변경 상세 + +- 코드/제품 소스 변경은 수행하지 않았다. +- 읽고 점검한 핵심 파일: + - `AGENTS.md` — Pre-Release Checklist 기준 + - `src/Signal.CANdy.Core/Signal.CANdy.Core.fsproj` — `0.3.2` + - `src/Signal.CANdy/Signal.CANdy.fsproj` — `0.3.2` + - `src/Signal.CANdy.Core/Api.fs` — `version () = "0.3.2"` + - `README.md`, `README.ko.md` — 설치 예시 `0.3.2` + - `src/Signal.CANdy.Core/README.NuGet.md`, `src/Signal.CANdy/README.NuGet.md` — 설치 예시 `0.3.2` +- 브랜치 상태: + - `git status --short --branch` 기준 `main...origin/main [ahead 15]` + - 최근 로컬 커밋에는 docs/report 정리뿐 아니라 codegen/test/refactor 성격 변경도 포함되어 있어, 버전업 PR을 별도 release-only PR로 보기 어렵다. +- Oracle 판정 요약: + - **REJECT** + - 이유: 로컬 상태는 건강하지만, remote baseline 없이 local-only 15커밋을 얹은 채 버전업 PR을 올리면 release bookkeeping과 실제 제품 변경이 섞인다. + +## ✅ 테스트 결과 + +- `dotnet build --configuration Release --nologo` → **PASS** (0 warnings, 0 errors) +- `dotnet test --configuration Release -v minimal --nologo` → **PASS** + - `Signal.CANdy.Core.Tests`: 130 passed + - `Generator.Tests`: 27 passed + - 총합: **157 passed, 0 failed** +- `fantomas --check src/ tests/` → **PASS** (이번 세션 직접 실행 결과 확보) +- 버전 문자열 정합성: + - `.fsproj` 2개 = `0.3.2` + - `Api.version()` = `0.3.2` + - README / README.ko / NuGet README 설치 예시 = `0.3.2` + +## ⏭ 다음 계획 + +- 버전업 PR 전에 우선 수행할 일: + 1. 현재 local `main`의 15커밋을 일반 변경 PR 흐름으로 먼저 remote에 반영하거나, 별도 release candidate branch로 정리 + 2. remote에서 CI green baseline 확보 + 3. 그 다음 **버전 metadata / release notes만 포함한 작은 version-bump PR**를 별도로 생성 +- 추가 확인 권고: + - GitHub release workflow / NuGet publish secrets (`NUGET_API_KEY`)는 repo 밖 설정이라 별도 UI 점검 필요 diff --git "a/Reports/20260316-1144_main\353\263\265\352\265\254_\354\236\221\354\227\205\353\270\214\353\236\234\354\271\230\353\266\204\353\246\254.md" "b/Reports/20260316-1144_main\353\263\265\352\265\254_\354\236\221\354\227\205\353\270\214\353\236\234\354\271\230\353\266\204\353\246\254.md" new file mode 100644 index 0000000..7f46075 --- /dev/null +++ "b/Reports/20260316-1144_main\353\263\265\352\265\254_\354\236\221\354\227\205\353\270\214\353\236\234\354\271\230\353\266\204\353\246\254.md" @@ -0,0 +1,36 @@ +## 📝 작업 요약 + +- local `main`에서 장기간 직접 작업하던 상태를 안전하게 복구했다. +- 현재 `main` HEAD를 `recovery/local-main-20260316` 브랜치로 보존한 뒤, local `main`을 `origin/main` 기준으로 복구했다. +- `dev`는 오래 분기된 별도 흐름으로 확인되어 이번 복구에서는 병합하지 않고 유지했다. + +## 🛠 변경 상세 + +- 수행한 git 복구 절차: + 1. `git switch -c recovery/local-main-20260316` + 2. `git switch main` + 3. `git reset --hard origin/main` + 4. `git switch recovery/local-main-20260316` +- 복구 후 브랜치 포인터: + - `main` → `b2d0059` (`origin/main`과 일치) + - `recovery/local-main-20260316` → `ea1a5d1` (기존 local main 작업 보존) + - `dev` → `81a4d6b` (별도 유지) +- 이번 세션 판단: + - `dev`는 `v0.3.2` 전후 release/oracle 흐름이 길게 누적된 별도 역사라, recovery 브랜치에 즉시 merge하면 복구보다 혼합 위험이 크다고 판단했다. + - 따라서 recovery 브랜치를 우선 정식 작업브랜치로 확정하고, `dev` 병합은 후속 검토로 보류했다. + +## ✅ 테스트 결과 + +- git 상태/브랜치 검증: + - 복구 전: `main...origin/main [ahead 15]` + - 복구 후: `main` = `origin/main` + - 복구 브랜치에서 untracked readiness 보고서(`Reports/20260316-1121_버전업PR_readiness_점검.md`) 보존 확인 +- 추가 코드 빌드/테스트는 이번 세션의 목적이 브랜치 복구이므로 수행하지 않았다. + +## ⏭ 다음 계획 + +- 이후 모든 작업은 `recovery/local-main-20260316`에서 계속 진행한다. +- 다음 세션 우선 과제: + 1. `recovery/local-main-20260316`의 local-only 커밋을 논리 단위로 분리/정리 + 2. 필요 시 `dev`에서 정말 필요한 커밋만 cherry-pick 또는 별도 통합 검토 + 3. remote에 올릴 reviewable branch 체계 수립 후 PR 준비 diff --git "a/Reports/20260316-1309_dev\353\270\214\353\236\234\354\271\230_\353\246\254\354\273\244\353\262\204\353\246\254\354\212\271\352\262\251.md" "b/Reports/20260316-1309_dev\353\270\214\353\236\234\354\271\230_\353\246\254\354\273\244\353\262\204\353\246\254\354\212\271\352\262\251.md" new file mode 100644 index 0000000..911423d --- /dev/null +++ "b/Reports/20260316-1309_dev\353\270\214\353\236\234\354\271\230_\353\246\254\354\273\244\353\262\204\353\246\254\354\212\271\352\262\251.md" @@ -0,0 +1,37 @@ +## 📝 작업 요약 + +- `recovery/local-main-20260316`의 검증된 상태를 `dev`에 반영해, 다시 `dev`를 주 개발 브랜치로 사용 가능한 상태로 승격했다. +- 실행 직전 원격 상태를 재확인한 뒤 `git merge --ff-only recovery/local-main-20260316`로 `dev`를 fast-forward 했고, 승격 후 빌드/테스트를 다시 수행해 통합 상태를 재검증했다. + +## 🛠 변경 상세 + +- 브랜치 상태 점검 + - `origin/dev = 81a4d6b`, `origin/main = b2d0059`, `recovery/local-main-20260316 = 0e8e560` 확인. + - Oracle 검토 결과에 따라 non-FF merge나 force-push 없이 fast-forward-only 전략을 채택. +- `dev` 승격 + - `git switch dev` + - `git merge --ff-only recovery/local-main-20260316` + - 결과적으로 `dev`가 `0e8e560`까지 fast-forward 되었고, 기존 recovery 브랜치와 동일한 검증 완료 상태를 가리키게 됨. +- 작업 보고서 작성 + - 현재 세션 보고서 파일 `Reports/20260316-1309_dev브랜치_리커버리승격.md` 작성. + +## ✅ 테스트 결과 + +- 브랜치 승격 전/후 안전성 판단 + - Oracle read-only 검토: `dev`는 recovery의 조상이며, `--ff-only` 승격과 일반 push가 적절하다고 판단. +- 빌드 + - `dotnet build --configuration Release --nologo` ✅ 통과 +- 테스트 + - `dotnet test --configuration Release -v minimal --nologo` ✅ 통과 + - `Signal.CANdy.Core.Tests`: 130/130 통과 + - `Generator.Tests`: 27/27 통과 +- 추가 상태 확인 + - fast-forward 직후 `git status --short --branch`에서 `## dev...origin/dev [ahead 51]` 확인. + +## ⏭ 다음 계획 + +- 현재 활성 계획 문서 `Plans/ROADMAP.md` 기준 다음 진입점은 **Oracle reference decoder 비호환 DBC 대응 전략** 검토다. +- 선행 조건: + 1. `origin/dev`로 이번 fast-forward 결과를 정상 push해 원격 개발 기준점을 갱신할 것. + 2. 이후 새 개발 작업은 `recovery/local-main-20260316` 대신 `dev`에서 시작할 것. + 3. Oracle/cantools 비호환 대응이 실제 제품 우선순위가 되면 `Plans/ROADMAP.md`의 active item을 새 세대 계획으로 승격할 것. diff --git "a/Reports/20260316-1324_0.4.0-alpha.1_\353\262\204\354\240\204\354\240\225\355\225\251\354\204\261_\353\260\217_PR\354\244\200\353\271\204.md" "b/Reports/20260316-1324_0.4.0-alpha.1_\353\262\204\354\240\204\354\240\225\355\225\251\354\204\261_\353\260\217_PR\354\244\200\353\271\204.md" new file mode 100644 index 0000000..c1ee8d5 --- /dev/null +++ "b/Reports/20260316-1324_0.4.0-alpha.1_\353\262\204\354\240\204\354\240\225\355\225\251\354\204\261_\353\260\217_PR\354\244\200\353\271\204.md" @@ -0,0 +1,49 @@ +## 📝 작업 요약 + +- `dev` 브랜치를 기준으로 다음 추천 pre-release 버전을 **`0.4.0-alpha.1`**로 결정하고, 공개 버전 표면과 설치 문서 정합성을 맞췄다. +- 판단 근거는 `v0.3.2` 이후 변경이 단순 patch 수준이 아니라, `>64` valid byte-array 지원/CRC·Counter 생성 검증 경로/테스트·예제 확장 등 **새 capability를 포함한 minor-line 진전**이라는 점이다. + +## 🛠 변경 상세 + +- 버전 메타데이터 갱신 + - `src/Signal.CANdy.Core/Signal.CANdy.Core.fsproj` → `0.4.0-alpha.1` + - `src/Signal.CANdy/Signal.CANdy.fsproj` → `0.4.0-alpha.1` + - `src/Signal.CANdy.Core/Api.fs` → `version () = "0.4.0-alpha.1"` +- 공개 설치 문서 정합성 갱신 + - `README.md` + - `README.ko.md` + - `src/Signal.CANdy.Core/README.NuGet.md` + - `src/Signal.CANdy/README.NuGet.md` +- 커밋 분리 + - `8c22dfe` — `build(version): bump package metadata to 0.4.0-alpha.1` + - `0338bf7` — `docs(readme): align install docs for 0.4.0-alpha.1` + - `25ed7a1` — `docs(nuget): align package install snippets for 0.4.0-alpha.1` +- 참고 + - `artifacts-versioncheck/`는 pack 검증용 임시 출력으로만 생성 후 삭제했다. + +## ✅ 테스트 결과 + +- 버전 참조 스캔 + - live public surface에서 `0.3.2` 잔존 참조 없음 확인 + - `0.4.0-alpha.1` 참조는 의도한 7개 파일에만 존재함 확인 +- 진단 + - `src/Signal.CANdy.Core/Api.fs` LSP diagnostics → 이상 없음 +- 빌드 + - `dotnet build --configuration Release --nologo` ✅ 통과 +- 테스트 + - `dotnet test --configuration Release -v minimal --nologo` ✅ 통과 + - `Signal.CANdy.Core.Tests`: 130/130 통과 + - `Generator.Tests`: 27/27 통과 +- 패키지 생성 검증 + - `dotnet pack -c Release src/Signal.CANdy.Core/Signal.CANdy.Core.fsproj -o artifacts-versioncheck` ✅ + - `dotnet pack -c Release src/Signal.CANdy/Signal.CANdy.fsproj -o artifacts-versioncheck` ✅ + - 생성 패키지명에 `0.4.0-alpha.1`이 반영됨 확인 + +## ⏭ 다음 계획 + +- 다음 단계는 현재 세션 내에서 이어서 수행: + 1. `dev`를 원격에 push + 2. `v0.4.0-alpha.1` pre-release 태그를 `dev`에 생성/푸시 + 3. `dev -> main` integration PR 생성 +- 활성 계획 문서 `Plans/ROADMAP.md` 기준 제품 backlog는 여전히 **Oracle reference decoder 비호환 DBC 대응 전략**이다. +- 단, 이번 세션의 직접 목적은 backlog 구현이 아니라 release/PR 정합성 정리이므로, 위 release 작업 완료 후 다음 개발 세션에서 `Plans/ROADMAP.md` 진입점을 다시 사용한다. diff --git "a/Reports/20260316-1346_PR15_\354\275\224\355\214\214\354\235\274\353\237\277\353\246\254\353\267\260_\353\260\230\354\230\201\353\260\217\354\212\244\353\240\210\353\223\234\355\225\264\352\262\260.md" "b/Reports/20260316-1346_PR15_\354\275\224\355\214\214\354\235\274\353\237\277\353\246\254\353\267\260_\353\260\230\354\230\201\353\260\217\354\212\244\353\240\210\353\223\234\355\225\264\352\262\260.md" new file mode 100644 index 0000000..527b440 --- /dev/null +++ "b/Reports/20260316-1346_PR15_\354\275\224\355\214\214\354\235\274\353\237\277\353\246\254\353\267\260_\353\260\230\354\230\201\353\260\217\354\212\244\353\240\210\353\223\234\355\225\264\352\262\260.md" @@ -0,0 +1,58 @@ +## 📝 작업 요약 + +- PR #15에 달린 Copilot review 댓글 5개를 모두 수집·분석하고, 지적된 내용이 실제 결함인지 확인한 뒤 필요한 수정만 반영했다. +- 수정 후 로컬 build/test를 다시 통과시켰고, GitHub review thread 5개를 모두 resolve하여 PR을 merge 가능한 상태로 정리했다. + +## 🛠 변경 상세 + +- 리뷰 코멘트 수집 + - `gh pr view 15 --json ...` + - `gh api repos/InitusNovus/Signal-CANdy/pulls/15/comments` + - `gh api graphql`로 review thread 상태 확인 +- 확인된 Copilot 지적 5건 + 1. `tests/Signal.CANdy.Core.Tests/golden/mux65_msg.h`의 `VALID...` 매크로 네이밍 오류 + 2. `tests/Signal.CANdy.Core.Tests/golden/mux65_msg.c`의 동일 오류 + 3. `README.md` 예시 코드의 잘못된 매크로 이름 + 4. `README.ko.md` 예시 코드의 잘못된 매크로 이름 + 5. `tests/Signal.CANdy.Core.Tests/CodegenTests.fs`의 플랫폼 의존 테스트 경로 사용 +- 반영 내용 + - `tests/Signal.CANdy.Core.Tests/golden/mux65_msg.h` + - `MUX65_MSG_VALIDMUXSEL` → `MUX65_MSG_VALID_MUXSEL` + - `MUX65_MSG_VALIDBRANCH_*` → `MUX65_MSG_VALID_BRANCH_*` + - `tests/Signal.CANdy.Core.Tests/golden/mux65_msg.c` + - 위와 동일한 매크로 이름으로 전면 동기화 + - `README.md`, `README.ko.md` + - 예시 코드 `MUX65_MSG_VALIDBRANCH_5` → `MUX65_MSG_VALID_BRANCH_5` + - `tests/Signal.CANdy.Core.Tests/CodegenTests.fs` + - `generate ir "C:/tmp/nonexistent" ...` 제거 + - `createTempOutDir()` + `finally cleanupDir outDir` 패턴으로 변경 +- 커밋 분리 + - `0d934e7` — `test(golden): align mux65 valid macro names` + - `a7faaba` — `docs(readme): fix mux65 valid macro examples` + - `54cc723` — `test(codegen): use temp output dir for 1025-signal guard` +- GitHub thread 처리 + - review thread 5개 모두 resolve 완료 + +## ✅ 테스트 결과 + +- 식별자 검증 + - `VALIDBRANCH_` / `VALIDMUXSEL` 잔존 검색 → 없음 + - `VALID_BRANCH_5` / `VALID_MUXSEL` 기대 문자열 존재 확인 +- 진단 + - `tests/Signal.CANdy.Core.Tests/CodegenTests.fs` LSP diagnostics → 이상 없음 +- 빌드 + - `dotnet build --configuration Release --nologo` ✅ 통과 +- 테스트 + - `dotnet test --configuration Release -v minimal --nologo` ✅ 통과 + - `Signal.CANdy.Core.Tests`: 130/130 통과 + - `Generator.Tests`: 27/27 통과 +- GitHub 상태 + - PR #15 `mergeable = MERGEABLE` + - review thread 5개 모두 `isResolved = true` + - push 후 재실행된 CI에서 `lint`는 green, `build-test`는 보고서 작성 시점 기준 진행 중 + +## ⏭ 다음 계획 + +- 현재 직접 후속 작업은 PR #15의 재실행 CI `build-test` 완료 확인이다. +- CI까지 green이면 PR은 사실상 merge-ready 상태다. +- 제품 backlog 관점의 다음 활성 항목은 `Plans/ROADMAP.md`의 **Oracle reference decoder 비호환 DBC 대응 전략**이며, PR 머지 후 다음 개발 세션에서 그 진입점을 다시 사용하면 된다. diff --git a/src/Signal.CANdy.Core/Api.fs b/src/Signal.CANdy.Core/Api.fs index d24c7cf..5113e30 100644 --- a/src/Signal.CANdy.Core/Api.fs +++ b/src/Signal.CANdy.Core/Api.fs @@ -8,7 +8,7 @@ open Signal.CANdy.Core.Dbc open Signal.CANdy.Core.Codegen /// Returns the current library snapshot version. Placeholder until full API is moved. -let version () = "0.3.2" +let version () = "0.4.0-alpha.1" /// Parse a DBC file into IR. Stub for now. let parseDbc (path: string) : Result = Signal.CANdy.Core.Dbc.parseDbcFile path diff --git a/src/Signal.CANdy.Core/Codegen.fs b/src/Signal.CANdy.Core/Codegen.fs index 4784003..4192c42 100644 --- a/src/Signal.CANdy.Core/Codegen.fs +++ b/src/Signal.CANdy.Core/Codegen.fs @@ -101,6 +101,18 @@ module Codegen = | Some meta -> meta.Algorithm = CrcAlgorithmId.CRC8_8H2F | None -> false)) + let hasValidArray = + ir.Messages + |> List.exists (fun m -> + let hasMuxSwitch = + m.Signals |> List.exists (fun s -> s.MultiplexerIndicator = Some "M") + + let hasMuxBranches = + m.Signals + |> List.exists (fun s -> s.MultiplexerIndicator = Some "m" && s.MultiplexerSwitchValue.IsSome) + + hasMuxSwitch && hasMuxBranches && m.Signals.Length > 64) + let model: (string * obj) list = [ "banner", box (banner config) "header_guard", box (guard config.FilePrefix "utils_h") @@ -114,6 +126,7 @@ module Codegen = box "void set_bits_be(uint8_t* data, uint16_t start_bit, uint16_t length, uint64_t value);" "canfd_dlc_to_len_decl", box "uint8_t canfd_dlc_to_len(uint8_t dlc);" "canfd_len_to_dlc_decl", box "uint8_t canfd_len_to_dlc(uint8_t len);" + "has_valid_array", box hasValidArray "has_crc_j1850", box hasCrcJ1850 "has_crc_8h2f", box hasCrc8h2f ] @@ -489,12 +502,22 @@ module Codegen = | Some _, _ :: _ -> true | _ -> false + let useValidArray = isMux && message.Signals.Length > 64 + let validType, shiftSuffix, initLiteral = - if isMux && message.Signals.Length > 32 then + if useValidArray then + "uint8_t", "", "" + elif isMux && message.Signals.Length > 32 then "uint64_t", "1ULL", "0ULL" else "uint32_t", "1u", "0u" + let validArraySize = + if useValidArray then + (message.Signals.Length + 7) / 8 + else + 0 + let validMacro (sigName: string) = sprintf "%s_VALID_%s" (message.Name.ToUpperInvariant()) (sigName.ToUpperInvariant()) @@ -505,7 +528,10 @@ module Codegen = let body = signalDecodeFor s in if isMux then - body + (sprintf "\n msg->valid |= %s;" (validMacro s.Name)) + if useValidArray then + body + (sprintf "\n sc_valid_set(msg->valid, %s);" (validMacro s.Name)) + else + body + (sprintf "\n msg->valid |= %s;" (validMacro s.Name)) else body @@ -532,13 +558,16 @@ module Codegen = |> String.concat "\n") |> String.concat "\n" - [ if isMux then - sprintf " msg->valid = %s;" initLiteral - else - "" - swBlock - baseBlock - branchesBlock ] + let initBlock = + if isMux then + if useValidArray then + " memset(msg->valid, 0, sizeof(msg->valid));" + else + sprintf " msg->valid = %s;" initLiteral + else + "" + + [ initBlock; swBlock; baseBlock; branchesBlock ] |> List.filter (fun s -> not (String.IsNullOrWhiteSpace s)) |> String.concat "\n\n" | _ -> message.Signals |> List.map signalDecodeFor |> String.concat "\n\n" @@ -718,20 +747,43 @@ module Codegen = message.Signals |> List.iteri (fun idx s -> + if useValidArray then + preambleLines.Add( + sprintf + "#define %s %d" + (sprintf + "%s_VALID_%s" + (message.Name.ToUpperInvariant()) + (s.Name.ToUpperInvariant())) + idx + ) + else + preambleLines.Add( + sprintf + "#define %s (%s << %d)" + (sprintf + "%s_VALID_%s" + (message.Name.ToUpperInvariant()) + (s.Name.ToUpperInvariant())) + shiftSuffix + idx + )) + + if useValidArray then preambleLines.Add( - sprintf - "#define %s (%s << %d)" - (sprintf "%s_VALID_%s" (message.Name.ToUpperInvariant()) (s.Name.ToUpperInvariant())) - shiftSuffix - idx - )) + sprintf "#define %s_VALID_BYTES %d" (message.Name.ToUpperInvariant()) validArraySize + ) preambleLines.Add "" - structFieldLines.Add(sprintf " %s valid;" validType) + if useValidArray then + structFieldLines.Add(sprintf " uint8_t valid[%d];" validArraySize) + elif isMux && message.Signals.Length > 32 then + structFieldLines.Add(" uint64_t valid; /* valid field widened to 64-bit */") + else + structFieldLines.Add(" uint32_t valid;") if validType = "uint64_t" then - structFieldLines.Add(" /* valid field widened to uint64_t: signal count > 32 */") structFieldLines.Add(" /* decode init literal: = 0ULL; */") structFieldLines.Add(sprintf " %s_mux_e mux_active;" message.Name) @@ -871,6 +923,8 @@ module Codegen = "struct_extra_fields", box muxStructFields "signal_declarations_h", box signalDeclarationsH "message_name", box message.Name + "needs_utils_include", box useValidArray + "utils_header_name", box (Utils.utilsHeaderName config) "has_counter", box hasCounter "counter_state_type_decl", box counterStateTypeDecl "counter_check_func_decl", box counterCheckFuncDecl ] @@ -1050,14 +1104,14 @@ module Codegen = msg.Signals |> List.exists (fun s -> s.MultiplexerIndicator = Some "m" && s.MultiplexerSwitchValue.IsSome) - hasMuxSwitch && hasMuxBranches && msg.Signals.Length > 64) + hasMuxSwitch && hasMuxBranches && msg.Signals.Length > 1024) match overflowGuard with | Some msg -> Error( CodeGenError.UnsupportedFeature( sprintf - "Message '%s' has %d signals (>64); valid bitmask cannot be represented in 64 bits." + "Message '%s' has %d signals (>1024); valid bitmask cannot support more than 1024 signals." msg.Name msg.Signals.Length ) diff --git a/src/Signal.CANdy.Core/README.NuGet.md b/src/Signal.CANdy.Core/README.NuGet.md index 9d141d4..87cd132 100644 --- a/src/Signal.CANdy.Core/README.NuGet.md +++ b/src/Signal.CANdy.Core/README.NuGet.md @@ -8,7 +8,7 @@ Core library for SignalCandy: parse DBC files, validate config, and generate C99 ## Install ``` -dotnet add package SignalCandy.Core --version 0.3.2 +dotnet add package SignalCandy.Core --version 0.4.0-alpha.1 ``` ## Quick start (F#) diff --git a/src/Signal.CANdy.Core/Signal.CANdy.Core.fsproj b/src/Signal.CANdy.Core/Signal.CANdy.Core.fsproj index 6602b6b..bf7ddaa 100644 --- a/src/Signal.CANdy.Core/Signal.CANdy.Core.fsproj +++ b/src/Signal.CANdy.Core/Signal.CANdy.Core.fsproj @@ -12,7 +12,7 @@ CAN;DBC;codegen;C;F#;embedded true false - 0.3.2 + 0.4.0-alpha.1 MIT README.NuGet.md true diff --git a/src/Signal.CANdy/README.NuGet.md b/src/Signal.CANdy/README.NuGet.md index c4da4ab..0788dc0 100644 --- a/src/Signal.CANdy/README.NuGet.md +++ b/src/Signal.CANdy/README.NuGet.md @@ -8,7 +8,7 @@ C#-friendly facade over SignalCandy Core. Wraps Result-based F# API with excepti ## Install ``` -dotnet add package SignalCandy --version 0.3.2 +dotnet add package SignalCandy --version 0.4.0-alpha.1 ``` ## Quick start (C#) diff --git a/src/Signal.CANdy/Signal.CANdy.fsproj b/src/Signal.CANdy/Signal.CANdy.fsproj index 3e31a78..aea3955 100644 --- a/src/Signal.CANdy/Signal.CANdy.fsproj +++ b/src/Signal.CANdy/Signal.CANdy.fsproj @@ -12,7 +12,7 @@ CAN;DBC;codegen;C;F#;facade true false - 0.3.2 + 0.4.0-alpha.1 MIT README.NuGet.md true diff --git a/templates/message.h.scriban b/templates/message.h.scriban index 370efe6..8de0218 100644 --- a/templates/message.h.scriban +++ b/templates/message.h.scriban @@ -3,7 +3,8 @@ #define {{ header_guard }} #include -#include +#include {{ if needs_utils_include }} +#include "{{ utils_header_name }}"{{ end }} #ifdef __cplusplus extern "C" { diff --git a/templates/utils.h.scriban b/templates/utils.h.scriban index b771b41..6cffaff 100644 --- a/templates/utils.h.scriban +++ b/templates/utils.h.scriban @@ -27,7 +27,19 @@ extern "C" { {{ if has_crc_j1850 }}uint8_t sc_crc8_sae_j1850(const uint8_t* data, size_t len); {{ end }}{{ if has_crc_8h2f }}uint8_t sc_crc8_8h2f(const uint8_t* data, size_t len); -{{ end }}{{ end }} +{{ end }}{{ end }}{{ if has_valid_array }} + +/* ── Valid bitmask helpers (byte-array, >64 signals) ── */ +static inline void sc_valid_set(uint8_t* arr, unsigned bit) { + arr[bit >> 3] |= (uint8_t)(1u << (bit & 7u)); +} +static inline void sc_valid_clear(uint8_t* arr, unsigned bit) { + arr[bit >> 3] &= (uint8_t)~(1u << (bit & 7u)); +} +static inline bool sc_valid_test(const uint8_t* arr, unsigned bit) { + return (arr[bit >> 3] & (uint8_t)(1u << (bit & 7u))) != 0; +} +{{ end }} #ifdef __cplusplus } diff --git a/tests/Signal.CANdy.Core.Tests/CodegenTests.fs b/tests/Signal.CANdy.Core.Tests/CodegenTests.fs index 3fcb577..24319d9 100644 --- a/tests/Signal.CANdy.Core.Tests/CodegenTests.fs +++ b/tests/Signal.CANdy.Core.Tests/CodegenTests.fs @@ -1609,12 +1609,14 @@ module CodegenTests = let msgH = files.Headers |> List.find (fun f -> Path.GetFileName(f) = "mux64_msg.h") let content = File.ReadAllText(msgH) content |> should haveSubstring "uint64_t valid;" + content |> should not' (haveSubstring "uint8_t valid[") + content |> should haveSubstring "(1ULL <<" | Error e -> failwithf "Expected Ok, got: %A" e finally cleanupDir outDir [] - let ``codegen fails with UnsupportedFeature for 65-signal mux message valid bitmask`` () = + let ``valid bitmask uses uint8_t byte array for 65-signal mux message`` () = let switchSig = mkMuxSwitch "MuxSel" 0us 4us let branchSignals = @@ -1622,22 +1624,176 @@ module CodegenTests = |> List.map (fun i -> mkBranchSignal (sprintf "Branch_%d" i) (uint16 ((i + 1) % 64)) 1us i) let ir = mkMuxMessage "MUX65_MSG" 903u switchSig branchSignals [] - let result = generate ir "C:/tmp/nonexistent" defaultConfig + let outDir = createTempOutDir () + + try + match generate ir outDir defaultConfig with + | Ok files -> + let msgH = files.Headers |> List.find (fun f -> Path.GetFileName(f) = "mux65_msg.h") + let msgC = files.Sources |> List.find (fun f -> Path.GetFileName(f) = "mux65_msg.c") + let headerContent = File.ReadAllText(msgH) + let sourceContent = File.ReadAllText(msgC) + headerContent |> should haveSubstring "uint8_t valid[9];" + headerContent |> should haveSubstring "#define MUX65_MSG_VALID_BYTES 9" + headerContent |> should haveSubstring "#define MUX65_MSG_VALID_BRANCH_0 1" + headerContent |> should haveSubstring "#define MUX65_MSG_VALID_BRANCH_63 64" + headerContent |> should haveSubstring "#include \"sc_utils.h\"" + + sourceContent + |> should haveSubstring "memset(msg->valid, 0, sizeof(msg->valid))" + + sourceContent |> should haveSubstring "sc_valid_set(msg->valid," + sourceContent |> should not' (haveSubstring "msg->valid |=") + sourceContent |> should not' (haveSubstring "msg->valid = 0") + | Error e -> failwithf "Expected Ok, got: %A" e + finally + cleanupDir outDir + + [] + let ``valid bitmask uses uint8_t byte array for 128-signal mux message`` () = + let switchSig = mkMuxSwitch "MuxSel" 0us 4us + + let branchSignals = + [ 0..126 ] + |> List.map (fun i -> mkBranchSignal (sprintf "Branch_%d" i) (uint16 ((i + 1) % 64)) 1us i) + + let ir = mkMuxMessage "MUX128_MSG" 906u switchSig branchSignals [] + let outDir = createTempOutDir () + + try + match generate ir outDir defaultConfig with + | Ok files -> + let msgH = + files.Headers |> List.find (fun f -> Path.GetFileName(f) = "mux128_msg.h") + + let content = File.ReadAllText(msgH) + content |> should haveSubstring "uint8_t valid[16];" + content |> should haveSubstring "#define MUX128_MSG_VALID_BYTES 16" + | Error e -> failwithf "Expected Ok, got: %A" e + finally + cleanupDir outDir + + [] + let ``utils header emits sc_valid helpers only when a message has more than 64 mux signals`` () = + let outDir64 = createTempOutDir () + + try + let ir64 = + mkMuxMessage + "MUX64B_MSG" + 907u + (mkMuxSwitch "MuxSel" 0us 4us) + ([ 0..62 ] + |> List.map (fun i -> mkBranchSignal (sprintf "Branch_%d" i) (uint16 ((i + 1) % 64)) 1us i)) + [] + + match generate ir64 outDir64 defaultConfig with + | Ok files -> + let utilsH = files.Headers |> List.find (fun f -> f.EndsWith("utils.h")) + let content = File.ReadAllText(utilsH) + content |> should not' (haveSubstring "sc_valid_set") + | Error e -> failwithf "Expected Ok, got: %A" e + finally + cleanupDir outDir64 + + let outDir65 = createTempOutDir () + + try + let ir65 = + mkMuxMessage + "MUX65B_MSG" + 908u + (mkMuxSwitch "MuxSel" 0us 4us) + ([ 0..63 ] + |> List.map (fun i -> mkBranchSignal (sprintf "Branch_%d" i) (uint16 ((i + 1) % 64)) 1us i)) + [] + + match generate ir65 outDir65 defaultConfig with + | Ok files -> + let utilsH = files.Headers |> List.find (fun f -> f.EndsWith("utils.h")) + let content = File.ReadAllText(utilsH) + content |> should haveSubstring "sc_valid_set" + content |> should haveSubstring "sc_valid_clear" + content |> should haveSubstring "sc_valid_test" + | Error e -> failwithf "Expected Ok, got: %A" e + finally + cleanupDir outDir65 + + [] + let ``codegen fails with UnsupportedFeature for 1025-signal mux message`` () = + let switchSig = mkMuxSwitch "MuxSel" 0us 4us + + let branchSignals = + [ 0..1023 ] + |> List.map (fun i -> mkBranchSignal (sprintf "Branch_%d" i) (uint16 ((i + 1) % 64)) 1us i) + + let ir = mkMuxMessage "MUX1025_MSG" 909u switchSig branchSignals [] + let outDir = createTempOutDir () + + try + let result = generate ir outDir defaultConfig + + match result with + | Error(UnsupportedFeature msg) -> msg |> should haveSubstring "1024" + | _ -> failwith "Expected UnsupportedFeature error" + finally + cleanupDir outDir - match result with - | Error(UnsupportedFeature msg) -> msg |> should haveSubstring "65" - | _ -> failwith "Expected UnsupportedFeature error" + [] + let ``valid bitmask uses uint8_t byte array for 72-signal mux message`` () = + let switchSig = mkMuxSwitch "MuxSel" 0us 4us + + let branchSignals = + [ 0..70 ] + |> List.map (fun i -> mkBranchSignal (sprintf "Branch_%d" i) (uint16 ((i + 1) % 64)) 1us i) + + let ir = mkMuxMessage "MUX72_MSG" 910u switchSig branchSignals [] + let outDir = createTempOutDir () + + try + match generate ir outDir defaultConfig with + | Ok files -> + let msgH = files.Headers |> List.find (fun f -> Path.GetFileName(f) = "mux72_msg.h") + let content = File.ReadAllText(msgH) + content |> should haveSubstring "uint8_t valid[9];" + | Error e -> failwithf "Expected Ok, got: %A" e + finally + cleanupDir outDir + + [] + let ``valid bitmask uses uint8_t byte array for 1024-signal mux message`` () = + let switchSig = mkMuxSwitch "MuxSel" 0us 4us + + let branchSignals = + [ 0..1022 ] + |> List.map (fun i -> mkBranchSignal (sprintf "Branch_%d" i) (uint16 ((i + 1) % 64)) 1us i) + + let ir = mkMuxMessage "MUX1024_MSG" 911u switchSig branchSignals [] + let outDir = createTempOutDir () + + try + match generate ir outDir defaultConfig with + | Ok files -> + let msgH = + files.Headers |> List.find (fun f -> Path.GetFileName(f) = "mux1024_msg.h") + + let content = File.ReadAllText(msgH) + content |> should haveSubstring "uint8_t valid[128];" + content |> should haveSubstring "#define MUX1024_MSG_VALID_BYTES 128" + | Error e -> failwithf "Expected Ok, got: %A" e + finally + cleanupDir outDir [] - let ``non-mux message with many signals has no valid field valid bitmask`` () = + let ``non-mux message with 100 signals has no valid field`` () = let signals = - [ 0..39 ] + [ 0..99 ] |> List.map (fun i -> mkSignal (sprintf "Plain_%d" i) (uint16 (i % 64)) 1us) let ir = { Messages = - [ { Name = "PLAIN40_MSG" - Id = 904u + [ { Name = "PLAIN100_MSG" + Id = 912u IsExtended = false Length = 8us Signals = signals @@ -1651,7 +1807,7 @@ module CodegenTests = match generate ir outDir defaultConfig with | Ok files -> let msgH = - files.Headers |> List.find (fun f -> Path.GetFileName(f) = "plain40_msg.h") + files.Headers |> List.find (fun f -> Path.GetFileName(f) = "plain100_msg.h") let content = File.ReadAllText(msgH) content |> should not' (haveSubstring "valid") diff --git a/tests/Signal.CANdy.Core.Tests/FacadeTests.fs b/tests/Signal.CANdy.Core.Tests/FacadeTests.fs index 9e74d48..01e7a3f 100644 --- a/tests/Signal.CANdy.Core.Tests/FacadeTests.fs +++ b/tests/Signal.CANdy.Core.Tests/FacadeTests.fs @@ -60,12 +60,12 @@ module FacadeTests = let switchSig = mkMuxSwitch "MuxSel" 0us 4us let branchSignals = - [ 0..63 ] + [ 0..1023 ] |> List.map (fun i -> mkBranchSignal (sprintf "Branch_%d" i) (uint16 ((i + 1) % 64)) 1us i) { Messages = - [ { Name = "MUX65_MSG" - Id = 903u + [ { Name = "MUX1025_MSG" + Id = 950u IsExtended = false Length = 8us Signals = [ switchSig ] @ branchSignals @@ -182,7 +182,7 @@ phys_type: INVALID_TYPE Assert.Throws(fun () -> facade.GenerateCode(mkUnsupportedMuxIr (), outDir, defaultConfig) |> ignore) - ex.Message |> should haveSubstring ">64" + ex.Message |> should haveSubstring ">1024" finally if Directory.Exists(outDir) then Directory.Delete(outDir, true) diff --git a/tests/Signal.CANdy.Core.Tests/golden/mux65_msg.c b/tests/Signal.CANdy.Core.Tests/golden/mux65_msg.c new file mode 100644 index 0000000..bcb4516 --- /dev/null +++ b/tests/Signal.CANdy.Core.Tests/golden/mux65_msg.c @@ -0,0 +1,800 @@ +/* Generated by Signal CANdy + file_prefix=sc_, phys_type=float, phys_mode=double, dispatch=binary_search, motorola_start_bit=msb */ + +#include "mux65_msg.h" +#include "sc_utils.h" +#include +#include + +bool MUX65_MSG_decode(MUX65_MSG_t* msg, const uint8_t data[], uint8_t dlc) { + if (dlc < 8) { return false; } + memset(msg->valid, 0, sizeof(msg->valid)); + + uint64_t raw_MuxSel = 0; + // MuxSel: start=0 len=8 factor=1 offset=0 + raw_MuxSel = get_bits_le(data, 0, 8); + msg->MuxSel = (float)((double)raw_MuxSel * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_MUXSEL); + msg->mux_active = (MUX65_MSG_mux_e)((int)raw_MuxSel); + + if ((int)raw_MuxSel == 0) { + uint64_t raw_Branch_0 = 0; + // Branch_0: start=1 len=8 factor=1 offset=0 + raw_Branch_0 = get_bits_le(data, 1, 8); + msg->Branch_0 = (float)((double)raw_Branch_0 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_0); + } + if ((int)raw_MuxSel == 1) { + uint64_t raw_Branch_1 = 0; + // Branch_1: start=2 len=8 factor=1 offset=0 + raw_Branch_1 = get_bits_le(data, 2, 8); + msg->Branch_1 = (float)((double)raw_Branch_1 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_1); + } + if ((int)raw_MuxSel == 2) { + uint64_t raw_Branch_2 = 0; + // Branch_2: start=3 len=8 factor=1 offset=0 + raw_Branch_2 = get_bits_le(data, 3, 8); + msg->Branch_2 = (float)((double)raw_Branch_2 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_2); + } + if ((int)raw_MuxSel == 3) { + uint64_t raw_Branch_3 = 0; + // Branch_3: start=4 len=8 factor=1 offset=0 + raw_Branch_3 = get_bits_le(data, 4, 8); + msg->Branch_3 = (float)((double)raw_Branch_3 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_3); + } + if ((int)raw_MuxSel == 4) { + uint64_t raw_Branch_4 = 0; + // Branch_4: start=5 len=8 factor=1 offset=0 + raw_Branch_4 = get_bits_le(data, 5, 8); + msg->Branch_4 = (float)((double)raw_Branch_4 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_4); + } + if ((int)raw_MuxSel == 5) { + uint64_t raw_Branch_5 = 0; + // Branch_5: start=6 len=8 factor=1 offset=0 + raw_Branch_5 = get_bits_le(data, 6, 8); + msg->Branch_5 = (float)((double)raw_Branch_5 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_5); + } + if ((int)raw_MuxSel == 6) { + uint64_t raw_Branch_6 = 0; + // Branch_6: start=7 len=8 factor=1 offset=0 + raw_Branch_6 = get_bits_le(data, 7, 8); + msg->Branch_6 = (float)((double)raw_Branch_6 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_6); + } + if ((int)raw_MuxSel == 7) { + uint64_t raw_Branch_7 = 0; + // Branch_7: start=8 len=8 factor=1 offset=0 + raw_Branch_7 = get_bits_le(data, 8, 8); + msg->Branch_7 = (float)((double)raw_Branch_7 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_7); + } + if ((int)raw_MuxSel == 8) { + uint64_t raw_Branch_8 = 0; + // Branch_8: start=9 len=8 factor=1 offset=0 + raw_Branch_8 = get_bits_le(data, 9, 8); + msg->Branch_8 = (float)((double)raw_Branch_8 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_8); + } + if ((int)raw_MuxSel == 9) { + uint64_t raw_Branch_9 = 0; + // Branch_9: start=10 len=8 factor=1 offset=0 + raw_Branch_9 = get_bits_le(data, 10, 8); + msg->Branch_9 = (float)((double)raw_Branch_9 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_9); + } + if ((int)raw_MuxSel == 10) { + uint64_t raw_Branch_10 = 0; + // Branch_10: start=11 len=8 factor=1 offset=0 + raw_Branch_10 = get_bits_le(data, 11, 8); + msg->Branch_10 = (float)((double)raw_Branch_10 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_10); + } + if ((int)raw_MuxSel == 11) { + uint64_t raw_Branch_11 = 0; + // Branch_11: start=12 len=8 factor=1 offset=0 + raw_Branch_11 = get_bits_le(data, 12, 8); + msg->Branch_11 = (float)((double)raw_Branch_11 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_11); + } + if ((int)raw_MuxSel == 12) { + uint64_t raw_Branch_12 = 0; + // Branch_12: start=13 len=8 factor=1 offset=0 + raw_Branch_12 = get_bits_le(data, 13, 8); + msg->Branch_12 = (float)((double)raw_Branch_12 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_12); + } + if ((int)raw_MuxSel == 13) { + uint64_t raw_Branch_13 = 0; + // Branch_13: start=14 len=8 factor=1 offset=0 + raw_Branch_13 = get_bits_le(data, 14, 8); + msg->Branch_13 = (float)((double)raw_Branch_13 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_13); + } + if ((int)raw_MuxSel == 14) { + uint64_t raw_Branch_14 = 0; + // Branch_14: start=15 len=8 factor=1 offset=0 + raw_Branch_14 = get_bits_le(data, 15, 8); + msg->Branch_14 = (float)((double)raw_Branch_14 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_14); + } + if ((int)raw_MuxSel == 15) { + uint64_t raw_Branch_15 = 0; + // Branch_15: start=16 len=8 factor=1 offset=0 + raw_Branch_15 = get_bits_le(data, 16, 8); + msg->Branch_15 = (float)((double)raw_Branch_15 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_15); + } + if ((int)raw_MuxSel == 16) { + uint64_t raw_Branch_16 = 0; + // Branch_16: start=17 len=8 factor=1 offset=0 + raw_Branch_16 = get_bits_le(data, 17, 8); + msg->Branch_16 = (float)((double)raw_Branch_16 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_16); + } + if ((int)raw_MuxSel == 17) { + uint64_t raw_Branch_17 = 0; + // Branch_17: start=18 len=8 factor=1 offset=0 + raw_Branch_17 = get_bits_le(data, 18, 8); + msg->Branch_17 = (float)((double)raw_Branch_17 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_17); + } + if ((int)raw_MuxSel == 18) { + uint64_t raw_Branch_18 = 0; + // Branch_18: start=19 len=8 factor=1 offset=0 + raw_Branch_18 = get_bits_le(data, 19, 8); + msg->Branch_18 = (float)((double)raw_Branch_18 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_18); + } + if ((int)raw_MuxSel == 19) { + uint64_t raw_Branch_19 = 0; + // Branch_19: start=20 len=8 factor=1 offset=0 + raw_Branch_19 = get_bits_le(data, 20, 8); + msg->Branch_19 = (float)((double)raw_Branch_19 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_19); + } + if ((int)raw_MuxSel == 20) { + uint64_t raw_Branch_20 = 0; + // Branch_20: start=21 len=8 factor=1 offset=0 + raw_Branch_20 = get_bits_le(data, 21, 8); + msg->Branch_20 = (float)((double)raw_Branch_20 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_20); + } + if ((int)raw_MuxSel == 21) { + uint64_t raw_Branch_21 = 0; + // Branch_21: start=22 len=8 factor=1 offset=0 + raw_Branch_21 = get_bits_le(data, 22, 8); + msg->Branch_21 = (float)((double)raw_Branch_21 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_21); + } + if ((int)raw_MuxSel == 22) { + uint64_t raw_Branch_22 = 0; + // Branch_22: start=23 len=8 factor=1 offset=0 + raw_Branch_22 = get_bits_le(data, 23, 8); + msg->Branch_22 = (float)((double)raw_Branch_22 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_22); + } + if ((int)raw_MuxSel == 23) { + uint64_t raw_Branch_23 = 0; + // Branch_23: start=24 len=8 factor=1 offset=0 + raw_Branch_23 = get_bits_le(data, 24, 8); + msg->Branch_23 = (float)((double)raw_Branch_23 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_23); + } + if ((int)raw_MuxSel == 24) { + uint64_t raw_Branch_24 = 0; + // Branch_24: start=25 len=8 factor=1 offset=0 + raw_Branch_24 = get_bits_le(data, 25, 8); + msg->Branch_24 = (float)((double)raw_Branch_24 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_24); + } + if ((int)raw_MuxSel == 25) { + uint64_t raw_Branch_25 = 0; + // Branch_25: start=26 len=8 factor=1 offset=0 + raw_Branch_25 = get_bits_le(data, 26, 8); + msg->Branch_25 = (float)((double)raw_Branch_25 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_25); + } + if ((int)raw_MuxSel == 26) { + uint64_t raw_Branch_26 = 0; + // Branch_26: start=27 len=8 factor=1 offset=0 + raw_Branch_26 = get_bits_le(data, 27, 8); + msg->Branch_26 = (float)((double)raw_Branch_26 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_26); + } + if ((int)raw_MuxSel == 27) { + uint64_t raw_Branch_27 = 0; + // Branch_27: start=28 len=8 factor=1 offset=0 + raw_Branch_27 = get_bits_le(data, 28, 8); + msg->Branch_27 = (float)((double)raw_Branch_27 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_27); + } + if ((int)raw_MuxSel == 28) { + uint64_t raw_Branch_28 = 0; + // Branch_28: start=29 len=8 factor=1 offset=0 + raw_Branch_28 = get_bits_le(data, 29, 8); + msg->Branch_28 = (float)((double)raw_Branch_28 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_28); + } + if ((int)raw_MuxSel == 29) { + uint64_t raw_Branch_29 = 0; + // Branch_29: start=30 len=8 factor=1 offset=0 + raw_Branch_29 = get_bits_le(data, 30, 8); + msg->Branch_29 = (float)((double)raw_Branch_29 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_29); + } + if ((int)raw_MuxSel == 30) { + uint64_t raw_Branch_30 = 0; + // Branch_30: start=31 len=8 factor=1 offset=0 + raw_Branch_30 = get_bits_le(data, 31, 8); + msg->Branch_30 = (float)((double)raw_Branch_30 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_30); + } + if ((int)raw_MuxSel == 31) { + uint64_t raw_Branch_31 = 0; + // Branch_31: start=32 len=8 factor=1 offset=0 + raw_Branch_31 = get_bits_le(data, 32, 8); + msg->Branch_31 = (float)((double)raw_Branch_31 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_31); + } + if ((int)raw_MuxSel == 32) { + uint64_t raw_Branch_32 = 0; + // Branch_32: start=33 len=8 factor=1 offset=0 + raw_Branch_32 = get_bits_le(data, 33, 8); + msg->Branch_32 = (float)((double)raw_Branch_32 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_32); + } + if ((int)raw_MuxSel == 33) { + uint64_t raw_Branch_33 = 0; + // Branch_33: start=34 len=8 factor=1 offset=0 + raw_Branch_33 = get_bits_le(data, 34, 8); + msg->Branch_33 = (float)((double)raw_Branch_33 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_33); + } + if ((int)raw_MuxSel == 34) { + uint64_t raw_Branch_34 = 0; + // Branch_34: start=35 len=8 factor=1 offset=0 + raw_Branch_34 = get_bits_le(data, 35, 8); + msg->Branch_34 = (float)((double)raw_Branch_34 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_34); + } + if ((int)raw_MuxSel == 35) { + uint64_t raw_Branch_35 = 0; + // Branch_35: start=36 len=8 factor=1 offset=0 + raw_Branch_35 = get_bits_le(data, 36, 8); + msg->Branch_35 = (float)((double)raw_Branch_35 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_35); + } + if ((int)raw_MuxSel == 36) { + uint64_t raw_Branch_36 = 0; + // Branch_36: start=37 len=8 factor=1 offset=0 + raw_Branch_36 = get_bits_le(data, 37, 8); + msg->Branch_36 = (float)((double)raw_Branch_36 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_36); + } + if ((int)raw_MuxSel == 37) { + uint64_t raw_Branch_37 = 0; + // Branch_37: start=38 len=8 factor=1 offset=0 + raw_Branch_37 = get_bits_le(data, 38, 8); + msg->Branch_37 = (float)((double)raw_Branch_37 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_37); + } + if ((int)raw_MuxSel == 38) { + uint64_t raw_Branch_38 = 0; + // Branch_38: start=39 len=8 factor=1 offset=0 + raw_Branch_38 = get_bits_le(data, 39, 8); + msg->Branch_38 = (float)((double)raw_Branch_38 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_38); + } + if ((int)raw_MuxSel == 39) { + uint64_t raw_Branch_39 = 0; + // Branch_39: start=40 len=8 factor=1 offset=0 + raw_Branch_39 = get_bits_le(data, 40, 8); + msg->Branch_39 = (float)((double)raw_Branch_39 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_39); + } + if ((int)raw_MuxSel == 40) { + uint64_t raw_Branch_40 = 0; + // Branch_40: start=41 len=8 factor=1 offset=0 + raw_Branch_40 = get_bits_le(data, 41, 8); + msg->Branch_40 = (float)((double)raw_Branch_40 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_40); + } + if ((int)raw_MuxSel == 41) { + uint64_t raw_Branch_41 = 0; + // Branch_41: start=42 len=8 factor=1 offset=0 + raw_Branch_41 = get_bits_le(data, 42, 8); + msg->Branch_41 = (float)((double)raw_Branch_41 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_41); + } + if ((int)raw_MuxSel == 42) { + uint64_t raw_Branch_42 = 0; + // Branch_42: start=43 len=8 factor=1 offset=0 + raw_Branch_42 = get_bits_le(data, 43, 8); + msg->Branch_42 = (float)((double)raw_Branch_42 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_42); + } + if ((int)raw_MuxSel == 43) { + uint64_t raw_Branch_43 = 0; + // Branch_43: start=44 len=8 factor=1 offset=0 + raw_Branch_43 = get_bits_le(data, 44, 8); + msg->Branch_43 = (float)((double)raw_Branch_43 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_43); + } + if ((int)raw_MuxSel == 44) { + uint64_t raw_Branch_44 = 0; + // Branch_44: start=45 len=8 factor=1 offset=0 + raw_Branch_44 = get_bits_le(data, 45, 8); + msg->Branch_44 = (float)((double)raw_Branch_44 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_44); + } + if ((int)raw_MuxSel == 45) { + uint64_t raw_Branch_45 = 0; + // Branch_45: start=46 len=8 factor=1 offset=0 + raw_Branch_45 = get_bits_le(data, 46, 8); + msg->Branch_45 = (float)((double)raw_Branch_45 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_45); + } + if ((int)raw_MuxSel == 46) { + uint64_t raw_Branch_46 = 0; + // Branch_46: start=47 len=8 factor=1 offset=0 + raw_Branch_46 = get_bits_le(data, 47, 8); + msg->Branch_46 = (float)((double)raw_Branch_46 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_46); + } + if ((int)raw_MuxSel == 47) { + uint64_t raw_Branch_47 = 0; + // Branch_47: start=48 len=8 factor=1 offset=0 + raw_Branch_47 = get_bits_le(data, 48, 8); + msg->Branch_47 = (float)((double)raw_Branch_47 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_47); + } + if ((int)raw_MuxSel == 48) { + uint64_t raw_Branch_48 = 0; + // Branch_48: start=49 len=8 factor=1 offset=0 + raw_Branch_48 = get_bits_le(data, 49, 8); + msg->Branch_48 = (float)((double)raw_Branch_48 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_48); + } + if ((int)raw_MuxSel == 49) { + uint64_t raw_Branch_49 = 0; + // Branch_49: start=50 len=8 factor=1 offset=0 + raw_Branch_49 = get_bits_le(data, 50, 8); + msg->Branch_49 = (float)((double)raw_Branch_49 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_49); + } + if ((int)raw_MuxSel == 50) { + uint64_t raw_Branch_50 = 0; + // Branch_50: start=51 len=8 factor=1 offset=0 + raw_Branch_50 = get_bits_le(data, 51, 8); + msg->Branch_50 = (float)((double)raw_Branch_50 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_50); + } + if ((int)raw_MuxSel == 51) { + uint64_t raw_Branch_51 = 0; + // Branch_51: start=52 len=8 factor=1 offset=0 + raw_Branch_51 = get_bits_le(data, 52, 8); + msg->Branch_51 = (float)((double)raw_Branch_51 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_51); + } + if ((int)raw_MuxSel == 52) { + uint64_t raw_Branch_52 = 0; + // Branch_52: start=53 len=8 factor=1 offset=0 + raw_Branch_52 = get_bits_le(data, 53, 8); + msg->Branch_52 = (float)((double)raw_Branch_52 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_52); + } + if ((int)raw_MuxSel == 53) { + uint64_t raw_Branch_53 = 0; + // Branch_53: start=54 len=8 factor=1 offset=0 + raw_Branch_53 = get_bits_le(data, 54, 8); + msg->Branch_53 = (float)((double)raw_Branch_53 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_53); + } + if ((int)raw_MuxSel == 54) { + uint64_t raw_Branch_54 = 0; + // Branch_54: start=55 len=8 factor=1 offset=0 + raw_Branch_54 = get_bits_le(data, 55, 8); + msg->Branch_54 = (float)((double)raw_Branch_54 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_54); + } + if ((int)raw_MuxSel == 55) { + uint64_t raw_Branch_55 = 0; + // Branch_55: start=56 len=8 factor=1 offset=0 + raw_Branch_55 = get_bits_le(data, 56, 8); + msg->Branch_55 = (float)((double)raw_Branch_55 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_55); + } + if ((int)raw_MuxSel == 56) { + uint64_t raw_Branch_56 = 0; + // Branch_56: start=57 len=8 factor=1 offset=0 + raw_Branch_56 = get_bits_le(data, 57, 8); + msg->Branch_56 = (float)((double)raw_Branch_56 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_56); + } + if ((int)raw_MuxSel == 57) { + uint64_t raw_Branch_57 = 0; + // Branch_57: start=58 len=8 factor=1 offset=0 + raw_Branch_57 = get_bits_le(data, 58, 8); + msg->Branch_57 = (float)((double)raw_Branch_57 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_57); + } + if ((int)raw_MuxSel == 58) { + uint64_t raw_Branch_58 = 0; + // Branch_58: start=59 len=8 factor=1 offset=0 + raw_Branch_58 = get_bits_le(data, 59, 8); + msg->Branch_58 = (float)((double)raw_Branch_58 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_58); + } + if ((int)raw_MuxSel == 59) { + uint64_t raw_Branch_59 = 0; + // Branch_59: start=60 len=8 factor=1 offset=0 + raw_Branch_59 = get_bits_le(data, 60, 8); + msg->Branch_59 = (float)((double)raw_Branch_59 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_59); + } + if ((int)raw_MuxSel == 60) { + uint64_t raw_Branch_60 = 0; + // Branch_60: start=61 len=8 factor=1 offset=0 + raw_Branch_60 = get_bits_le(data, 61, 8); + msg->Branch_60 = (float)((double)raw_Branch_60 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_60); + } + if ((int)raw_MuxSel == 61) { + uint64_t raw_Branch_61 = 0; + // Branch_61: start=62 len=8 factor=1 offset=0 + raw_Branch_61 = get_bits_le(data, 62, 8); + msg->Branch_61 = (float)((double)raw_Branch_61 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_61); + } + if ((int)raw_MuxSel == 62) { + uint64_t raw_Branch_62 = 0; + // Branch_62: start=63 len=8 factor=1 offset=0 + raw_Branch_62 = get_bits_le(data, 63, 8); + msg->Branch_62 = (float)((double)raw_Branch_62 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_62); + } + if ((int)raw_MuxSel == 63) { + uint64_t raw_Branch_63 = 0; + // Branch_63: start=0 len=8 factor=1 offset=0 + raw_Branch_63 = get_bits_le(data, 0, 8); + msg->Branch_63 = (float)((double)raw_Branch_63 * 1 + 0); + sc_valid_set(msg->valid, MUX65_MSG_VALID_BRANCH_63); + } + return true; +} + +bool MUX65_MSG_encode(uint8_t data[], uint8_t* out_dlc, const MUX65_MSG_t* msg) { + memset(data, 0, 8); + *out_dlc = 8; + + double tmp_MuxSel = ((double)msg->MuxSel - 0) / 1; + int64_t raw_MuxSel = (int64_t)(tmp_MuxSel >= 0 ? tmp_MuxSel + 0.5 : tmp_MuxSel - 0.5); + set_bits_le(data, 0, 8, (uint64_t)raw_MuxSel); + + if ((int)raw_MuxSel == 0) { + double tmp_Branch_0 = ((double)msg->Branch_0 - 0) / 1; + int64_t raw_Branch_0 = (int64_t)(tmp_Branch_0 >= 0 ? tmp_Branch_0 + 0.5 : tmp_Branch_0 - 0.5); + set_bits_le(data, 1, 8, (uint64_t)raw_Branch_0); + } + if ((int)raw_MuxSel == 1) { + double tmp_Branch_1 = ((double)msg->Branch_1 - 0) / 1; + int64_t raw_Branch_1 = (int64_t)(tmp_Branch_1 >= 0 ? tmp_Branch_1 + 0.5 : tmp_Branch_1 - 0.5); + set_bits_le(data, 2, 8, (uint64_t)raw_Branch_1); + } + if ((int)raw_MuxSel == 2) { + double tmp_Branch_2 = ((double)msg->Branch_2 - 0) / 1; + int64_t raw_Branch_2 = (int64_t)(tmp_Branch_2 >= 0 ? tmp_Branch_2 + 0.5 : tmp_Branch_2 - 0.5); + set_bits_le(data, 3, 8, (uint64_t)raw_Branch_2); + } + if ((int)raw_MuxSel == 3) { + double tmp_Branch_3 = ((double)msg->Branch_3 - 0) / 1; + int64_t raw_Branch_3 = (int64_t)(tmp_Branch_3 >= 0 ? tmp_Branch_3 + 0.5 : tmp_Branch_3 - 0.5); + set_bits_le(data, 4, 8, (uint64_t)raw_Branch_3); + } + if ((int)raw_MuxSel == 4) { + double tmp_Branch_4 = ((double)msg->Branch_4 - 0) / 1; + int64_t raw_Branch_4 = (int64_t)(tmp_Branch_4 >= 0 ? tmp_Branch_4 + 0.5 : tmp_Branch_4 - 0.5); + set_bits_le(data, 5, 8, (uint64_t)raw_Branch_4); + } + if ((int)raw_MuxSel == 5) { + double tmp_Branch_5 = ((double)msg->Branch_5 - 0) / 1; + int64_t raw_Branch_5 = (int64_t)(tmp_Branch_5 >= 0 ? tmp_Branch_5 + 0.5 : tmp_Branch_5 - 0.5); + set_bits_le(data, 6, 8, (uint64_t)raw_Branch_5); + } + if ((int)raw_MuxSel == 6) { + double tmp_Branch_6 = ((double)msg->Branch_6 - 0) / 1; + int64_t raw_Branch_6 = (int64_t)(tmp_Branch_6 >= 0 ? tmp_Branch_6 + 0.5 : tmp_Branch_6 - 0.5); + set_bits_le(data, 7, 8, (uint64_t)raw_Branch_6); + } + if ((int)raw_MuxSel == 7) { + double tmp_Branch_7 = ((double)msg->Branch_7 - 0) / 1; + int64_t raw_Branch_7 = (int64_t)(tmp_Branch_7 >= 0 ? tmp_Branch_7 + 0.5 : tmp_Branch_7 - 0.5); + set_bits_le(data, 8, 8, (uint64_t)raw_Branch_7); + } + if ((int)raw_MuxSel == 8) { + double tmp_Branch_8 = ((double)msg->Branch_8 - 0) / 1; + int64_t raw_Branch_8 = (int64_t)(tmp_Branch_8 >= 0 ? tmp_Branch_8 + 0.5 : tmp_Branch_8 - 0.5); + set_bits_le(data, 9, 8, (uint64_t)raw_Branch_8); + } + if ((int)raw_MuxSel == 9) { + double tmp_Branch_9 = ((double)msg->Branch_9 - 0) / 1; + int64_t raw_Branch_9 = (int64_t)(tmp_Branch_9 >= 0 ? tmp_Branch_9 + 0.5 : tmp_Branch_9 - 0.5); + set_bits_le(data, 10, 8, (uint64_t)raw_Branch_9); + } + if ((int)raw_MuxSel == 10) { + double tmp_Branch_10 = ((double)msg->Branch_10 - 0) / 1; + int64_t raw_Branch_10 = (int64_t)(tmp_Branch_10 >= 0 ? tmp_Branch_10 + 0.5 : tmp_Branch_10 - 0.5); + set_bits_le(data, 11, 8, (uint64_t)raw_Branch_10); + } + if ((int)raw_MuxSel == 11) { + double tmp_Branch_11 = ((double)msg->Branch_11 - 0) / 1; + int64_t raw_Branch_11 = (int64_t)(tmp_Branch_11 >= 0 ? tmp_Branch_11 + 0.5 : tmp_Branch_11 - 0.5); + set_bits_le(data, 12, 8, (uint64_t)raw_Branch_11); + } + if ((int)raw_MuxSel == 12) { + double tmp_Branch_12 = ((double)msg->Branch_12 - 0) / 1; + int64_t raw_Branch_12 = (int64_t)(tmp_Branch_12 >= 0 ? tmp_Branch_12 + 0.5 : tmp_Branch_12 - 0.5); + set_bits_le(data, 13, 8, (uint64_t)raw_Branch_12); + } + if ((int)raw_MuxSel == 13) { + double tmp_Branch_13 = ((double)msg->Branch_13 - 0) / 1; + int64_t raw_Branch_13 = (int64_t)(tmp_Branch_13 >= 0 ? tmp_Branch_13 + 0.5 : tmp_Branch_13 - 0.5); + set_bits_le(data, 14, 8, (uint64_t)raw_Branch_13); + } + if ((int)raw_MuxSel == 14) { + double tmp_Branch_14 = ((double)msg->Branch_14 - 0) / 1; + int64_t raw_Branch_14 = (int64_t)(tmp_Branch_14 >= 0 ? tmp_Branch_14 + 0.5 : tmp_Branch_14 - 0.5); + set_bits_le(data, 15, 8, (uint64_t)raw_Branch_14); + } + if ((int)raw_MuxSel == 15) { + double tmp_Branch_15 = ((double)msg->Branch_15 - 0) / 1; + int64_t raw_Branch_15 = (int64_t)(tmp_Branch_15 >= 0 ? tmp_Branch_15 + 0.5 : tmp_Branch_15 - 0.5); + set_bits_le(data, 16, 8, (uint64_t)raw_Branch_15); + } + if ((int)raw_MuxSel == 16) { + double tmp_Branch_16 = ((double)msg->Branch_16 - 0) / 1; + int64_t raw_Branch_16 = (int64_t)(tmp_Branch_16 >= 0 ? tmp_Branch_16 + 0.5 : tmp_Branch_16 - 0.5); + set_bits_le(data, 17, 8, (uint64_t)raw_Branch_16); + } + if ((int)raw_MuxSel == 17) { + double tmp_Branch_17 = ((double)msg->Branch_17 - 0) / 1; + int64_t raw_Branch_17 = (int64_t)(tmp_Branch_17 >= 0 ? tmp_Branch_17 + 0.5 : tmp_Branch_17 - 0.5); + set_bits_le(data, 18, 8, (uint64_t)raw_Branch_17); + } + if ((int)raw_MuxSel == 18) { + double tmp_Branch_18 = ((double)msg->Branch_18 - 0) / 1; + int64_t raw_Branch_18 = (int64_t)(tmp_Branch_18 >= 0 ? tmp_Branch_18 + 0.5 : tmp_Branch_18 - 0.5); + set_bits_le(data, 19, 8, (uint64_t)raw_Branch_18); + } + if ((int)raw_MuxSel == 19) { + double tmp_Branch_19 = ((double)msg->Branch_19 - 0) / 1; + int64_t raw_Branch_19 = (int64_t)(tmp_Branch_19 >= 0 ? tmp_Branch_19 + 0.5 : tmp_Branch_19 - 0.5); + set_bits_le(data, 20, 8, (uint64_t)raw_Branch_19); + } + if ((int)raw_MuxSel == 20) { + double tmp_Branch_20 = ((double)msg->Branch_20 - 0) / 1; + int64_t raw_Branch_20 = (int64_t)(tmp_Branch_20 >= 0 ? tmp_Branch_20 + 0.5 : tmp_Branch_20 - 0.5); + set_bits_le(data, 21, 8, (uint64_t)raw_Branch_20); + } + if ((int)raw_MuxSel == 21) { + double tmp_Branch_21 = ((double)msg->Branch_21 - 0) / 1; + int64_t raw_Branch_21 = (int64_t)(tmp_Branch_21 >= 0 ? tmp_Branch_21 + 0.5 : tmp_Branch_21 - 0.5); + set_bits_le(data, 22, 8, (uint64_t)raw_Branch_21); + } + if ((int)raw_MuxSel == 22) { + double tmp_Branch_22 = ((double)msg->Branch_22 - 0) / 1; + int64_t raw_Branch_22 = (int64_t)(tmp_Branch_22 >= 0 ? tmp_Branch_22 + 0.5 : tmp_Branch_22 - 0.5); + set_bits_le(data, 23, 8, (uint64_t)raw_Branch_22); + } + if ((int)raw_MuxSel == 23) { + double tmp_Branch_23 = ((double)msg->Branch_23 - 0) / 1; + int64_t raw_Branch_23 = (int64_t)(tmp_Branch_23 >= 0 ? tmp_Branch_23 + 0.5 : tmp_Branch_23 - 0.5); + set_bits_le(data, 24, 8, (uint64_t)raw_Branch_23); + } + if ((int)raw_MuxSel == 24) { + double tmp_Branch_24 = ((double)msg->Branch_24 - 0) / 1; + int64_t raw_Branch_24 = (int64_t)(tmp_Branch_24 >= 0 ? tmp_Branch_24 + 0.5 : tmp_Branch_24 - 0.5); + set_bits_le(data, 25, 8, (uint64_t)raw_Branch_24); + } + if ((int)raw_MuxSel == 25) { + double tmp_Branch_25 = ((double)msg->Branch_25 - 0) / 1; + int64_t raw_Branch_25 = (int64_t)(tmp_Branch_25 >= 0 ? tmp_Branch_25 + 0.5 : tmp_Branch_25 - 0.5); + set_bits_le(data, 26, 8, (uint64_t)raw_Branch_25); + } + if ((int)raw_MuxSel == 26) { + double tmp_Branch_26 = ((double)msg->Branch_26 - 0) / 1; + int64_t raw_Branch_26 = (int64_t)(tmp_Branch_26 >= 0 ? tmp_Branch_26 + 0.5 : tmp_Branch_26 - 0.5); + set_bits_le(data, 27, 8, (uint64_t)raw_Branch_26); + } + if ((int)raw_MuxSel == 27) { + double tmp_Branch_27 = ((double)msg->Branch_27 - 0) / 1; + int64_t raw_Branch_27 = (int64_t)(tmp_Branch_27 >= 0 ? tmp_Branch_27 + 0.5 : tmp_Branch_27 - 0.5); + set_bits_le(data, 28, 8, (uint64_t)raw_Branch_27); + } + if ((int)raw_MuxSel == 28) { + double tmp_Branch_28 = ((double)msg->Branch_28 - 0) / 1; + int64_t raw_Branch_28 = (int64_t)(tmp_Branch_28 >= 0 ? tmp_Branch_28 + 0.5 : tmp_Branch_28 - 0.5); + set_bits_le(data, 29, 8, (uint64_t)raw_Branch_28); + } + if ((int)raw_MuxSel == 29) { + double tmp_Branch_29 = ((double)msg->Branch_29 - 0) / 1; + int64_t raw_Branch_29 = (int64_t)(tmp_Branch_29 >= 0 ? tmp_Branch_29 + 0.5 : tmp_Branch_29 - 0.5); + set_bits_le(data, 30, 8, (uint64_t)raw_Branch_29); + } + if ((int)raw_MuxSel == 30) { + double tmp_Branch_30 = ((double)msg->Branch_30 - 0) / 1; + int64_t raw_Branch_30 = (int64_t)(tmp_Branch_30 >= 0 ? tmp_Branch_30 + 0.5 : tmp_Branch_30 - 0.5); + set_bits_le(data, 31, 8, (uint64_t)raw_Branch_30); + } + if ((int)raw_MuxSel == 31) { + double tmp_Branch_31 = ((double)msg->Branch_31 - 0) / 1; + int64_t raw_Branch_31 = (int64_t)(tmp_Branch_31 >= 0 ? tmp_Branch_31 + 0.5 : tmp_Branch_31 - 0.5); + set_bits_le(data, 32, 8, (uint64_t)raw_Branch_31); + } + if ((int)raw_MuxSel == 32) { + double tmp_Branch_32 = ((double)msg->Branch_32 - 0) / 1; + int64_t raw_Branch_32 = (int64_t)(tmp_Branch_32 >= 0 ? tmp_Branch_32 + 0.5 : tmp_Branch_32 - 0.5); + set_bits_le(data, 33, 8, (uint64_t)raw_Branch_32); + } + if ((int)raw_MuxSel == 33) { + double tmp_Branch_33 = ((double)msg->Branch_33 - 0) / 1; + int64_t raw_Branch_33 = (int64_t)(tmp_Branch_33 >= 0 ? tmp_Branch_33 + 0.5 : tmp_Branch_33 - 0.5); + set_bits_le(data, 34, 8, (uint64_t)raw_Branch_33); + } + if ((int)raw_MuxSel == 34) { + double tmp_Branch_34 = ((double)msg->Branch_34 - 0) / 1; + int64_t raw_Branch_34 = (int64_t)(tmp_Branch_34 >= 0 ? tmp_Branch_34 + 0.5 : tmp_Branch_34 - 0.5); + set_bits_le(data, 35, 8, (uint64_t)raw_Branch_34); + } + if ((int)raw_MuxSel == 35) { + double tmp_Branch_35 = ((double)msg->Branch_35 - 0) / 1; + int64_t raw_Branch_35 = (int64_t)(tmp_Branch_35 >= 0 ? tmp_Branch_35 + 0.5 : tmp_Branch_35 - 0.5); + set_bits_le(data, 36, 8, (uint64_t)raw_Branch_35); + } + if ((int)raw_MuxSel == 36) { + double tmp_Branch_36 = ((double)msg->Branch_36 - 0) / 1; + int64_t raw_Branch_36 = (int64_t)(tmp_Branch_36 >= 0 ? tmp_Branch_36 + 0.5 : tmp_Branch_36 - 0.5); + set_bits_le(data, 37, 8, (uint64_t)raw_Branch_36); + } + if ((int)raw_MuxSel == 37) { + double tmp_Branch_37 = ((double)msg->Branch_37 - 0) / 1; + int64_t raw_Branch_37 = (int64_t)(tmp_Branch_37 >= 0 ? tmp_Branch_37 + 0.5 : tmp_Branch_37 - 0.5); + set_bits_le(data, 38, 8, (uint64_t)raw_Branch_37); + } + if ((int)raw_MuxSel == 38) { + double tmp_Branch_38 = ((double)msg->Branch_38 - 0) / 1; + int64_t raw_Branch_38 = (int64_t)(tmp_Branch_38 >= 0 ? tmp_Branch_38 + 0.5 : tmp_Branch_38 - 0.5); + set_bits_le(data, 39, 8, (uint64_t)raw_Branch_38); + } + if ((int)raw_MuxSel == 39) { + double tmp_Branch_39 = ((double)msg->Branch_39 - 0) / 1; + int64_t raw_Branch_39 = (int64_t)(tmp_Branch_39 >= 0 ? tmp_Branch_39 + 0.5 : tmp_Branch_39 - 0.5); + set_bits_le(data, 40, 8, (uint64_t)raw_Branch_39); + } + if ((int)raw_MuxSel == 40) { + double tmp_Branch_40 = ((double)msg->Branch_40 - 0) / 1; + int64_t raw_Branch_40 = (int64_t)(tmp_Branch_40 >= 0 ? tmp_Branch_40 + 0.5 : tmp_Branch_40 - 0.5); + set_bits_le(data, 41, 8, (uint64_t)raw_Branch_40); + } + if ((int)raw_MuxSel == 41) { + double tmp_Branch_41 = ((double)msg->Branch_41 - 0) / 1; + int64_t raw_Branch_41 = (int64_t)(tmp_Branch_41 >= 0 ? tmp_Branch_41 + 0.5 : tmp_Branch_41 - 0.5); + set_bits_le(data, 42, 8, (uint64_t)raw_Branch_41); + } + if ((int)raw_MuxSel == 42) { + double tmp_Branch_42 = ((double)msg->Branch_42 - 0) / 1; + int64_t raw_Branch_42 = (int64_t)(tmp_Branch_42 >= 0 ? tmp_Branch_42 + 0.5 : tmp_Branch_42 - 0.5); + set_bits_le(data, 43, 8, (uint64_t)raw_Branch_42); + } + if ((int)raw_MuxSel == 43) { + double tmp_Branch_43 = ((double)msg->Branch_43 - 0) / 1; + int64_t raw_Branch_43 = (int64_t)(tmp_Branch_43 >= 0 ? tmp_Branch_43 + 0.5 : tmp_Branch_43 - 0.5); + set_bits_le(data, 44, 8, (uint64_t)raw_Branch_43); + } + if ((int)raw_MuxSel == 44) { + double tmp_Branch_44 = ((double)msg->Branch_44 - 0) / 1; + int64_t raw_Branch_44 = (int64_t)(tmp_Branch_44 >= 0 ? tmp_Branch_44 + 0.5 : tmp_Branch_44 - 0.5); + set_bits_le(data, 45, 8, (uint64_t)raw_Branch_44); + } + if ((int)raw_MuxSel == 45) { + double tmp_Branch_45 = ((double)msg->Branch_45 - 0) / 1; + int64_t raw_Branch_45 = (int64_t)(tmp_Branch_45 >= 0 ? tmp_Branch_45 + 0.5 : tmp_Branch_45 - 0.5); + set_bits_le(data, 46, 8, (uint64_t)raw_Branch_45); + } + if ((int)raw_MuxSel == 46) { + double tmp_Branch_46 = ((double)msg->Branch_46 - 0) / 1; + int64_t raw_Branch_46 = (int64_t)(tmp_Branch_46 >= 0 ? tmp_Branch_46 + 0.5 : tmp_Branch_46 - 0.5); + set_bits_le(data, 47, 8, (uint64_t)raw_Branch_46); + } + if ((int)raw_MuxSel == 47) { + double tmp_Branch_47 = ((double)msg->Branch_47 - 0) / 1; + int64_t raw_Branch_47 = (int64_t)(tmp_Branch_47 >= 0 ? tmp_Branch_47 + 0.5 : tmp_Branch_47 - 0.5); + set_bits_le(data, 48, 8, (uint64_t)raw_Branch_47); + } + if ((int)raw_MuxSel == 48) { + double tmp_Branch_48 = ((double)msg->Branch_48 - 0) / 1; + int64_t raw_Branch_48 = (int64_t)(tmp_Branch_48 >= 0 ? tmp_Branch_48 + 0.5 : tmp_Branch_48 - 0.5); + set_bits_le(data, 49, 8, (uint64_t)raw_Branch_48); + } + if ((int)raw_MuxSel == 49) { + double tmp_Branch_49 = ((double)msg->Branch_49 - 0) / 1; + int64_t raw_Branch_49 = (int64_t)(tmp_Branch_49 >= 0 ? tmp_Branch_49 + 0.5 : tmp_Branch_49 - 0.5); + set_bits_le(data, 50, 8, (uint64_t)raw_Branch_49); + } + if ((int)raw_MuxSel == 50) { + double tmp_Branch_50 = ((double)msg->Branch_50 - 0) / 1; + int64_t raw_Branch_50 = (int64_t)(tmp_Branch_50 >= 0 ? tmp_Branch_50 + 0.5 : tmp_Branch_50 - 0.5); + set_bits_le(data, 51, 8, (uint64_t)raw_Branch_50); + } + if ((int)raw_MuxSel == 51) { + double tmp_Branch_51 = ((double)msg->Branch_51 - 0) / 1; + int64_t raw_Branch_51 = (int64_t)(tmp_Branch_51 >= 0 ? tmp_Branch_51 + 0.5 : tmp_Branch_51 - 0.5); + set_bits_le(data, 52, 8, (uint64_t)raw_Branch_51); + } + if ((int)raw_MuxSel == 52) { + double tmp_Branch_52 = ((double)msg->Branch_52 - 0) / 1; + int64_t raw_Branch_52 = (int64_t)(tmp_Branch_52 >= 0 ? tmp_Branch_52 + 0.5 : tmp_Branch_52 - 0.5); + set_bits_le(data, 53, 8, (uint64_t)raw_Branch_52); + } + if ((int)raw_MuxSel == 53) { + double tmp_Branch_53 = ((double)msg->Branch_53 - 0) / 1; + int64_t raw_Branch_53 = (int64_t)(tmp_Branch_53 >= 0 ? tmp_Branch_53 + 0.5 : tmp_Branch_53 - 0.5); + set_bits_le(data, 54, 8, (uint64_t)raw_Branch_53); + } + if ((int)raw_MuxSel == 54) { + double tmp_Branch_54 = ((double)msg->Branch_54 - 0) / 1; + int64_t raw_Branch_54 = (int64_t)(tmp_Branch_54 >= 0 ? tmp_Branch_54 + 0.5 : tmp_Branch_54 - 0.5); + set_bits_le(data, 55, 8, (uint64_t)raw_Branch_54); + } + if ((int)raw_MuxSel == 55) { + double tmp_Branch_55 = ((double)msg->Branch_55 - 0) / 1; + int64_t raw_Branch_55 = (int64_t)(tmp_Branch_55 >= 0 ? tmp_Branch_55 + 0.5 : tmp_Branch_55 - 0.5); + set_bits_le(data, 56, 8, (uint64_t)raw_Branch_55); + } + if ((int)raw_MuxSel == 56) { + double tmp_Branch_56 = ((double)msg->Branch_56 - 0) / 1; + int64_t raw_Branch_56 = (int64_t)(tmp_Branch_56 >= 0 ? tmp_Branch_56 + 0.5 : tmp_Branch_56 - 0.5); + set_bits_le(data, 57, 8, (uint64_t)raw_Branch_56); + } + if ((int)raw_MuxSel == 57) { + double tmp_Branch_57 = ((double)msg->Branch_57 - 0) / 1; + int64_t raw_Branch_57 = (int64_t)(tmp_Branch_57 >= 0 ? tmp_Branch_57 + 0.5 : tmp_Branch_57 - 0.5); + set_bits_le(data, 58, 8, (uint64_t)raw_Branch_57); + } + if ((int)raw_MuxSel == 58) { + double tmp_Branch_58 = ((double)msg->Branch_58 - 0) / 1; + int64_t raw_Branch_58 = (int64_t)(tmp_Branch_58 >= 0 ? tmp_Branch_58 + 0.5 : tmp_Branch_58 - 0.5); + set_bits_le(data, 59, 8, (uint64_t)raw_Branch_58); + } + if ((int)raw_MuxSel == 59) { + double tmp_Branch_59 = ((double)msg->Branch_59 - 0) / 1; + int64_t raw_Branch_59 = (int64_t)(tmp_Branch_59 >= 0 ? tmp_Branch_59 + 0.5 : tmp_Branch_59 - 0.5); + set_bits_le(data, 60, 8, (uint64_t)raw_Branch_59); + } + if ((int)raw_MuxSel == 60) { + double tmp_Branch_60 = ((double)msg->Branch_60 - 0) / 1; + int64_t raw_Branch_60 = (int64_t)(tmp_Branch_60 >= 0 ? tmp_Branch_60 + 0.5 : tmp_Branch_60 - 0.5); + set_bits_le(data, 61, 8, (uint64_t)raw_Branch_60); + } + if ((int)raw_MuxSel == 61) { + double tmp_Branch_61 = ((double)msg->Branch_61 - 0) / 1; + int64_t raw_Branch_61 = (int64_t)(tmp_Branch_61 >= 0 ? tmp_Branch_61 + 0.5 : tmp_Branch_61 - 0.5); + set_bits_le(data, 62, 8, (uint64_t)raw_Branch_61); + } + if ((int)raw_MuxSel == 62) { + double tmp_Branch_62 = ((double)msg->Branch_62 - 0) / 1; + int64_t raw_Branch_62 = (int64_t)(tmp_Branch_62 >= 0 ? tmp_Branch_62 + 0.5 : tmp_Branch_62 - 0.5); + set_bits_le(data, 63, 8, (uint64_t)raw_Branch_62); + } + if ((int)raw_MuxSel == 63) { + double tmp_Branch_63 = ((double)msg->Branch_63 - 0) / 1; + int64_t raw_Branch_63 = (int64_t)(tmp_Branch_63 >= 0 ? tmp_Branch_63 + 0.5 : tmp_Branch_63 - 0.5); + set_bits_le(data, 0, 8, (uint64_t)raw_Branch_63); + } + return true; +} diff --git a/tests/Signal.CANdy.Core.Tests/golden/mux65_msg.h b/tests/Signal.CANdy.Core.Tests/golden/mux65_msg.h new file mode 100644 index 0000000..468203e --- /dev/null +++ b/tests/Signal.CANdy.Core.Tests/golden/mux65_msg.h @@ -0,0 +1,227 @@ +/* Generated by Signal CANdy + file_prefix=sc_, phys_type=float, phys_mode=double, dispatch=binary_search, motorola_start_bit=msb */ + +#ifndef MUX65_MSG_H +#define MUX65_MSG_H + +#include +#include +#include "sc_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MUX65_MSG_MUX_0 = 0, + MUX65_MSG_MUX_1 = 1, + MUX65_MSG_MUX_2 = 2, + MUX65_MSG_MUX_3 = 3, + MUX65_MSG_MUX_4 = 4, + MUX65_MSG_MUX_5 = 5, + MUX65_MSG_MUX_6 = 6, + MUX65_MSG_MUX_7 = 7, + MUX65_MSG_MUX_8 = 8, + MUX65_MSG_MUX_9 = 9, + MUX65_MSG_MUX_10 = 10, + MUX65_MSG_MUX_11 = 11, + MUX65_MSG_MUX_12 = 12, + MUX65_MSG_MUX_13 = 13, + MUX65_MSG_MUX_14 = 14, + MUX65_MSG_MUX_15 = 15, + MUX65_MSG_MUX_16 = 16, + MUX65_MSG_MUX_17 = 17, + MUX65_MSG_MUX_18 = 18, + MUX65_MSG_MUX_19 = 19, + MUX65_MSG_MUX_20 = 20, + MUX65_MSG_MUX_21 = 21, + MUX65_MSG_MUX_22 = 22, + MUX65_MSG_MUX_23 = 23, + MUX65_MSG_MUX_24 = 24, + MUX65_MSG_MUX_25 = 25, + MUX65_MSG_MUX_26 = 26, + MUX65_MSG_MUX_27 = 27, + MUX65_MSG_MUX_28 = 28, + MUX65_MSG_MUX_29 = 29, + MUX65_MSG_MUX_30 = 30, + MUX65_MSG_MUX_31 = 31, + MUX65_MSG_MUX_32 = 32, + MUX65_MSG_MUX_33 = 33, + MUX65_MSG_MUX_34 = 34, + MUX65_MSG_MUX_35 = 35, + MUX65_MSG_MUX_36 = 36, + MUX65_MSG_MUX_37 = 37, + MUX65_MSG_MUX_38 = 38, + MUX65_MSG_MUX_39 = 39, + MUX65_MSG_MUX_40 = 40, + MUX65_MSG_MUX_41 = 41, + MUX65_MSG_MUX_42 = 42, + MUX65_MSG_MUX_43 = 43, + MUX65_MSG_MUX_44 = 44, + MUX65_MSG_MUX_45 = 45, + MUX65_MSG_MUX_46 = 46, + MUX65_MSG_MUX_47 = 47, + MUX65_MSG_MUX_48 = 48, + MUX65_MSG_MUX_49 = 49, + MUX65_MSG_MUX_50 = 50, + MUX65_MSG_MUX_51 = 51, + MUX65_MSG_MUX_52 = 52, + MUX65_MSG_MUX_53 = 53, + MUX65_MSG_MUX_54 = 54, + MUX65_MSG_MUX_55 = 55, + MUX65_MSG_MUX_56 = 56, + MUX65_MSG_MUX_57 = 57, + MUX65_MSG_MUX_58 = 58, + MUX65_MSG_MUX_59 = 59, + MUX65_MSG_MUX_60 = 60, + MUX65_MSG_MUX_61 = 61, + MUX65_MSG_MUX_62 = 62, + MUX65_MSG_MUX_63 = 63 +} MUX65_MSG_mux_e; + +#define MUX65_MSG_VALID_MUXSEL 0 +#define MUX65_MSG_VALID_BRANCH_0 1 +#define MUX65_MSG_VALID_BRANCH_1 2 +#define MUX65_MSG_VALID_BRANCH_2 3 +#define MUX65_MSG_VALID_BRANCH_3 4 +#define MUX65_MSG_VALID_BRANCH_4 5 +#define MUX65_MSG_VALID_BRANCH_5 6 +#define MUX65_MSG_VALID_BRANCH_6 7 +#define MUX65_MSG_VALID_BRANCH_7 8 +#define MUX65_MSG_VALID_BRANCH_8 9 +#define MUX65_MSG_VALID_BRANCH_9 10 +#define MUX65_MSG_VALID_BRANCH_10 11 +#define MUX65_MSG_VALID_BRANCH_11 12 +#define MUX65_MSG_VALID_BRANCH_12 13 +#define MUX65_MSG_VALID_BRANCH_13 14 +#define MUX65_MSG_VALID_BRANCH_14 15 +#define MUX65_MSG_VALID_BRANCH_15 16 +#define MUX65_MSG_VALID_BRANCH_16 17 +#define MUX65_MSG_VALID_BRANCH_17 18 +#define MUX65_MSG_VALID_BRANCH_18 19 +#define MUX65_MSG_VALID_BRANCH_19 20 +#define MUX65_MSG_VALID_BRANCH_20 21 +#define MUX65_MSG_VALID_BRANCH_21 22 +#define MUX65_MSG_VALID_BRANCH_22 23 +#define MUX65_MSG_VALID_BRANCH_23 24 +#define MUX65_MSG_VALID_BRANCH_24 25 +#define MUX65_MSG_VALID_BRANCH_25 26 +#define MUX65_MSG_VALID_BRANCH_26 27 +#define MUX65_MSG_VALID_BRANCH_27 28 +#define MUX65_MSG_VALID_BRANCH_28 29 +#define MUX65_MSG_VALID_BRANCH_29 30 +#define MUX65_MSG_VALID_BRANCH_30 31 +#define MUX65_MSG_VALID_BRANCH_31 32 +#define MUX65_MSG_VALID_BRANCH_32 33 +#define MUX65_MSG_VALID_BRANCH_33 34 +#define MUX65_MSG_VALID_BRANCH_34 35 +#define MUX65_MSG_VALID_BRANCH_35 36 +#define MUX65_MSG_VALID_BRANCH_36 37 +#define MUX65_MSG_VALID_BRANCH_37 38 +#define MUX65_MSG_VALID_BRANCH_38 39 +#define MUX65_MSG_VALID_BRANCH_39 40 +#define MUX65_MSG_VALID_BRANCH_40 41 +#define MUX65_MSG_VALID_BRANCH_41 42 +#define MUX65_MSG_VALID_BRANCH_42 43 +#define MUX65_MSG_VALID_BRANCH_43 44 +#define MUX65_MSG_VALID_BRANCH_44 45 +#define MUX65_MSG_VALID_BRANCH_45 46 +#define MUX65_MSG_VALID_BRANCH_46 47 +#define MUX65_MSG_VALID_BRANCH_47 48 +#define MUX65_MSG_VALID_BRANCH_48 49 +#define MUX65_MSG_VALID_BRANCH_49 50 +#define MUX65_MSG_VALID_BRANCH_50 51 +#define MUX65_MSG_VALID_BRANCH_51 52 +#define MUX65_MSG_VALID_BRANCH_52 53 +#define MUX65_MSG_VALID_BRANCH_53 54 +#define MUX65_MSG_VALID_BRANCH_54 55 +#define MUX65_MSG_VALID_BRANCH_55 56 +#define MUX65_MSG_VALID_BRANCH_56 57 +#define MUX65_MSG_VALID_BRANCH_57 58 +#define MUX65_MSG_VALID_BRANCH_58 59 +#define MUX65_MSG_VALID_BRANCH_59 60 +#define MUX65_MSG_VALID_BRANCH_60 61 +#define MUX65_MSG_VALID_BRANCH_61 62 +#define MUX65_MSG_VALID_BRANCH_62 63 +#define MUX65_MSG_VALID_BRANCH_63 64 + +#define MUX65_MSG_VALID_BYTES 9 + +typedef struct { + float MuxSel; + float Branch_0; + float Branch_1; + float Branch_2; + float Branch_3; + float Branch_4; + float Branch_5; + float Branch_6; + float Branch_7; + float Branch_8; + float Branch_9; + float Branch_10; + float Branch_11; + float Branch_12; + float Branch_13; + float Branch_14; + float Branch_15; + float Branch_16; + float Branch_17; + float Branch_18; + float Branch_19; + float Branch_20; + float Branch_21; + float Branch_22; + float Branch_23; + float Branch_24; + float Branch_25; + float Branch_26; + float Branch_27; + float Branch_28; + float Branch_29; + float Branch_30; + float Branch_31; + float Branch_32; + float Branch_33; + float Branch_34; + float Branch_35; + float Branch_36; + float Branch_37; + float Branch_38; + float Branch_39; + float Branch_40; + float Branch_41; + float Branch_42; + float Branch_43; + float Branch_44; + float Branch_45; + float Branch_46; + float Branch_47; + float Branch_48; + float Branch_49; + float Branch_50; + float Branch_51; + float Branch_52; + float Branch_53; + float Branch_54; + float Branch_55; + float Branch_56; + float Branch_57; + float Branch_58; + float Branch_59; + float Branch_60; + float Branch_61; + float Branch_62; + float Branch_63; + uint8_t valid[9]; + MUX65_MSG_mux_e mux_active; +} MUX65_MSG_t; + +bool MUX65_MSG_decode(MUX65_MSG_t* msg, const uint8_t data[], uint8_t dlc); +bool MUX65_MSG_encode(uint8_t data[], uint8_t* out_dlc, const MUX65_MSG_t* msg); + +#ifdef __cplusplus +} +#endif + +#endif // MUX65_MSG_H diff --git a/tests/oracle/CATEGORY_C_EXCEPTIONS.md b/tests/oracle/CATEGORY_C_EXCEPTIONS.md index 69fd9b4..788a332 100644 --- a/tests/oracle/CATEGORY_C_EXCEPTIONS.md +++ b/tests/oracle/CATEGORY_C_EXCEPTIONS.md @@ -40,19 +40,21 @@ Float32 rounding during the encode→decode cycle can introduce a ±1 LSB diverg **Category**: `float32_rounding` ### Exception 3 — 32-bit valid bitmask limit for messages with >32 signals -The generated `valid` bitmask was a fixed `uint32_t`. Auto-widening (B-O3, v0.3.2) now selects `uint32_t` for ≤32 signals or `uint64_t` for 33–64 signals. Messages with >64 signals emit `CodeGenError.UnsupportedFeature` at generation time. +The generated `valid` bitmask was a fixed `uint32_t`. Auto-widening (B-O3, v0.3.2) now selects `uint32_t` for ≤32 signals or `uint64_t` for 33–64 signals. A further byte-array fallback extension (2026-03-16) adds support for 65–1024 signals via `uint8_t valid[(N+7)/8]` with `sc_valid_set/clear/test()` helpers. Messages with >1024 signals emit `CodeGenError.UnsupportedFeature` at generation time. | Criterion | Status | Justification | | :--- | :--- | :--- | | Technical Limitation | PASS | Architectural choice of `uint32_t` for the `valid` field. | | Scoped Impact | PASS | Impact limited to complex industrial/heavy-duty DBCs. | -| No Feasible Alternative | **RESOLVED** | Implemented auto-widening to `uint64_t` in `Codegen.fs` (B-O3, v0.3.2). | -| Backlog Entry | PASS | Tracked under B-O3 (completed 0.3.2). | +| No Feasible Alternative | **RESOLVED** | Auto-widening to `uint64_t` (B-O3, v0.3.2) + byte-array fallback for 65–1024 signals (2026-03-16). | +| Backlog Entry | PASS | Tracked under B-O3 (completed 0.3.2); ROADMAP item #1 closed and removed per ROADMAP inclusion rules. | **Category**: `valid_mask_width` > **RESOLVED** (2026-03-13, commits `6bbe11d`, `da4f018`): Auto-widening implemented in `Codegen.fs` (B-O3). Messages with ≤32 signals use `uint32_t valid`; 33–64 signals use `uint64_t valid` + `1ULL` shift; >64 signals emit `CodeGenError.UnsupportedFeature`. Backward-compatible. +> **RESOLVED** (2026-03-16, commits `63d62a7`, `60064c9`): Byte-array fallback implemented in `Codegen.fs`. Messages with 65–1024 multiplexed signals now generate `uint8_t valid[(N+7)/8]` with `sc_valid_set()`, `sc_valid_clear()`, `sc_valid_test()` helpers from `sc_utils.h`. The threshold for `CodeGenError.UnsupportedFeature` is now >1024 signals. ROADMAP item #1 closed. + ### Exception 4 — cantools parsing incompatibility (hyundai, toyota, vw) Specific vendor DBCs contain syntax anomalies or 29-bit extended IDs that `cantools` (v41.2.1) rejects, while Signal-CANdy successfully parses and generates code for them. These files cannot be verified against `cantools`. diff --git a/tests/oracle/ORACLE_RESULTS.md b/tests/oracle/ORACLE_RESULTS.md index 324cca0..d6e7aeb 100644 --- a/tests/oracle/ORACLE_RESULTS.md +++ b/tests/oracle/ORACLE_RESULTS.md @@ -137,7 +137,7 @@ Matrix summary: **8/8 configs passed**, 4,704 passed, 0 failed, 0 skipped. 1. **DBC raw-range detection heuristic** (ROADMAP): ~~add optional heuristic to detect when `[min|max]` likely stores raw counts (e.g., offset < min, or factor makes physical range impossible). Would resolve 1,101 Category C failures.~~ **COMPLETED (B-O1)** — `isRawRangeSentinel` added in `Codegen.fs`; see `CATEGORY_C_EXCEPTIONS.md` for resolved status. 2. **Oracle multiplex mode** (ROADMAP): ~~extend `run_oracle.py` to support multi-branch signal selection; would cover the 83 currently-skipped signals.~~ **COMPLETED (B-O2)** — `_generate_mux_vectors()` in `engine.py` provides per-branch oracle testing. -3. **Valid bitmask auto-widening** (ROADMAP L-3): ~~auto-widen to `uint64_t` for messages with >32 signals.~~ **COMPLETED (B-O3)** — ≤32 uses `uint32_t`, 33–64 uses `uint64_t`, >64 returns `UnsupportedFeature`. +3. **Valid bitmask auto-widening** (ROADMAP L-3): ~~auto-widen to `uint64_t` for messages with >32 signals.~~ **COMPLETED (B-O3 + byte-array extension, 2026-03-16)** — ≤32 uses `uint32_t`, 33–64 uses `uint64_t`, 65–1024 uses `uint8_t valid[(N+7)/8]` with `sc_valid_set/clear/test()` helpers; >1024 returns `UnsupportedFeature`. 4. **CI corpus gate**: keep examples+matrix as required pass gate; track corpus adjusted pass rate (≥99%) as a trend metric. ## Reproduction Commands