패킷 흐름
접속 → 설정 → 수집 시작 → 스트리밍 → 종료까지의 전형적 시나리오.
모든 명령 MSG는 ACK가 따라온다.
케이스 1. 정상 접속 및 수집 시작
SW Device
│ TCP connect (:31024) ───────────────────────────▶ │
│ │
│ SCOPE_SET_PACKET ──────────────────────────────▶ │ (선택, 설정 동기화)
│ ◀──── MSG(MSG_SCOPE_SET_ERROR, error=0) ACK │
│ │
│ PQN_SET_PACKET ──────────────────────────────▶ │
│ ◀──── MSG(MSG_PQN_SET_ERROR, error=0) ACK │
│ │
│ HW_SET_PACKET ──────────────────────────────▶ │
│ ◀──── MSG(MSG_HW_SET_ERROR, error=0) ACK │
│ │
│ MSG(MSG_SEND_DATA_START) ────────────────────────▶ │
│ ◀──── MSG(MSG_SEND_DATA_START) ACK │
│ │
│ ◀──────────────── SCOPE_DATA_PACKET (TRIG 즉시) │
│ ◀──────────────── PQN_DATA_PACKET (사이클당) │
│ ◀──────────────── ... 스트리밍 계속 ... │
실무상 SW는 접속 직후 SCOPE_SET → PQN_SET → HW_SET를 먼저 보내
장비 설정을 동기화한 뒤 START. PDF는 START 명령만 정의했지만
SET 패킷의 존재 자체가 이 시퀀스를 함의함.
케이스 2. Keep-alive
SW Device
│ 3s tick │
│ MSG(MSG_ALIVE) ───────────────────▶ │
│ ◀──────── MSG(MSG_ALIVE) ACK │
│ ...반복... │
Rev 4 에 추가된 단순 살아있음 확인. 페이로드 없음, 같은 msg 코드로 응답.
케이스 3. 정상 종료
SW Device
│ MSG(MSG_SEND_DATA_STOP) ───────────────────▶ │
│ ◀──── MSG(MSG_SEND_DATA_STOP) ACK │
│ (이후 SCOPE/PQN 스트림 중단) │
│ TCP close ──────────────────────────────▶ │
STOP 보낸 후 짧게 드레인 뒤 TCP close. ACK 받기 전 끊으면 장비 측에
이전 세션 잔여가 남을 수 있음(펌웨어 의존).
케이스 4. SET 명령 + 에러 응답
SET 패킷은 항상 대응 MSG로 ACK 됨. 에러 없으면 error=0,
있으면 error 필드에 코드 + 채널 오프셋.
SW Device
│ SCOPE_SET_PACKET (3번 채널 ch_on=잘못된값) ────▶ │
│ ◀──── MSG(MSG_SCOPE_SET_ERROR, error=10023) │
│ │ │
│ └─ 디코드: base=10020 (CH_ON_INVALID) │
│ ch_offset=3 (3번 채널) │
케이스 5. NET_CFG 조회 / 설정
SW Device
│ MSG(MSG_NET_CFG_INFO) ────────────────────────────▶ │
│ ◀──── MSG(MSG_NET_CFG_INFO, error=0) │
│ padding[0..5]=MAC, padding[6..9]=IP │
│ │
│ MSG(MSG_NET_CFG_SET) padding[0..9]=새 MAC+IP ──────▶ │
│ ◀──── MSG(MSG_NET_CFG_SET, error=0) 정상 │
│ 또는 │
│ ◀──── MSG(_, error=90011) b 옥텟 잘못 │
│ ◀──── MSG(_, error=90020) HW 변경실패 │
케이스 6. SELF_SYNC_ENABLE
SW Device
│ MSG(MSG_SELF_SYNC_ENABLE) ─────────────────────────────▶ │
│ padding[0..3] = 'T','-','9','0' (84,45,57,48) │
│ padding[4] = 1 (ON) 또는 0 (OFF) │
│ │
│ ◀──── MSG(_, error=0) 정상 ON/OFF │
│ ◀──── MSG(_, error=100100) magic 불일치 (DENIED)
│ ◀──── MSG(_, error=100110) padding[4] 0/1 아님
│ ◀──── MSG(_, error=100120) sync_freq 불일치 (제조사 문의)
케이스 7. REBOOT
SW Device
│ MSG(MSG_REBOOT) ───────────────────────▶ │
│ ◀──── MSG(MSG_REBOOT) ACK │
│ │
│ ◀──── (TCP close, 펌웨어 재시작) │
│ 잠시 후 SW가 재접속 시도 │
케이스 8. 비정상 단절
SW Device
│ ◀────×──── (network drop / power off / 펌웨어 hang)
│ (read EOF) │
│ → "disconnect" 이벤트 emit │
│ → 재접속 정책에 따라 dial 재시도 │
장비가 STOP 받기 전에 끊긴 경우, 재접속 시점에 이전 세션
streaming 잔여가 흘러나올 수 있음 (펌웨어 의존).
SCOPE vs PQN 도착 타이밍
두 스트림은 같은 시각의 사이클이라도 도착 시각이 다르다.
- SCOPE: TRIG 발생 즉시 전송 (낮은 지연)
- PQN:
buffer_size 만큼 모은 후 sync 정렬해 묶음 전송 (지연 ≈ buffer_size/256 주기)
그래서 cycle_index 가 SCOPE=80 일 때 PQN=75 같은 차이가 정상.
SW 에서 두 스트림 매칭은 cycle_index 키로 정렬해서.
phase wrap-around
SCOPE_DATA_PACKET.phase 는 sync 주파수에 따라 다음 최대값
도달 후 다음 사이클 진입하면서 wrap.
| sync_freq | phase 최대값 | wrap 예시 (PDF) |
| 60 Hz | 1,666,666 | 1,656,738 → 4,979 |
| 120 Hz | 833,333 | 831,189 → 902 |
일반식: phase_max ≈ 1e8 / sync_freq (10ns 단위로 한 주기).