Подсистема контроля доступа: кто, куда и когда может пройти. Документ описывает устройство подсистемы, принятые решения и проверенные на живом оборудовании факты.
Дата: 2026-09-29.
До появления подсистемы СКУД работал с картами напрямую: карта была и учётной записью, и владельцем, и носителем прав. Для одного контроллера и десятка карт этого хватало. Когда система разворачивается в организации с отделами и несколькими контроллерами, такого подхода не хватает.
Три причины, по которым сущности разделены:
- У человека несколько карт. Основная карта и брелок. Пока карта была владельцем, уход сотрудника требовал найти и удалить все его носители вручную, и одну забытую карту никто бы не заметил.
- Права зависят от отдела. Бухгалтерии нужна одна дверь, складу — другая. Заводить права каждому сотруднику значит повторять действия и ошибаться при приёме нового человека.
- Дверь — это не контроллер. У одного устройства бывает два проёма, и им нужны разные права.
Пользователь (ФИО, должность, фото)
│
├── карта (основная) ─┐
├── карта (брелок) │ N карт у одного человека
└── ... ─┘
│
├── группа «Бухгалтерия» ──┐
├── группа «Склад» │ M групп у одного человека
└── ... ─┘
│
│ права группы
▼
дверь «Входная» ──── контроллер Z5R
дверь «Склад» ──── контроллер SKUD-01
Права живут у нас, на контроллер уходит результат. Контроллер про группы и отделы не знает — он получает коды карт и зоны доступа. Отсюда масштабируемость: новый контроллер достаточно завести и выдать ему базу, а не настраивать права заново.
Итоговое право на дверь складывается из двух источников:
- Группы. Дверь доступна, если она разрешена хотя бы одной группой владельца.
- Личные правила. Перекрывают групповые в обе стороны: разрешают дверь вне групп или забирают её, даже если группа разрешает.
Запрет важнее разрешения. Если группа дверь разрешает, а лично она запрещена — доступа нет. Иначе нельзя было бы закрыть доступ человеку, не убирая его из группы.
В интерфейсе у каждой двери видно происхождение права (source):
| Источник | Что значит |
|---|---|
group |
разрешено группой (перечислены группы) |
personal |
личное разрешение |
denied |
личный запрет перекрыл групповое право |
Дверь с denied остаётся в списке намеренно: оператор должен видеть,
что доступ есть у группы, но закрыт персонально, — иначе он будет искать
причину в настройках группы.
blocked = true закрывает доступ по всем картам сразу. Это главный
сценарий увольнения: одна отметка вместо поиска всех носителей.
Карты при этом не удаляются, а помечаются неактивными: при возвращении доступа (отпуск, восстановление карты) не нужно заводить их заново. Запись владельца тоже сохраняется — на неё ссылается история проходов.
Ключевой факт, выяснившийся на живом парке: контроллеры работают не с Wiegand-26.
Контроллер Z5R WEB BT использует шестибайтовый код: два старших байта — facility, четыре младших — номер карты. То есть facility занимает 16 бит, номер — 32 бита.
Первая версия схемы ограничивала поля по Wiegand-26 (facility 8 бит,
номер 16 бит). На живом парке из 21 карты ни одна не помещалась в эти
пределы: номера вида 12570523 больше 65535.
Поэтому:
| Поле | Диапазон | Обоснование |
|---|---|---|
facility |
0 … 65535 | 16 бит, формат контроллера |
card |
0 … 4294967295 | 32 бита, формат контроллера |
Проверка общая (отсекает то, что не примет ни одно устройство). Свои, более узкие пределы проверяет адаптер конкретного вендора: SKUD работает по Wiegand-26 и откажет в карте с большим номером сам.
Режим означает «дверь открывается всем, поднесённые карты записываются». Нужен для быстрой записи карт на объекте: оператор обходит бригаду и подносит существующие карты, не заводя их вручную.
Режим опасен — на время его действия проём фактически не заперт. Поэтому:
- Срок ограничен: от 5 до 20 минут. Меньше пяти — оператор не успеет дойти до считывателя. Больше двадцати — риск, что о режиме забудут, перевешивает удобство.
- Выключается сам по истечении срока. Фоновый обход раз в минуту страхует даже те случаи, когда таймер не сработал.
- Автор записывается. Если проём окажется открытым в нерабочее время, по журналу должно быть видно, кто включил режим.
- Счётчик записанных карт показывается оператору: если он не растёт, значит карты не считываются.
Важное расхождение с ожиданием: режим управляется через REST-интерфейс
POST /offline, а не командой access_mode протокола WEBJSON.
Проверено на живом контроллере: команды access_mode по WEBJSON нет,
и контроллер её молча игнорирует. Через REST работает:
POST /offline {"mode":5,"state":1} # включить (setup_mode станет "5")
POST /offline {"mode":0,"state":0} # выключить (setup_mode вернётся "0")
GET /offline # {"access_mode":"0","setup_mode":"5","time":"0"}Значения приходят строками, а не числами — "5", а не 5.
После команды адаптер проверяет setup_mode. Проверка обязательна:
на живом устройстве команда уходила без ошибки, а режим не менялся —
оператор подносил бы карты в уверенности, что они записываются.
SyncHolderCards выдаёт карты владельца на контроллеры, где у него есть
доступ, и убирает оттуда, где доступа больше нет. Вызывается автоматически:
- после правки владельца (включая блокировку);
- после правки группы (права всех участников);
- после привязки или отвязки карты;
- после удаления группы или двери.
SyncAll выдаёт всю базу на все контроллеры. Нужен при заведении нового
устройства: права у людей уже настроены, а карты на нём ещё не заведены.
Поле sync_pending отмечает карты, ожидающие выгрузки. Полная выдача
при каждом изменении переписывала бы всю базу, а контроллер подключён
по Wi-Fi — это лишние обращения и риск потерять связь в середине записи.
Страница /access в Web UI. Отдельно от /acs: там железо (контроллеры,
их состояние, события), здесь люди, группы и права. Оператору, который
выдаёт пропуск, разбираться в настройках контроллеров не нужно.
Четыре вкладки:
- Сотрудники — карточки владельцев: фотография, ФИО, должность, отдел, телефон, заметка, признак «доступ закрыт», группы, права по дверям с указанием источника и список карт.
- Группы — отделы с набором дверей.
- Двери — точки прохода с привязкой к контроллеру и направлением.
- Запись карт — режим Accept с выбором длительности.
Права в карточке показываются как результат расчёта, а не как хранимые записи: у каждой двери видно, откуда пришёл доступ — из группы или из личного правила. Иначе при разборе жалобы «у него не открывается» непонятно, что править: состав групп или личное исключение.
Настольные USB-считыватели работают как клавиатура: печатают номер карты
цифрами и завершают Enter. Отдельный драйвер не нужен — достаточно
перехватить нажатия (CardReaderInput в HolderModal.tsx).
Признак считывателя — скорость набора: пауза между символами у него меньше 80 мс, человек так не печатает. Без этого признака ручной ввод номера превращался бы в случайную карту из первых цифр.
Разбор номера: до 5 цифр и 6–8 цифр считаются Wiegand (первые три цифры —
facility, остальные — код), длиннее 8 — десятичный код карты целиком.
Проверено: код 0001234567 завелся как 0:1234567.
Карта заводится сразу на контроллер и привязывается к владельцу, чтобы оператор приложил её один раз, а не набирал код руками на двух экранах.
Сброшенный контроллер в сети не находится обычным сканированием, и это не поломка — так задумано производителем.
На плате контроллера есть перемычка с тремя положениями, и она меняет поведение устройства при подаче питания:
| Положение | Что происходит |
|---|---|
| CONFIG | Контроллер запускается в режиме настройки. Он не работает с локальной сетью — поднимает собственную точку доступа |
| NORMAL | Обычный режим: модуль связи входит в локальную сеть, веб-интерфейс доступен по его адресу |
| LOCK | То же, но веб-интерфейс настройки заблокирован |
Отсюда главное: в режиме CONFIG контроллер по Ethernet молчит. Это не поломка и не потеря связи, а нормальное поведение — устройство перешло на другой канал.
Установить перемычку в CONFIG, подать питание, затем подключиться одним из двух способов:
- по Wi-Fi — дождаться сети с именем
Z5RWEB-BT_XXXXXXи подключиться к ней; пароль —AUTH_KEY; - по Ethernet — соединить компьютер и контроллер кабелем, дождаться
сообщения о подключении (до 45 секунд) и открыть в браузере
http://192.168.10.1.
Логин z5rweb, пароль AUTH_KEY.
192.168.10.1 — настоящий адрес, а не пример из руководства. Он
относится не к заводским настройкам, а к режиму настройки: контроллер
берёт его, когда подано питание с перемычкой CONFIG. Ошибиться здесь легко:
тот же адрес встречается в руководстве как образец, и его можно принять за
пример, тогда как он указывает на реальное поведение устройства.
Если в машине нет Wi-Fi, соединиться с точкой доступа контроллера не получится: серверу нужен беспроводной адаптер. Настройку выполняют с ноутбука, планшета или телефона. Вариант по Ethernet этого не требует — достаточно кабеля до контроллера.
Перемычка CLR (№2) стирает память контроллера: карты, историю событий и заданное время открывания (возвращается заводское — 3 секунды).
Если при этом дополнительно установить перемычку в положение №3 (ADD), стираются и конфигурации — контроллер возвращается к заводским настройкам.
Процедура в обоих случаях одна: выключить питание → установить перемычки → включить питание. О завершении стирания сообщает серия коротких звуковых сигналов. Без выключения и повторной подачи питания стирание не выполнится — перемычки читаются только при запуске.
Перемычка CONFIG и сброс мешают друг другу. Если вместе с CLR и ADD установлена ещё и CONF, контроллер стартует в режиме настройки, и стирание может не выполниться. Правильный порядок — двумя отдельными запусками: сначала сброс (CLR и ADD, без CONF), затем настройка (CONF).
- Выключить питание. Установить перемычки сброса (CLR и ADD), CONF снять. Подать питание, дождаться серии коротких сигналов.
- Выключить питание. Снять перемычки сброса, установить CONF. Подать
питание — контроллер поднимется в режиме настройки на
192.168.10.1. - Зайти с логином
z5rwebи паролемAUTH_KEYс наклейки на корпусе. Задать адрес из подсети сервера либо включить получение адреса по DHCP, и выбрать нужный режим связи. - Выключить питание. Снять CONF, установить NORMAL. Подать питание.
- Контроллер доступен по своему адресу обычным образом — ищется сканированием и заводится в систему.
Заводские настройки модуля связи: Ethernet, режим DHCP, способ связи «Сервер», локальный порт 1000.
| Проверка | Итог |
|---|---|
| Перемычка CONF + питание | Контроллер поднялся на 192.168.10.1 и отвечает |
| Заголовок без пароля | Basic realm="Z-5R WEB BT 45006509" — тот же серийный номер, что был до настройки |
AUTH_KEY с наклейки |
Отвергнут (401) — значит пароль остался прежним |
Отсюда практический вывод: по сетевому адресу и по паролю нельзя судить о том, что сброс выполнен. У этого контроллера адрес остался статическим из прежней установки, а пароль — прежним; то есть стирание конфигурации не произошло, хотя перемычки были установлены. Причина, по-видимому, в одновременно стоявшей перемычке CONFIG.
Признак устройства в ответе на запрос без пароля. Контроллер отвечает
кодом 401 и заголовком WWW-Authenticate: Basic realm="Z-5R WEB BT <серийный номер без дефиса>". По этому заголовку контроллер опознаётся
надёжно, ещё не имея пароля, и по нему же видно, что за устройство:
серийный номер в заголовке отличает один контроллер от другого.
Контроллер может быть виден в сети и без рабочего адреса. Устройство
отправляет служебные кадры, поэтому его MAC-адрес появляется в таблице
коммутатора. Это подтверждает, что устройство подключено и работает, и
отличает этот случай от случая, когда оно выключено. Сопоставлять надо по
началу MAC-адреса: у IronLogic это 14:13:30. Когда устройство молчит
полностью — ни кадров, ни таблицы — это признак того, что оно выключено или
находится в режиме настройки с другим каналом связи.
Этот раздел появился потому, что чтение базы карт с контроллера долго не получалось, а без понимания общей схемы обмена причина не находилась.
ПК ──USB или Ethernet──> конвертер Z-397 ──собственный интерфейс──> контроллер
Конвертер — отдельное устройство той же линейки. Модели: Z-397 (USB),
Z-397 IP (Ethernet), Z-397 Web. Контроллеры подключаются к конвертеру,
а не к компьютеру напрямую, и у контроллера есть сетевой адрес 1–254
внутри этой внутренней шины. Именно поэтому у Z-5R WEB BT есть поле
сетевого адреса, которое ни на что в IP-сети не влияет — оно относится
к шине конвертера.
Из этого следует важное практическое ограничение: по сети контроллер доступен только своим веб-интерфейсом и протоколом событий. Полноценная работа с базой карт (чтение и запись ключей) идёт по интерфейсу конвертера, и проверить её можно только при наличии самого конвертера; у нас сейчас он USB, а не Ethernet.
Наш сервер работает с контроллером через WEBJSON — протокол событий
и команд. Он даёт: журнал проходов, активацию, запись карт. Чтения базы
он не даёт, хотя документация его описывает (раздел 3.9 — команда
read_cards). Проверено на живом устройстве: команда уходит корректно,
а в ответ приходят только ping. Подробности — в разделе «Открытые
вопросы» ниже.
Рабочая замена чтению — считывание карты со считывателя двери. Оно описано там же.
Контроллер хранит ключи не как пару «facility + номер», а как записи с дополнительными признаками. Это важно при переносе базы на другое устройство:
| Признак | Что означает |
|---|---|
| Размер | размер номера ключа в байтах (ключ — массив произвольной длины) |
| Тип | простой, мастер, блокирующий |
| Банк / ячейка | номер банка памяти и ячейки в нём |
| Доступ | «Всегда», «Никогда» или номера временных зон |
| Короткий | сравнение номера по 3 байтам (24 бита) |
| Двойной | ключ состоит из двух частей: карта + код клавиатуры |
| Заблокирован | ключ остаётся в памяти, но доступ всегда запрещён |
У нас хранятся только facility и card (плюс тип ключа). Временные
зоны, банки и ячейки не сохраняются: пока устройство отдаёт события,
потерь не видно, но при переносе базы эти параметры придётся задавать
заново.
Причина установлена по документации производителя (описание протокола WEBJSON) и проверкой на живой прошивке 2.41.
Протокол чтение описывает, но контроллер его не выполняет. В разделе
3.9 сказано, что read_cards вычитывает все карты и присылает их
пачками по 11 штук, а ответом служит документ {"cards":[...]} без
поля operation. Мы разобрали этот формат — и всё равно ничего не
пришло.
Проверено на живом устройстве: за один сеанс чтения контроллер прислал
1943 документа, и все — ping. Ни одного с полем cards. Команда
read_cards при этом уходила корректно, в ответе на ping, с
подставленным id.
Асимметрия подтверждена:
| Операция | Результат |
|---|---|
Запись (add_cards) |
Работает — контроллер отвечает {"success":1} |
Чтение (read_cards) |
Не работает — 1943 ping и ни одного cards |
То есть выдать ключ на контроллер мы можем, а снять базу с него — нет.
Проверь также порты контроллера: открыт только веб-порт 80. Порты
1000 (режим TCP-сервера, server.port в конфигурации) и 25000
(client.remport) закрыты. Значит, канала программирования, который
использует штатная программа управления, у нас нет.
Что это значит. Выгрузить базу с чужого контроллера нельзя — он покажет только те карты, которые через него провели. Для резервного копирования это ограничение непреодолимо без конвертера Z-397.
Что работает вместо чтения: считывание карты со считывателя двери (см. ниже). Оно даёт номера карт, но не даёт сведений о том, какие карты уже лежат в памяти устройства.
Задача: оператору нужно назначить человеку пропуск, но номера карты он не знает — на самой карте номер не напечатан.
Решение: поймать карту из потока событий. Считыватель на двери отдаёт
карту событием доступа, поэтому «прочитать карту» здесь означает
«подождать событие». Механизм работает всегда, в отличие от read_cards.
Как это устроено:
- Оператор нажимает «Ждать карту» на странице «Доступ», вкладка «Запись карт».
- Сервер запоминает ожидание на этом контроллере (
CardCaptureManager). - Карта подносится к считывателю двери. Контроллер присылает событие.
- Обработчик потока событий забирает карту из события до записи в журнал — иначе карта, поднесённая для назначения пропуска, выглядела бы в журнале проходом человека.
- Интерфейс опрашивает состояние раз в 2 секунды и показывает номер.
Дверь при этом не открывается всем — этим считывание и отличается от режима записи карт (Accept).
Проверено на живом контроллере: карта 0:16717700 поймана со
считывателя двери, тип события access_granted (карта контроллеру
известна), в журнал проходов запись не попала.
Ожидание одно на контроллер: считыватель физически один, и второе ожидание означало бы, что карта уйдёт не тому оператору. Срок ограничен (1–30 минут) и истекает сам — если оператор закрыл браузер, следующая карта не попадёт в брошенный сеанс.
USB-считыватели работают как клавиатура: печатают номер цифрами и
завершают Enter. Отдельного драйвера не нужно — интерфейс перехватывает
нажатия (CardReaderInput в HolderModal.tsx).
Признак считывателя — скорость набора: пауза между символами у него меньше 80 мс, человек так не печатает. Без этого признака ручной ввод номера превращался бы в случайную карту из первых цифр.
У контроллеров IronLogic ключ несёт не только «кому можно», но и «для чего он». Три типа, как их называет веб-интерфейс устройства и штатная программа управления:
| Тип | Значение |
|---|---|
simple |
обычный пропуск: открывает дверь по правам владельца |
master |
служебный: программирует контроллер, дверь не открывает |
blocking |
переключатель режима: в обычном состоянии работает как пропуск |
Ограничение протокола. WEBJSON назначение ключа отдельным полем не
передаёт: в разделе 3.6 описаны только два бита — 8 (блокирующая
карта) и 32 (короткий код, три байта). Поэтому:
- при записи мастер-ключ выдаётся с флагом блокировки — так контроллер опознаёт ключ, но не открывает дверь, что и требуется от мастер-ключа;
- при чтении с устройства тип восстанавливается как «простой»: знать, какой ключ был мастером, серверу неоткуда. Мастер-ключом контроллер программируют через штатную программу управления.
Тип хранится в нашей базе, и оператор видит назначение ключа в карточке владельца. Если тип не задан, ключ считается простым — поле появилось позже самих карт, и требовать его от старых клиентов нельзя.
Обходной путь (режим Accept + события access_granted) остаётся рабочим
для первичного наполнения, но выгрузить базу с чужого контроллера им
нельзя — он покажет только те карты, которые через него провели.
Полное снятие образа контроллера требует канала к конвертеру, которого у нас нет. Поэтому копию имеет смысл строить из нашей базы, которая является источником истины, и дополнять чтением того, что контроллер отдаёт сам.
Список «на выброс» (если чтение с железа так и не получится): карты, заведённые на контроллере в обход сервера. Их видно только через штатную программу управления — то есть при замене контроллера такие карты придётся заводить заново вручную.
Работает. Проверено на живом Z5R: команды add_cards уходят в ответ на
ping контроллера, устройство подтверждает их ответом {"success":1}.
Порядок выдачи важен, и две ловушки выглядят как «запись не работает»:
- Права считаются от владельца. Контроллеры, где у человека есть доступ хотя бы к одной двери, получают его карты. Карта с привязкой к другому контроллеру не выдаётся — это не ошибка.
- Заблокированный владелец теряет доступ везде, и его карты снимаются с контроллеров. В базе оставался тестовый флаг блокировки, и на вопрос «почему карта не выдаётся» отвечал не код выдачи, а права.
Проверять выдачу надо после снятия блокировки, иначе ошибку будешь искать не там.
sync_pending снимается только после успешной записи на устройство.
В UpsertCardOnController была ошибка: при успешном обновлении карты
функция выходила до отметки, и карта навсегда оставалась «ожидающей
выдачи». В интерфейсе она выглядела невыданной, хотя на контроллере уже
была, и оператор выдавал её повторно. Отметка теперь ставится в обоих
случаях — и при обновлении, и при добавлении.
Контроллер обращается к нам по адресу, который ему когда-то записали.
Если он указывает на 127.0.0.1, контроллер опрашивает сам себя:
ping в логах не появляется, выдача карт висит до таймаута, а /stat
показывает conn":"http://127.0.0.1:8080/...".
Адрес берётся из PUBLIC_URL. Она должна быть проброшена в контейнер
backend в docker-compose.yml: переменная, лежащая только в .env,
внутрь не попадает, и сервис молча подставляет localhost. Проверка:
docker compose exec backend printenv | grep PUBLIC_URLВернуть адрес контроллеру можно из интерфейса или запросом
POST /acs/controllers/{id}/workmode/server — он перечитает PUBLIC_URL
и перезапишет настройку контроллера.
Домофон DS07P-LP заведён в СКУД как контроллер доступа с вендором
beward. Причина не в удобстве, а в устройстве объекта.
Реле домофона подключено к входу «кнопка выхода» контроллера Z5R. Это значит: когда дверь открывают с домофона, для Z5R это выглядит нажатием кнопки выхода, а не проходом по карте.
Следствие видно в журнале. Открытие с домофона оставляет в Z5R запись
exit_button — и по ней нельзя понять, кто открыл дверь и откуда: с
домофона, из веб-интерфейса Z5R или с настоящей кнопки у двери. Все три
случая выглядят одинаково.
Поэтому журнал ведём у себя. Открытие через наш интерфейс попадает в журнал СКУД с причиной и указанием, что оно отправлено домофоном. Без этого о таком открытии не остаётся следа.
Важно учитывать при разборе журнала: одно открытие с домофона даёт
две записи — нашу (с причиной) и exit_button от Z5R. Это не два
разных прохода, а одно действие, увиденное с двух сторон.
Устройство про двери ничего не знает: у него реле, а не дверь. Поэтому в
списке одна дверь с адресом relay1 и названием «Дверь (реле домофона)».
Выдавать три выхода, которыми никто не управляет, значило бы показать
оператору кнопки, ведущие в никуда.
Открытие даёт импульс на одну секунду — ровно то, что делает кнопка выхода. Длительность задаётся в самом устройстве, поэтому в команде не передаётся.
Устройство сообщает только тревоги, и на проверенном экземпляре они
всегда отвечают NO Alarm. Положение двери — открыта она или заперта —
из домофона узнать нельзя.
Поэтому поля «заперто» и «открыто» намеренно не выставляются, а состояние
в списке показывается словом unknown. Показать «заперто» там, где мы
этого не знаем, — хуже, чем показать неизвестность: оператор не пойдёт
проверять то, что ему подтвердили как исправное. На такой ошибке в этом
проекте уже попадались часы камеры, показывавшие «точно» при
непрочитанном времени.
Домофон не отдаёт журнал событий по запросу — он сам обращается к серверу, когда что-то происходит (вызов, открытие двери, прочитанный номер). На проверенном экземпляре эта отправка выключена.
Опрос вместо отправки не годится: единственное доступное чтением —
состояние тревог, и оно отвечает NO Alarm всегда. Опрос давал бы
пустоту, но с постоянными обращениями к устройству.
Поэтому подписка на события возвращает открытый молчащий канал. Выдуманных записей в журнале не будет: запись о проходе должна означать, что событие действительно произошло.
| Что | Итог |
|---|---|
Связь (Ping) |
Работает — в списке контроллеров значится online |
| Список дверей | Работает — одна дверь найдена |
| Состояние двери | Отвечает NO Alarm (положение двери недоступно) |
| Открытие двери | Не проверялось — проверка означает открыть реальную дверь |
Адрес, имена параметров и значения команды открытия выверены по описанию интерфейса и покрыты проверками на подставном устройстве: команда собирается именно та, что замыкает реле. Но сработает ли она на живой двери, покажет только открытие.
Устройство уже настроено на объекте: регистрация на SIP-сервере
192.168.1.68, счёт 101, вызовы уходят на номера 200 и 111.
Нажатие цифр 123 во время разговора замыкает реле и завершает вызов.
Двусторонний звук со стороны устройства включён (DoubleAudio=on):
микрофон и динамик отдаются в RTSP-поток. То есть звук уже есть в потоке,
который мы и так принимаем, — отдельная настройка устройства не нужна.
Что для этого нужно с нашей стороны — отдельная работа, в этот заход не входила.
| Метод | Путь | Назначение |
|---|---|---|
| GET/POST | /acs/holders |
список и создание владельцев |
| GET/PUT/DELETE | /acs/holders/{id} |
карточка владельца |
| POST/GET | /acs/holders/{id}/photo |
фотография (загрузка телом запроса) |
| POST | /acs/holders/{id}/cards |
привязать карту |
| DELETE | /acs/holders/cards/{cardID} |
отвязать карту |
| GET/POST | /acs/groups |
группы доступа |
| GET/PUT/DELETE | /acs/groups/{id} |
группа с правами |
| GET/POST | /acs/doors |
двери |
| PUT/DELETE | /acs/doors/{id} |
дверь |
| POST | /acs/sync-all |
выдать всю базу |
| POST | /acs/check-access |
проверка доступа по карте |
| GET/POST/DELETE | /acs/controllers/{id}/accept |
режим Accept |
| GET/POST/DELETE | /acs/controllers/{id}/capture |
считывание карты со считывателя двери |
Формат хранения — minio:<key> или local:<путь>, как у эталонных
снимков лиц. В базе хранится путь, а не двоичные данные: снимки
по 200–500 КБ на несколько сотен карт превратили бы базу в архив
изображений.
Отдаётся фотография через сервер, а не ссылкой на MinIO:
presigned-ссылка привязана к имени хоста и не работает снаружи, а снимок
нужен в интерфейсе по обычному тегу img.
Не работает. Команда уходит с форматом, точно совпадающим с документацией (раздел 3.9):
{"id":1073741825,"operation":"read_cards"}Но контроллер не отвечает ни подтверждением {"id":N,"success":1},
ни документом cards. При этом он присылает ping каждые 10 секунд,
то есть активен и связь есть.
Наблюдение: контроллер не подтверждает ни одну команду сервера, кроме
set_active. Команды через REST (open_door, режим Accept) при этом
работают.
Гипотеза для проверки: команды чтения и записи карт у Z5R доступны только
через REST-интерфейс, а WEBJSON поддерживает лишь события и активацию.
Прямые REST-запросы /cards, /read_cards, /keys возвращают 404.
Обходной путь, который работает: включить режим Accept, поднести
карты — они приходят событиями access_granted с кодом карты, и оттуда
записываются в базу. Это надёжнее для первичного наполнения, но не
заменяет чтение: без него нельзя снять резервную копию с контроллера,
который работал автономно.
Проверено на живом контроллере: режим Accept (POST /offline
{"mode":5,"state":1}) переводит setup_mode в 5 и возвращает в 0 при
выключении. Кнопки интерфейса проверены end-to-end — состояние на железе
меняется.
Причина установлена (см. раздел «Как устроен обмен с контроллером»): чтение ключей идёт через конвертер Z-397, а не через WEBJSON. Наш контроллер подключён к USB-конвертеру, поэтому канала для программирования у сервера нет. Возвращаться к этой задаче имеет смысл только при появлении доступа к конвертеру.
Не реализовано. План: снимок контроллера (карты + настройки) в JSON в MinIO, восстановление заливкой на новый контроллер. Упирается в ту же проблему чтения карт.
Схема готова (ACSSchedule, ACSZone), но не используется: сейчас доступ
либо есть, либо нет, и контроллеру выдаётся полный доступ по времени
(tz = 255). Добавляется без изменения структуры прав.