DNP3 드라이버
개요
IEEE 1815 표준 DNP3 (Distributed Network Protocol). 전력/수도/가스 utility SCADA 의 사실상 표준. Master(client) 가 outstation(server) 에 TCP 접속해 Application Layer READ 로 측정값 (Analog Input / Binary Input / Counter) 을 수집한다.
| 항목 | 값 |
|---|---|
opc_type | DNP3 |
| 구현 클래스 | plantpulse.driver.protocol.dnp3.DNP3Driver |
| 라이브러리 | 자체 자바 구현 (외부 의존 없음) |
| read | ✅ (단일 포인트 + class poll) |
| write | ✅ (G12V1 CROB, G41V1~V4 Analog Output) |
| 보안 | 없음 (DNP3-SA 미구현) |
| 기본 포트 | 20000 (TCP) |
클래스 구조
| 클래스 | 역할 |
|---|---|
DNP3Driver | TCP socket / AL+TL+DLL 조립 / 단일 포인트 read / class poll / write |
DnpDataLink | 10-byte DLL 헤더 (start 0x05 0x64) + 16-byte 블록마다 LE CRC |
DnpTransport | 1-byte TL 헤더 (FIN/FIR/SEQ 6-bit) |
DnpApplication | Function Code, IIN, Object header, qualifier, response 파서 |
DnpObject | Group 1/2/10/12/20/30/32/40/41 의 객체 인코더/디코더 |
DnpCrc | DNP3 전용 CRC-16 (poly 0x3D65, reflected 0xA6BC, ~complement, LE 직렬화) |
DnpAddress | gNvM.idx / classN 주소 파서 |
Wire 포맷
TCP payload =
DLL header(10 byte: start 0x0564 + len + ctl + dst LE + src LE + CRC LE)
+ 16-byte 블록마다 user data + LE CRC
↳ TL byte (1: FIN/FIR + SEQ 0..63)
↳ AL bytes:
byte 0 App Control (FIR/FIN/CON/UNS + SEQ 0..15)
byte 1 Function Code (1=READ, 5=DIRECT_OPERATE, 129=RESPONSE)
byte 2.. Object header (group + variation + qualifier) + range/data
CRC: DnpCrc.compute(data) — table-driven, init 0, reflected 0xA6BC, 마지막 ~crc, LE 2 byte 직렬화.
검증된 vector: [05 64 05 C9 01 00 00 04] → CRC 0xE521 (LE 21 E5).
OPC 등록 옵션
| 필드 | 의미 | 디폴트 |
|---|---|---|
opc_agent_ip / opc_agent_port | outstation IP / 포트 | port 0이면 20000 |
options.master-address | DNP3 master DLL 주소 | 1 |
options.outstation-address | outstation DLL 주소 | 10 |
options.connect-timeout | TCP connect 타임아웃 (ms) | 3000 |
options.read-timeout | read SO 타임아웃 (ms) | 5000 |
태그 plc_address 형식
| 표기 | 의미 |
|---|---|
g30v1.5 | Group 30 Var 1, point 5 — Analog Input 32-bit signed (with flag) |
g30v5.0 | Analog Input 32-bit float |
g30v6.0 | Analog Input 64-bit double |
g1v2.0 | Binary Input 0 (with flag) |
g20v1.0 | 32-bit Counter |
g40v1.0 | Analog Output 32-bit |
class0 | Integrity poll (G60V1) — Static + 모든 클래스 |
class1 ~ class3 | Event class poll (G60V2/V3/V4) |
class poll 은 한 번 fire 후 응답을 캐시 (lastValueByKey). 후속 단일 포인트 read 가 캐시 히트면 즉시 반환.
write 주소 (g12v1.N + value):
| 입력 value | CROB op |
|---|---|
Latch_On 또는 1 | opType=3 (Latch_On), TCC=0 |
Latch_Off 또는 0 | opType=4 (Latch_Off), TCC=0 |
Pulse_On:<onMs>:<offMs>:<count> | opType=1 |
Close / Trip | TCC=1 (Close) / TCC=2 (Trip) |
g41v1..g41v4 는 각각 32-bit signed / 16-bit signed / 32-bit float / 64-bit double Analog Output.
미지원 영역
- DNP3-SA (Secure Authentication, IEEE 1815-2012 §7).
- Multi-segment TL 응답 (현재 single-segment FIR=FIN=1 송신만).
- Unsolicited responses (FC 130) 비동기 수신 — 송신은 가능, 클라이언트 단의 unsolicited handler 미구현.
- File transfer (FC 25-30), Time sync (FC 23 DELAY_MEASURE 는 상수만 정의).
테스트 커버리지
test/java/plantpulse/driver/protocol/dnp3/
| 테스트 | 검증 |
|---|---|
DnpCrcTest | spec vector + 임의 길이 CRC |
DnpDataLinkTest | 10-byte 헤더 + block CRC round-trip |
DnpTransportTest | FIN/FIR/SEQ 인코딩 |
DnpApplicationTest / DnpApplicationWriteTest | READ/DIRECT_OPERATE 빌드, IIN 디코드 |
DnpObjectTest / DnpObjectWriteTest | G1/G30/G40/G12V1/G41 인코더/디코더 |
DnpAddressTest / DnpAddressWriteTest | 주소 파서 |
DNP3DriverTest | mock outstation 으로 read/write end-to-end |
DnpSpecExtraTest | 추가 spec 적합성 |
참고
- IEEE 1815-2012, "DNP3 Specification".
- 코드:
plantpulse-edge-driver/src/plantpulse/driver/protocol/dnp3/. - 운영: outstation 측 master/outstation address 정합 필수. 일치하지 않으면 DLL 헤더에서 즉시 폐기.