Произвольный код в фоновом режиме

Программирование - Практика программирования

94
Задача: реализовать выполнение произвольного кода в фоновом режиме без изменения конфигурации, т.е. во внешней обработке.

Механизм фоновых заданий все чаще используется для распаралеливания задач. Подвисающее на несколько минут окно - уже не комильфо, а для каждой длительной операции пользователь хочет видеть прогресс бар. И все бы ничего пока код пишет программист, вставляет в конфигурацию и потом запускает, но как быть если код пишется в процессе и заранее не известен? Менеджер фоновых заданий для процедуры "Запустить" требует имя процедуры, код есть только в виде строки. Ограничения платформы? Не в этот раз!

Итак, прежде чем прикручивать педали, посмотрим что уже есть в БСП: ВыполнитьПроцедуруМодуляОбъектаОбработки(Параметры, АдресХранилища). Заполняем параметры:

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

Процедура получит внешнюю обработку из справочника или из файла(наш случай) и запустит указанный метод. Соль в том, что файл наш лежит на клиентском компьютере, местоположение на сервере скорее всего не доступно. Передаем файл на сервер и записываем его там: 

&НаКлиенте
ДвоичныеДанные = новый ДвоичныеДанные(АдресФайлаОбработки());
ФайлОбработкиВоВременномХранилище = ПоместитьВоВременноеХранилище(ДвоичныеДанные);
&НаСервере
ДвоичныеДанныеНаСервере = ПолучитьИзВременногоХранилища(ФайлОбработкиВоВременномХранилище);
АдресФайлаНаСервере = КаталогВременныхФайлов()+ИмяФайла;
ДвоичныеДанныеНаСервере.Записать(АдресФайлаНаСервере);


&НаСервере
Функция АдресФайлаОбработки()
	Возврат РеквизитФормыВЗначение("Объект").ИспользуемоеИмяФайла;
КонецФункции

Итак, файл записали на сервере, запускаем фоновое задание, которому говорим запустить функцию из БСП, которая запустит наш метод из этого файла внешней обработки:

ДвоичныеДанныеНаСервере = ПолучитьИзВременногоХранилища(ФайлОбработкиВоВременномХранилище);
            АдресФайлаНаСервере = КаталогВременныхФайлов()+ИмяФайла;
            ДвоичныеДанныеНаСервере.Записать(АдресФайлаНаСервере);
            
            СтруктураПараметров = Новый Структура("РезультатЗапроса,Код,Параметры",Результат[Результат.ВГраница()],Код,ПараметрыЗапроса);    

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

Ну и наконец подключим обработчик ожидания, который будет мониторить работу нашего фонового процесса и выводить прогресс и сообщения:

ПодключитьОбработчикОжидания("ПроверкаФоновогоЗадания",1,Ложь);

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

Функция ПроверкаФоновогоЗаданияНаСервере()
	Результат = Новый Структура("Прогресс,Состояние");
		
	Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ИдентификаторЗадания);
	ЗаполнитьЗначенияСвойств(Результат,Задание);
	Результат.Состояние= СТрока(Результат.Состояние);
	Если Задание = Неопределено Тогда
		Возврат Неопределено;
	Иначе
		
		Сообщения=Задание.ПолучитьСообщенияПользователю(Ложь);
		Для Каждого Сообщение ИЗ Сообщения Цикл
			Если Лев(Сообщение.Текст,1) <> "{" Тогда
				Сообщить(Сообщение.Текст);
			КонецЕсли;
		КонецЦикла;
		Прогресс = ДлительныеОперации.ПрочитатьПрогресс(ИдентификаторЗадания);
		Если Прогресс <> Неопределено Тогда
			ПрогрессОбработки = Прогресс.Процент;//?(Прогресс.Процент<>0,Прогресс.Процент,ПрогрессОбработки);
		КонецЕсли;
		Результат.Вставить("Прогресс",Прогресс);
	КонецЕсли;

	Возврат Результат;
КонецФункции

&НаКлиенте
Процедура УбитьПроцесс(Команда)
	
	УбитьПроцессНаСервере();
	ОтключитьОбработчикОжидания("ПроверкаФоновогоЗадания");
	Сообщить(""+ТекущаяДата()+" задание выполнено");
	
КонецПроцедуры

&НаСервере
Процедура УбитьПроцессНаСервере()
	Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ИдентификаторЗадания);
	Задание.Отменить();
	Элементы.ГруппаФоновоеЗадание.Видимость=Ложь;
КонецПроцедуры

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

Все описанные методы постоянно используются мной при создании обработок и периодически дорабатываются. Будут предложения как улучшить - пишите в комментариях, если сам допилю - обновлю статью. Всем Удачи!

94

См. также

Комментарии
Сортировка: Древо
1. Infactum 263 03.09.18 17:26 Сейчас в теме
Чем типовые механизмы БСП не угодили? Там еще и контроль доступа для фонового задания задействовать можно будет.
DrAku1a; kiruha; +2 Ответить
2. nikita0832 108 03.09.18 22:36 Сейчас в теме
(1) читал я про эти методы, к сожалению нет идентификатора задания, а хотелось бы получать прогресс и выводить сообщения из фонового задания. а то от обычного кружка или котика радости мало))
BigB; DrAku1a; Vlad33k; +3 Ответить
3. RocKeR_13 396 04.09.18 15:37 Сейчас в теме
4. nikita0832 108 04.09.18 16:31 Сейчас в теме
(3) о да, она даже у меня в закладках есть) там есть привязка к "ИмяМодуля.ИмяЭкспортнойПроцедуры", ещё и замена общей формы, а задача - реализовать без изменения конфигурации.
5. Malfarion 189 04.09.18 18:52 Сейчас в теме
(4) Привет, не пробовал такой хак пройдет аудит на Fresh ? С учетом того что обработка во внешних, из файла понятно что не возьмет.
6. nikita0832 108 04.09.18 21:46 Сейчас в теме
(5) Нет, не пробовал, попробуй - нам расскажешь.
7. Malfarion 189 05.09.18 01:05 Сейчас в теме
8. asved.ru 35 05.09.18 09:25 Сейчас в теме
Необходимо понимать, что объект внешней обработки, вызов метода которого выполняется в фоне - не тот же самый объект, форма которого открыта на клиенте.
9. nikita0832 108 05.09.18 12:01 Сейчас в теме
(8) Да, контекст в этом случае конечно отвалится. Я вообще вызываю процедуру из модуля объекта этой внешней обработки и туда передаю все что нужно. Рабочий пример если интересно здесь - https://infostart.ru/public/585055/ это по сути мой рабочий инструмент.
Оставьте свое сообщение