1С БСП. Запуск фонового задания во внешней обработке с индикацией

В данной статье описан пример кода для запуска фонового задания из формы внешней обработки средствами 1С БСП с возможностью отображения индикации.

Описание используемых процедур

Данный пример кода реализован с использованием функционала Длительных операций 1С:Библиотеки стандартных подсистем версии 2.3.2 и выше.
В 1С БСП существует функционал для запуска фоновых заданий из дополнительных обработок в общем модуле ДлительныеОперации.
Основные процедуры функционала Длительных операций БСП, используемые в данном примере:
— Запуск экспортной функции внешней обработки происходит через функцию ДлительныеОперации.ВыполнитьПроцедуруМодуляОбъектаОбработки, в которую необходимо передать данные для запуска: имя процедуры, ссылка на дополнительную обработку, имя обработки и другие;
— Процедура ДлительныеОперации.ВыполнитьВФоне: Запустить выполнение процедуры в фоновом задании, если это возможно;
— Функция ДлительныеОперации.ПрочитатьПрогресс: Считывает информацию о ходе выполнения фонового задания;
— Функция ДлительныеОперации.СообщитьПрогресс: Регистрирует информацию о ходе выполнения фонового задания;
— Процедура ДлительныеОперацииКлиент.ОжидатьЗавершение: Ожидать завершения выполнение процедуры в фоновом задании и открыть форму ожидания длительной операции.

Со всеми детальными описаниями и параметрами функций и процедур можно ознакомится в комментариях перед соответствующими процедурами в общих модулях.

Рассмотрим пример обработки.

Пример запуска процедуры внешней обработки в фоновом режиме с помощью функционала 1С БСП

В модуле объекта необходимо описать сведения о внешней обработке. Команда будет запускаться через команду формы.

Функция СведенияОВнешнейОбработке() Экспорт

	ДанныеДляРег = Новый Структура();
	ДанныеДляРег.Вставить("Наименование", "Пример длит. операций во внешней обработке");
	ДанныеДляРег.Вставить("БезопасныйРежим", Ложь);
	ДанныеДляРег.Вставить("Версия", "ver.: 1");
	ДанныеДляРег.Вставить("Информация", "Пример длит. операций во внешней обработке");
	ДанныеДляРег.Вставить("Вид", "ДополнительнаяОбработка");

	ТабЗнКоманды = Новый ТаблицаЗначений;
	ТабЗнКоманды.Колонки.Добавить("Идентификатор");
	ТабЗнКоманды.Колонки.Добавить("Использование");
	ТабЗнКоманды.Колонки.Добавить("Представление");

	НовСтрока = ТабЗнКоманды.Добавить();
	НовСтрока.Идентификатор = "ОткрытьОбработку";
	НовСтрока.Использование = "ОткрытиеФормы";
	НовСтрока.Представление = "Пример длит. операций во внешней обработке";
	ДанныеДляРег.Вставить("Команды", ТабЗнКоманды);

	Возврат ДанныеДляРег;

КонецФункции

Также вставим экспортную функцию, которая будет запускаться в фоновом задании. В качестве параметров у функции:
— СтруктураПараметров (тип Структура) — параметры, которые мы передали при запуске длительной операции;
— АдресРезультата (тип УникальныйИдентификатор) — Адрес временного хранилища, присвоенный при запуске задания, в котором будут записаны результаты работы процедуры.
В нашем примере на вход передается число — количество итераций, которое задается пользователем.
На выходе получаем массив чисел и помещаем во временное хранилище.

В рамках одного запущенного фонового задания можно отслеживать прогресс выполнения с помощью процедур ДлительныеОперации.СообщитьПрогресс и ДлительныеОперации.ПолучитьПрогресс.
В процедуре выполнения выполняем процедуру ДлительныеОперации.СообщитьПрогресс, которая регистрирует информацию о ходе выполнения фонового задания. Передаем параметры «Процент» и «Текст». Также можно передать структуру с дополнительными параметрами.

Процедура ДлительнаяОперация(СтруктураПараметров, АдресРезультата) Экспорт
	
	МассивВозврат = Новый Массив;
	Для Ин = 1 ПО СтруктураПараметров.КоличествоИтераций Цикл
		
		//ваш код
		Если Ин/100 - Окр(Ин/100, 0) = 0 Тогда
			МассивВозврат.Добавить(Ин);
		КонецЕсли;
		
		ТекущийПроцентВыполнения = Окр(100*Ин/СтруктураПараметров.КоличествоИтераций, 0);
		ДлительныеОперации.СообщитьПрогресс(ТекущийПроцентВыполнения, СтрШаблон("Выполняется итерация %1", Ин));
		
	КонецЦикла;	
	
	ПоместитьВоВременноеХранилище(МассивВозврат, АдресРезультата);
	
КонецПроцедуры

На форме добавляем команду, при выполнении которой будет запускаться фоновое задание.

&НаКлиенте
Функция ПодготовитьДанныеДляДлительнойОперации()
	Возврат Новый Структура("КоличествоИтераций", КоличествоИтераций);	
КонецФункции

&НаКлиенте
Процедура ЗапуститьВыполнение(Команда)
	
        //сохраним идентификатор задания в реквизите формы
        ИДЗадания  = "";
        //готовим входящие данные для фоновой процедуры 
	ПараметрыЗапуска = ПодготовитьДанныеДляДлительнойОперации();
        //запускаем фоновое задание, параметры: структура вх. данных и уникальный идентификатор формы-владельца 
	СтруктураФоновогоЗадания = ВыполнитьФоновоеЗаданиеНаСервере(ПараметрыЗапуска, УникальныйИдентификатор);
	ИДЗадания =  СтруктураФоновогоЗадания.ИдентификаторЗадания;
	
	//получаем структуру параметров ожидания и устанавливаем необходимые параметры
	ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
	// указываем необходимость вывода окна с индикацией
	ПараметрыОжидания.ВыводитьОкноОжидания = Истина;
	// указываем необходимость вывода прогресса состояния
	ПараметрыОжидания.ВыводитьПрогрессВыполнения = Истина;
	// указываем интервал обновления состояния в секундах, если не указать, 
	// то интервал будет увеличиваться при каждой итерации в 1.4 раза.
	ПараметрыОжидания.Интервал = 2;

        //Ожидать завершения выполнение процедуры в фоновом задании и открыть форму ожидания длительной операции.
	ДлительныеОперацииКлиент.ОжидатьЗавершение(
		СтруктураФоновогоЗадания,
		Новый ОписаниеОповещения("ОбработатьДанные", ЭтотОбъект),
		ПараметрыОжидания);
	
КонецПроцедуры

&НаСервереБезКонтекста
Функция ВыполнитьФоновоеЗаданиеНаСервере(ПараметрыЗапуска, УникальныйИдентификатор)

	НаименованиеЗадания = НСтр("ru = 'Запуск длительной операции'");
	
        //ссылка на доп. обработку, подключенную к базе. В данном примере ищем по наименованию, указанному в Сведениях о внешней обработке
	ДополнительнаяОбработкаСсылка =  Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию("Пример длит. операций во внешней обработке");
	
        //запуск фоновой процедуры происходит через метод "ВыполнитьПроцедуруМодуляОбъектаОбработки", куда мы передаем данные об обработке и о процедуре, которую необходимо запустить
	ВыполняемыйМетод = "ДлительныеОперации.ВыполнитьПроцедуруМодуляОбъектаОбработки";
	
	ПараметрыЗадания = Новый Структура;
        //имя внешней обработки
	ПараметрыЗадания.Вставить("ИмяОбработки", "ВнешняяОбработка.ДлительныеОперацииВоВнешнейОбработке");
        //имя экспортной серверной процедуры обработки
	ПараметрыЗадания.Вставить("ИмяМетода", "ДлительнаяОперация");
        //входящие параметры процедуры 
	ПараметрыЗадания.Вставить("ПараметрыВыполнения", ПараметрыЗапуска);
        //признак внешней обработки
	ПараметрыЗадания.Вставить("ЭтоВнешняяОбработка", Истина);
        //ссылка на доп. обработку в базе
	ПараметрыЗадания.Вставить("ДополнительнаяОбработкаСсылка",ДополнительнаяОбработкаСсылка);
	
        //получаем пустую структуру параметров выполнения фонового задания и заполняем ее необходимыми данными
	ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
	ПараметрыВыполнения.НаименованиеФоновогоЗадания = НаименованиеЗадания;
	ПараметрыВыполнения.ЗапуститьВФоне = Истина;
	ПараметрыВыполнения.Вставить("ИдентификаторФормы", УникальныйИдентификатор); 
	
        //запускаем выполнение фонового задания
	СтруктураФоновогоЗадания = ДлительныеОперации.ВыполнитьВФоне(ВыполняемыйМетод, ПараметрыЗадания, ПараметрыВыполнения);
	
	Возврат СтруктураФоновогоЗадания;
	
КонецФункции

&НаКлиенте
Процедура ОбработатьДанные(Результат, ДополнительныеПараметры) Экспорт

	Если Результат = Неопределено Тогда
		Возврат;
	ИначеЕсли Результат.Статус = "Ошибка" Тогда
		ОбщегоНазначенияКлиентСервер.СообщитьПользователю(Результат.ПодробноеПредставлениеОшибки);
	ИначеЕсли Результат.Статус = "Выполнено" Тогда
		// обрабатываем результат
		Данные = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
		ВозвратноеЗначение = Данные.Количество();
	КонецЕсли;

КонецПроцедуры

Таким образом, происходит запуск и выполнение фонового задания из внешней обработки, подключенной к базе с помощью функционала дополнительных обработок БСП.
Процесс работы будет выглядеть следующим образом:

Данное выполнение реализовано только с помощью функционала БСП. Часто бывает, что данное отображение недостаточно наглядно отображает работу процесса, и пользователи требуют строку индикации, потому что, известно, смотреть бесконечно можно на три вещи: огонь, воду и заполняющуюся строку индикации в 1с.

Эту возможность можно реализовать несколькими способами, в данной же статье предлагается 2 варианта: с помощью редактирования общей формы конфигурации «ДлительнаяОперация» и с выводом индикации прямо на форму внешней обработки.

1. Вывод индикации в форме «ДлительнаяОперация».

Редактировать форму будем с помощью расширений. Важно отметить, что данная возможность использования расширений — замена произвольной процедуры в заимствованном модуле — стала реализована только в версии платформы 8.3.9, поэтому режим совместимости конфигурации и расширения должен быть 8.3.9 и выше или вообще отключен.
В форме добавляем в процедуру «ПриСозданииНаСервере» код по добавлению реквизита и элемента формы «Индикатор»:

&НаСервере
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
	ДобавляемыеРеквизиты = Новый Массив;
	РеквизитИндикатор = Новый РеквизитФормы("Индикатор", ОбщегоНазначения.ОписаниеТипаЧисло(10,0));
	ДобавляемыеРеквизиты.Добавить(РеквизитИндикатор);
	ЭтаФорма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);
	
	ЭлементИндикатор = ЭтаФорма.Элементы.Добавить("Индикатор", Тип("ПолеФормы"), ЭтаФорма);
	ЭлементИндикатор.Вид = ВидПоляФормы.ПолеИндикатора;
    ЭлементИндикатор.ПутьКДанным= "Индикатор";
	ЭлементИндикатор.ОтображатьПроценты = Истина;
КонецПроцедуры

Далее заменяем исполняемую в модуле общей формы процедуру, в которой происходит считывание прогресса выполнения и вывод информации на форму, и добавляем строку:

	//...
	ЭтаФорма.Индикатор = Задание.Прогресс.Процент;
	//...

Таким образом, при запуске фонового задания будет открыта форма длительной операции с индикатором прогресса.

2. Вывод индикации в форме внешней обработки.

Для реализации данного способа добавим на форму 2 реквизита и соответствующих элемента: Индикатор (тип Число) и СтрокаСостояния (тип Строка).
Идея способа заключается в том, чтобы прочитать прогресс выполняемого задания в форме и вывести данные пользователю, не открывая форму длительной операции.
Для этого добавим Обработчик ожидания, который будет считывать прогресс и выводить его на форму, и саму процедуру считывания прогресса на сервере.

&НаКлиенте
#Область Индикатор
&НаКлиенте 
Процедура ОбработчикОжиданияИндикатор() Экспорт 
	Прогресс = ПрочитатьПрогресс(ИДЗадания);
	Если НЕ ТипЗнч(Прогресс) = Тип("Структура") Тогда
		СтрокаСостояния = "";
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(Прогресс) = Тип("Структура") 
		И Прогресс.Свойство("ЗавершеноАварийно") Тогда
		ОтключитьОбработчикОжидания("ОбработчикОжиданияИндикатор");
		Возврат;
	КонецЕсли;
	
	Если Прогресс.Свойство("ЗаданиеВыполнено") И Прогресс.ЗаданиеВыполнено Тогда
		Индикатор = 100;
		СтрокаСостояния = "Задание завершено.";
	Иначе
		Если Прогресс.Свойство("Процент") Тогда
			Индикатор = Прогресс.Процент;
		КонецЕсли;
		Если Прогресс.Свойство("Текст") Тогда
			СтрокаСостояния = Прогресс.Текст;
		КонецЕсли;
	КонецЕсли;
КонецПроцедуры
	
Функция ПрочитатьПрогресс(Знач ИдентификаторФоновогоЗадания) Экспорт
	
	Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ИдентификаторФоновогоЗадания);
	Если Задание = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если Задание.Состояние = СостояниеФоновогоЗадания.ЗавершеноАварийно Тогда
		ОтключитьОбработчикОжидания("ОбработчикОжиданияИндикатор");
		Возврат Неопределено;
	КонецЕсли;
	
	ПрогрессЗадания = ДлительныеОперации.ПрочитатьПрогресс(ИдентификаторФоновогоЗадания);
	
	// Добавляем флаг "ЗаданиеВыполнено", чтобы различать случаи: когда отсутствуют сообщения и когда завершено задание.
	Если ПрогрессЗадания = Неопределено
	 Или ТипЗнч(ПрогрессЗадания)  Тип("Структура") Тогда // или нет задания, или нет сообщений
		ПрогрессЗадания = Новый Структура;
	КонецЕсли;
	ПрогрессЗадания.Вставить("ЗаданиеВыполнено", ДлительныеОперации.ЗаданиеВыполнено(ИдентификаторФоновогоЗадания));
	
	Возврат ПрогрессЗадания;
	
КонецФункции

#КонецОбласти 

В ранее добавленные процедуры добавим код для работы индикации:
— в процедуре ЗапуститьВыполнение обнулим наши реквизиты при запуске нового задания, отключим необходимость открытия окна ожидания и подключим наш обработчик ожидания:

&НаКлиенте
&НаКлиенте
Процедура ЗапуститьВыполнение(Команда)
	
	ИДЗадания =  "";
	Индикатор = 0;
	СтрокаСостояния = "";
	
	ПараметрыЗапуска = ПодготовитьДанныеДляДлительнойОперации();
	СтруктураФоновогоЗадания = ВыполнитьФоновоеЗаданиеНаСервере(ПараметрыЗапуска, УникальныйИдентификатор);
	ИДЗадания =  СтруктураФоновогоЗадания.ИдентификаторЗадания;
	
	ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
	// указываем необходимость вывода окна с индикацией
	//если используем индикатор на форме, то вывод окна можно установить как Ложь
	ПараметрыОжидания.ВыводитьОкноОжидания = Ложь;
	// указываем необходимость вывода прогресса состояния
	ПараметрыОжидания.ВыводитьПрогрессВыполнения = Истина;
	// указываем интервал обновления состояния в секундах, если не указать, 
	// то интервал будет увеличиваться при каждой итерации в 1.4 раза.
	ПараметрыОжидания.Интервал = 2;
	
	ДлительныеОперацииКлиент.ОжидатьЗавершение(
		СтруктураФоновогоЗадания,
		Новый ОписаниеОповещения("ОбработатьДанные", ЭтотОбъект),
		ПараметрыОжидания);
	
	//++Индикатор
	ПодключитьОбработчикОжидания("ОбработчикОжиданияИндикатор",2);
 	//--Индикатор
	
КонецПроцедуры

-в процедуре ОбработатьДанные добавим обработку прогресса при завершении выполнения задания:

&НаКлиенте
&НаКлиенте
Процедура ОбработатьДанные(Результат, ДополнительныеПараметры) Экспорт

	//++Индикатор
	ОтключитьОбработчикОжидания("ОбработчикОжиданияИндикатор");
 	//--Индикатор
	
	Если Результат = Неопределено Тогда
		Возврат;
	ИначеЕсли Результат.Статус = "Ошибка" Тогда
		ОбщегоНазначенияКлиентСервер.СообщитьПользователю(Результат.ПодробноеПредставлениеОшибки);
		//++Индикатор
		СтрокаСостояния = "Задание завершено с ошибками.";
	 	//--Индикатор
	ИначеЕсли Результат.Статус = "Выполнено" Тогда
		// обрабатываем результат
		Данные = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
		ВозвратноеЗначение = Данные.Количество();
		//++Индикатор
		Индикатор = 100;
		СтрокаСостояния = "Задание завершено.";
	 	//--Индикатор
	КонецЕсли;

КонецПроцедуры

Архив с обработкой и расширением:
ДлительныеОперацииВоВнешнейОбработке

Comments

So empty here ... leave a comment!

Добавить комментарий

Sidebar