M-Bus 드라이버
개요
M-Bus (Meter-Bus, EN 13757-2/3) 의 자체 자바 native 구현. 운영 환경에서 흔한 wired M-Bus master 게이트웨이 또는 시리얼-TCP 컨버터 (Moxa NPort, USR-TCP232 등) 에 TCP socket 으로 연결해 표준 M-Bus 프레임을 송수신합니다.
| 항목 | 값 |
|---|---|
opc_type | MBUS |
| 구현 클래스 | plantpulse.driver.protocol.mbus.MBusDriver |
| 라이브러리 | (없음 — 자체 구현. EN 13757-2/3) |
| read | 양호 (REQ_UD2 → RSP_UD) |
| write | 양호 (Slave Selection / Application Reset) |
| 디폴트 포트 | 10001 (Moxa NPort 기본) |
| 보안 | 없음 |
클래스 / 구조
MBusDriver (BaseProtocolDriver)
├── Socket / DataInputStream / OutputStream
├── defaultAddress — primary 1~250 (옵션)
├── synchronized requestUd2 / sendSlaveSelection / sendAppReset
├── MBusFrame — buildSndNke / buildReqUd2 / buildSlaveSelection
│ buildSndUdAppReset / parseLongFrame / parseAck
│ ACK=0xE5, START_LONG=0x68, SECONDARY_ADDRESS=253
├── MBusChecksum — sum(c+a+ci+data) mod 256
├── MBusBcd — encodeIdToBcd4(long), BCD 디코드
└── MBusDataRecord — DIF/VIF 파싱, parseAll(buf, off, len) → List<Record>
(Record { value, multiplier, unit, ... })
connect() :
- TCP 연결 (host:10001).
- SND_NKE (Short Frame, C=0x40, A=
default-address) 송신. - ACK (0xE5) 수신 검증 — 미회신해도 connected=true (게이트웨이마다 차이).
wire 포맷 요약
Short Frame (SND_NKE / Slave Selection 응답 ACK)
0x10 | C(1) | A(1) | CS(1) | 0x16
ACK 단독 = 0xE5
Long Frame (REQ_UD2 / RSP_UD)
0x68 | L | L | 0x68 | C(1) | A(1) | CI(1) | data... | CS(1) | 0x16
L = C + A + CI + data 의 byte 수
CS = (C+A+CI+data) mod 256
| 명령 | C | CI | 의미 |
|---|---|---|---|
| SND_NKE | 0x40 | — | Initialize slave |
| REQ_UD2 | 0x5B | — | User data 요청 |
| Slave Selection | 0x53 | 0x52 | Secondary addressing |
| App Reset | 0x53 | 0x50 | Application reset |
OPC 등록 옵션 (options)
| 키 | 의미 | 디폴트 |
|---|---|---|
default-address | primary address (1~250) | 1 |
connect-timeout | ms | 3000 |
read-timeout | ms | 3000 |
태그 주소 형식
Primary addressing
| 표기 | 의미 |
|---|---|
1:0 | primary 1, 첫 번째 record |
5:2 | primary 5, 세 번째 record |
1 | primary 1, 전체 user-data hex |
Secondary addressing
| 표기 | 의미 |
|---|---|
secondary:12345678AB1C0107 | 16 hex 문자 (ID 4 + Mfg 2 + Ver 1 + Med 1) |
secondary:<16-hex>:<recordIndex> | 위 + record 인덱스 |
select:12345678:43932:1:7 | 십진 별칭 (id, mfg, ver, med) |
select:12345678:43932:1:7:0 | + record 인덱스 |
trySecondaryAddress() 는 secondary slave selection 을 먼저 송신 → primary=253 으로 REQ_UD2.
ID 는 EN 13757-3 wire format 의 LSB-first BCD 로 인코딩됩니다 (MBusBcd.encodeIdToBcd4).
write 주소 형식
| 표기 | 동작 |
|---|---|
secondary:<hex> / select:<id>:<mfg>:<ver>:<med> | Slave selection 만 송신 |
app-reset:<subcode> | primary=default-address 에 application reset |
app-reset:<primary>:<subcode> | primary 명시 |
select-and-reset:<id>:<mfg>:<ver>:<med>:<subcode> | secondary 선택 후 reset |
subcode 는 address.value 로도 받을 수 있음.
record 디코딩
MBusDataRecord.parseAll(userData, off=12, len) 가 user-data (header 12 byte 이후) 를 DIF/VIF 기반으로
순회하며 Record { value, multiplier, unit, ... } 리스트를 반환합니다.
formatValue(record) :
- 정수 + multiplier=1 →
Long.toString(...) - Number →
value * multiplier(double) - 기타 →
String.valueOf(value)
지원 / 미지원
- ✅ SND_NKE → REQ_UD2 → RSP_UD 사이클
- ✅ Primary addressing (1~250)
- ✅ Secondary addressing (slave selection + primary 253 read)
- ✅ Application Reset (CI=0x50)
- ✅ DIF/VIF data record 파싱 + multiplier 스케일
- ❌ FCB (frame count bit) toggle / Multi-frame UD2
- ❌ Wireless M-Bus (EN 13757-4)
- ❌ Encryption (mode 5 / 7 — 스마트 미터)
테스트 커버리지
test/java/plantpulse/driver/protocol/mbus/
| 클래스 | 테스트 수 |
|---|---|
MBusAckTest | 4 |
MBusBcdTest | 13 |
MBusChecksumTest | 4 |
MBusDataRecordTest | 7 |
MBusDriverAddressTest | 10 |
MBusDriverWriteTest | 9 |
MBusFrameTest | 14 |
MBusSpecExtraTest | 28 |
총 89 테스트.
참고
- EN 13757-2 Physical and link layer
- EN 13757-3 Application layer / DIF/VIF / Secondary addressing
- 코드:
src/plantpulse/driver/protocol/mbus/