You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
7.3 KiB
244 lines
7.3 KiB
|
1 month ago
|
# 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 테이블
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- 테이블 구조 (변경 불가)
|
||
|
|
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:43` `ALLOWED_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 테이블
|
||
|
|
|
||
|
|
```sql
|
||
|
|
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의 정확한 필드명과 타입:
|
||
|
|
|
||
|
|
```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_sign` 6개 IMU remap 필드는 **v1.4.5 부터 Web Configurator 가
|
||
|
|
> 더 이상 작성하지 않는다**(UI 에서 제거됨, src 에 참조 0). dpworldapp 이
|
||
|
|
> 자체적으로 소유·관리하는 값이므로 위 스키마 예시에서 의도적으로 제외했다.
|
||
|
|
> Web Configurator 의 partial-merge POST 는 기존 행을 통째로 덮어쓰지 않으므로
|
||
|
|
> (`/setting/device` 부분 병합), dpworldapp 이 기록한 이 키들은 보존된다.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. protocol_config JSON 스키마
|
||
|
|
|
||
|
|
```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` 테이블에 쓰기 없음
|
||
|
|
|
||
|
|
### 검증 방법
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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()))
|
||
|
|
"
|
||
|
|
```
|