.. _g3_spec_server_user_activity_events: События активности пользователя в форме ========================================== :term:`Прикладной код` может узнать, что пользователь перестал работать в :term:`форме <Форма>` и что он к ней вернулся. Активность отслеживается отдельно для главной формы приложения и каждой :term:`MDI`-формы. У модальных форм и лукапов своего отсчёта нет. Работа в них продлевает активность формы, из которой они открыты. .. versionadded:: 1.29.0-ms5 Включение и время бездействия ------------------------------- Механизм включается :term:`метаданными <Метаданные>`. Форма отслеживается, если у представления её главной :term:`выборки <Выборка>` задан атрибут :xsd:attr:`View.Representation.userIdleTimeout`. Настраивать сервер для этого не нужно. .. code-block:: xml Атрибут задаёт время бездействия в секундах. Значение 0 отключает отслеживание формы. Форма без атрибута тоже не отслеживается. Значение читается один раз при открытии формы и дальше не меняется. Общее время бездействия для многих форм базы задают умолчанием в конфигурации сервера, секцией :xsd:class:`Configuration.Databases.Database.UserActivity`: .. code-block:: xml Умолчание применяется к формам, у которых атрибут в представлении главной выборки не задан. Значение атрибута перекрывает умолчание. Если секции нет, нет и умолчания, но формы со своим атрибутом отслеживаются всё равно. Конфигурация перечитывается только при перезапуске сервера. Если ни одна открытая форма не отслеживается, механизм не расходует ресурсы сервера. .. note:: Не путайте с :xsd:attr:`Configuration.SessionPool.idleClientTimeout`. Тот механизм решает задачу безопасности: он отключает бездействующего клиента от сессии, см. :ref:`spec_server_security_idleclienttimeout`. Здесь клиент остаётся подключённым, а прикладной код получает уведомление. Прикладные события -------------------- :term:`События <Событие>` приходят в главную выборку формы без аргументов: .. list-table:: :header-rows: 1 * - Что произошло - Событие - Обработчик в :term:`Avi`-классе * - Пользователь перестал работать в форме - :btk:event:`form.UserIdle` - ``onUserIdle()`` * - Пользователь вернулся к работе в форме - :btk:event:`form.UserActive` - ``onUserActive()`` :term:`Обработчики <Обработчик события>` объявляются в Avi-классе главной выборки формы методами с аннотацией ``@Event``. Если обработчика нет, форма событие пропускает. Это не ошибка. Что считается активностью в форме ----------------------------------- На этой странице *командой* называется сообщение, которое клиент отправляет серверу, когда пользователь выполняет действие в форме. Это не то же самое, что команда браузерного :term:`плагина <Плагин>`. Активность формы — это любая команда этой формы или открытой из неё модальной формы, лукапа или диалога. Примеры действий, которые отправляют команду: - нажатие кнопки, пункта меню или горячей клавиши; - переход по записям списка, разворачивание узла дерева, сортировка, применение фильтра; - редактирование значения в поле, вставка и удаление записи, сохранение и отмена; - работа в модальной форме, открытой из этой формы, и ответ на её диалог. Активностью **не** считается: - автоматическое обновление данных по таймеру: пользователь в нём не участвует; - действия вне форм: работа с панелью уведомлений и закрытие приложения; - движение мыши, прокрутка и набор текста в поле ввода: команд они не отправляют. Неочевидные случаи -------------------- **Набор текста и прокрутка списка.** Пока пользователь набирает значение в поле, команда не отправляется, и долгий набор выглядит как простой. То же с прокруткой уже загруженной части списка. Активность засчитается, когда пользователь закончит ввод значения или прокрутит список за пределы загруженной части. **Открытый диалог.** Ожидание ответа на диалог активностью не считается. Забытый на ночь диалог даст ``UserIdle`` по истечении времени бездействия. Ответ на диалог — активность: форма получит ``UserActive``, и только потом продолжится прерванная операция. Если ожидание закончилось без ответа, по таймауту или из-за разрыва связи, активности нет: пользователь к форме не вернулся. **Долгая команда.** Пока сервер выполняет команду формы, ``UserIdle`` не придёт. Время бездействия отсчитывается после завершения команды. **Невыполненная команда.** Если сервер не выполнил команду, потому что форма к этому моменту уже изменилась, активность всё равно продлевается: пользователь действие совершил. **Закрытие формы в простое.** Закрытие начинается такой же командой, как любая другая. Поэтому форма сначала получит ``UserActive``, а затем :btk:event:`beforeCloseForm`. Если обработчик ``UserActive`` захватывает ресурс, ресурс будет захвачен и тут же отпущен. Сервер не может заранее знать, что команда закроет форму, поэтому обработчик должен быть к этому готов, см. :ref:`g3_spec_server_user_activity_events_recommendations`. **Разрыв связи с клиентом.** Отключение клиента отсчёт не останавливает: форма сессии, от которой отключился клиент, тоже должна освободить занятые ресурсы. Отслеживание прекращается, когда закрывается форма или сессия. Когда и в каком порядке приходят события ------------------------------------------ 1. ``UserIdle`` отправляется, когда простой формы достиг времени бездействия. Событие может опоздать: сервер проверяет формы периодически. Период проверки равен половине наименьшего времени бездействия среди открытых отслеживаемых форм, но не меньше одной и не больше тридцати секунд. Опоздание не превышает этого периода. 2. ``UserActive`` отправляется до того, как сервер выполнит команду, которая его вызвала. Если пользователь вернулся, ответив на диалог, событие отправляется до продолжения прерванной операции. 3. События строго чередуются: ``UserActive`` приходит только после ``UserIdle``, дублей нет ни в ту, ни в другую сторону. Начальное состояние формы — активна. 4. События приходят, пока закрытие формы ещё можно отменить: результатом :btk:event:`onCloseFormQuery`, отказом пользователя сохранять данные или ошибкой обработчика :btk:event:`beforeCloseForm`. Форма, закрытие которой отменили, работает дальше как обычно. Когда закрытие стало окончательным, события прекращаются. Ресурсы, освобождённые в простое, остаются освобождёнными, окончательную очистку выполняет обработчик :btk:event:`beforeCloseForm`. Если этот обработчик показал диалог и пользователь долго не отвечает, форма может получить ``UserIdle`` во время закрытия. 5. Исключение обработчика записывается в журнал сервера и дальше не передаётся. Ошибка в обработчике ``UserActive`` не отменяет команду. Ошибка в обработчике ``UserIdle`` не мешает проверке остальных форм, повторно событие не отправляется. .. _g3_spec_server_user_activity_events_recommendations: Рекомендации прикладному разработчику --------------------------------------- Один из вариантов использования — на время простоя освобождать занятые формой ресурсы, например прикладные блокировки, и захватывать их заново, когда пользователь вернётся к работе. Обработчики должны быть готовы к повторному вызову: событие может прийти, когда ресурс уже освобождён или уже захвачен. ``UserActive`` должен переживать и случай, когда освобождённый ресурс за время простоя занял другой пользователь. Неудачный захват — нормальный сценарий, его нужно обработать. Готового признака «форма в простое» механизм не даёт. Если он нужен прикладному коду, его ведут в обработчиках: события чередуются без дублей, поэтому состояние всегда известно. .. seealso:: :btk:event:`form.UserIdle` :btk:event:`form.UserActive` :ref:`spec_server_security_idleclienttimeout` :xsd:class:`Configuration.Databases.Database.UserActivity` :xsd:attr:`View.Representation.userIdleTimeout`