디바이스 서버는 긴 cable이 아니다
Serial-to-Ethernet 디바이스 서버는 terminal server 또는 serial gateway라고도 부르며, RS-232나 RS-485 포트를 네트워크 너머로 옮겨 준다. SCADA node가 legacy RTU meter, drive, protection relay와 멀리 떨어져 있거나, 하나의 공유 multidrop을 여러 곳에서 읽어야 할 때 쓴다.
문제는 이 장비를 투명한 연장 cable로 착각하는 것이다. 그렇지 않다. 디바이스 서버는 byte를 buffering하고, 언제 TCP로 내보낼지 스스로 판단하며, Modbus RTU가 의존하는 timing을 바꿔 놓을 수 있다. Bench에서 request 하나씩 보낼 때는 완벽해 보이던 링크가, SCADA driver가 빠르게 poll하거나 두 번째 master가 붙는 순간 frame을 흘리기 시작한다.
시운전은 직접 serial 링크와 같은 층을 나누되, 한 층을 더 본다.
- RS-485 또는 RS-232 배선이 깨끗한 signal을 전달하는지.
- 모든 장비의 serial setting이 같은지.
- 디바이스 서버 framing이 Modbus RTU message 경계를 보존하는지.
- SCADA driver가 올바른 TCP 경로로 올바른 serial port에 닿는지.
SCADA timeout부터 만지지 않는다. 각 층을 순서대로 증명한다.
무엇보다 동작 모드를 먼저 고른다
디바이스 서버는 보통 여러 모드를 제공하고, 모드에 따라 Modbus framing 처리 방식이 달라진다.
| 모드 | 하는 일 | 사용 시점 |
|---|---|---|
| Raw TCP socket | 프로토콜 인식 없이 serial byte를 TCP로 전달 | SCADA driver가 Modbus RTU를 말하고 framing을 직접 관리할 때 |
| RealCOM / virtual COM | 원격 port를 SCADA node의 local COM port로 제시 | COM port만 여는 legacy driver, native TCP 미지원 |
| Modbus gateway | 네트워크 쪽 Modbus TCP를 serial 쪽 Modbus RTU로 변환 | SCADA는 Modbus TCP로 poll, field device는 RTU |
| UDP | Connectionless datagram | Modbus에는 드묾, vendor가 요구하지 않으면 피함 |
흔한 실수는 raw TCP socket으로 돌리면서 SCADA driver는 Modbus TCP로 설정하거나 그 반대다. Modbus gateway 모드에서는 box가 MBAP header와 unit ID를 붙이고 떼어 준다. Raw TCP 모드에서는 그렇지 않고, driver를 Modbus RTU over TCP, 즉 RTU-over-TCP 또는 encapsulated RTU로 설정해야 한다. 둘은 서로 바꿀 수 없다. Box가 무엇을 하고 driver가 무엇을 기대하는지 먼저 확인하고 나머지를 건드린다.
Packing과 force-transmit timer가 framing을 정한다
Modbus RTU는 최소 3.5 character time의 무음 구간으로 frame 끝을 표시하고, frame 안에서는 1.5 character time보다 긴 gap을 허용하지 않는다. Modbus over Serial Line Specification and Implementation Guide V1.02 §2.5.1.1이다. 숫자는 baud rate에서 바로 나온다. 8N1 character 하나가 11 bit이니 9600 baud에서 character 하나는 1.15 밀리초, t1.5는 1.7 밀리초, t3.5는 4.0 밀리초다. 19200 baud를 넘으면 같은 clause가 비례 계산을 멈추고 값을 고정한다. t1.5는 750 µs, t3.5는 1.750 밀리초. 실제 serial line에서는 UART가 그 gap을 본다. Raw나 virtual COM 모드의 디바이스 서버는 그 gap을 자동으로 보존하지 않는다. 대신 들어오는 serial byte를 모아 자기 규칙에 따라 내보내는데, 보통은 이렇다.
- Packing timer(또는 delimiter, force-transmit timer)가 짧은 idle 뒤에 buffer를 flush.
- Packing length가 byte 수에 도달하면 flush.
- 특정 byte에서 flush하는 optional delimiter character.
Packing timer가 너무 길면 box가 response와 다음 request의 앞부분을 하나의 TCP payload로 합쳐 driver가 깨진 frame을 본다. 너무 짧으면 하나의 Modbus response가 여러 TCP segment로 쪼개져 엄격한 driver가 거부한다.
설정하고 기록할 값:
- Force-transmit(packing) timer는 Modbus response 하나가 payload 하나로 나가도록 조정한다. 설정된 baud의 t3.5에서 출발한다. 9600에서 4.0 밀리초, 115200에서 1.750 밀리초다. 설정 단위가 1 밀리초인 box는 이 범위의 빠른 쪽을 아예 표현하지 못한다.
- Packing length는 disable하거나 최소 256 바이트로 잡는다. RTU frame은 256 바이트가 상한이고(253 바이트 PDU에 address와 CRC), 합법적으로 가장 큰 holding register 읽기인 125 register, function 0x03의 응답이 1 + 1 + 1 + 250 + 2 = 255 바이트다. Packing length 128은 그 응답을 매번 쪼갠다. 256 이상은 쪼갤 수 없다.
- Delimiter flush는 vendor가 Modbus용으로 권하지 않으면 disable.
순수 Modbus gateway 모드에서는 box가 frame을 이해하므로 이 부분이 덜 중요하다. 완전한 RTU message를 parse한 뒤 변환하기 때문이다. Raw와 virtual COM 모드에서는 매우 중요하다. 애매하면 RTU field device에는 Modbus gateway 모드를 우선한다. Framing 추측을 없애 주기 때문이다.
TCP idle timeout과 재연결
TCP는 stream이고, 어느 쪽이든 연결을 오래 열어 둘 수 있다. 두 설정이 장애 후 복구 방식을 정한다.
- 디바이스 서버의 TCP idle timeout. Box가 idle 연결을 공격적으로 닫으면 느린 poll group이 request 사이에 끊긴다. 반대로 절대 닫지 않으면, crash한 SCADA session이 남긴 stale 연결이 재연결을 막는다. Port당 연결 하나만 허용하는 box에서 특히 그렇다.
- Keepalive. TCP keepalive를 켜서 cable 분리나 node 재부팅으로 생긴 half-open 연결을 방치하지 말고 감지해 정리한다. 그리고 간격을 바꾼다. 기본값은 도움이 안 된다. RFC 1122 §4.2.3.6은 keepalive timer 기본값이 두 시간보다 짧으면 안 된다고 못박았고, Linux는 그대로 따른다.
tcp_keepalive_time7200초, 그다음tcp_keepalive_probes9회를tcp_keepalive_intvl75초 간격으로. socket이 죽었다고 판정되기까지 대략 7875초다. Serial poll loop에 필요한 값은 수십 초 단위다.
현장 점검:
- 해당 port가 동시 TCP 연결을 몇 개 허용하는지 적는다.
- Poll 중에 네트워크 cable을 뽑고 SCADA driver가 예상 시간 안에 재연결하는지 확인.
- SCADA node를 재부팅하고 이전 연결이 풀려 새 연결이 붙는지 확인.
- 가장 느린 정상 poll 간격 동안 box가 연결을 닫지 않는지 확인.
몇 분 잘 되다가 manual driver restart 전까지 멈추는 것은 흔한 현장 증상이고, 배선 문제가 아니라 stale 연결과 single-connection port 조합일 때가 많다.
하나의 serial bus, 둘 이상의 master
디바이스 서버를 설치하는 솔깃한 이유 중 하나는 같은 multidrop을 두 시스템이 읽게 하는 것이다. 예를 들어 SCADA와 별도 energy dashboard다. Modbus RTU는 한 bus에 두 master가 있을 때 arbitration이 없다. 둘이 동시에 보내면 frame이 충돌한다.
일부 디바이스 서버는 여러 TCP 연결의 request를 하나의 serial line에 직렬화하는 request queue나 serial command scheduling을 제공한다. 동작할 수는 있지만 timing을 바꾸고 latency를 더한다. 공짜 기능이 아니라 실제 설계 결정으로 다룬다.
bus를 꼭 공유해야 한다면:
- Multi-master 접근을 명시적으로 직렬화하고 동작을 문서화한 box를 우선한다.
- 전체 request rate를 가장 느린 slave가 견디는 값보다 넉넉히 낮게 유지한다.
- 두 master가 동시에 활성일 때 timeout과 CRC counter가 오르는지 본다.
- Free-run 대신 각 시스템에 별도 poll schedule을 준다.
공유가 필요 없다면 SCADA에 serial line 소유권을 주고, 두 번째 reader는 별도 port나 gateway로 뺀다.
Half-duplex RS-485 방향 제어
2-wire RS-485는 half-duplex다. 디바이스 서버는 보낼 때만 transmitter를 켜고, slave가 응답할 수 있도록 line을 놓아야 한다. Box는 automatic direction control로 처리하며 대개 안정적이지만, 현장에서 두 가지가 어긋난다.
- Box가 전송 후 transmitter를 조금 오래 켜 두면 slave 응답의 앞부분을 밟는다. 간헐 no-reply나 CRC error로 보이고 baud rate가 높을수록 심해진다. 여유는 계산할 수 있는 숫자다. 규격을 지키는 slave는 응답 전에 t3.5를 기다리므로, 9600 baud에서 box가 transmitter를 내릴 시간은 약 4.0 밀리초, 115200 baud에서는 고정값 1.750 밀리초다. 같은 box, 같은 설정인데 여유는 절반이 안 된다. Error가 배선이 아니라 baud rate와 함께 나타나는 이유가 이것이다.
- Fail-safe biasing이 없으면 전송 사이에 line이 떠서 box가 noise를 partial frame으로 볼 수 있다. Biasing과 termination은 box가 아니라 물리 bus의 몫이다. TIA/EIA-485-A는 한 segment에 32 unit load를 허용하고 양 끝에 120 Ω을 요구한다. 이 둘을 어긴 bus는 어떤 디바이스 서버 설정으로도 살아나지 않는다.
현장 점검:
- Port가 배선과 맞게 4-wire나 RS-232가 아닌 2-wire RS-485로 설정됐는지 확인.
- 직접 RTU 링크와 똑같이 물리 bus의 termination과 fail-safe biasing 확인.
- Baud rate를 올릴 때 error가 늘면 rate를 낮춰 사라지는지 본다. 사라지면 addressing이 아니라 turnaround timing 문제다.
Unit ID와 register base는 그대로 적용된다
Serial 링크를 네트워크로 옮겨도 Modbus addressing은 바뀌지 않고, 전형적인 두 mismatch가 그대로 따라온다.
- Unit ID(slave address). Modbus gateway 모드에서 box가 TCP unit ID를 serial slave address로 그대로 넘기는지, 아니면 translation table이 필요한지 확인한다. 여기서 어긋나면 엉뚱한 device가 응답하거나 응답하지 않는다. Unit ID는 7 바이트 MBAP header 중 1 바이트이고, serial 쪽은 1부터 247만 쓴다. 0은 broadcast, 248부터 255는 reserved다. Serial sub-network가 없는 Modbus TCP client는 0xFF를 보내도록 되어 있다. 그래서 driver가 0xFF 기본값에 그대로 있고 gateway가 그 byte를 곧이곧대로 넘기면, bus에는 아무 이상이 없는데 응답만 없다.
- Register base convention. Field device, test tool, SCADA driver가 holding register를 40001 방식으로 볼지 zero-based로 볼지 합의해야 한다. Wire 위에서 holding register는 0x0000부터 0xFFFF까지의 zero-based 16 bit address다. Vendor manual의 40001은 문서 표기이고 wire address 0x0000을 가리킨다. 디바이스 서버는 base-address 불일치를 고쳐 주지 않고 잘못된 request를 충실히 전달할 뿐이다. 하나 밀리면 옆 register를 읽는데, 그럴듯한 값이 돌아오니 시운전을 그냥 통과한다.
시운전 순서
Field device에서 바깥으로 작업해, 이전 층을 증명한 뒤에만 새 층을 올린다.
- Serial 링크를 현장에서 증명한다. RS-485나 RS-232 port에 laptop을 직접 연결해 알려진 tool로 문서화된 register 하나를 읽는다.
- 디바이스 서버 serial port를 field device와 정확히 맞춘다. Baud rate, parity, data bit, stop bit, 2-wire 또는 4-wire 모드.
- 동작 모드를 의도적으로 고른다. RTU field device에는 Modbus gateway, 필요하면 RTU-over-TCP driver와 함께 raw TCP.
- Force-transmit와 idle timeout을 설정하고 TCP keepalive를 켠다.
- SCADA node에서 box를 통해 같은 register 하나를 읽는다.
- 전체 poll list를 추가하고 timeout과 CRC counter를 모을 만큼 충분히 돌린다.
- 복구를 시험한다. 네트워크 cable을 뽑고 SCADA node를 재부팅하며 매번 깨끗하게 재연결하는지 확인한다.
현장에서 자주 만나는 증상
Register 하나는 되는데 빠른 polling에서 깨진다
Packing 또는 force-transmit timer가 frame을 합치거나 쪼개고 있다. Timer를 조정하거나, box가 완전한 RTU message를 parse하도록 Modbus gateway 모드로 바꾼다.
몇 분 잘 되다가 driver restart 전까지 멈춘다
Single-connection port의 stale TCP 연결이다. Keepalive를 켜고, idle timeout을 조정하고, SCADA 재부팅 후 box가 연결을 놓는지 확인한다.
간헐 no-reply가 baud가 높을수록 심해진다
디바이스 서버의 RS-485 turnaround timing, 또는 biasing과 termination 누락이다. 2-wire direction control과 물리 bus 상태를 확인한 뒤 baud rate를 낮춰 시험한다.
응답이 엉뚱한 device에서 온다
Gateway 모드의 Unit ID mapping, 또는 공유 bus의 중복 slave address다. Translation table과 field address를 확인한다.
Bus에 두 시스템, error 상승
직렬화 없는 두 master다. Request를 queue하는 box를 쓰거나 reader를 분리한다.
남길 자료
다음 엔지니어가 box를 역공학하지 않고도 설정을 재현할 수 있게 남긴다.
- 디바이스 서버 IP, port 번호, serial port별 동작 모드.
- Serial setting과 2-wire/4-wire 선택.
- Force-transmit, idle timeout, keepalive 설정.
- 사용했다면 Unit ID mapping 또는 translation table.
- 설정된 baud로 계산한 t3.5와, 실제로 넣은 force-transmit 값을 나란히.
- 안정 운전 후 timeout/CRC counter 기준값.
- Cable 분리와 SCADA 재부팅 재연결 시험 결과.
사인오프 전에 마지막 하나. Packing timer와 RS-485 turnaround는 bench test가 절대 건드리지 않는 두 설정이다. Bench test는 slave 하나를 느리게 poll하기 때문이다. 실제 poll list를 실제 rate로 걸고 한 시간쯤 돌리며 CRC와 timeout counter를 본다. 투명한 cable이라는 착각이 그냥 지나쳐 버리는 문제가 바로 거기 숨어 있다.