← 전체 글
Modbus/약 11분 읽기/— 조회

매핑 안 된 레지스터 하나가 Modbus 블록 읽기 전체를 실패시킨다

SCADA 드라이버가 Modbus 레지스터를 블록으로 묶는 방식, gap tolerance와 블록 크기, 매핑 안 된 주소 하나가 블록 전체를 무너뜨리는 이유.

ModbusSCADA네트워킹문제 해결

블록 읽기가 중요한 이유

태그 400개짜리 Modbus 맵이 세 번에 한 번씩 타임아웃 난다면 보통 PLC가 느려서가 아니다. 드라이버가 다섯 번이면 될 것을 한 레지스터씩 400번 나눠 읽고 있어서다. Modbus에는 구독 모델이 없다. SCADA 드라이버는 폴링을 해야 하고, 폴링 한 번은 회선 위의 요청 하나와 응답 하나다. 태그 400개를 한 번에 한 레지스터씩 읽으면 스캔마다 400 트랜잭션이다. 같은 400개를 몇 개의 연속 블록으로 읽으면 네다섯 트랜잭션이 된다. 데이터는 같지만 PLC와 게이트웨이, 시리얼 회선에 걸리는 부하는 다르다.

폴 블록 최적화는 어떤 레지스터를 하나의 function code 요청으로 함께 읽을지 정하는 작업이다. 잘하면 스캔 시간이 줄고 타임아웃이 사라진다. 무작정 하면 느린 장치에 수백 개의 작은 요청을 던지거나, SCADA가 화면에 쓰지도 않는 큰 구간을 통째로 읽게 된다.

이것은 폴 주기나 타임아웃 튜닝과는 다른 문제다. 타임아웃이 맞아도 드라이버가 태그마다 요청 하나씩 던지면 맵은 여전히 느리고 비효율적이다.

드라이버가 블록을 정하는 방식

대부분의 SCADA Modbus 드라이버는 설정한 태그 주소로부터 블록을 자동으로 만든다. 보통 규칙은 이렇다.

  • function code와 주소 범위별로 묶는다(holding register, input register, coil, discrete input은 서로 다른 공간이다).
  • 같은 공간에서 인접하거나 가까운 주소는 하나의 요청으로 합친다.
  • 한 요청의 최대 레지스터 수를 넘으면 그룹을 나눈다.
  • 태그 사이 주소 간격이 크면 그룹을 둘로 쪼갠다.

보통 만질 수 있는 값은 최대 블록 크기(요청당 레지스터 수)와 gap tolerance(두 태그를 같은 블록에 두려고 드라이버가 읽어줄 미사용 레지스터 수)다. 이 두 설정만 이해하면 블록 동작 대부분이 설명된다.

빈 구간 트레이드오프

빈 구간을 넘어 읽는 것은 거래다. 태그 A가 레지스터 100에 있고 태그 B가 140에 있으면 드라이버는 둘 중 하나를 한다.

  • 작은 요청 두 개(100과 140)를 던지거나,
  • 100부터 140까지 한 요청으로 읽으면서 쓰지 않는 레지스터 39개를 함께 읽거나.

큰 요청 하나가 왕복 두 번보다 거의 항상 싸다. 특히 turnaround time과 silent interval이 지배적인 시리얼 Modbus RTU에서 그렇다. 그래서 gap tolerance 40~50 정도는 이득인 경우가 많다. 하지만 tolerance를 너무 높이면 드라이버가 빈 레지스터 수백 개를 읽고, 어떤 PLC는 매핑되지 않은 레지스터를 읽을 때 exception(illegal data address)을 돌려주며 블록 전체를 실패시킨다.

바로 이 지점이 함정이다. 정상적인 블록 안에 매핑 안 된 주소 하나만 있어도 블록 전체가 실패해 그 블록의 모든 태그가 한꺼번에 bad quality가 된다. 대량 읽기는 효율적이지만 취약하다. 함께 성공하거나 함께 실패한다.

실무 그룹핑 규칙

  1. 함께 읽을 태그가 연속되도록 레지스터 맵을 배치한다. 주소 공간에 흩어진 맵은 드라이버 최적화로 고칠 수 없다.
  2. 빠른 태그와 느린 태그를 다른 블록에 둔다. 알람 워드 하나는 250 ms 폴링이 필요하고 트렌드 태그 300개는 5초면 충분하다면, 드라이버가 이를 하나의 큰 빠른 블록으로 합치게 두지 마라.
  3. 매핑 안 됐거나 예약된 레지스터로 넘어가는 구간은 건너뛰지 않는다. 추측이 아니라 장치 문서로 확인한다.
  4. 실제 요청 한계를 지킨다. Modbus는 요청당 holding/input 레지스터 최대 125개, coil 2000개를 허용하지만, 많은 게이트웨이와 구형 PLC는 훨씬 적게 받는다. 실제 상한을 테스트한다.
  5. 주소가 붙어 보여도 레지스터 타입은 분리한다. 40001식 표기의 coil은 holding register 공간이 아니다.

요청 크기 한계는 규격 한계와 다르다

프로토콜 최대는 요청당 125 레지스터지만, 이 숫자는 실제 장비에서 틀린 경우가 많다. 시리얼 게이트웨이, 프로토콜 컨버터, 구형 컨트롤러는 응답을 32, 50, 64 레지스터로 제한하거나 동시 요청 수를 제한하는 일이 잦다. 드라이버가 125 레지스터 블록으로 설정됐는데 게이트웨이가 50에서 조용히 잘라내거나 거절하면, 타임아웃처럼 보이지만 실제로는 요청이 너무 커서 생기는 간헐적 bad quality가 된다.

익숙하지 않은 장치를 시운전할 때는 블록 크기를 작게 시작해 깨끗한 읽기를 확인한 뒤, exception이나 잘린 응답을 지켜보며 늘린다. 동작하는 최대값을 프로젝트 노트에 남겨 다음 엔지니어가 장애 중에 다시 찾아내지 않게 한다.

블록 안의 혼합 데이터 타입

32비트 float, 32비트 정수, 16비트 상태 워드가 레지스터 맵에서 나란히 있을 수 있다. 드라이버는 이를 하나의 블록에서 raw 16비트 레지스터로 읽고, 그 위에서 각 태그가 자기 디코딩(word order, byte order, scaling)을 적용한다. 블록 최적화는 디코딩을 바꾸지 않는다.

두 가지를 주의한다.

  • 32비트 값은 레지스터 두 개에 걸친다. 블록 경계가 이를 쪼개게 두면 한쪽이 다른 요청에서 읽혀, 과도 상태 동안 값이 찢어질 수 있다.
  • word-swap과 byte-swap 설정은 블록이 아니라 태그 단위다. 레지스터를 함께 묶는다고 같은 endian 규약을 공유하게 되지 않는다.

결과 읽기

블록 설정을 바꾼 뒤에는 이론을 믿지 말고 측정한다.

  • 드라이버 진단에서 스캔당 요청 수와 평균 응답 시간을 본다. 요청 수는 적고 응답 시간은 안정적인, 더 적고 큰 요청이 목표다.
  • gap tolerance를 넓힌 뒤 exception 응답(illegal data address)이 늘었는지 확인한다. 늘었다면 블록이 이제 매핑 안 된 레지스터까지 뻗은 것이다.
  • 느린 태그가 빠른 스캔 그룹으로 끌려 들어가 빠른 스캔 부하를 키우지 않았는지 확인한다.
  • 시리얼 링크라면 전후 총 스캔 시간을 비교한다. 트랜잭션이 줄면 주기가 눈에 띄게 짧아져야 한다.

요청 수는 줄었는데 bad quality가 생겼다면 최적화가 지나친 것이다. 읽기가 깨끗해질 때까지 gap tolerance나 블록 크기를 되돌린 뒤 멈춘다.

시운전 체크리스트

  1. 레지스터 맵을 내보내 자주 읽는 태그가 연속되는지 확인한다.
  2. 블록 크기를 만지기 전에 태그를 필요한 폴 주기별로 묶는다.
  3. 모르는 장치는 최대 블록 크기를 작게 잡고 깨끗한 읽기를 확인한다.
  4. 장치의 실제 한계를 찾을 때까지 블록 크기를 늘린 뒤 한 단계 물린다.
  5. gap tolerance는 가까운 태그를 합칠 만큼만 높이고, 매핑 안 된 레지스터를 읽을 만큼 높이지 않는다.
  6. 32비트 값이 블록 경계를 넘어 쪼개지지 않는지 확인한다.
  7. 스캔당 요청 수, 평균 응답 시간, 동작하는 블록 크기를 프로젝트 노트에 남긴다.

태그가 같은 곳에 살고 같은 폴 주기를 공유하면 함께 읽고, 빈 구간이 매핑 안 된 레지스터에 닿거나 값이 경계를 넘어 찢어질 수 있으면 나눈다. 프로토콜이 기술적으로 허용하는 가장 큰 블록이 아니라, 깨끗하고 온전한 값을 돌려주는 가장 적은 수의 요청이 이긴다.