Приветствуем вас, уважаемые коллеги! Сегодня мы детально разберем одну очень интересную и частую задачу, с которой сталкиваются разработчики при создании сложных механизмов автоматизации в среде 1С:Предприятие. Задача звучит следующим образом: как программно определить время ближайшего (следующего) запуска регламентного задания?
На первый взгляд кажется, что платформа 1С должна предоставлять простой и лаконичный метод для получения этой даты напрямую из объекта расписания. Однако на практике разработчики быстро обнаруживают, что у объекта платформы РасписаниеРегламентногоЗадания отсутствует прямой метод получения даты следующего выполнения. Единственный доступный инструмент, который нам предлагает платформа для анализа времени запуска — это метод ТребуетсяВыполнение(), который возвращает лишь логическое «Истина» или «Ложь» для конкретной секунды времени.
В рамках этой статьи мы подробно рассмотрим три эффективных подхода к решению данной задачи: использование готовых механизмов БСП (Библиотека стандартных подсистем), применение математического метода половинного деления (дихотомии) на чистой платформе, а также логику, заложенную в профессиональных инструментах разработчика.
Если вы ведете разработку в типовой конфигурации 1С (например, 1С:Бухгалтерия, 1С:ERP Управление предприятием, 1С:Комплексная автоматизация, 1С:Управление торговлей) или используете БСП в своей самописной базе, то вам крупно повезло. В составе библиотеки уже присутствует готовый, оптимизированный и протестированный функционал для решения нашей задачи.
Давайте обратим внимание на общий модуль ОчередьЗаданийСлужебный. В нем реализована экспортная функция ПолучитьЗапланированныйМоментЗапускаЗадания(), которая идеально подходит для расчета даты следующего старта.
Давайте разберем по шагам, как вызвать этот метод из своего кода:
// 1. Получаем расписание нужного регламентного задания
РегЗадание = РегламентныеЗадания.НайтиПредопределенное(Метаданные.РегламентныеЗадания.ОбновлениеИнформационнойБазы);
Расписание = РегЗадание.Расписание;
// 2. Определяем часовой пояс (обычно используется часовой пояс сеанса или ИБ)
ЧасовойПояс = ЗаполнениеСвойствПолнотекстовогоПоиска.ЧасовойПоясОбластиДанных();
// 3. Получаем даты предыдущих запусков задания
ДатаНачалаПоследнегоЗапуска = РегЗадание.ПоследнееНачало;
ДатаЗавершенияПоследнегоЗапуска = РегЗадание.ПоследнееОкончание;
// 4. Рассчитываем планируемый момент запуска
СледующийСтартUTC = ОчередьЗаданийСлужебный.ПолучитьЗапланированныйМоментЗапускаЗадания(
Расписание,
ЧасовойПояс,
ДатаНачалаПоследнегоЗапуска,
ДатаЗавершенияПоследнегоЗапуска
);
// Переводим время обратно из UTC в локальное для вывода пользователю
СледующийСтартЛокальный = МестноеВремя(СледующийСтартUTC, ЧасовойПояс);
Сообщить("Ближайший запуск регламентного задания запланирован на: " + СледующийСтартЛокальный);
Проанализируем ситуацию: как эта функция работает изнутри? Внутри общего модуля БСП вызывается служебный алгоритм СледующаяДатаВыполненияРасписания(). Он математически сопоставляет переданное расписание с историей предыдущих запусков и текущим временем, аккуратно учитывая цикличность, детальные настройки по дням недели, месяцам, интервалам внутри дня и исключениям. Это самый надежный способ, гарантирующий полное соответствие системным алгоритмам планировщика 1С.
Что делать, если ваша конфигурация написана с нуля без использования БСП, или вам по каким-то причинам недоступны ее модули? В таком случае нам на помощь приходит классическая математика, а именно — метод половинного деления (дихотомия).
Как мы уже выяснили, у объекта расписания есть стандартный метод: РасписаниеРегламентногоЗадания.ТребуетсяВыполнение(ДатаПроверки, ДатаНачалаПоследнегоЗапуска, ДатаЗавершенияПоследнегоЗапуска).
Очевидно, что перебирать каждую секунду в цикле, начиная от текущего момента и до бесконечности — это крайне неэффективное решение, которое намертво повесит рабочий сервер (ведь в одном годе содержится более 31 миллиона секунд!). Однако если мы применим алгоритм дихотомии, количество итераций сократится до минимума.
Давайте разберем этот алгоритм по шагам:
Низ) — это текущая дата и время, а верхняя граница (Верх) — текущая дата плюс один год (максимальный обозримый интервал ожидания запуска).ТребуетсяВыполнение. Если в течение года выполнение не требуется вообще, значит, задание либо выключено, либо настроено на запуск в необозримом будущем. Выходим из расчета.Среднее).ТребуетсяВыполнение для средней точки. Этот метод проверяет, требуется ли выполнение задания хотя бы один раз на временном интервале от начала времен до указанной даты.Верх) в точку Среднее.Низ) в точку Среднее.Ниже представлен готовый программный код функции, которую вы можете использовать в любом общем модуле вашей конфигурации:
&НаСервере
Функция ПолучитьБлижайшийСтартРасписанияДихотомией(Знач Расписание, Знач ДатаНачалаПоследнегоЗапуска = Неопределено, Знач ДатаЗавершенияПоследнегоЗапуска = Неопределено) Экспорт
ТекущееВремя = ТекущаяДата();
// Задаем интервал поиска (например, 1 год)
Низ = ТекущееВремя;
Верх = ТекущееВремя + (365 * 24 * 60 * 60);
// Первичная проверка на то, планируется ли запуск в принципе в течение года
Если Не Расписание.ТребуетсяВыполнение(Верх, ДатаНачалаПоследнегоЗапуска, ДатаЗавершенияПоследнегоЗапуска) Тогда
Возврат '00010101000000'; // Возвращаем пустую дату, если запусков не предвидится
КонецЕсли;
// Основной цикл половинного деления отрезка времени
Пока Верх - Низ > 1 Цикл
Среднее = Низ + Цел((Верх - Низ) / 2);
// Метод ТребуетсяВыполнение проверяет необходимость запуска до указанного момента времени включительно
Если Расписание.ТребуетсяВыполнение(Среднее, ДатаНачалаПоследнегоЗапуска, ДатаЗавершенияПоследнегоЗапуска) Тогда
// Искомая точка находится в левом интервале
Верх = Среднее;
Иначе
// Искомая точка находится в правом интервале
Низ = Среднее;
КонецЕсли;
КонецЦикла;
Возврат Верх;
КонецФункции
Проанализируем производительность этого алгоритма: для точного поиска одной секунды внутри целого года (31 536 000 секунд) компьютеру потребуется совершить всего 25–26 итераций цикла! Это занимает доли миллисекунды процессорного времени, что делает данный метод невероятно быстрым и абсолютно независимым от сторонних библиотек.
В популярном профессиональном инструменте «Консоль задач» из пакета «Инструменты разработчика» (ИР) колонка «Следующее начало» вычисляется динамически на лету. Автор инструмента реализовал продвинутый парсер объектов расписания.
Если вы хотите реализовать максимально точное отображение времени запуска в своих интерфейсах (например, в самописной консоли администрирования), вам необходимо дополнительно учитывать следующие факторы:
Использование установлено в значение Ложь, то рассчитывать дату следующего старта не имеет практического смысла. В интерфейсах такую дату лучше скрывать или отображать серым цветом.Давайте сопоставим рассмотренные подходы, чтобы вам было проще определиться с выбором архитектурного решения:
Теперь в вашем арсенале есть проверенные инструменты для программного управления планированием задач. Выбирайте наиболее подходящий под вашу архитектуру метод и делайте ваши решения на платформе 1С еще более интеллектуальными и удобными для пользователей!