이런 전화가 시작되는 전형적인 증상: SCADA가 1년 동안 ControlLogix 랙에서 태그 300개를 잘 읽어 왔는데, 누군가 HMI 하나와 리포트 서버 하나를 추가하자 세 클라이언트 모두 간헐적으로 bad quality가 뜬다. PLC 스캔 타임은 그대로다. 바뀐 것은 컨트롤러로 열린 CIP 연결의 개수이고, 아무도 적어 두지 않은 한도에 조용히 부딪힌 것이다.
EtherNet/IP는 Modbus 같은 의미의 필드버스가 아니다. ODVA가 정의한 CIP(Common Industrial Protocol)를 표준 이더넷 위에 실어 나르는 방식이다. SCADA가 Logix 태그를 읽을 때는 어떤 주소의 레지스터를 읽는 것이 아니다. 먼저 협상해 둔 CIP 연결을 통해, 심볼릭 태그의 값을 이름으로 컨트롤러에 요청하는 것이다. EtherNet/IP를 빠르게도 느리게도 만드는 모든 것은 결국 이 연결과 요청을 어떻게 구성했느냐로 돌아온다.
메시징에는 두 종류가 있고, SCADA는 하나만 쓴다
CIP에는 implicit과 explicit 메시징이 있는데, 사람들이 이 둘을 뒤섞기 때문에 구분이 중요하다.
Implicit(Class 1) 메시징은 주기적 I/O 데이터다 — 드라이브, 원격 I/O 랙, Point I/O 어댑터가 자기 입력·출력 이미지를 정해진 주기로 컨트롤러와 교환하는 트래픽이다. UDP(포트 2222)로 동작하고, connected이며, 그 주기가 바로 RPI(Requested Packet Interval)다. 이것은 PLC와 장치 사이의 트래픽이고, SCADA는 여기에 거의 참여하지 않는다.
Explicit(Class 3) 메시징은 요청/응답이다: "Tank_101_Level 값을 달라." TCP(포트 44818)로 동작하며, SCADA의 EtherNet/IP 드라이버가 실제로 하는 일이 이것이다. connected(Forward Open으로 드라이버가 재사용하는 Class 3 연결을 맺는 방식)일 수도, unconnected(각 요청이 독립적이고 Unconnected Message Manager로 라우팅되는 방식)일 수도 있다.
실무 규칙: 같은 태그 목록을 꾸준히 폴링한다면 드라이버가 connected Class 3 메시징을 쓰게 해야 한다. Unconnected 메시징은 시운전 중 일회성 읽기에는 괜찮지만, 폴링 전략으로 쓰면 매 요청마다 라우팅 오버헤드를 다시 치르고 재사용할 것이 하나도 남지 않는다.
진짜 한도는 연결 예산이다
대역폭이나 스캔 타임보다 결국 더 문제가 되는 숫자가 이것이다: Logix 통신 모듈은 CIP 연결 풀이 유한하다. 흔히 쓰는 ControlLogix 이더넷 모듈인 1756-EN2T는 CIP 연결 256개, TCP/IP 연결 128개를 지원한다. 이 자원은 모든 것이 나눠 쓴다 — 컨트롤러 간 produced/consumed 태그, 드라이브, 원격 I/O, 그리고 Class 3 연결을 열어 둔 모든 HMI·SCADA 클라이언트까지.
이 예산을 고려하지 않고 클라이언트를 늘리면 완만한 성능 저하가 오는 게 아니다. Forward Open 거부가 난다. 기존 연결은 멀쩡히 동작하는데 새 연결만 실패하니, 문제가 "컨트롤러가 꽉 찼다"가 아니라 "세 번째 클라이언트가 고장 났다"처럼 보이는 것이다. 네트워크 탓을 하기 전에 모듈의 웹 진단이나 컨트롤러 오거나이저에서 연결 개수부터 확인하라.
연결이 유한하다는 데서 두 가지가 따라온다:
- 연결을 조금만 쥐고 태그를 많이 읽는 SCADA 서버 하나가, 각자 연결을 쥐는 여러 클라이언트보다 훨씬 낫다. 가능하면 하나의 OPC/드라이버 서버로 읽기를 모으고, 하위 클라이언트는 PLC가 아니라 그 서버에서 읽게 하라.
- 독립된 SCADA 노드 3개, 리포트 도구, RSLinx가 뜬 엔지니어 노트북을 같은 EN2T에 모두 겨누고 다 받아 주길 기대하지 마라.
원자 태그 200개 말고 배열을 읽어라
가장 큰 성능 실수는 개별 원자 태그 200개를 구성해 놓고 드라이버가 CIP 읽기를 200번 따로 날리게 두는 것이다. 읽기 하나가 요청/응답 왕복 하나다. 컨트롤러는 다 답해 주지만, 태그마다 지연 시간을 치르게 된다.
CIP에는 빠져나갈 길이 두 개 있고, 좋은 드라이버는 둘 다 쓴다:
- **Multiple Service Packet(서비스 0x0A)**은 여러 태그 읽기를 하나의 CIP 요청으로 묶는다. 왕복 50번 대신, 임베디드 읽기 서비스 50개를 담은 요청 하나를 날린다. 웬만한 Logix 드라이버는 이걸 자동으로 하며, 눈에 보이는 설정은 보통 "요청당 태그 수"나 "읽기 최적화"다.
- 멤버 말고 집합체를 읽어라. 원하는 데이터가 UDT나 배열에 들어 있다면, Read Tag Service 한 번으로 구조 전체를 읽고 멤버는 드라이버에서 풀어라.
Reactor[0]부터Reactor[49]까지 배열 한 번으로 읽는 것이, 스칼라 50번 읽기보다 압도적으로 싸다. 태그 참조 하나가 블록 전체를 돌려주기 때문이다.
설계상의 함의는 PLC 프로그램까지 거슬러 올라간다: 어떤 값 묶음을 SCADA가 긁어 갈 것을 안다면, 컨트롤러 쪽에서 그것을 연속된 배열이나 UDT로 묶어라. 흩어진 평면 태그 데이터베이스는 드라이버를 원자 읽기로 몰아넣고, 어떤 튜닝으로도 못 고친다. 이것은 Modbus 폴 블록 배치의 EtherNet/IP 사촌 격이다 — 전송 효율은 드라이버 설정이 아니라 메모리 배치가 결정한다.
500바이트 벽
unconnected CIP 메시지, 그리고 표준 Forward Open 연결은 페이로드가 약 504바이트로 제한된다. Multiple Service Packet 하나에 태그를 너무 많이 묶거나 너무 큰 배열 하나를 읽으면 응답이 이 한도를 넘는다. 컨트롤러는 CIP 오류로 답한다 — 흔히 0x06, "Reply data too large", 또는 부분 전송이다.
해결책 두 가지:
- 드라이버가 요청을 쪼개게 하라. 좋은 드라이버는 한도를 감지해 큰 묶음을 여러 요청으로 자동 분할한다. 안 되면 요청당 태그 수를 수동으로 낮춰라.
- 수 킬로바이트짜리 UDT를 한 번에 읽는 것처럼 정말로 큰 구조를 옮겨야 하면 Large Forward Open(확장 연결 크기의 connected 메시징)을 써라. 이건 드라이버/모듈의 기능이지 모든 장치가 지원하는 것이 아니다 — 의존하기 전에 확인하라.
어떤 태그인지가 아니라 한 번에 몇 개 태그가 바뀌었는지와 상관관계가 있는 간헐적 실패가 보이면, 네트워크보다 이 크기 벽을 먼저 의심하라.
태그 스코프가 오전 하나를 날린다
Logix 태그에는 스코프가 둘이고, 처음 겪는 사람 거의 전부가 여기서 걸린다. 컨트롤러 스코프 태그는 전역이라 SCADA 클라이언트가 이름으로 바로 참조한다. 프로그램 스코프 태그는 특정 프로그램에 속하고, 전체 경로로 주소를 지정하지 않으면 보이지 않는다: Motor_Run이 아니라 Program:MainProgram.Motor_Run이다.
프로그램 스코프 태그를 전역인 양 구성하면 읽기가 오타처럼 보이는 경로/세그먼트 오류로 실패한다. 컨트롤러에 분명히 있는 태그가 not found로 돌아오면, 철자보다 스코프를 먼저 확인하라. 습관을 들이자면, SCADA가 필요로 하는 것은 컨트롤러 스코프로 노출하거나, 최소한 태그 데이터베이스에서 Program: 접두어를 일관되게 표준화하라.
RPI는 당신 설정이 아니다 — 어느 순간부터는 맞다
순수 Class 3 폴링에서는 RPI가 없다. 폴링 주기는 다른 드라이버와 똑같이 SCADA 스캔 클래스에 설정한 값이고, 값이 실제로 얼마나 빨리 변하는지에 맞춰야 한다 — 빠른 인터록은 1초 미만, 탱크 레벨은 몇 초. 할 수 있다고 전부 100 ms로 폴링하지 마라.
RPI는 SCADA가 produced/consumed 태그를 쓰거나 주기적 데이터를 받는 순간 다시 삶에 들어온다 — 어떤 구성에서는 컨트롤러가 데이터 구조를 produce 하고, 게이트웨이나 소프트 SCADA 노드가 Class 1 연결로 그것을 consume 한다. 그때는 RPI가 적용되고 연결 타임아웃 배수가 딸려 온다: 패킷이 없을 때 RPI × 배수가 지나면 연결을 죽은 것으로 선언하는데, 배수는 보통 4, 8, 16에서 최대 512까지다. RPI 100 ms에 배수 4면 400 ms 동안 조용하면 연결이 끊긴다. 지터가 가끔 있는 링크에서 그렇게 빡빡한 배수는 연결이 튄다. 정상적인 네트워크에서 주기적 연결이 자꾸 끊기면 RPI를 늦추기 전에 배수를 먼저 완화하라.
읽기가 나빠질 때 확인할 것
- 새 클라이언트만 실패하고 기존은 멀쩡 → 이더넷 모듈의 연결 예산. 추측 말고 CIP 연결 개수를 세라.
- 한 묶음의 태그가 통째로 bad로 가고 다른 건 멀쩡 → 잘못된 태그 참조(스코프 오류, 이름 변경) 하나가 Multiple Service Packet 전체를 실패시키거나, 묶음이 약 504바이트를 넘긴 것. 요청당 태그 수를 줄여 원인을 좁혀라.
- 온라인에서 보이는 태그인데 "Tag not found" → 프로그램 스코프.
Program:경로를 붙여라. - 읽기가 느리지만 깨끗하고 PLC 스캔은 그대로 → 원자 읽기가 너무 많다. 배열/UDT 읽기로 바꾸고 요청 최적화를 켜라.
- 주기적/produced 태그 연결이 튄다 → RPI 자체가 아니라 링크 지터에 비해 타임아웃 배수가 너무 빡빡한 것.
곤란을 피하게 해 주는 사고 모델: EtherNet/IP는 바이트가 아니라 왕복 횟수와 열린 연결 수로 요금을 매긴다. SCADA가 한 요청으로 많이 읽을 수 있게 컨트롤러 태그 배치를 설계하고, 아키텍처가 허용하는 한 연결을 적게 쥐면 프로토콜은 잘 확장된다. 태그 하나하나로 싸우면 누군가 네 번째 클라이언트를 붙이는 날 다시 전화기를 잡게 된다.