BACnet/IP 드라이버
개요
BACnet/IP — 빌딩 자동화(HVAC / 조명 / 화재 / 출입통제) 표준 프로토콜. UDP/47808 위에서 BVLC + NPDU + APDU 계층으로 동작. 2026-07 네이티브 재작성 — 옛 Apache PLC4j bacnet-ip 위임은 제거되었습니다(PLC4j 0.13.1 BACnet 은 canRead()=false(passive/EDE 전용)라 connect 시 "doesn't support reading" 예외로 수집 자체가 불가했음). 지금은 plantpulse-plc-protocol 의 자체 BACnet/IP confirmed ReadProperty 폴링 구현(BacnetIpPLC)을 사용 — AB-ETH 와 동일한 네이티브 패턴.
| 항목 | 값 |
|---|---|
opc_type | BACNET |
| 구현 클래스 | plantpulse.driver.protocol.bacnet.BACnetDriver |
| 라이브러리 | 자체 네이티브 (plantpulse-plc-protocol 의 bacnet — ReadProperty) |
| 상속 | BaseProtocolDriver (PLC4j 아님) |
| 기본 포트 | 47808 (0xBAC0, UDP) |
| 상태 | 안정 (ReadProperty 폴링) |
| read | OK (ReadProperty, 단일 property) |
| write | ❌ (읽기 전용 — WriteProperty 미구현) |
| 보안 | 없음 (BACnet/SC 미지원) |
옛 PLC4j 구현은 EDE(Engineering Data Exchange) CSV 파일로 객체 목록을 정적 매핑했습니다. 네이티브 드라이버는 EDE 파일을 쓰지 않습니다 — 태그 주소로 직접 confirmed ReadProperty 를 보냅니다. 옛 폼의 ede-file-path / ede-directory-path 옵션은 무시됩니다(제거됨).
클래스 구조
BaseProtocolDriver
└── BACnetDriver — connect() 에서 BacnetIpPLC(host, port) 생성 (UDP connectionless)
UDP 는 connectionless 라 핸드셰이크가 없습니다 — 소켓 준비가 곧 연결이며, 통신 라이브니스는 read 실패 시 setConnected(false) 로 관리(AB-ETH 패턴). read 실패 시 소켓을 무효화해 엣지가 재연결하도록 유도합니다.
wire 포맷 요약 (자체 구현)
- BVLC(Block Virtual Link Control) → NPDU(Network) → APDU(Application).
- ReadProperty-Request:
objectIdentifier+propertyIdentifier. - ReadProperty-ACK: BACnet 프리미티브 타입(Real / Unsigned / Boolean / Enumerated / CharacterString)을 문자열로 디코드.
- BBMD(Foreign Device Registration)는 미사용 — 동일 서브넷의 broadcast 도메인에서만 동작.
OPC 등록 옵션
| 필드 | 의미 | 기본 |
|---|---|---|
opc_agent_ip | BACnet device IP | 192.168.1.50 |
opc_agent_port | UDP 포트 | 47808 (보통 고정) |
options.read-timeout | 응답 타임아웃 (ms) | 3000 |
태그 plc_address 형식
<object-type>:<instance>[:<property>]
property 를 생략하면 present-value 로 간주합니다. object-type / property 는 이름 또는 숫자 코드 둘 다 허용. 옛 PLC4j 표기(property 앞에 점: analog-input:1.present-value)도 호환 수용합니다.
| 표기 | 의미 |
|---|---|
analog-input:0 | AI 0 의 present-value (REAL) — property 생략 |
analog-input:0:present-value | 동일 (명시) |
binary-input:1 | BI 1 present-value (Boolean) |
analog-value:10 | AV 10 (계산값) |
multi-state-input:2 | MSI 2 (Unsigned) |
device:1:object-name | Device 1 의 이름 (CharacterString) |
0:5:85 | object-type 0(AI) / instance 5 / property 85(present-value) — 숫자 코드 |
support / 미지원
- read: ReadProperty (단일 property) — OK.
- write: 미지원 —
isWriteSupported() = false,write()는 항상false. (WriteProperty 는 후속 구현.) - COV (Change Of Value) subscription: 미지원 — 폴링 기반.
- BBMD / Foreign Device: 미지원 — 동일 서브넷만.
- BACnet/SC (Secure Connect WebSocket) 및 BACnet MS/TP 시리얼: 미지원.
curl 등록 예시
curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_BACNET_AHU1",
"opc_type": "BACNET",
"opc_name": "AHU-1 Controller",
"opc_agent_ip": "192.168.1.50",
"opc_agent_port": "47808",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 2000,
"options": { "read-timeout": "3000" },
"tag_list": [
{"tag_id":"OPC_BACNET_AHU1_T01","tag_name":"SupplyTemp","plc_address":"analog-input:0","data_type":"Float"},
{"tag_id":"OPC_BACNET_AHU1_T02","tag_name":"FanRun","plc_address":"binary-input:1","data_type":"Boolean"}
]
}'
참고
- BACnet ASHRAE 135 표준.
- 코드:
plantpulse-edge-driver/src/plantpulse/driver/protocol/bacnet/BACnetDriver.java+plantpulse-plc-protocol의bacnet패키지. - 운영: 동일 L2 broadcast 도메인이 필요. 라우터 너머의 device 는 BBMD 또는 별도 게이트웨이가 필요(미지원).