7.3 KiB
dpworldapp 호환성 명세서
Web Configurator 교체 시 dpworldapp과의 호환성을 보장하기 위한 계약(Contract) 문서.
1. 개요
dpworldapp(/usr/bin/dpworldapp)은 IoT 장비의 메인 애플리케이션으로, Web Configurator가 저장한 설정을 SQLite DB에서 직접 읽어 사용합니다.
핵심 원칙: dpworldapp은 일체 변경하지 않으므로, 새 Web Configurator는 기존 Java 앱이 생성하는 것과 비트 단위까지 호환되는 데이터를 생성해야 합니다.
2. 공유 인터페이스
2.1 SQLite DB 파일 경로
~/db/dynamic_data.db (= /home/root/db/dynamic_data.db)
- 절대 변경 불가: dpworldapp이 이 경로를 하드코딩하고 있을 가능성 높음
- busy_timeout:
5000ms(동시 접근 시 잠금 대기)
2.2 board_config 테이블
-- 테이블 구조 (변경 불가)
CREATE TABLE board_config (
key TEXT PRIMARY KEY NOT NULL,
value TEXT NOT NULL
);
| key 값 | 역할 | 쓰기 주체 | 읽기 주체 |
|---|---|---|---|
device_config |
디바이스 네트워크/하드웨어 설정 | Web Configurator | dpworldapp |
protocol_config |
프로토콜/매핑 설정 | Web Configurator | dpworldapp |
log_config |
로그 관리(auto-compress/cleanup 등) — Python 전용 | Web Configurator | (dpworldapp 미사용) |
net_config |
Network Apply Engine watchdog 설정(kill-switch 등) — Python 전용 | Web Configurator | (dpworldapp 미사용) |
ap_config |
Wi-Fi AP(ap0) 설정 — Python 전용 |
Web Configurator | (dpworldapp 미사용) |
[!NOTE]
board_config의 전체 허용 키는 위 5개다(db_manager.py:43ALLOWED_KEYS). dpworldapp 이 읽는 것은device_config/protocol_config둘 뿐이고,log_config/net_config/ap_config는 Web Configurator 자체 기능을 위한 Python 전용 키로 dpworldapp 은 읽지 않는다(별도 key ownership 정책 — dpworldapp 공유 키를 오염시키지 않기 위함). 한편 네트워크 적용 엔진이 dpworldapp 과 byte-exact 호환을 위해 생성하는network_config.json은board_config행이 아니라/home/root/network/의 파일이다(혼동 주의).
2.3 event_history 테이블
CREATE TABLE event_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data TEXT NOT NULL
);
| 역할 | 쓰기 주체 | 읽기 주체 |
|---|---|---|
| 이벤트/경고 이력 | dpworldapp | (선택적 조회 가능) |
[!CAUTION] Web Configurator는
event_history에 절대 쓰지 않습니다. dpworldapp 전용.
3. device_config JSON 스키마
dpworldapp이 읽는 JSON의 정확한 필드명과 타입:
{
"wifi_static": "on",
"wifi_ip": "10.227.231.38",
"wifi_netmask": "255.255.255.0",
"wifi_gateway": "10.227.231.1",
"wifi_dns1": "10.226.4.4",
"wifi_dns2": "10.226.4.5",
"wifi_country_code": "KR",
"eth_ip": "192.168.55.44",
"eth_netmask": "255.255.255.0",
"eth_gateway": "192.168.55.1",
"protocol_server_ip": "10.226.22.48",
"protocol_server_port": 20101,
"update_server_ip": "10.226.22.244",
"update_server_port": 9999,
"rtcm_server_ip": "10.226.22.48",
"rtcm_server_port": 20104,
"opc_ua_server_ip": "192.168.55.11",
"opc_ua_server_port": 53530,
"modbus_server_ip": "192.168.55.100",
"modbus_server_port": 502,
"lte_server_ip": "...",
"lte_server_port": 0,
"can_bus_type": "extended",
"can_baudrate": 1000,
"rs485_mode": "half",
"rs485_baudrate": 9600,
"rs485_parity": "...",
"lte_ip": "...",
"lte_netmask": "...",
"lte_gateway": "...",
"log_save": "on",
"log_max_size": 100,
"log_max_duration": 1,
"WIFI_SSID": [
{
"wifi_ssid": "KROF01-IT-DEV-Y5",
"wifi_passwd": "password",
"wifi_security": "wpa/wpa2"
}
]
}
타입 규칙
| 필드 패턴 | 타입 | 예시 |
|---|---|---|
*_port |
Integer (JSON number) |
20101 |
*_baudrate |
Integer |
9600 |
log_max_size, log_max_duration |
Integer |
100 |
wifi_static, log_save |
String ("on" / "off") |
"on" |
| 나머지 | String |
"192.168.55.44" |
[!NOTE] dpworldapp-owned 필드 (Web Configurator 미작성):
imu_remap_x/imu_remap_x_sign/imu_remap_y/imu_remap_y_sign/imu_remap_z/imu_remap_z_sign6개 IMU remap 필드는 v1.4.5 부터 Web Configurator 가 더 이상 작성하지 않는다(UI 에서 제거됨, src 에 참조 0). dpworldapp 이 자체적으로 소유·관리하는 값이므로 위 스키마 예시에서 의도적으로 제외했다. Web Configurator 의 partial-merge POST 는 기존 행을 통째로 덮어쓰지 않으므로 (/setting/device부분 병합), dpworldapp 이 기록한 이 키들은 보존된다.
4. protocol_config JSON 스키마
{
"dev_type": "RTLS",
"equipment": "ITV",
"equipment_id": "01",
"MEID": "7000",
"protocol": "OPC_UA",
"version": "v1.0",
"can_input": "on",
"speed_data": "CAN",
"dr_on": "on",
"heading_on": "on",
"heading_imu_on": "on",
"fix_mode_on": "on",
"analog_input_level": "6",
"ai0": "0", "ai1": "1", "ai2": "0", "ai3": "0",
"di0": "0", "di1": "0", "di2": "0", "di3": "0",
"two_byte_order": "big",
"four_byte_order": "big",
"CAN": [
{
"field": "CAN1",
"id": "0x18FEFC28",
"shift": 0,
"mask": "0xffff",
"expr": "x*0.05+10.0",
"odt": "integer",
"dv": "-9"
}
],
"OPC_UA": [
{
"field": "TMP1",
"ns": "3",
"addr": "1001",
"shift": 0,
"expr": "x*10.0",
"odt": "float64",
"dv": "-9"
}
],
"MODBUS": [
{
"field": "TEMP",
"addr": "100",
"idt": "integer",
"odt": "float64",
"dv": "-9",
"shift": 0,
"expr": "x*0.1",
"mask": "0xffff"
}
]
}
프로토콜별 조건부 필드 존재 규칙
| 조건 | 결과 |
|---|---|
protocol == "OPC_UA" |
OPC_UA[] 포함, MODBUS 제외 (null → JSON에서 생략) |
protocol == "MODBUS" |
MODBUS[] 포함, OPC_UA 제외 |
protocol == "NONE" |
둘 다 제외 |
can_input == "on" |
CAN[] 포함 |
can_input == "off" |
CAN 제외 |
5. 호환성 체크리스트
배포 전 필수 확인
~/db/dynamic_data.db경로 동일board_config테이블 구조 동일key값 문자열 동일 ("device_config","protocol_config")- JSON 필드명 snake_case 동일
- Integer 필드가 JSON number 타입으로 저장
- null/None 필드가 JSON에서 제외됨
- MEID가 항상 문자열로 저장
- 비활성 프로토콜 매핑이 JSON에서 제외됨
busy_timeout=5000설정됨event_history테이블에 쓰기 없음
검증 방법
# 1. 기존 Java 앱으로 설정 저장 후 DB 덤프
sqlite3 ~/db/dynamic_data.db "SELECT value FROM board_config WHERE key='device_config'" > java_device.json
# 2. 새 Python 앱으로 동일 설정 저장 후 DB 덤프
sqlite3 ~/db/dynamic_data.db "SELECT value FROM board_config WHERE key='device_config'" > python_device.json
# 3. JSON 비교 (필드 순서 무시)
python3 -c "
import json
a = json.loads(open('java_device.json').read())
b = json.loads(open('python_device.json').read())
print('MATCH' if a == b else 'MISMATCH')
print('Diff keys:', set(a.keys()) ^ set(b.keys()))
"