Идентификаторы: как найти нужное помещение
Любая интеграция из этого раздела сводится к одному вопросу: какому узлу графа здания соответствует запись в вашей системе? Ответите правильно — всё остальное трубопровод. Ответите неправильно — увидите 200 OK с written: 0 или значения, приехавшие не на тот этаж.
Граф, коротко
площадка → здание → этаж → помещение → элемент (оборудование) → точка (показание датчика)Слои данных крепятся к помещениям. Заявки ссылаются на spaceId, storeyId или elementId. Телеметрия крепится к точке, которая принадлежит элементу, а тот стоит в помещении. Дерево одно, разная глубина.
Два идентификатора у каждого узла
| Что это | Переживает | Когда использовать | |
|---|---|---|---|
id | Внутренний UUID, который присваивает платформа | Всё, кроме переимпорта с replace | У здания нет BIM-модели либо вы уже разрешили идентификатор |
externalId | Идентификатор из системы-источника — IFC GlobalId, если здание пришло из BIM | Переимпорт: GlobalId приходит из самой модели | Здание пришло из BIM и ваши данные привязаны к модели |
У каждого узла есть ещё sourceSystem — метка происхождения: ifc у всего, что создал импорт IFC. Уникальность задана на тройке (buildingId, sourceSystem, externalId). Это значит, что ваша система может проставить свои externalId под своим sourceSystem и не столкнуться с уже существующими идентификаторами IFC.
Есть ещё code ("B1-S02-SP003") — машиночитаемая метка для человека, уникальная в пределах здания. Полезна в выгрузках; ключом поиска в API она не является.
Почему для BIM-здания GlobalId — более надёжный ключ связи
Внутренние идентификаторы пересоздаются, когда модель переимпортируют с replace: true. IFC GlobalId — нет: он приходит из самой модели и переживает цикл. Если здание пришло из BIM, стройте интеграцию на externalId — и вам не придётся заново разрешать всё после очередной ревизии модели.
Какой идентификатор ждёт каждый API
| API | Принимает |
|---|---|
Значения слоя (PUT …/values) | Любой — keyBy: "externalId" (по умолчанию, GlobalId) либо keyBy: "id" (внутренние UUID) |
Заявки (POST /requests) | Только внутренние UUID — spaceId, storeyId, elementId |
Телеметрия (POST …/observations) | pointId — внутренний UUID, точка должна уже существовать в этом здании |
| Создание помещений / элементов | Ваши собственные externalId + sourceSystem, если они вам нужны |
Слои данных — самый снисходительный случай: передайте GlobalId, и они сами их разрешат. Именно поэтому «идентификатор помещения и какие-то данные» — это действительно весь payload слоя, и именно поэтому слой — самая быстрая первая интеграция.
Как построить соответствие
Всему, что требует внутренних UUID (в первую очередь заявкам), нужна карта GlobalId → id. Постройте её один раз и держите в кэше:
GET /api/v1/buildings/{buildingId}/spaces?limit=1000&offset=0В каждой строке есть id, externalId, code, name, этаж и тип помещения — одного прохода хватает на все ключи связи.
Серверного поиска по externalId нет
Список помещений фильтруется по storeyId, spaceTypeSlug, status и isLeasable — но не по externalId и не по code. Спросить «какое помещение соответствует GlobalId X» нельзя: вы постранично выбираете здание и строите индекс у себя. Для здания в несколько тысяч помещений это несколько запросов, один раз, на старте.
Размер страницы: текущий tv-api допускает limit до 1000. Если приходит 400, значит контур, с которым вы говорите, всё ещё ограничен сотней, — переходите на 100, а не падайте.
Обновляйте карту после переимпорта модели. Значения, привязанные к GlobalId, переимпорт переживают; закэшированные внутренние UUID — не обязательно.
Когда BIM-модели нет
Таких зданий много. Их помещения рисуют в редакторе карт или создают через API, и GlobalId у них нет — externalId пустой. Два варианта:
- Использовать внутренние идентификаторы: грузить значения слоя с
keyBy: "id". - Проставить свои внешние идентификаторы: при создании помещения (
POST /api/v1/buildings/{buildingId}/spaces) передайте свойexternalIdвместе с выбранным вамиsourceSystem("acme-fm"). ДальшеkeyBy: "externalId"будет совпадать с вашими идентификаторами напрямую, и таблица соответствия вам вообще не понадобится.
Вариант 2 лучше, когда именно ваша система знает состав помещений.
Когда не совпало
Загрузка, не совпавшая ни с чем, сама скажет, какая из трёх причин сработала:
diagnostics.reason | Что значит | Что делать |
|---|---|---|
BUILDING_HAS_NO_SPACES | Граф пуст: ничего не импортировали и не создавали | Импортировать модель или создать помещения |
BUILDING_HAS_NO_EXTERNAL_IDS | Помещения есть, GlobalId нет ни у одного | Перейти на keyBy: "id" или проставить внешние идентификаторы |
IDENTIFIERS_NOT_IN_BUILDING | Обе стороны заполнены, но идентификаторы не из этого здания | Проверить buildingId — обычно это устаревший id в конфиге либо идентификаторы из другой модели той же площадки |
Частичное совпадение ошибкой не считается: в unmatched перечислены ровно те ключи, которым не нашлось помещения, остальные записаны. Логируйте это и поднимайте тревогу по порогу — так вы поймаете день, когда на этаже перенумеровали помещения.
Один пограничный случай: одно и то же помещение может фигурировать под несколькими GlobalId, если у площадки несколько моделей по разделам. Если два идентификатора в одном payload разрешаются в одно помещение, побеждает последний — ошибки не будет, поэтому держите в одном payload один идентификатор на помещение.