나를 다시 현장으로 부르는 건 대개 잘못된 register map이 아니다. Port 502의 PLC에 다섯 번째로 붙은 무언가다. Bench test는 잘 됐다 — 노트북 한 대가 장비 한 대를 읽었으니까. 그다음 HMI server, standby HMI, historian, 켜 둔 채 잊어버린 engineering 노트북, VPN 위의 vendor 진단 tool이 전부 같은 주소를 가리키고, 이제 operator는 멈춘 데이터를 보며 network 탓을 한다.
Modbus TCP는 이 문제를 숨긴다. MBAP header에는 transaction identifier가 있어 socket 하나로도 여러 요청을 동시에 띄울 수 있고, 그냥 두면 client는 poll마다 새 socket을 열기도 한다. 그런데 server가 socket을 몇 개까지 참아줄지는 protocol 어디에도 없다. 그건 장비 구현에 달린 값이고, 대개 사람들이 짐작하는 것보다 작다. 저가 전력계량기나 serial-to-Ethernet gateway는 동시 연결을 1~4개로 막는 경우가 흔하고, 큰 숫자를 광고하는 PLC Ethernet module도 뒤에서는 요청을 하나씩 처리하는 경우가 많다.
Tag보다 socket을 먼저 센다
Tag list는 connection 압박을 전혀 알려주지 않는다. Register를 보기 전에 나는 누가 socket을 여는지부터 적는다.
| Client | 왜 연결하나 | 무엇이 물리나 |
|---|---|---|
| Primary SCADA server | 화면, alarm, operator write | 항상 예측 가능하게 연결돼 있어야 함 |
| Standby SCADA server | Failover | Active-active면 request rate가 두 배 |
| Historian interface | 시계열 수집 | SCADA가 이미 가진 register를 다시 읽음 |
| Engineering 노트북 | Online troubleshooting | 부족한 slot을 잡고는 꽂힌 채 방치됨 |
| MES / report connector | 생산 데이터 | 인수 몇 달 뒤, 도면에 없던 채로 추가됨 |
| Vendor remote tool | Service, 진단 | 불안정한 VPN 위에서 공격적으로 reconnect |
각 client마다 source IP, software, polling 역할, 그리고 사람들이 빠뜨리는 칸 — write 허용 여부 — 를 적는다. 시운전 때 종이에 이걸 해 두면, 나중에 세 번째 socket을 누가 열었는지 아무도 모르는 말싸움을 하지 않는다.
Datasheet는 "Modbus TCP 지원"이라 쓰고 끝난다
그 한 줄은 용량 계획에 아무 의미가 없다. 시운전 때 나는 이런 것을 실제로 시험한다.
- 동시에 받아들이는 TCP 연결 수.
- 한계에 닿았을 때의 동작 — 새 연결을 거절하는가, 아니면 조용히 가장 오래된 연결을 끊는가? "마지막 연결이 이긴다"식 축출이 가장 고약하다. 방금 꽂은 tool이 잘 돌던 HMI를 걷어차기 때문이다.
- 요청을 병렬로 처리하는가, 내부에서 순차로 처리하는가. Socket 8개를 받으면서도 PDU는 한 번에 하나씩만 처리할 수 있다.
- 비정상 단절 뒤 half-open socket이 얼마나 남는가. Application idle timeout이 아니라 TCP keepalive에 의존하면, 뽑힌 cable이 slot 하나를 몇 분 동안 붙잡는다.
- 아무 연결에서 온 write든 소유권 규칙 없이 받아들이는가. 저가 장비 대부분은 누가 보내든
0x06write를 그냥 받는다.
그리고 gateway에서 다들 잊는 하나 — MBAP header의 Unit Identifier가 bridge가 뒤쪽 serial slave로 라우팅하는 방식이다. Ethernet client 10개가 깔끔하게 붙고도 결국 19200 baud RS-485 한 선에 몰릴 수 있다. TCP는 정상처럼 보이는데 serial 쪽은 timeout이 쌓인다. 여기서 계획하는 건 Ethernet socket이 아니라 two-wire bus 하나의 airtime다.
데이터에 소유자를 하나 준다
Operator가 실제로 보는 데이터라면 나는 authoritative poller 하나를 원한다. SCADA server가 PLC를 읽고, historian·화면·report는 그 데이터를 SCADA platform이나 OPC UA 계층을 통해 받는다 — PLC에 직접 socket을 열지 않는다. 잘 이해된 client 하나와 난장판의 차이다.
여러 system이 직접 polling해도 되지만, 아래가 전부 성립할 때만이다.
- 장비가 합산 request rate를 bench가 아니라 부하 상태에서 버틴다는 게 증명됨.
- 같은 holding register 값을 세 번씩 사 오지 않도록 read 범위를 조정함.
- Write 권한은 정확히 하나의 system — operator command를 소유한 system — 에만 있음.
- Maintenance tool은 troubleshooting 중일 때만 연결하고 끝나면 끊음. "다시 붙기 귀찮으니 켜 둘게"가 네 번째 slot이 사라지는 경로다.
Historian이 정말 직접 polling해야 한다면 느린 scan group에 둔다 — sub-second가 아니라 몇 초. Background report 수집기가 command feedback·alarm과 같은 socket 예산을 두고 경쟁할 이유는 없다.
Modbus Messaging on TCP/IP Implementation Guide(v1.0b, Modbus.org)는 client가 server에 TCP 연결 하나를 열어 두고 여러 transaction에 재사용하라고 — 요청마다 연결을 새로 열지 말라고 — 명시한다. Poll마다 socket을 열고 닫는 stack은 connection table을 태우고 TIME_WAIT socket을 사방에 남긴다. 그런 stack을 물려받으면 그것만으로도 connection limit처럼 보인다.
Redundancy는 조용히 부하를 두 배로 만든다
Redundant SCADA에서 request rate가 슬그머니 올라온다. Active-active는 두 node가 모든 장비를 항상 읽는다 — failover는 빠르지만 traffic이 두 배다. Warm standby는 traffic을 줄이지만 primary가 죽을 때 충분히 빨리 올라오는지 증명해야 한다. Cold standby는 가장 많이 아끼지만 하필 failover 도중에 connection·cache 버그를 드러내는 경향이 있다.
무엇을 고르든 실제 장비 수로 시험한다. PLC 한 대 bench redundancy test는 gateway 50대짜리 line에 대해 아무것도 말해 주지 않는다. 정작 중요한 실패 방식 — socket table이 꽉 차는 것 — 은 규모가 커야 나오기 때문이다.
Network 장애처럼 보이지만 아닐 때
Connection limit 문제는 불안정한 Ethernet로 위장하길 좋아한다. 알아채는 신호들:
- 새 client가 붙는 순간 기존 HMI가 곧바로 bad quality로 간다.
- Historian service를 끄면 그제야 engineering software가 잘 된다.
- Gateway 앞쪽 몇 장비는 응답하고, 어느 지점부터는 전부 timeout이 난다.
- Cable을 뽑으면 장비가 몇 분 동안 새 연결을 거절한다.
- Packet capture에서 TCP handshake는 끝나는데 Modbus response가 전혀 없고 — 중요한 건 exception code도 없다. 요청이 application 처리까지 못 가서 code를 만들 수조차 없기 때문이다.
이런 게 보이면 scan rate나 retry를 올리지 마라. 그게 본능이고, 경쟁만 더 심해진다. 이미 여유가 없는 장비에 요청을 더 얹는 일이다.
인수 전 drill
실제 client로, 실제 운전 상태로, 사인하기 전에 돌린다.
- 계획된 모든 client와 source IP를 적는다.
- 운전 때와 같은 조합으로 전부 연결한다.
- PLC·gateway·firewall 중 볼 수 있는 곳에서 current connection count를 읽는다.
- Historian이나 report connector를 켜고 SCADA quality를 본다 — 무엇이 깜빡였나?
- Engineering tool을 열어도 HMI socket이 밀려나지 않는지 확인한다.
- Client cable을 뽑고 stale socket이 정리되는 시간을 잰다.
- Failover를 돌리고 늘어난 request rate를 측정한다.
- Write 권한이 없어야 할 경로에서 write를 시도해 막히는지 확인한다.
결과는 project 파일에 남긴다. 나중에 report connector를 붙이려는 사람은 원래 용량과 소유권 모델이 무엇이었는지 알아야 한다 — 모르면, 전화는 당신에게 온다.