466 lines
35 KiB
TypeScript
466 lines
35 KiB
TypeScript
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"> = {},
|
||
): DeviceProfileField => ({ key, label, path, ...options });
|
||
|
||
const managed = (
|
||
key: string,
|
||
label: string,
|
||
path: string,
|
||
options: Omit<DeviceProfileField, "key" | "label" | "path" | "access"> = {},
|
||
) => field(key, label, path, { ...options, access: "managed" });
|
||
|
||
const protectedField = (
|
||
key: string,
|
||
label: string,
|
||
path: string,
|
||
options: Omit<DeviceProfileField, "key" | "label" | "path" | "access"> = {},
|
||
) => 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<DeviceProfileField, "key" | "label" | "path" | "access"> = {},
|
||
) => 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<string, string> = {
|
||
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<string, string>)[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<string, DeviceProfileCatalog>([
|
||
[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];
|
||
}
|