Уже опубликовано немало способов отобразить прогресс выполнения во внешней обработке, но все они (по крайней мере те, что я нашел), требуют подключения внешней обработки к справочнику дополнительных отчетов/обработок в составе конфигурации.
Мне же нужно было показать прогресс выполнения, не подключая обработку к этому справочнику.
В качестве базового шаблона для решения моей задачи я использовал пример, опубликованный здесь: https://wiki.programstore.ru/zapusk-fonovogo-zadaniya-vo-vneshnej-obrabotke-s-indikaciei/
Изменения, которые я внес в этот пример, минимальны - в основном, причесал код, распределил процедуры и функции по областям, добавил комментарии и исправил пару мелких ошибок. Работоспособность проверял в конфигурации "Зарплата и управление персоналом 3.1.12.144", версия БСП "3.1.2.264".
Важное замечание
Этот пример рассчитан на работу в двух вариантах:
1. Без подключения к подсистеме внешних отчетов/обработок;
2. С подключением к подсистеме.
Для тех, кто решает подобную проблему впервые, описываю принцип работы этого примера. В конце прикрепил файл внешней обработки, которую можно использовать в качестве шаблона.
Модуль объекта внешней обработки
На случай, если вы планируете подключать обработку к подсистеме внешних отчетов/обработок, я оставил экспортный метод "СведенияОВнешнейОбработке".
За реализацию длительной операции отвечает экспортный метод "ДлительнаяОперация", в которой организовано заполнение массива по тысяче строк, а в первом параметре передается количество таких заполнений (итераций). Рекомендую устанавливать количество итераций от 10 000.
// Процедура - выполняет некоторые действия, которые длятся долго
//
// Параметры:
// СтруктураПараметров - Структура - содержит исходные данные для расчетов
// АдресРезультата - Строка - адрес во временном хранилище, пот которому нужно поместить результат расчетов
//
Процедура ДлительнаяОперация(СтруктураПараметров, АдресРезультата) Экспорт
Результат = 0;
ПредыдущийПроцент = -1;
Для Сч = 1 по СтруктураПараметров.КоличествоИтераций Цикл
// выполнить нечто долгое
Стр = "";
Для i=1 По 1000 Цикл
Стр = Стр + ?(i=1, "", ",") + "Подстрока"+Строка(i);
КонецЦикла;
М = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(Стр, ",");
Если Сч/100 - Цел(Сч/100) = 0 Тогда
Результат = Результат + 1;
КонецЕсли;
ПроцентВыполнения = Мин(100, Окр(100 * Сч / СтруктураПараметров.КоличествоИтераций, 0, РежимОкругления.Окр15как20));
Если ПроцентВыполнения>ПредыдущийПроцент Тогда
ДлительныеОперации.СообщитьПрогресс(ПроцентВыполнения, СтрШаблон("Выполняется итерация %1", Сч));
КонецЕсли;
ПредыдущийПроцент = ПроцентВыполнения;
КонецЦикла;
ПоместитьВоВременноеХранилище(Результат, АдресРезультата);
КонецПроцедуры
Модуль формы внешней обработки
На форму добавлен собственный индикатор и строка состояния, а также реквизит "Количество итераций" и кнопка запуска длительной операции. В этом примере прогресс работает достаточно наглядно, если количество итераций от 10 000.
Подготовка данных для длительной операции
В этом примере исходные данные - это количество итераций и расположение файла внешней обработки.
Обратите внимание на код процедуры "ПодготовитьДанныеДляДлительнойОперации":
1. Если обработка подключена к подсистеме внешних отчетов/обработок, то в нее при создании передана ссылка на справочник доп. отчетов/обработок, и параметры готовятся так, чтобы запустить подключенную в подсистему обработку;
2. Если же обработка не подключена к подсистеме, то определяется имя файла этой обработки, а ссылка устанавливается пустая.
#Область СлужебныеПроцедурыИФункции
// Функция - возвращает имя файла этой внешней обработки.
// Внимание!!! В клиент-серверном варианте файл обработки должен быть виден с сервера,
// либо вы должны позаботиться самостоятельно о переносе файла обработки с клиента на сервер,
// в этом случае результат работы этой функции - имя файла, которое вы поместили на сервер самостоятельно
//
// Возвращаемое значение:
// - Строка
//
&НаСервере
Функция ИспользуемоеИмяФайла()
Возврат РеквизитФормыВЗначение("Объект").ИспользуемоеИмяФайла;
КонецФункции
// Функция - создает структуру, содержащую исходные данные для выполнения длительной операции
//
// Возвращаемое значение:
// - Структура
//
&НаКлиенте
Функция ПодготовитьДанныеДляДлительнойОперации()
Перем Рез;
Рез = Новый Структура;
Рез.Вставить("КоличествоИтераций", КоличествоИтераций);
Если ЗначениеЗаполнено(ОбъектСсылка) Тогда
// обработка подключена к подсистеме дополнительных отчетов/обработок
Рез.Вставить("ИспользуемоеИмяФайла", "ВнешняяОбработка.ДлительныеОперацииВоВнешнейОбработке");
Рез.Вставить("ДополнительнаяОбработкаСсылка", ОбъектСсылка);
Иначе
// не подключена
Рез.Вставить("ИспользуемоеИмяФайла", ИспользуемоеИмяФайла()); // имя файла этой внешней обработки
Рез.Вставить("ДополнительнаяОбработкаСсылка", ПредопределенноеЗначение("Справочник.ДополнительныеОтчетыИОбработки.ПустаяСсылка"));
КонецЕсли;
Возврат Рез;
КонецФункции
Обработчик команды "ЗапуститьВыполнение"
Выполняется при нажатии на кнопку "Запустить выполнение", и выполняет следующие действия:
1. Обнуляет индикатор прогресса;
2. Создает фоновое задание и запоминает его идентификатор в реквизите формы;
3. Подключает обработчики ожидания, отвечающие за обновление прогресса.
В этом примере используется отображение и через индикатор на форме, и через стандартное окно отображения состояния. Я бы не рекомендовал отключать стандартное окно, так как в нем есть кнопка "Отмена", которая позволяет прервать исполнение длительной операции.
#Область ОбработчикиКомандФормы
&НаКлиенте
Процедура ЗапуститьВыполнение(Команда)
// инициализация реквизитов формы
ИдентификаторЗадания = "";
Индикатор = 0;
СтрокаСостояния = "";
// инициализация параметов запуска фонового задания
ПараметрыЗапуска = ПодготовитьДанныеДляДлительнойОперации();
// подготовить фоновое задание на сервере и запомнить его идентификатор в реквизите формы "ИдентификаторЗадания"
СтруктураФоновогоЗадания = ВыполнитьФоновоеЗаданиеНаСервере(ПараметрыЗапуска, УникальныйИдентификатор);
ИдентификаторЗадания = СтруктураФоновогоЗадания.ИдентификаторЗадания;
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
//ПараметрыОжидания.ВыводитьОкноОжидания = Ложь;
// Если не нужно открывать стандартное окно ожидания, то ПараметрыОжидания.ВыводитьОкноОжидания = Ложь;
// необходимость вывода прогресса состояния
ПараметрыОжидания.ВыводитьПрогрессВыполнения = Истина;
// если не указать, интервал явно, то он будет увеличиваться при каждой итерации в 1.4 раза
ПараметрыОжидания.Интервал = 2;
ДлительныеОперацииКлиент.ОжидатьЗавершение(
СтруктураФоновогоЗадания,
Новый ОписаниеОповещения("ФоновоеЗаданиеЗавершение", ЭтотОбъект),
ПараметрыОжидания);
ПодключитьОбработчикОжидания("ОбработчикОжиданияИндикатор", 2);
КонецПроцедуры
#КонецОбласти
Когда длительная операция завершится, будет исполнен клиентский метод формы "ФоновоеЗаданиеЗавершение".
Здесь мы в первую очередь отключаем обработчик ожидания, который отвечает за обновление индикатора, а потом анализируем результат выполнения фонового задания. Если задание завершилось с ошибкой, можно организовать выполнение длительной операции без отображения индикатора. Для этого нужно вызвать экспортный метод объекта обработки "ДлительнаяОперация", передав в него исходные данные самостоятельно.
// Процедура - выполняется после завершения фонового задания
// Выполнить анализ завершения, в случае успеха получить данные результата из временного хранилища и обработать их
//
// Параметры:
// Результат - Структура - результат выполнения фонового задания
// ДополнительныеПараметры - Произвольный - дополнительные параметры фонового задания
//
&НаКлиенте
Процедура ФоновоеЗаданиеЗавершение(Результат, ДополнительныеПараметры) Экспорт
ОтключитьОбработчикОжидания("ОбработчикОжиданияИндикатор");
Если ТипЗнч(Результат) <> Тип("Структура") Тогда
Возврат;
КонецЕсли;
Если Результат.Статус = "Ошибка" Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(Результат.ПодробноеПредставлениеОшибки);
СтрокаСостояния = "Задание завершено с ошибками.";
// здесь можно запустить выполнение задания не в фоне, без отображения состояния и сообщить об этом в строке состояния
ИначеЕсли Результат.Статус = "Выполнено" Тогда
// успешное выполнение - получить результат из временного хранилища и обработать его
Данные = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
ВозвратноеЗначение = Данные;
Индикатор = 100;
СтрокаСостояния = "Задание завершено.";
КонецЕсли;
КонецПроцедуры
А теперь самое главное - запуск фонового задания без использования справочника "ДополнительныеОтчетыИОбработки"
Код практически не отличается от базового шаблона, изменений всего два, и оба при заполнении структуры "ПараметрыЗадания".
1. В исходном примере имя обработки всегда было такое: "ВнешняяОбработка.ДлительныеОперацииВоВнешнейОбработке"
Я же заполняю это значение из параметров запуска - это либо имя файла внешней обработки, либо строка из исходного примера;
2. В исходном примере "ДополнительнаяОбработкаСсылка" всегда была заполнена ссылкой на подключенную к подсистеме внешнюю обработку.
У меня это либо пустая ссылка (если не было подключения к подсистеме), либо ссылка на подключенную обработку.
&НаСервереБезКонтекста
Функция ВыполнитьФоновоеЗаданиеНаСервере(ПараметрыЗапуска, УникальныйИдентификатор)
НаименованиеЗадания = НСтр("ru = 'Запуск длительной операции'");
ВыполняемыйМетод = "ДлительныеОперации.ВыполнитьПроцедуруМодуляОбъектаОбработки";
ПараметрыЗадания = Новый Структура;
ПараметрыЗадания.Вставить("БезопасныйРежим", Ложь);
ПараметрыЗадания.Вставить("ИмяОбработки", ПараметрыЗапуска.ИспользуемоеИмяФайла); // имя файла этой обработки или "ВнешняяОбработка.ДлительныеОперацииВоВнешнейОбработке"
ПараметрыЗадания.Вставить("ИмяМетода", "ДлительнаяОперация");
ПараметрыЗадания.Вставить("ПараметрыВыполнения", ПараметрыЗапуска);
ПараметрыЗадания.Вставить("ЭтоВнешняяОбработка", Истина);
ПараметрыЗадания.Вставить("ДополнительнаяОбработкаСсылка", ПараметрыЗапуска.ДополнительнаяОбработкаСсылка); // СправочникСсылка.ДополнительныеОтчетыИОбработки (может быть пустой ссылкой)
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
ПараметрыВыполнения.НаименованиеФоновогоЗадания = НаименованиеЗадания;
ПараметрыВыполнения.ЗапуститьВФоне = Истина;
ПараметрыВыполнения.Вставить("ИдентификаторФормы", УникальныйИдентификатор);
СтруктураФоновогоЗадания = ДлительныеОперации.ВыполнитьВФоне(ВыполняемыйМетод, ПараметрыЗадания, ПараметрыВыполнения);
Возврат СтруктураФоновогоЗадания;
КонецФункции
Вот, собственно, и все.
Надеюсь, что эта статья поможет разобраться с проблемой отображения состояния длительной операции тем, кто такую проблему еще не решал.
Кроме того, эту ссылку могу использовать и я сам, не так ли? ))
Удачи в работе!