- Add GpnStatus type ('supported' | 'unsupported' | 'unknown') to Device domain
- Persist gpnStatus in DeviceRecord and all 3 persist paths
(cwmp_controller, main_state_machine, device_action_executor)
- Capture Fault 9000/9001 in getParameterNames → mark device as 'unsupported'
- Fix GPN NextLevel flag: false (non-recursive shallow scan)
- Add static TR-181 catalog (7 727 params, sourced from BBF cwmp-data-models)
- Add parameter_writability service with resolveWritability():
supported → live data; unsupported → catalog → default R/W;
unknown → catalog → null (genuinely unknown)
- Add scripts/generate-tr181-catalog.mjs to regenerate catalog from BBF XML
- Add design doc: docs/design-parameter-writability.md
- Add unit tests: 26 tests covering normalizePath and all gpnStatus branches
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
49 lines
1.9 KiB
TypeScript
49 lines
1.9 KiB
TypeScript
import { createRequire } from 'node:module'
|
|
import type { DeviceSnapshot } from '../domain/device/device.js'
|
|
|
|
const require = createRequire(import.meta.url)
|
|
const tr181Catalog: Array<{ path: string; writable: boolean; deviceClass: string }> =
|
|
require('../../config/tr181-catalog.json')
|
|
|
|
// Index chargé une fois au démarrage — O(1) lookup
|
|
const catalogIndex = new Map(tr181Catalog.map((e) => [e.path, e.writable]))
|
|
|
|
/** Remplace les segments numériques par {i} pour matcher les entrées du catalogue */
|
|
export function normalizePath(path: string): string {
|
|
return path.replace(/\.\d+\./g, '.{i}.').replace(/\.\d+$/, '.{i}')
|
|
}
|
|
|
|
export type WritabilitySource = 'live' | 'catalog' | 'default' | 'unknown'
|
|
|
|
export interface WritabilityResult {
|
|
writable: boolean | null
|
|
source: WritabilitySource
|
|
}
|
|
|
|
/**
|
|
* Résout la writabilité d'un paramètre à partir de l'état persisté du device.
|
|
*
|
|
* Logique :
|
|
* 1. gpnStatus = 'supported' → parameters[] = writable, properties[] = RO (source fiable)
|
|
* 2. gpnStatus = 'unsupported' → catalogue TR-181 → absent = writable=true (défaut R/W)
|
|
* 3. gpnStatus = 'unknown' → catalogue TR-181 → absent = writable=null (genuinement inconnu)
|
|
*/
|
|
export function resolveWritability(snapshot: DeviceSnapshot, paramPath: string): WritabilityResult {
|
|
const { gpnStatus, parameters, properties } = snapshot
|
|
|
|
if (gpnStatus === 'supported') {
|
|
if (parameters.some((p) => p.name === paramPath)) return { writable: true, source: 'live' }
|
|
if (properties.some((p) => p.name === paramPath)) return { writable: false, source: 'live' }
|
|
}
|
|
|
|
// gpnStatus = 'unsupported' ou 'unknown' → consulter le catalogue
|
|
const normalized = normalizePath(paramPath)
|
|
if (catalogIndex.has(normalized)) {
|
|
return { writable: catalogIndex.get(normalized)!, source: 'catalog' }
|
|
}
|
|
|
|
// Absent du catalogue
|
|
if (gpnStatus === 'unsupported') return { writable: true, source: 'default' }
|
|
return { writable: null, source: 'unknown' }
|
|
}
|