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.
132 lines
8.7 KiB
132 lines
8.7 KiB
|
1 month ago
|
# WebConfigurator / dpworldapp Config Contract Proposal
|
||
|
|
|
||
|
|
작성일: 2026-06-10
|
||
|
|
|
||
|
|
## 목적
|
||
|
|
|
||
|
|
이 문서는 WebConfigurator와 dpworldapp이 공유하는 `device_config`, `protocol_config`의 계약을 정리하기 위한 협의안이다. 현재 장비의 `/var/www/html/config_device.json`, `/var/www/html/config_protocol.json`는 dpworldapp 구동 중 DB read/parse 문제가 생겼을 때 fallback seed 또는 restore source로 쓰이는 것으로 이해한다.
|
||
|
|
|
||
|
|
핵심 원칙은 두 가지다.
|
||
|
|
|
||
|
|
1. 지금 dpworldapp이 실제로 읽는 `Legacy v1` 포맷은 즉시 깨지 않는다.
|
||
|
|
2. 사람이 유지보수하고 검증하기 쉬운 `Canonical v2` 포맷을 별도 계약안으로 만들고, WebConfigurator와 dpworldapp 사이에는 명시적인 `adapter`를 둔다.
|
||
|
|
|
||
|
|
## 현재 관찰
|
||
|
|
|
||
|
|
입력 기본 파일 위치:
|
||
|
|
|
||
|
|
- 장비 원본 경로: `/var/www/html/config_device.json`
|
||
|
|
- 장비 원본 경로: `/var/www/html/config_protocol.json`
|
||
|
|
- 분석에 사용한 복사본: `C:\Users\F1304\Downloads\default www`
|
||
|
|
|
||
|
|
현재 WebConfigurator는 SQLite `board_config` 테이블에 다음 key를 사용한다.
|
||
|
|
|
||
|
|
- `device_config`: dpworldapp config-reader 계약과 맞춘 flat config
|
||
|
|
- `protocol_config`: dpworldapp register 매핑 계약과 맞춘 flat config
|
||
|
|
- `log_config`: WebConfigurator/Python 전용 log-management 확장 영역
|
||
|
|
|
||
|
|
이미 코드에 들어 있는 중요한 정책은 `src/dpworldapp_enums.py`의 주석과 일치한다. `device_config`와 `protocol_config`는 dpworldapp config-reader 계약 그대로 유지하고, Python 자체 기능 데이터는 별도 `board_config` key로 분리해야 한다.
|
||
|
|
|
||
|
|
## Legacy v1 이슈 목록
|
||
|
|
|
||
|
|
| 위치 | 현재 값/형태 | 문제 | Canonical v2 제안 |
|
||
|
|
| --- | --- | --- | --- |
|
||
|
|
| `device_config.wifi_static` | `"on"` / `"off"` string | boolean 의미가 문자열 enum으로 저장됨 | `network.wifi.static: true/false` |
|
||
|
|
| `device_config.log_save` | `"on"` / `"off"` string | boolean 의미가 문자열 enum으로 저장됨 | `logging.enabled: true/false` |
|
||
|
|
| `device_config.WIFI_SSID` | 대문자 key + `wifi_passwd` | JSON naming이 불균일하고 password 명칭이 UI와 다름 | `network.wifi.ssid_profiles[].password` |
|
||
|
|
| `device_config.lte_server_port` | `"20111"` string | server port는 정수로 검증/저장되어야 함 | `servers.lte.port: 20111` |
|
||
|
|
| `device_config.lte_port` | `"534"` string | 의미가 network port인지 device interface id인지 불명확 | 의미 확정 전까지 `network.lte.interface_port: "534"` |
|
||
|
|
| `protocol_config.protocol` | `"OPC-UA"` | 현재 canonical enum은 `OPC_UA`; hyphen alias는 adapter 책임 | `interface.protocol: "opc_ua"` |
|
||
|
|
| `protocol_config.MEID` | `7000` number | 식별자는 산술 대상이 아니며 leading zero 가능성을 고려해야 함 | `device.meid: "7000"` |
|
||
|
|
| `protocol_config.dr_on`, `odo_on`, `heading_on` | `"on"` / `"off"` string | boolean 의미가 문자열 enum으로 저장됨 | `positioning.*: true/false` |
|
||
|
|
| `protocol_config.odo_speed.source` | `"CAN"` | 현재 Python validator의 source enum은 lower-case `can`/`hw` | `source: "can"` |
|
||
|
|
| `protocol_config.odo_speed.shift` | `"8"` string | 현재 WebConfigurator validator는 integer를 기대함 | `shift_bits: 8` |
|
||
|
|
| `protocol_config.ai0..ai3`, `di0..di3` | `"0"` / `"1"` string | channel count와 타입이 불명확함. 현재 validator는 0/1번만 엄격 관리 | `io.analog_inputs[]`, `io.digital_inputs[]` boolean array |
|
||
|
|
| mapping arrays `OPC_UA`, `CAN` | 대문자 array key + 숫자 문자열 | 저장 관례와 타입 의미가 섞여 있음 | `mappings.opc_ua[]`, `mappings.can[]` |
|
||
|
|
|
||
|
|
`odo_speed.shift`는 특히 협의가 필요하다. 현재 default fallback 파일은 string을 쓰지만 WebConfigurator backend는 nested odometer field에서 integer를 기대한다. 따라서 adapter에서 numeric string을 integer로 흡수할지, fallback 파일 자체를 integer로 고칠지 결정해야 한다.
|
||
|
|
|
||
|
|
## 제안 아키텍처
|
||
|
|
|
||
|
|
### 1. Legacy v1
|
||
|
|
|
||
|
|
dpworldapp이 지금 읽는 장비 운용 포맷이다. 이 포맷은 dpworldapp config-reader 호환성을 위해 유지한다.
|
||
|
|
|
||
|
|
특징:
|
||
|
|
|
||
|
|
- flat top-level key 유지: `wifi_static`, `protocol_server_ip`, `WIFI_SSID`, `OPC_UA`, `CAN`
|
||
|
|
- legacy boolean은 `"on"` / `"off"` 유지
|
||
|
|
- legacy enum은 dpworldapp 계약 값 유지: `OPC_UA`, `MODBUS`, `CAN`, `NONE`
|
||
|
|
- Java가 integer로 기대하는 server port는 integer로 저장
|
||
|
|
- dpworldapp이 string으로 기대하거나 의미가 불명확한 field는 합의 전까지 string 유지
|
||
|
|
|
||
|
|
### 2. Canonical v2
|
||
|
|
|
||
|
|
사람과 WebConfigurator 내부 모델이 기준으로 삼을 typed domain model이다. `docs/config-spec/proposed/` 아래 JSON 파일은 이 안을 default 파일에서 변환한 예시다.
|
||
|
|
|
||
|
|
공통 규칙:
|
||
|
|
|
||
|
|
- 모든 파일은 `schema_version`을 가진다.
|
||
|
|
- boolean 의미는 JSON boolean만 쓴다.
|
||
|
|
- port, baudrate, size, duration, namespace, shift 같은 수량은 number를 쓴다.
|
||
|
|
- 식별자, CAN id, mask, expression, password, equipment id는 string을 쓴다.
|
||
|
|
- enum은 한 가지 표기만 허용한다. v2에서는 lower snake case를 기본으로 한다.
|
||
|
|
- 단위가 있는 수량은 key에 단위를 넣는다. 예: `baudrate_kbps`, `max_size_mb`, `max_duration_days`, `shift_bits`.
|
||
|
|
- 장비별 차이는 `equipment` code와 protocol-specific mapping array로 표현하고, 공통 positioning/network/server/interface 영역은 유지한다.
|
||
|
|
|
||
|
|
### 3. adapter
|
||
|
|
|
||
|
|
adapter는 `Canonical v2`와 `Legacy v1` 사이의 유일한 변환 지점이다.
|
||
|
|
|
||
|
|
adapter 책임:
|
||
|
|
|
||
|
|
- `true/false` <-> `"on"/"off"`
|
||
|
|
- `opc_ua` <-> `OPC_UA`, legacy alias `OPC-UA` 흡수
|
||
|
|
- `device.meid` string <-> legacy `MEID`
|
||
|
|
- `io.analog_inputs[]` <-> legacy `ai0`, `ai1`, `ai2`, `ai3`
|
||
|
|
- `io.digital_inputs[]` <-> legacy `di0`, `di1`, `di2`, `di3`
|
||
|
|
- `mappings.opc_ua[]` <-> legacy `OPC_UA`
|
||
|
|
- `mappings.can[]` <-> legacy `CAN`
|
||
|
|
- default/fallback JSON과 DB row를 같은 golden file 테스트로 검증
|
||
|
|
|
||
|
|
중요: dpworldapp이 v2를 직접 지원하기 전에는 WebConfigurator가 DB의 `device_config`, `protocol_config`를 v2로 직접 저장하지 않는다. v2는 내부 모델 또는 별도 row/파일로만 관리하고, dpworldapp 경계에는 adapter가 만든 Legacy v1을 저장한다.
|
||
|
|
|
||
|
|
## 제안 파일
|
||
|
|
|
||
|
|
- `docs/config-spec/proposed/config_device.v2.proposed.json`
|
||
|
|
- `docs/config-spec/proposed/config_protocol.v2.proposed.json`
|
||
|
|
|
||
|
|
이 두 파일은 현재 default JSON을 사람이 이해하기 쉬운 typed 구조로 옮긴 협의안이다. 장비에 바로 복사하는 배포 파일이 아니다.
|
||
|
|
|
||
|
|
## Future Addition Policy
|
||
|
|
|
||
|
|
새 config field를 추가할 때는 다음 항목을 spec에 먼저 추가한 뒤 구현한다.
|
||
|
|
|
||
|
|
1. `owner`: `device_config`, `protocol_config`, `log_config`, 또는 새 전용 key 중 어디가 소유하는지 명시한다.
|
||
|
|
2. type: JSON type을 하나로 정한다. boolean 의미에 string enum을 쓰지 않는다.
|
||
|
|
3. default: 장비 factory default, WebConfigurator UI default, migration default를 구분한다.
|
||
|
|
4. required/optional: 누락 시 동작과 null 저장 여부를 명시한다.
|
||
|
|
5. enum: 허용 값과 alias 흡수 위치를 정한다. alias는 adapter에서만 처리한다.
|
||
|
|
6. unit: 수량 field에는 단위를 key에 포함한다.
|
||
|
|
7. security: password, token, certificate 등 masking/export policy를 함께 정의한다.
|
||
|
|
8. migration: 기존 DB row와 fallback file을 어떻게 변환할지 적고 idempotent test를 만든다.
|
||
|
|
9. compatibility: dpworldapp 구버전이 unknown field를 어떻게 처리하는지 확인한다.
|
||
|
|
10. golden file: default legacy 파일, v2 proposal, v1 round-trip 결과를 테스트 fixture로 고정한다.
|
||
|
|
|
||
|
|
## 변경 절차 제안
|
||
|
|
|
||
|
|
1. 현재 장비 fallback 파일을 Legacy v1 기준으로 먼저 정돈한다. 단, dpworldapp이 아직 string을 기대하는 field는 임의로 v2 타입으로 바꾸지 않는다.
|
||
|
|
2. WebConfigurator에 `legacy <-> canonical` adapter 테스트를 추가한다.
|
||
|
|
3. default fallback 파일을 사람이 직접 수정하지 않고 canonical source에서 생성하는 스크립트를 둔다.
|
||
|
|
4. dpworldapp이 v2를 직접 읽을 준비가 되면 `schema_version` 기반 feature flag 또는 별도 `device_config_v2`/`protocol_config_v2` row를 협의한다.
|
||
|
|
5. 전환 전까지 운영 DB의 `device_config`, `protocol_config`는 Legacy v1로 유지한다.
|
||
|
|
|
||
|
|
## 즉시 결정할 협의 항목
|
||
|
|
|
||
|
|
1. `odo_speed.shift`: legacy fallback 파일을 integer로 고칠지, WebConfigurator adapter가 numeric string을 integer로 normalize할지 결정.
|
||
|
|
2. `ai2/ai3/di2/di3`: dpworldapp config-reader 계약이 4 channel을 실제 지원하는지 확인. 지원한다면 WebConfigurator enum/validator도 확장.
|
||
|
|
3. `lte_port`: network port인지 serial/interface id인지 owner와 type 확정.
|
||
|
|
4. mapping row의 `dv`: output type에 맞춰 typed value로 갈지, fallback/default sentinel string으로 유지할지 결정.
|
||
|
|
5. log 관련 field: dpworldapp 소유 field와 WebConfigurator 전용 `log_config` field를 명확히 분리.
|
||
|
|
|