DNP3는 이벤트가 붙은 Modbus가 아니다
DNP3 회선은 벤치에서 Class 0 폴을 완벽하게 통과하고도, 현장에서 무선이 90초만 끊기면 데이터를 잃을 수 있다. 배선은 멀쩡하다. 아무도 confirm하지 않는 사이 outstation이 이벤트 버퍼를 다 채웠고, 가장 오래된 변화가 뒤로 밀려 떨어져 나간 것뿐이다. 현장에서 쫓아본 DNP3 문제는 거의 다 네 가지 설정 불일치 중 하나였다 — 클래스 배정, 아날로그 데드밴드, 비요청 활성화, 시간 동기화 — 물리적 고장이 아니었다.
이 프로토콜(IEEE 1815로 표준화됐고 대부분 현장은 2012년판을 쓴다)은 느리고 손실 많은 회선, 그리고 두절 중에도 데이터를 잃으면 안 되는 outstation을 위해 만들어졌다. Modbus는 현재 레지스터 값을 그냥 읽지만, DNP3는 시운전 방식을 바꾸는 두 가지 개념을 더한다.
- 이벤트(Event): outstation이 각 변화를 기억해 두고, 마스터가 수신을 확인(confirm)할 때까지 클래스별 버퍼에 보관한다.
- 비요청 응답(Unsolicited response): outstation이 폴링을 기다리지 않고 스스로 이벤트를 마스터에 밀어 올린다.
이 네 가지 설정을 맞추면 회선은 조용하다. 틀리면 이벤트를 잃거나, 마스터를 이벤트로 넘치게 하거나, 말이 안 되는 타임스탬프를 쫓게 된다.
정적 데이터와 이벤트 데이터
SCADA 엔지니어에게 가장 중요한 DNP3 개념은 정적 읽기와 이벤트 읽기의 차이다.
| 읽기 종류 | 반환값 | 언제 쓰나 |
|---|---|---|
| 정적(Class 0) | 설정된 모든 포인트의 현재 값 | 기동, 복구, 주기적 전체 갱신 |
| 이벤트(Class 1/2/3) | 마지막 confirm 이후 발생한 변화만 | 평상시 운전, 특히 느린 회선 |
이 둘은 함수 코드만 다른 게 아니라 서로 다른 객체 그룹(object group)이다. 바이너리 입력의 정적 값은 Group 1에, 그 이벤트는 Group 2에 있다. 아날로그 입력은 정적이 Group 30, 이벤트가 Group 32다. Class 0 폴은 정적 그룹(Group 1, 10, 20, 30, 40)을 요청해 모든 포인트의 현재 상태를 돌려주며 outstation에 대한 마스터의 인식을 새로 맞춘다. 이벤트 폴은 마지막 confirm 이후의 이벤트 그룹을 요청한다. 이벤트 폴만 하면 confirm 하나만 놓치거나 버퍼가 넘쳐도 마스터가 영구적으로 어긋난다. 반대로 integrity 폴만 하면 폴 사이에 지나간 짧은 변화를 잃는다.
둘 다 필요하다. 기술은 주기를 정하는 데 있다.
포인트를 클래스에 배정하기
outstation의 모든 포인트는 이벤트 클래스 1, 2, 3 중 하나에 배정된다. Class 0은 정적 클래스이며 관례상 모든 포인트를 포함한다. 이벤트 클래스가 셋인 이유는 중요한 변화를 사소한 변화보다 더 자주 폴링하기 위해서다.
현장에서 쓸 만한 배정 관례는 다음과 같다.
- Class 1: 빠르고 운전에 결정적인 변화. 차단기 상태, 펌프 운전 피드백, 트립·알람 비트, 보호 동작 표시.
- Class 2: 데드밴드가 있는 아날로그 계측값. 유량, 압력, 수위, 탱크 용적.
- Class 3: 느리거나 진단성 포인트. 카운터, 런타임 적산, 장치 상태, 급하지 않은 상태값.
클래스를 나누는 이유는 마스터가 Class 1을 몇 초마다, Class 2를 덜 자주, Class 3을 드물게 요청하면서도 가끔 Class 0 integrity 폴을 돌리게 하기 위해서다. 전부 Class 1에 넣으면 이 설계를 무너뜨리고 회선에 잡음을 얹는다.
모든 포인트의 클래스를 포인트 맵 옆에 기록한다. 문제를 볼 때 "이 포인트가 어느 클래스인가"는 가장 먼저 나오는 질문이며, 추측으로 답해서는 안 된다.
아날로그 데드밴드가 이벤트 발생량을 정한다
아날로그 포인트는 값이 설정된 데드밴드보다 더 움직여야만 outstation이 이벤트를 만든다. 이것은 히스토리언의 예외 보고에 해당하며, 마스터가 아니라 outstation에 존재한다.
데드밴드를 잘못 잡으면 두 가지 실패 중 하나가 난다.
- 너무 작으면: 잡음 있는 아날로그가 이벤트를 끊임없이 만들어 버퍼를 채우고, 진짜 상태 변화를 밀어낼 수 있다.
- 너무 크면: 느린 드리프트가 이벤트로 보고되지 않아, 마스터는 다음 integrity 폴에서야 알게 되고 트렌드가 계단처럼 보인다.
데드밴드 자체가 쓰기 가능한 DNP3 포인트다 — Group 34 아날로그 입력 데드밴드 — 그래서 많은 outstation에서는 로컬 설정 도구가 아니라 회선을 통해 마스터가 읽고 쓸 수 있다. outstation이 허용하면 공학 단위로 설정하고, 대충 잡은 반올림 값이 아니라 실제 신호 잡음을 기준으로 잡는다. 펌프가 반복 기동하는 위에 올라탄 수위 계측기는 같은 RTU에 물린 정적인 탱크와 다른 데드밴드가 필요하다. 이것은 종이 위에서 유도할 값이 아니라, 실제 하드웨어에 맞춰 조정하는 보정 손잡이다. 아날로그 데드밴드와 이벤트 변형 노트에서 데드밴드 선택과 각 이벤트를 인코딩하는 객체 변형을 더 깊이 다룬다.
비요청 응답(Unsolicited)
비요청 응답은 outstation이 폴링 없이 이벤트를 밀어 올리게 한다. 무선, 셀룰러, 전화 접속 회선에서는 드문 이벤트를 잡으려고 마스터가 계속 폴링할 필요가 없어져 지연과 전파 점유 시간을 크게 줄인다.
비요청 모드 시운전에는 사람들이 자주 걸리는 순서가 있다.
- 마스터가 밀어받고 싶은 클래스에 대해 비요청 응답을 활성화한다.
- outstation은 재기동을 알리는 초기 "null" 비요청 응답을 보낼 수 있다.
- 이후 outstation은 이벤트가 생길 때마다 보내고, 마스터는 각각을 confirm한다.
- 마스터는 동기를 유지하기 위해 주기적 integrity(Class 0) 폴을 계속 돌린다.
흔한 실수는 다음과 같다.
- outstation에서는 비요청을 켜고 마스터에서는 안 켜거나 그 반대. 양쪽이 일치해야 한다.
- 비요청 응답도 애플리케이션 계층 confirm이 필요하다는 것을 잊는다. confirm이 오지 않으면 이벤트는 버퍼에 남아 재전송된다.
- 비요청 모드가 integrity 폴을 없앤다고 착각한다. 아니다. 놓친 confirm에서 복구하려면 주기적 Class 0이 여전히 필요하다.
- 비요청 재시도·hold 타이머를 너무 공격적으로 잡아, 떨리는 포인트 하나가 느린 회선을 포화시킨다.
좋은 기본값은 결정적 상태에는 비요청 Class 1을 쓰고, 안전망으로 느린 integrity 폴을 함께 두는 것이다.
이벤트 버퍼와 오버플로
outstation은 이벤트를 클래스별로 크기가 정해진 버퍼에 저장한다. 마스터가 confirm을 멈추거나 아날로그가 버퍼를 넘치게 하면 버퍼가 찬다. 넘치면 outstation은 내부 표시 비트를 세우고 오래된 이벤트를 버릴 수 있다.
시운전에서 확인할 것.
- 사용하는 outstation 모델의 클래스별 버퍼 크기를 안다. 엔지니어가 생각하는 것보다 작을 때가 많다.
- 마스터가 실제로 모든 응답 헤더에 실려 오는 내부 표시(IIN, Internal Indications) 비트를 읽고 반응하는지 확인한다. IIN2.3(이벤트 버퍼 오버플로)은 outstation이 이벤트를 버렸다는 뜻이고, IIN1.7(장치 재기동)은 재부팅해 버퍼가 비었다는 뜻이다. IIN1.1/1.2/1.3도 지켜본다 — Class 1/2/3 이벤트가 대기 중임을 알려, 마스터가 폴할 시점을 아는 방법이다.
- 통신 두절 뒤 재접속 시 마스터가 integrity 폴을 수행해, 일부 이벤트를 잃었더라도 현재 상태는 맞게 되는지 확인한다.
- 가능하면 오버플로를 일부러 시험한다. 통신을 끊고, 버퍼 크기를 넘는 변화를 여러 번 만들고, 통신을 복구한 뒤 마스터가 오래된 상태가 아니라 올바른 상태를 회복하는지 본다.
이벤트 버퍼는 DNP3가 존재하는 이유 그 자체다. 마스터가 IIN 비트를 무시하면 이 프로토콜의 핵심 장점을 버린 것이다.
시간 동기화와 타임스탬프
DNP3 이벤트는 타임스탬프를 담는다. 그래서 순차 이벤트 기록(sequence-of-events) 분석에 쓰인다. 하지만 타임스탬프는 outstation 시계만큼만 정확하다.
마스터가 outstation 시계를 동기화하며, outstation은 재기동 후 "need time" 비트(IIN1.4)를 세워 시간 동기화를 요청한다. 시간 동기화가 설정되지 않았거나 outstation이 정전 시 시계를 잃으면, 이벤트 타임스탬프가 어긋나 사후 분석이 무용지물이 된다.
현장 확인 항목.
- 마스터가 고정 주기뿐 아니라 outstation의 "need time" 요청에도 응답하는지 확인한다.
- 이벤트를 outstation에서 타임스탬프하는지(순차 이벤트 기록에는 이 편이 낫다) 마스터에서 하는지 정하고 일관되게 유지한다.
- 로컬 시간과 UTC를 섞어 쓰는 outstation을 주의한다. 섞이면 몇 시간씩 어긋난 진단하기 어려운 타임스탬프가 나온다.
- outstation을 정전·재기동한 뒤에는 이벤트 시각을 믿기 전에 시계가 다시 동기화됐는지 확인한다.
시운전 체크리스트
인수 전에 포인트 하나 읽기가 아니라 DNP3 경로 전체를 점검한다.
- 마스터와 outstation의 링크 주소, 링크 계층 confirm·재시도 설정이 맞는지 확인한다.
- Class 0 integrity 폴이 기대한 모든 포인트를 올바른 스케일과 부호로 돌려주는지 확인한다.
- 각 포인트의 이벤트 클래스 배정이 설계 문서와 맞는지 확인한다.
- 아날로그 데드밴드를 기본값이 아니라 실제 신호 잡음에 맞춰 설정하고 시험한다.
- 비요청 응답을 쓴다면 양쪽이 모두 활성화됐고 confirm이 돌아오는지 확인한다.
- 마스터가 IIN 비트를 읽고 장치 재기동·버퍼 오버플로에 반응하는지 확인한다.
- 통신을 끊고 버퍼 크기를 넘는 변화를 만든 뒤 복구해, 마스터가 integrity 폴로 올바른 상태를 회복하는지 확인한다.
- outstation 정전·재기동 후 시간 동기화가 되는지, 이벤트 타임스탬프가 정상인지 확인한다.
- 폴 주기, 클래스, 데드밴드, 버퍼 크기, 비요청 설정을 포인트 맵과 함께 기록한다.
사람들이 건너뛰는 단계는 7번이다. 멀쩡히 돌아가는 회선을 일부러 끊어야 하기 때문이다. 그래도 해라. 버퍼를 일부러 넘쳐 본 적 없는 DNP3 시스템은, 마스터가 실제로 복구하는지 아니면 그냥 오래된 값을 계속 띄우는지 아무도 모르는 시스템이다 — 그 답은 다음 시운전 창이 아니라 다음 폭풍 때 진짜로 알게 된다.