Выполнение внешней обработки в фоновом задании

Публикация № 1058914

Разработка - Практика программирования

ВнешняяОбработка ДлительныеОперации ДлительнаяОперация ФоновыеЗадания Программирование БСП Фон Обработка СведенияОВнешнейОбработке ВыполнитьКоманду Форма ДополнительныеОтчетыИОбработки

144
Подробное описание подхода к созданию длительной операции на основе внешней обработки. Реализация протестирована на 1С 8.3.12.1714 (x64).

Warning

Данная статья не претендует на оригинальность и не является конечным решением.

Подходы решения задач и примеры программного кода несут исключительно обучающий характер.

 

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

Ниже представлен вариант реализации длительной операции из внешней обработки. Обязательные требования: обработка должна быть добавлена в справочник ДополнительныеОтчетыИОбработки и наличие БСП v3.

Итак, поехали!

  1. Управляемая форма:
  2.  
     Демонстрационный код модуля внешней обработки (тут все топорно просто):
    
    Функция СведенияОВнешнейОбработке() Экспорт
    	
    	ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
    	ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиДополнительнаяОбработка();
    	ПараметрыРегистрации.БезопасныйРежим = Ложь;
    	
    	НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
    	НоваяКоманда.Представление = Метаданные().Синоним;
    	НоваяКоманда.Идентификатор = "Открыть форму";
    	НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыОткрытиеФормы();
    	
    	НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
    	НоваяКоманда.Представление = "Выполнить мой алгоритм";
    	НоваяКоманда.Идентификатор = НоваяКоманда.Представление;
    	НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
    	
    	НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
    	НоваяКоманда.Представление = "Выполнить тест";
    	НоваяКоманда.Идентификатор = НоваяКоманда.Представление;
    	НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
    	
    	Возврат ПараметрыРегистрации;
    	
    КонецФункции
    
    Процедура МояДлительнаяПроцедура()
    	
    	ВремяФиниша = ТекущаяДата() + 100;
    	Пока ТекущаяДата() < ВремяФиниша Цикл
    		Процент = 100 - (ВремяФиниша - ТекущаяДата());
    		Если НЕ (Процент % 10) И Процент Тогда
    			ДлительныеОперации.СообщитьПрогресс(Процент, СтрШаблон("Задание пройдено на %1 процентов", Процент));
    		КонецЕсли;
    	КонецЦикла;
    	
    КонецПроцедуры
    
    Функция ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыКоманды) Экспорт
    	
    	// поиск и выполнение запрошенной команды
    	Если ИдентификаторКоманды = "Выполнить мой алгоритм" Тогда
    		МояДлительнаяПроцедура();
    	ИначеЕсли ИдентификаторКоманды = "Выполнить тест" Тогда
    		Сообщить("Тест пройден");
    	КонецЕсли;
    	
    КонецФункции
    

     

  3. Сама реализация запуска кода модуля внешней обработки в фоновом задании из управляемой формы:

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

     

  4. Результат:

Описание работы алгоритма:

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

Плюсы использования данного подхода:

  • Отсутствие какого либо другого варианта;
  • По сути делает аналогичные действия, как если в справочнике дополнительных отчетов и обработок - нажать на кнопку "Выполнить";
  • Выводит прогресс и сообщения;
  • Вместо пустого массива в "ОбъектыНазначения" можно передать полезные данные в фоновое задание.

 

144

См. также

Специальные предложения

Лучшие комментарии
16. Xershi 697 20.05.19 21:05 Сейчас в теме
(13) ключевой плюс этой публикации в том, что все разжевано без лишних соплей и отсебятины. Описание хромает, но зато четко дает понять, что пилить конфу не надо, рисовать форму не надо, а есть не только прогресс в процентах, но и доп текст, куда можно остаток времени записать или другую информацию! Все есть в БСП, главное это правильно написать код, а это мы и так умеем!
Остальные комментарии
Избранное Подписка Сортировка: Древо
1. qwinter 601 12.05.19 11:38 Сейчас в теме
Ну и любят же 1Сники велосипеды)))
creatermc; Eret1k; +2 Ответить
2. qwinter 601 12.05.19 11:45 Сейчас в теме
Отсутствие какого либо другого варианта;

Не благодарите)

3. Eret1k 598 12.05.19 11:50 Сейчас в теме
(2) Я имел в виду варианты запуска кода модуля в ФЗ, а за то что из обработки можно сделать регламентное задание благодарим БСП.
12. rpgshnik 1630 13.05.19 05:14 Сейчас в теме
4. Xershi 697 12.05.19 13:42 Сейчас в теме
Ранее процент выполнения длительной операции можно было вывести только если на форме нарисовать элементы. С версии БСП 3.0 это уже не нужно или и ранее так работало?
5. Xershi 697 12.05.19 13:57 Сейчас в теме
А понял в чем дело. Использовал другой код.
&НаКлиенте
Процедура ВыполнитьЗагрузкуКурсовФоново()
	
	Если ЗначениеЗаполнено(Объект.ИдентификаторКоманды) Тогда
		
		//ПараметрыКоманды = ДополнительныеОтчетыИОбработкиКлиент.ПараметрыВыполненияКомандыВФоне(Параметры.ДополнительнаяОбработкаСсылка);
		ПараметрыКоманды = Новый Структура();
		ПараметрыКоманды.Вставить("ДополнительнаяОбработкаСсылка",	Объект.ОбъектСсылка);
		ПараметрыКоманды.Вставить("СопровождающийТекст",			НСтр("ru = 'Выполняется загрузка курсов валют...'"));
		ПараметрыКоманды.Вставить("ФормаВладелец",					ЭтаФорма);
		ПараметрыКоманды.Вставить("СписокВалют",					ПолучитьАдресТаблицыВалют());
		ПараметрыКоманды.Вставить("НачалоПериода",					Объект.НачалоПериода);
		ПараметрыКоманды.Вставить("ОкончаниеПериода",				Объект.ОкончаниеПериода);
		
		ОписаниеОповещения = Новый ОписаниеОповещения("ЗавершениеЗагрузкиКурсовФоново", ЭтотОбъект);
		
		ДополнительныеОтчетыИОбработкиКлиент.ВыполнитьКомандуВФоне(Объект.ИдентификаторКоманды, ПараметрыКоманды, ОписаниеОповещения);
		
	Иначе
		
		ВыполнитьЗагрузкуКурсовНаСервере();
		
	КонецЕсли;

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

КонецПроцедуры
Показать

Не взлетело, прогресса нет. БСП в конфе 3.0.1.314.
В процедуре, которая считает все
ДлительныеОперации.СообщитьПрогресс(Процент, СтрШаблон("Загрузка курсов завершена на %1 %", Процент));		

В статье говорится, что команда должна иметь "ВызовСерверногоМетода". А я открываю форму же через "ОткрытиеФормы". Далее по кнопке вызываю длительную операцию. Выходит команда "ОткрытиеФормы", должна дернуть другую команду "ВызовСерверногоМетода"?
Возможно дело еще в:
ПараметрыРегистрации.Вставить("ВерсияБСП", "2.4.2.169");
Попробую поменять, но думаю не в этом дело.
7. Eret1k 598 12.05.19 17:13 Сейчас в теме
(6)
Команда которая должна быть выполнена в фоне, должна иметь тип: ДополнительныеОтчетыИОбработкиКлиентСервер. ТипКомандыВызовСерверногоМетода();

Код обработки можно отлаживать если, установлен параметр запуска "РежимОтладки", но правда каждый раз придется обновлять обработку в справочнике.

8. Xershi 697 12.05.19 20:40 Сейчас в теме
(7) да заработало! Как через файл, если добавить обработку в справочник, так и через внешние.
Единственный минус, мне нужно передавать параметры, когда я открываю форму.
Команда с "ВызовСерверногоМетода" используется для формирования регламентного задания. Подумаю как обойти это и будет вообще песня!

У меня также есть обработка, которая таким макаром запускает несколько потоков, но там везде был клиентский метод и прогресс не выводился. Возможно ли несколько потоков запустить таким вариантом?
Прикрепленные файлы:
10. Eret1k 598 12.05.19 22:10 Сейчас в теме
(8)
возможно ли несколько потоков запустить таким вариантом?

Да делал такое, НачатьВыполнениеСервернойКомандыВФоне() запускал в цикле, разделяя данные по порциям и передавал их в "ОбъектыНазначения". Собирал массив длительных операций и потом на клиенте опять в цикле передавал их в ДлительныеОперацииКлиент.ОжидатьЗавершение();
11. Xershi 697 12.05.19 23:07 Сейчас в теме
(10) не совсем понял.
ДлительнаяОперация = НачатьВыполнениеСервернойКомандыВФоне(ПараметрыКоманды, ЭтаФорма.УникальныйИдентификатор);
		ДлительныеОперацииКлиент.ОжидатьЗавершение(ДлительнаяОперация, ОписаниеОповещения, НастройкиОжидания);
		

Это делаем в цикле? Для чего собирать ДлительнаяОперация в массив?
9. Xershi 697 12.05.19 21:56 Сейчас в теме
Проверил на БСП 2.4.2.169 работает!
А вот на 2.3.2.51 пишет, что прогресс может выводить, а вот сообщения нет.
{ОбщийМодуль.ОбщегоНазначенияКлиентСервер.Модуль(2642)}: Если параметр ПараметрыОжидания.ВыводитьПрогрессВыполнения установлен в Истина, то параметр ПараметрыОжидания.ВыводитьСообщения должен быть установлен в Ложь в ДлительныеОперацииКлиент.ОжидатьЗавершение
		ВызватьИсключение ТекстИсключения;

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

Она вызывает исключение, а после комментирования все работает!
wowik; Eret1k; +2 Ответить
13. Oldsad 13.05.19 08:34 Сейчас в теме
Существуют ситуации, когда нужно реализовать некий алгоритм и выполнить его в фоновом режиме. Подобных ситуаций может быть масса, а возможностей реализаций в 1С мало и все они сложные.

ничего сложного, делаем поиск по ключевым словам "длительные операции" или "фоновые задания" и находим пару десятков тем на данном форуме с примерами / шаблонами и т.п.

и собственно возникает вопрос: чем данная статья отличается от десятка ей подобных?

Плюсы использования данного подхода:

Отсутствие какого либо другого варианта;

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

По сути делает аналогичные действия, как если в справочнике дополнительных отчетов и обработок - нажать на кнопку "Выполнить";

и это плюс? тогда еще в плюсы можно записать "обработка выполняет заданный ей алгоритм"

Вместо пустого массива в "ОбъектыНазначения" можно передать полезные данные в фоновое задание

без это с позволения сказать "плюса" мы получим обработку бесполезную чуть менее чем полностью

не стал придираться к еще одному плюсу (третьему), как ни странно не во всех примерах фоновых заданий выводятся сообщения
Evg-Lylyk; +1 Ответить
16. Xershi 697 20.05.19 21:05 Сейчас в теме
(13) ключевой плюс этой публикации в том, что все разжевано без лишних соплей и отсебятины. Описание хромает, но зато четко дает понять, что пилить конфу не надо, рисовать форму не надо, а есть не только прогресс в процентах, но и доп текст, куда можно остаток времени записать или другую информацию! Все есть в БСП, главное это правильно написать код, а это мы и так умеем!
18. mrx2012 20.05.19 23:24 Сейчас в теме
Все верно, но с готовым примером проще и быстрее.
14. mrx2012 20.05.19 17:06 Сейчас в теме
Ребята, у кого получился рабочий вариант. Пришлите рыбу , пожалуйста.
15. Eret1k 598 20.05.19 18:56 Сейчас в теме
(14) Держите может поможет
Прикрепленные файлы:
ВыполнитьАлгоритмВФоне.epf
wowik; user774630; +2 Ответить
17. mrx2012 20.05.19 23:20 Сейчас в теме
(15)огромное спасибо за статью и за обработку.
У меня уже была обработка, для для асинхронного выполнения , но в ней очень не хватало индикатора выполнения. Надеюсь теперь будет.
20. max_zhilin 30.08.19 17:45 Сейчас в теме
(15) не работает, не показывает прогресс, да и интерфейс блокирует. БСП 3.0.1.351

В Процедура СообщитьПрогресс срабатывает это:
	Если ПолучитьТекущийСеансИнформационнойБазы().ПолучитьФоновоеЗадание() = Неопределено Тогда
		Возврат;
	КонецЕсли;


Upd: разобрался, был включен режим отладки
19. viplelik 23.08.19 16:22 Сейчас в теме
А как сделать чтобы работало прямо из файла? (т.е. без добавления в "Дополнительные отчеты и обработки")

P.S. На новых БСП (3.0.2 и выше)
21. max_zhilin 30.08.19 17:54 Сейчас в теме
Кстати, вот этот фрагмент успевает за секунду назапускаться сотни раз. Надо как-то ограничить одним разом.
		Если НЕ (Процент % 10) И Процент Тогда
			ДлительныеОперации.СообщитьПрогресс(Процент, СтрШаблон("Задание пройдено на %1 процентов", Процент));
Оставьте свое сообщение