export type DeviceFieldAccess = "read-only" | "managed" | "protected"; export type DeviceFieldValueKind = "text" | "number" | "boolean" | "date"; export interface DeviceProfileField { key: string; label: string; path: string; access?: DeviceFieldAccess; valueKind?: DeviceFieldValueKind; unit?: string; description?: string; sensitive?: boolean; } export interface DeviceProfileSection { id: string; label: string; title: string; description: string; access: DeviceFieldAccess; fields: DeviceProfileField[]; } export interface DeviceProfileCatalog { profileRef: string; vendor: string; model: string; title: string; sections: DeviceProfileSection[]; } const field = ( key: string, label: string, path: string, options: Omit = {}, ): DeviceProfileField => ({ key, label, path, ...options }); const managed = ( key: string, label: string, path: string, options: Omit = {}, ) => field(key, label, path, { ...options, access: "managed" }); const protectedField = ( key: string, label: string, path: string, options: Omit = {}, ) => field(key, label, path, { ...options, access: "protected" }); const serverFields = (slot: number) => [ managed(`server-${slot}-host`, `Сервер ${slot}: DNS / IP`, `reported.configuration.monitoring.servers.${slot - 1}.host`), managed(`server-${slot}-port`, `Сервер ${slot}: порт`, `reported.configuration.monitoring.servers.${slot - 1}.port`, { valueKind: "number" }), managed(`server-${slot}-protocol`, `Сервер ${slot}: протокол`, `reported.configuration.monitoring.servers.${slot - 1}.protocol`), managed(`server-${slot}-identity`, `Сервер ${slot}: ID (SN)`, `reported.configuration.monitoring.servers.${slot - 1}.identity`), managed(`server-${slot}-password`, `Сервер ${slot}: пароль`, `reported.configuration.monitoring.servers.${slot - 1}.password`, { sensitive: true }), ]; const managedIndexedFields = ( count: number, prefix: string, label: string, path: string, options: Omit = {}, ) => Array.from({ length: count }, (_, index) => managed( `${prefix}-${index + 1}`, `${label} ${index + 1}`, `${path}.${index}`, options, )); const phoneFields = Array.from({ length: 5 }, (_, index) => [ managed(`phone-${index + 1}-number`, `Телефон ${index + 1}: номер`, `reported.configuration.phones.${index}.number`, { sensitive: true }), managed(`phone-${index + 1}-mode`, `Телефон ${index + 1}: режим`, `reported.configuration.phones.${index}.mode`), ]).flat(); const simFields = (slot: number) => [ managed(`sim-${slot}-gprs`, `SIM ${slot}: передача данных`, `reported.configuration.simCards.${slot - 1}.gprsEnabled`, { valueKind: "boolean" }), managed(`sim-${slot}-apn`, `SIM ${slot}: APN оператора`, `reported.configuration.simCards.${slot - 1}.apn`), managed(`sim-${slot}-login`, `SIM ${slot}: логин APN`, `reported.configuration.simCards.${slot - 1}.login`, { sensitive: true }), managed(`sim-${slot}-password`, `SIM ${slot}: пароль APN`, `reported.configuration.simCards.${slot - 1}.password`, { sensitive: true }), managed(`sim-${slot}-roaming`, `SIM ${slot}: роуминг`, `reported.configuration.simCards.${slot - 1}.roamingEnabled`, { valueKind: "boolean" }), managed(`sim-${slot}-operator`, `SIM ${slot}: приоритетный оператор`, `reported.configuration.simCards.${slot - 1}.preferredOperatorCode`), managed(`sim-${slot}-pin`, `SIM ${slot}: PIN`, `reported.configuration.simCards.${slot - 1}.pin`, { sensitive: true }), managed(`sim-${slot}-ussd`, `SIM ${slot}: USSD запроса баланса`, `reported.configuration.simCards.${slot - 1}.balanceUssd`, { sensitive: true }), managed(`sim-${slot}-poll`, `SIM ${slot}: период запроса баланса`, `reported.configuration.simCards.${slot - 1}.balancePollHours`, { valueKind: "number", unit: "ч" }), ]; const motionEventFields = ["acceleration", "braking", "cornering", "vertical"].flatMap((event) => { const labels: Record = { acceleration: "Разгон", braking: "Торможение", cornering: "Угловое ускорение", vertical: "Вертикальное ускорение", }; return Array.from({ length: 3 }, (_, level) => [ managed(`${event}-${level + 1}-threshold`, `${labels[event]} ${level + 1}: порог`, `reported.configuration.drivingStyle.${event}.${level}.thresholdMg`, { valueKind: "number", unit: "mg" }), managed(`${event}-${level + 1}-duration`, `${labels[event]} ${level + 1}: длительность превышения`, `reported.configuration.drivingStyle.${event}.${level}.durationMs`, { valueKind: "number", unit: "мс" }), managed(`${event}-${level + 1}-reset`, `${labels[event]} ${level + 1}: задержка сброса`, `reported.configuration.drivingStyle.${event}.${level}.resetDelayMs`, { valueKind: "number", unit: "мс" }), ]).flat(); }).flat(); const violationFields = ["speed", "rpm"].flatMap((kind) => Array.from({ length: 4 }, (_, level) => [ managed(`${kind}-${level + 1}-threshold`, `${kind === "speed" ? "Скорость" : "Обороты"} ${level + 1}: порог`, `reported.configuration.drivingStyle.violations.${kind}.${level}.threshold`, { valueKind: "number", unit: kind === "speed" ? "км/ч" : "об/мин" }), managed(`${kind}-${level + 1}-duration`, `${kind === "speed" ? "Скорость" : "Обороты"} ${level + 1}: минимальное время`, `reported.configuration.drivingStyle.violations.${kind}.${level}.minimumDurationSeconds`, { valueKind: "number", unit: "с" }), managed(`${kind}-${level + 1}-reset`, `${kind === "speed" ? "Скорость" : "Обороты"} ${level + 1}: порог сброса`, `reported.configuration.drivingStyle.violations.${kind}.${level}.resetThreshold`, { valueKind: "number", unit: kind === "speed" ? "км/ч" : "об/мин" }), ]).flat()).flat(); const modbusRegisterFields = Array.from({ length: 10 }, (_, index) => [ managed(`modbus-register-${index + 1}`, `Регистр ${index + 1}: номер`, `reported.configuration.modbus.registers.${index}.number`, { valueKind: "number" }), managed(`modbus-register-${index + 1}-pair`, `Регистр ${index + 1}: читать два регистра`, `reported.configuration.modbus.registers.${index}.readPair`, { valueKind: "boolean" }), ]).flat(); const bleFields = Array.from({ length: 10 }, (_, index) => [ managed(`ble-${index + 1}-mac`, `BLE датчик ${index + 1}: MAC`, `reported.configuration.bluetooth.sensors.${index}.mac`), managed(`ble-${index + 1}-integration`, `BLE датчик ${index + 1}: интеграция`, `reported.configuration.bluetooth.sensors.${index}.integrationExpression`), ]).flat(); export const ARUSNAVI_B2_CATALOG: DeviceProfileCatalog = { profileRef: "arusnavi.b2.internal.v1", vendor: "ARUSNAVI", model: "B2", title: "ARUSNAVI B2", sections: [ { id: "passport", label: "Паспорт", title: "Паспорт и состояние устройства", description: "Реестровая идентичность, профиль модели и текущее состояние канала. Исходный идентификатор показывается только в проекции, разрешённой Device Core.", access: "read-only", fields: [ field("display-name", "Название", "device.displayName"), field("device-key", "Ключ устройства", "device.deviceKey"), field("device-ref", "Device Core ref", "device.deviceRef"), field("vendor", "Производитель", "profile.vendor"), field("model", "Модель", "profile.model"), field("device-type", "Тип", "profile.deviceType"), field("profile", "Профиль модели", "device.modelProfileRef"), field("identifier-kind", "Тип идентификатора", "device.identifier.kind"), field("identifier", "Идентификатор", "device.identifier.masked"), field("iccid-1", "ICCID 1", "reported.identity.iccid1"), field("iccid-2", "ICCID 2", "reported.identity.iccid2"), field("lifecycle", "Состояние реестра", "device.lifecycleState"), field("created", "Зарегистрирован", "device.createdAt", { valueKind: "date" }), field("updated", "Обновлён", "device.updatedAt", { valueKind: "date" }), field("reported-at", "Снимок устройства получен", "reported.observedAt", { valueKind: "date" }), managed("asset-model", "Модель актива", "reported.metadata.model"), managed("registration", "Регистрационный номер", "reported.metadata.registrationNumber"), managed("object", "Объект", "reported.metadata.object"), managed("description", "Описание", "reported.metadata.description"), managed("sim-label-1", "Метка SIM 1", "reported.metadata.simLabel1"), managed("sim-label-2", "Метка SIM 2", "reported.metadata.simLabel2"), ], }, { id: "live", label: "Онлайн", title: "Живой канал и телеметрия", description: "Значения обновляются из последней gateway-сессии и безопасного снимка телеметрии. Интерфейс опрашивает Device Core, пока открыта карточка.", access: "read-only", fields: [ field("session-state", "Состояние соединения", "session.lifecycleState"), field("session-route", "Маршрут", "session.routeName"), field("session-connected", "Подключён", "session.connectedAt", { valueKind: "date" }), field("session-last-seen", "Последний пакет", "session.lastSeenAt", { valueKind: "date" }), field("session-frames", "Принято пакетов", "session.frameCount", { valueKind: "number" }), field("session-bytes", "Принято данных", "session.byteCount", { valueKind: "number", unit: "байт" }), field("latitude", "Широта", "reported.telemetry.navigation.latitude"), field("longitude", "Долгота", "reported.telemetry.navigation.longitude"), field("speed", "Скорость", "reported.telemetry.navigation.speedKph", { valueKind: "number", unit: "км/ч" }), field("altitude", "Высота", "reported.telemetry.navigation.altitudeMeters", { valueKind: "number", unit: "м" }), field("satellites", "Спутники", "reported.telemetry.navigation.satellites", { valueKind: "number" }), field("course", "Курс", "reported.telemetry.navigation.courseDegrees", { valueKind: "number", unit: "°" }), field("hdop", "HDOP", "reported.telemetry.navigation.hdop"), field("gsm-signal", "Уровень GSM", "reported.telemetry.gsm.signal"), field("gsm-operator", "Оператор", "reported.telemetry.gsm.operator"), field("gsm-lac", "LAC", "reported.telemetry.gsm.lac"), field("gsm-cid", "CID", "reported.telemetry.gsm.cid"), field("external-voltage", "Внешнее напряжение", "reported.telemetry.system.externalVoltageMv", { valueKind: "number", unit: "мВ" }), field("internal-voltage", "Внутреннее напряжение", "reported.telemetry.system.internalVoltageMv", { valueKind: "number", unit: "мВ" }), field("errors", "Ошибки и статусы", "reported.telemetry.system.status"), field("inputs", "Входы и выходы", "reported.telemetry.system.io"), field("modules", "Статусы модулей", "reported.telemetry.system.modules"), field("engine-hours", "Моточасы", "reported.telemetry.can.engineHours"), field("odometer", "Пробег", "reported.telemetry.can.odometer"), field("fuel-total", "Полный расход топлива", "reported.telemetry.can.fuelTotal"), field("fuel-level", "Уровень топлива", "reported.telemetry.can.fuelLevel"), field("rpm", "Обороты двигателя", "reported.telemetry.can.rpm"), field("engine-temp", "Температура двигателя", "reported.telemetry.can.engineTemperature"), field("vehicle-speed", "Скорость по CAN", "reported.telemetry.can.vehicleSpeed"), field("axle-pressure", "Давление на оси", "reported.telemetry.can.axlePressure"), field("crash", "Контроллер аварии", "reported.telemetry.can.crashController"), field("instant-fuel", "Моментальный расход", "reported.telemetry.can.instantFuel"), field("adblue", "Уровень AdBlue", "reported.telemetry.can.adBlueLevel"), ], }, { id: "firmware", label: "Прошивка", title: "Версия программного обеспечения", description: "Версию и доступность обновления показываем, но запуск обновления для пилотного B2 запрещён. Этот запрет не снимается включением обычного командного канала.", access: "protected", fields: [ field("firmware-current", "Текущая версия", "reported.firmware.currentVersion"), field("firmware-applied", "Версия применена", "reported.firmware.appliedAt", { valueKind: "date" }), field("firmware-available", "Доступная версия", "reported.firmware.availableVersion"), field("firmware-description", "Описание версии", "reported.firmware.description"), protectedField("firmware-action", "Обновление прошивки", "policies.firmwareUpdate", { description: "Заблокировано для пилотного устройства" }), ], }, { id: "templates", label: "Шаблоны", title: "Шаблоны настроек", description: "Шаблон хранит именованный снимок конфигурации модели. Применение должно создавать новую desired-ревизию, а не менять устройство в обход command ledger.", access: "managed", fields: [ field("template-current", "Применённый шаблон", "reported.configurationTemplate.name"), field("template-applied", "Шаблон применён", "reported.configurationTemplate.appliedAt", { valueKind: "date" }), managed("template-select", "Выбранный шаблон", "reported.configurationTemplate.selected"), managed("template-name", "Название нового шаблона", "reported.configurationTemplate.draft.name"), managed("template-description", "Описание нового шаблона", "reported.configurationTemplate.draft.description"), ], }, { id: "monitoring", label: "Серверы", title: "Серверы мониторинга", description: "B2 поддерживает четыре серверных слота. Существующий Gelios сохраняется параллельно; новый маршрут не должен его перетирать.", access: "managed", fields: [1, 2, 3, 4].flatMap(serverFields), }, { id: "transmission", label: "Передача", title: "Набор передаваемых данных", description: "Флаги определяют состав телеметрии, которую формирует устройство.", access: "managed", fields: [ managed("tx-nav-position", "Навигация: широта и долгота", "reported.configuration.transmission.navigation.position", { valueKind: "boolean" }), managed("tx-nav-motion", "Навигация: скорость, высота, спутники и курс", "reported.configuration.transmission.navigation.motion", { valueKind: "boolean" }), managed("tx-nav-hdop", "Навигация: HDOP", "reported.configuration.transmission.navigation.hdop", { valueKind: "boolean" }), managed("tx-gsm-operator", "GSM: сигнал и оператор", "reported.configuration.transmission.gsm.operator", { valueKind: "boolean" }), managed("tx-gsm-cell", "GSM: LAC и CID", "reported.configuration.transmission.gsm.cell", { valueKind: "boolean" }), managed("tx-system-status", "Системные: ошибки и статусы", "reported.configuration.transmission.system.status", { valueKind: "boolean" }), managed("tx-system-io", "Системные: входы, выходы и модули", "reported.configuration.transmission.system.io", { valueKind: "boolean" }), managed("tx-system-voltage", "Системные: напряжения", "reported.configuration.transmission.system.voltage", { valueKind: "boolean" }), ...["statuses", "engineHours", "odometer", "fuelTotal", "fuelLevel", "rpm", "engineTemperature", "vehicleSpeed", "axlePressure", "crashController", "instantFuel", "adBlueLevel"].map((key) => managed(`tx-can-${key}`, `CAN: ${({ statuses: "статусы работы", engineHours: "моточасы", odometer: "пробег", fuelTotal: "полный расход топлива", fuelLevel: "уровень топлива", rpm: "обороты двигателя", engineTemperature: "температура двигателя", vehicleSpeed: "скорость", axlePressure: "давление на оси", crashController: "контроллер аварии", instantFuel: "моментальный расход", adBlueLevel: "уровень AdBlue" } as Record)[key]}`, `reported.configuration.transmission.can.${key}`, { valueKind: "boolean" })), ], }, { id: "trajectory", label: "Траектория", title: "Отрисовка траектории и датчик движения", description: "Обычные и роуминговые интервалы, заморозка координат и параметры встроенного датчика движения.", access: "managed", fields: [ ...["normal", "roaming"].flatMap((mode) => { const label = mode === "normal" ? "Основной режим" : "Роуминг"; return [ managed(`${mode}-course`, `${label}: изменение курса`, `reported.configuration.trajectory.${mode}.courseDeltaDegrees`, { valueKind: "number", unit: "°" }), managed(`${mode}-speed`, `${label}: изменение скорости`, `reported.configuration.trajectory.${mode}.speedDeltaKph`, { valueKind: "number", unit: "км/ч" }), managed(`${mode}-distance`, `${label}: расстояние между точками`, `reported.configuration.trajectory.${mode}.distanceMeters`, { valueKind: "number", unit: "м" }), managed(`${mode}-parking`, `${label}: интервал на стоянке`, `reported.configuration.trajectory.${mode}.parkingIntervalSeconds`, { valueKind: "number", unit: "с" }), ]; }), managed("freeze-low-speed", "Заморозка координат при скорости ниже 2 км/ч", "reported.configuration.trajectory.freeze.lowSpeed", { valueKind: "boolean" }), managed("freeze-motion", "Заморозка по датчику движения", "reported.configuration.trajectory.freeze.motionSensor", { valueKind: "boolean" }), managed("freeze-ignition", "Заморозка по зажиганию", "reported.configuration.trajectory.freeze.ignition", { valueKind: "boolean" }), managed("freeze-quiet", "Тихоходная техника", "reported.configuration.trajectory.freeze.lowSpeedVehicle", { valueKind: "boolean" }), managed("motion-sensitivity", "Чувствительность датчика движения", "reported.configuration.motionSensor.sensitivity", { valueKind: "number" }), managed("motion-delay", "Задержка срабатывания", "reported.configuration.motionSensor.delaySeconds", { valueKind: "number", unit: "с" }), managed("motion-impact", "Порог удара", "reported.configuration.motionSensor.impact", { valueKind: "number" }), managed("motion-tilt", "Порог наклона", "reported.configuration.motionSensor.tilt", { valueKind: "number" }), ], }, { id: "io", label: "Входы / выходы", title: "Входы и выходы", description: "Режимы PIN0–PIN7 и пороги. Непосредственное переключение выходов относится к защищённым командам.", access: "managed", fields: [ ...managedIndexedFields(8, "pin-mode", "Режим PIN", "reported.configuration.io.pinModes"), managed("speed-coefficient", "Коэффициент датчика скорости", "reported.configuration.io.speedSensorCoefficient", { valueKind: "number" }), managed("virtual-ignition", "Порог виртуального зажигания", "reported.configuration.io.virtualIgnitionThresholdMv", { valueKind: "number", unit: "мВ" }), managed("analog-pin-2", "Порог аналогового входа PIN2", "reported.configuration.io.analogThresholds.pin2Mv", { valueKind: "number", unit: "мВ" }), managed("analog-pin-3", "Порог аналогового входа PIN3", "reported.configuration.io.analogThresholds.pin3Mv", { valueKind: "number", unit: "мВ" }), protectedField("output-4", "Команда выхода PIN4", "reported.operations.outputs.pin4"), protectedField("output-5", "Команда выхода PIN5", "reported.operations.outputs.pin5"), protectedField("output-6", "Команда выхода PIN6", "reported.operations.outputs.pin6"), ], }, { id: "ports", label: "Порты", title: "Цифровые порты и датчики", description: "RS232, RS485, CAN, Wi‑Fi, фотоснимки, 1‑Wire и фильтрация датчиков.", access: "managed", fields: [ managed("rs232", "RS232", "reported.configuration.ports.rs232.mode"), managed("rs485", "RS485", "reported.configuration.ports.rs485.mode"), managed("can-program", "Номер программы CAN", "reported.configuration.ports.can.program", { valueKind: "number" }), managed("can-internal", "Активировать внутренний CAN", "reported.configuration.ports.can.internalEnabled", { valueKind: "boolean" }), managed("can-seatbelt", "Контролировать ремень по CAN", "reported.configuration.ports.can.seatbelt", { valueKind: "boolean" }), managed("can-headlight", "Контролировать ближний свет по CAN", "reported.configuration.ports.can.headlight", { valueKind: "boolean" }), managed("wifi-ssid", "Wi‑Fi: имя сети", "reported.configuration.ports.wifi.ssid"), managed("wifi-password", "Wi‑Fi: пароль", "reported.configuration.ports.wifi.password", { sensitive: true }), managed("photo-interval", "Интервал фотоснимков", "reported.configuration.ports.camera.intervalMinutes", { valueKind: "number", unit: "мин" }), managed("photo-resolution", "Разрешение фотоснимков", "reported.configuration.ports.camera.resolution"), managed("one-wire-auto", "Сохранять новые термодатчики", "reported.configuration.ports.oneWire.autoDiscover", { valueKind: "boolean" }), ...managedIndexedFields(10, "one-wire", "Адрес термодатчика", "reported.configuration.ports.oneWire.sensorAddresses"), managed("median-filter", "Медианный фильтр датчиков", "reported.configuration.ports.sensorFilter.medianEnabled", { valueKind: "boolean" }), ...managedIndexedFields(4, "lls-filter", "Степень фильтрации LLS", "reported.configuration.ports.sensorFilter.lls", { valueKind: "number" }), ], }, { id: "modbus", label: "Modbus", title: "Параметры Modbus", description: "Последовательный порт, сетевые адреса и до десяти читаемых регистров.", access: "managed", fields: [ managed("modbus-baud", "Скорость обмена", "reported.configuration.modbus.baudRate", { valueKind: "number" }), managed("modbus-poll", "Таймер опроса", "reported.configuration.modbus.pollSeconds", { valueKind: "number", unit: "с" }), managed("modbus-parity", "Проверка на чётность", "reported.configuration.modbus.parity"), managed("modbus-stop", "Stop bits", "reported.configuration.modbus.stopBits"), managed("modbus-address-a", "Сетевой адрес датчика для регистров 1–5", "reported.configuration.modbus.addresses.first", { valueKind: "number" }), managed("modbus-address-b", "Сетевой адрес датчика для регистров 6–10", "reported.configuration.modbus.addresses.second", { valueKind: "number" }), ...modbusRegisterFields, ], }, { id: "bluetooth", label: "Bluetooth", title: "Bluetooth (BLE) датчики", description: "Режим BLE-модуля, код сопряжения и десять датчиков с выражениями универсальной интеграции.", access: "managed", fields: [ managed("ble-mode", "Режим работы Bluetooth", "reported.configuration.bluetooth.mode"), managed("ble-pairing", "Код сопряжения", "reported.configuration.bluetooth.pairingCode", { sensitive: true }), ...bleFields, ], }, { id: "driving-style", label: "Стиль вождения", title: "Стиль вождения", description: "Пороговые профили акселерометра и превышений скорости/оборотов.", access: "managed", fields: [ ...motionEventFields, managed("accelerometer-transmit", "Передавать данные акселерометра", "reported.configuration.drivingStyle.transmitAccelerometer", { valueKind: "boolean" }), managed("accelerometer-reset-events", "Передавать события сброса", "reported.configuration.drivingStyle.transmitResetEvents", { valueKind: "boolean" }), managed("accelerometer-bitmask", "Передавать состояния сработок", "reported.configuration.drivingStyle.transmitTriggerMask", { valueKind: "boolean" }), managed("accelerometer-average", "Глубина усреднения акселерометра", "reported.configuration.drivingStyle.averagingDepth", { valueKind: "number" }), ...violationFields, ], }, { id: "phones", label: "Телефоны", title: "Разрешённые телефоны", description: "До пяти номеров и индивидуальный режим доступа для SMS-управления.", access: "managed", fields: phoneFields, }, { id: "sim", label: "SIM-карты", title: "SIM-карты и мобильная сеть", description: "Параметры двух SIM-профилей. Пароли, PIN и USSD не возвращаются в открытом виде.", access: "managed", fields: [...simFields(1), ...simFields(2)], }, { id: "navigation", label: "Навигация", title: "Навигация и фильтрация координат", description: "Источники координат, спутниковые группировки, внешний локатор и фильтры качества.", access: "managed", fields: [ managed("nav-satellite", "Спутниковая навигация", "reported.configuration.navigation.sources.satellite", { valueKind: "boolean" }), managed("nav-wifi", "Wi‑Fi локатор", "reported.configuration.navigation.sources.wifi", { valueKind: "boolean" }), managed("nav-lbs", "LBS локатор", "reported.configuration.navigation.sources.lbs", { valueKind: "boolean" }), managed("nav-tag", "Навигационная метка", "reported.configuration.navigation.sources.tag", { valueKind: "boolean" }), ...["gps", "glonass", "galileo", "beidou"].map((key) => managed(`nav-${key}`, key.toUpperCase(), `reported.configuration.navigation.constellations.${key}`, { valueKind: "boolean" })), managed("locator-url", "URL локатора", "reported.configuration.navigation.locator.url", { sensitive: true }), managed("locator-moving", "Интервал локатора в движении", "reported.configuration.navigation.locator.movingIntervalSeconds", { valueKind: "number", unit: "с" }), managed("locator-parked", "Интервал локатора на стоянке", "reported.configuration.navigation.locator.parkedIntervalSeconds", { valueKind: "number", unit: "с" }), managed("filter-satellites", "Минимальное число спутников", "reported.configuration.navigation.filter.minimumSatellites", { valueKind: "number" }), managed("filter-hdop", "Максимальный HDOP × 10", "reported.configuration.navigation.filter.maximumHdopTimesTen", { valueKind: "number" }), managed("filter-altitude-min", "Минимальная высота", "reported.configuration.navigation.filter.minimumAltitudeMeters", { valueKind: "number", unit: "м" }), managed("filter-altitude-max", "Максимальная высота", "reported.configuration.navigation.filter.maximumAltitudeMeters", { valueKind: "number", unit: "м" }), managed("filter-speed-min", "Минимальная мгновенная скорость", "reported.configuration.navigation.filter.minimumInstantSpeedKph", { valueKind: "number", unit: "км/ч" }), managed("filter-speed-max", "Максимальная мгновенная скорость", "reported.configuration.navigation.filter.maximumInstantSpeedKph", { valueKind: "number", unit: "км/ч" }), managed("filter-speed-average", "Максимальная средняя скорость", "reported.configuration.navigation.filter.maximumAverageSpeedKph", { valueKind: "number", unit: "км/ч" }), managed("filter-time", "Максимальное время фильтрации", "reported.configuration.navigation.filter.maximumSeconds", { valueKind: "number", unit: "с" }), ], }, { id: "system", label: "Системные", title: "Системные параметры", description: "Системные интервалы и энергосбережение. Секретные значения отображаются только как факт наличия.", access: "managed", fields: [ managed("sms-password", "Пароль устройства (SMS)", "reported.configuration.system.smsPassword", { sensitive: true }), managed("web-check-hours", "Проверять WEB-конфигуратор каждые", "reported.configuration.system.webConfiguration.checkHours", { valueKind: "number", unit: "ч" }), managed("web-check-start", "Проверять WEB-конфигуратор при старте", "reported.configuration.system.webConfiguration.onStart", { valueKind: "boolean" }), managed("sleep-mode", "Режим сна", "reported.configuration.system.powerSaving.mode"), managed("sleep-wake-interval", "Выходить на связь каждые", "reported.configuration.system.powerSaving.wakeIntervalMinutes", { valueKind: "number", unit: "мин" }), managed("sleep-online", "Время пребывания на связи", "reported.configuration.system.powerSaving.onlineMinutes", { valueKind: "number", unit: "мин" }), managed("sleep-motion", "Выходить из сна по датчику движения", "reported.configuration.system.powerSaving.wakeOnMotion", { valueKind: "boolean" }), managed("sleep-input", "Выходить из сна по изменению входа", "reported.configuration.system.powerSaving.wakeOnInput", { valueKind: "boolean" }), managed("battery-ignition", "Заряжать АКБ только при включённом зажигании", "reported.configuration.system.chargeBatteryOnIgnitionOnly", { valueKind: "boolean" }), ], }, { id: "diagnostics", label: "Диагностика", title: "Диагностика и операции", description: "Доступные B2 операции отражены полностью, но выполняются только через подтверждённый двусторонний канал и отдельный command ledger.", access: "protected", fields: [ field("debug-last-session", "Последняя удалённая отладка", "reported.diagnostics.lastSessionAt", { valueKind: "date" }), field("debug-output", "Результат удалённой отладки", "reported.diagnostics.output"), protectedField("op-packet", "Запросить пакет телеметрии", "reported.operations.requestTelemetry"), protectedField("op-info", "Запросить информацию", "reported.operations.requestInfo"), protectedField("op-coordinates", "Запросить координаты", "reported.operations.requestCoordinates"), protectedField("op-config", "Синхронизировать настройки", "reported.operations.syncConfiguration"), protectedField("op-restart", "Перезапустить устройство", "reported.operations.restart"), protectedField("op-clear", "Очистить память", "reported.operations.clearMemory"), protectedField("op-firmware", "Обновить прошивку", "reported.operations.updateFirmware", { description: "Запрещено для пилотного B2" }), ], }, ], }; const genericCatalog: DeviceProfileCatalog = { profileRef: "generic.device.v1", vendor: "NODE.DC", model: "Generic device", title: "Устройство", sections: ARUSNAVI_B2_CATALOG.sections.filter((section) => ["passport", "live"].includes(section.id)), }; const catalogs = new Map([ [ARUSNAVI_B2_CATALOG.profileRef, ARUSNAVI_B2_CATALOG], ]); export function getDeviceProfileCatalog(profileRef: string): DeviceProfileCatalog { return catalogs.get(profileRef) ?? { ...genericCatalog, profileRef }; } export function accessLabel(access: DeviceFieldAccess) { return ({ "read-only": "Только чтение", managed: "Управляемая настройка", protected: "Защищённая операция", })[access]; }