← 전체 글
SECS/GEM/약 13분 읽기/— 조회

SELECTED는 통신 중이라는 뜻이 아니다: S1F13 전에 보낸 요청은 SxF0로 돌아온다

HSMS는 SELECTED인데 S1F17이 S1F0로 돌아온다. 같은 소켓에서 찍은 실제 캡처로 E37의 연결 상태와 E30의 통신 상태를 가른다.

SECS/GEMMESSCADA문제 해결체크리스트

호스트 화면에는 SELECTED가 떠 있다. Select.rsp는 4ms 만에 돌아왔고, Linktest도 계속 답한다. 그런데 S1F17을 던지면 S1F18이 아니라 01 00 — Stream 1 Function 0이 돌아온다. 운이 나쁘면 아무것도 안 오고 T3만 태운다. 둘 다 같은 원인이다. HSMS 연결 상태와 GEM 통신 상태는 서로 다른 상태 기계이고, 앞의 것이 끝났다고 뒤의 것이 시작된 건 아니다.

같은 소켓에서의 네 번의 요청 — Select.req는 Select.rsp로 4ms에 돌아오고, S1F13 전에 보낸 S1F17은 S1F0 abort로 돌아오며, S1F13에 S1F14 COMMACK 0이 온 뒤에야 같은 S1F17이 S1F18로 답을 받는다 Host장비 Select.req Select.rsp · 4ms · SELECTED S1F13 · 그 다음 S1F17 S1F14 COMMACK 0 · 그 다음 S1F18 S1F17 · S1F13 전에 S1F0 abort · 1ms NOT COMMUNICATING

같은 소켓, 여섯 번의 요청

passive 리스너에 직접 소켓을 열고 호스트 역할을 했다. 왕복 시간은 호스트 쪽에서 측정한 값이다.

TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 05 01
RX Select.rsp   00 00 00 0A 00 0B 00 00 00 02 00 00 05 01        (4ms)
   → 장비 상태: hsmsState SELECTED / commState NOT COMMUNICATING

TX S1F17        00 00 00 0A 00 0B 81 11 00 00 00 00 05 02
RX S1F0         00 00 00 0A 00 0B 01 00 00 00 00 00 05 02        (1ms)

TX S2F13        00 00 00 0C 00 0B 82 0D 00 00 00 00 05 03 01 00
RX S2F0         00 00 00 0A 00 0B 02 00 00 00 00 00 05 03        (17ms)

TX S1F13        00 00 00 1A 00 0B 81 0D 00 00 00 00 05 04
                01 02 41 07 48 4F 53 54 4D 45 53 41 03 32 2E 31
RX S1F14        00 00 00 37 00 0B 01 0E 00 00 00 00 05 04
                01 02 21 01 00 01 02 41 15 56 58 2D 39 30 30 30
                20 50 6C 61 73 6D 61 20 45 74 63 68 65 72 41 0D
                53 45 43 53 47 45 4D 2D 31 2E 34 2E 31           (11ms)
   → 장비 상태: hsmsState SELECTED / commState COMMUNICATING

TX S1F17        00 00 00 0A 00 0B 81 11 00 00 00 00 05 05
RX S1F18        00 00 00 0D 00 0B 01 12 00 00 00 00 05 05 21 01 00  (2ms)

TX Linktest.req 00 00 00 0A 00 0B 00 00 00 05 00 00 05 06
RX Linktest.rsp 00 00 00 0A 00 0B 00 00 00 06 00 00 05 06        (3ms)

같은 S1F17이다. SystemBytes만 05 02에서 05 05로 바뀌었다. 첫 번째는 Function 0으로 끊기고 두 번째는 S1F18로 답을 받았다. 그 사이에 바뀐 것은 단 하나, S1F13 / S1F14 교환이다.

Linktest를 맨 뒤에 둔 이유가 있다. 앞의 abort가 "소켓이 죽어서"가 아니라는 걸 증명해야 하기 때문이다. 3ms에 왕복했다. 소켓도 프로세스도 멀쩡하다.

abort 헤더를 바이트로 뜯으면

요청과 응답을 나란히 놓으면 Function 0의 정체가 보인다.

TX 00 00 00 0A 00 0B 81 11 00 00 00 00 05 02
                     ^^ ^^                        Byte 2 = 81, Byte 3 = 11
                     W-bit 1 + Stream 1 / Function 17

RX 00 00 00 0A 00 0B 01 00 00 00 00 00 05 02
                     ^^ ^^                        Byte 2 = 01, Byte 3 = 00
                     W-bit 0 + Stream 1 / Function 0

Stream은 그대로 1이고 Function만 0이다. SystemBytes 00 00 05 02도 요청 그대로다. 바디는 길이 10 — 헤더뿐이다. S2F13에 돌아온 것도 같은 모양이다. 02 00, 즉 Stream 2 Function 0.

SECS-II(SEMI E5)에서 Function 0은 그 트랜잭션을 버린다는 뜻이다. 거절 코드가 아니다. 왜 못 하는지를 안 알려준다. 그래서 호스트 로그에 S1F0만 남으면 원인은 바이트에 없고, 상태에 있다. 이 패턴만 다룬 글이 따로 있다 — S1F0가 먼저 오는 통신 게이트.

W-bit가 내려간 것도 중요하다. 응답이므로 답을 요구하지 않는다. 요청에 W-bit를 안 세우고 답을 기다리는 반대쪽 버그는 헤더 바이트 2에서 다뤘다.

S1F14가 실제로 뭘 돌려줬나

01 02                 L[2]
  21 01 00            B[1] 0x00        ← COMMACK = 0, 수락
  01 02               L[2]
    41 15 56 58 ...   A[21] 'VX-9000 Plasma Etcher'   ← MDLN
    41 0D 53 45 ...   A[13] 'SECSGEM-1.4.1'           ← SOFTREV

포맷 코드 두 개만 알면 읽힌다. 21은 Binary 1바이트, 41은 ASCII이고 뒤의 바이트가 길이다 — 41 15면 21바이트. 이 계산은 아이템 헤더의 길이 바이트 쪽이 자세하다.

COMMACK이 0이면 수락이다. 0이 아니면 거부인데, 값별 의미는 E30의 표와 장비 매뉴얼에서 확인해야 한다 — 나는 실제 장비에서 0이 아닌 COMMACK을 받아본 캡처가 없다. 여기 리스너도 항상 0을 돌려준다.

그리고 S1F14를 받은 그 순간 장비의 commState가 NOT COMMUNICATING에서 COMMUNICATING으로 넘어갔다. 호스트가 "온라인"을 켜야 하는 지점이 여기다. Select.rsp가 아니다.

상태 기계가 두 개다

SEMI E37(HSMS-SS)이 관리하는 건 연결이다. NOT CONNECTED에서 시작해 TCP가 붙으면 CONNECTED, Select가 끝나야 SELECTED가 된다. 데이터 메시지(SType 0)는 SELECTED에서만 흐를 수 있다. 여기까지가 E37이 책임지는 전부다.

SEMI E30(GEM)의 통신 상태는 그 위에 따로 있다. 장비가 통신을 허용하는 상태인지(DISABLED / ENABLED), 그리고 호스트와 실제로 통신 중인지(NOT COMMUNICATING / COMMUNICATING)를 구분한다. NOT COMMUNICATING에서 COMMUNICATING으로 넘기는 열쇠가 S1F13 / S1F14 교환이고, 그 전에는 S1F13 외의 어떤 데이터 메시지도 통과하지 못한다. 위 캡처에서 S1F17과 S2F13이 Function 0으로 돌아온 게 그 게이트다.

그래서 SELECTED는 "소켓과 세션이 열렸다"까지만 말해준다. MES 화면에 SELECTED를 "온라인"으로 그려놓은 시스템은 통신 확립 전의 장비를 초록불로 그린다. 그 상태로 S2F41을 던지면 S2F0가 돌아오고, 운전원은 "명령이 씹혔다"고 말한다.

무응답과 SxF0는 호스트에게 다른 사건이다

같은 원인이라도 호스트 쪽에서 보이는 모양은 둘로 갈린다.

장비 동작호스트에서 보이는 것걸리는 시간
SxF0로 abort트랜잭션 즉시 실패왕복 시간 (여기선 1–17ms)
아무 응답 없음T3 만료T3 값 — E37 기본값 45초

E37의 파라미터 표가 T3(Reply Timeout)에 주는 기본값은 45초, 범위는 1–120초다. 그리고 SxF0는 E5에서 엄연한 응답이라 받는 순간 T3가 멈춘다. 그러니 abort를 주는 장비 쪽이 훨씬 친절하다. 둘을 같은 "S1F17 실패" 로그 한 줄로 뭉개는 호스트 스택이 여기서 시간을 다 버린다. 타이머 구분은 어느 HSMS 타임아웃이 터졌는지 가르는 법에 정리해 뒀다.

이 캡처가 증명하지 않는 것

이 리스너는 데이터 메시지에 항상 답한다. 그래서 위 캡처는 게이트의 abort 경로만 보여주고, 실제 장비에서 흔한 완전 무응답은 이 도구로 만들 수 없다. 두 번째 줄의 침묵을 재현하려면 장비가 S1F13을 그냥 버려야 하는데, 그 동작은 여기 없다.

COMMACK이 0이 아닌 S1F14도 못 받아봤다. 거부 경로는 이 캡처 밖이다.

캡처가 증명하는 건 세 가지다. 바이트가 실제 소켓을 타고 장비 쪽 RX 로그에 찍혔다는 것, SELECTED 상태에서도 S1F13 전의 데이터 메시지가 Function 0으로 끊긴다는 것, 그리고 S1F14 하나로 같은 요청이 통과한다는 것.

호스트에서 정리할 것

  • SELECTED를 통신 확립으로 취급하지 않는다. 온라인 판정은 S1F14의 COMMACK을 읽은 뒤다.
  • 로그에 SxF0를 받은 사실을 남긴다. Function 0은 "타임아웃"이 아니라 "거절당했다"에 가깝다. 두 줄을 구분해서 찍어라.
  • S1F13에 답이 없다고 T3 주기로 무한 재시도하지 않는다. GEM은 통신 확립 재시도에 별도 지연을 두는 쪽이고, 그 지연은 장비 상수로 노출되는 경우가 많다. 값과 이름은 장비 매뉴얼에서 확인해라.
  • Linktest 성공은 링크만 증명한다. 감시 화면에 이것만 걸어두면 Select도 안 끝난 세션까지 초록불로 보인다.
  • 장비 쪽 RX 로그부터 본다. 바이트가 도착조차 안 했으면 이건 GEM 문제가 아니라 프레이밍 문제다.

여기서 시간 제일 많이 버리는 지점은 따로 있다. 호스트 로그에 "T3 timeout"만 남기고 어떤 메시지가 죽었는지 안 적는 스택이다. S1F13이 죽은 것과 S6F11이 죽은 것은 완전히 다른 사건인데, 로그만 보면 구별이 안 된다.

재현

캡처는 SECS/GEM 시뮬레이터의 passive 리스너에 소켓을 직접 열어서 만들었다. 순서가 핵심이다 — S1F13 전에 아무 데이터 메시지나 하나 던져보고, 돌아온 Byte 3이 00인지 확인해라. 00이면 바이트를 더 볼 필요가 없다. 게이트다.