8.7 KiB
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로 쓰이는 것으로 이해한다.
핵심 원칙은 두 가지다.
- 지금 dpworldapp이 실제로 읽는
Legacy v1포맷은 즉시 깨지 않는다. - 사람이 유지보수하고 검증하기 쉬운
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 configprotocol_config: dpworldapp register 매핑 계약과 맞춘 flat configlog_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. - 장비별 차이는
equipmentcode와 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 aliasOPC-UA흡수device.meidstring <-> legacyMEIDio.analog_inputs[]<-> legacyai0,ai1,ai2,ai3io.digital_inputs[]<-> legacydi0,di1,di2,di3mappings.opc_ua[]<-> legacyOPC_UAmappings.can[]<-> legacyCAN- 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.jsondocs/config-spec/proposed/config_protocol.v2.proposed.json
이 두 파일은 현재 default JSON을 사람이 이해하기 쉬운 typed 구조로 옮긴 협의안이다. 장비에 바로 복사하는 배포 파일이 아니다.
Future Addition Policy
새 config field를 추가할 때는 다음 항목을 spec에 먼저 추가한 뒤 구현한다.
owner:device_config,protocol_config,log_config, 또는 새 전용 key 중 어디가 소유하는지 명시한다.- type: JSON type을 하나로 정한다. boolean 의미에 string enum을 쓰지 않는다.
- default: 장비 factory default, WebConfigurator UI default, migration default를 구분한다.
- required/optional: 누락 시 동작과 null 저장 여부를 명시한다.
- enum: 허용 값과 alias 흡수 위치를 정한다. alias는 adapter에서만 처리한다.
- unit: 수량 field에는 단위를 key에 포함한다.
- security: password, token, certificate 등 masking/export policy를 함께 정의한다.
- migration: 기존 DB row와 fallback file을 어떻게 변환할지 적고 idempotent test를 만든다.
- compatibility: dpworldapp 구버전이 unknown field를 어떻게 처리하는지 확인한다.
- golden file: default legacy 파일, v2 proposal, v1 round-trip 결과를 테스트 fixture로 고정한다.
변경 절차 제안
- 현재 장비 fallback 파일을 Legacy v1 기준으로 먼저 정돈한다. 단, dpworldapp이 아직 string을 기대하는 field는 임의로 v2 타입으로 바꾸지 않는다.
- WebConfigurator에
legacy <-> canonicaladapter 테스트를 추가한다. - default fallback 파일을 사람이 직접 수정하지 않고 canonical source에서 생성하는 스크립트를 둔다.
- dpworldapp이 v2를 직접 읽을 준비가 되면
schema_version기반 feature flag 또는 별도device_config_v2/protocol_config_v2row를 협의한다. - 전환 전까지 운영 DB의
device_config,protocol_config는 Legacy v1로 유지한다.
즉시 결정할 협의 항목
odo_speed.shift: legacy fallback 파일을 integer로 고칠지, WebConfigurator adapter가 numeric string을 integer로 normalize할지 결정.ai2/ai3/di2/di3: dpworldapp config-reader 계약이 4 channel을 실제 지원하는지 확인. 지원한다면 WebConfigurator enum/validator도 확장.lte_port: network port인지 serial/interface id인지 owner와 type 확정.- mapping row의
dv: output type에 맞춰 typed value로 갈지, fallback/default sentinel string으로 유지할지 결정. - log 관련 field: dpworldapp 소유 field와 WebConfigurator 전용
log_configfield를 명확히 분리.