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








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

Warning

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

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

 

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

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

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

  1. Управляемая форма:
  2.  

     Демонстрационный код модуля внешней обработки (тут все топорно просто):

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

     

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

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

     

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

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

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

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

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

 

22 Comments

  1. qwinter

    Ну и любят же 1Сники велосипеды)))

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

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



    Reply
  3. Eret1k

    (2) Я имел в виду варианты запуска кода модуля в ФЗ, а за то что из обработки можно сделать регламентное задание благодарим БСП.

    Reply
  4. Xershi

    Ранее процент выполнения длительной операции можно было вывести только если на форме нарисовать элементы. С версии БСП 3.0 это уже не нужно или и ранее так работало?

    Reply
  5. Xershi

    А понял в чем дело. Использовал другой код.

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

    Показать

    Reply
  6. Xershi

    Переписал на ваш метод:

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

    Показать

    Не взлетело, прогресса нет. БСП в конфе 3.0.1.314.

    В процедуре, которая считает все

    ДлительныеОперации.СообщитьПрогресс(Процент, СтрШаблон(«Загрузка курсов завершена на %1 %», Процент));
    

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

    Возможно дело еще в:

    ПараметрыРегистрации.Вставить(«ВерсияБСП», «2.4.2.169»);

    Попробую поменять, но думаю не в этом дело.

    Reply
  7. Eret1k

    (6)

    Команда которая должна быть выполнена в фоне, должна иметь тип: ДополнительныеОтчетыИОбработкиКлиентСервер. ТипКомандыВызовСерверногоМетода();

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



    Reply
  8. Xershi

    (7) да заработало! Как через файл, если добавить обработку в справочник, так и через внешние.

    Единственный минус, мне нужно передавать параметры, когда я открываю форму.

    Команда с «ВызовСерверногоМетода» используется для формирования регламентного задания. Подумаю как обойти это и будет вообще песня!

    У меня также есть обработка, которая таким макаром запускает несколько потоков, но там везде был клиентский метод и прогресс не выводился. Возможно ли несколько потоков запустить таким вариантом?

    Reply
  9. Xershi

    Проверил на БСП 2.4.2.169 работает!

    А вот на 2.3.2.51 пишет, что прогресс может выводить, а вот сообщения нет.

    {ОбщийМодуль.ОбщегоНазначенияКлиентСервер.Модуль(2642)}: Если параметр ПараметрыОжидания.ВыводитьПрогрессВыполнения установлен в Истина, то параметр ПараметрыОжидания.ВыводитьСообщения должен быть установлен в Ложь в ДлительныеОперацииКлиент.ОжидатьЗавершение
    ВызватьИсключение ТекстИсключения;

    Посмотрел код, там тоже работает! Только нужно закомментить проверку в общем модуле «ДлительныеОперацииКлиент» процедуре «ПроверитьПараметрыОжидатьЗавершение»

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

    Она вызывает исключение, а после комментирования все работает!

    Reply
  10. Eret1k

    (8)

    возможно ли несколько потоков запустить таким вариантом?

    Да делал такое, НачатьВыполнениеСервернойКомандыВФоне() запускал в цикле, разделяя данные по порциям и передавал их в «ОбъектыНазначения». Собирал массив длительных операций и потом на клиенте опять в цикле передавал их в ДлительныеОперацииКлиент.ОжидатьЗавершение();

    Reply
  11. Xershi

    (10) не совсем понял.

    ДлительнаяОперация = НачатьВыполнениеСервернойКомандыВФоне(ПараметрыКоманды, ЭтаФорма.УникальныйИдентификатор);
    ДлительныеОперацииКлиент.ОжидатьЗавершение(ДлительнаяОперация, ОписаниеОповещения, НастройкиОжидания);
    

    Это делаем в цикле? Для чего собирать ДлительнаяОперация в массив?

    Reply
  12. rpgshnik

    (2) картинок не видно 🙁

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

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

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

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

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

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

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

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

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

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

    не стал придираться к еще одному плюсу (третьему), как ни странно не во всех примерах фоновых заданий выводятся сообщения

    Reply
  14. mrx2012

    Ребята, у кого получился рабочий вариант. Пришлите рыбу , пожалуйста.

    Reply
  15. Eret1k

    (14) Держите может поможет

    Reply
  16. mrx2012

    (15)огромное спасибо за статью и за обработку.

    У меня уже была обработка, для для асинхронного выполнения , но в ней очень не хватало индикатора выполнения. Надеюсь теперь будет.

    Reply
  17. mrx2012

    Все верно, но с готовым примером проще и быстрее.

    Reply
  18. viplelik

    А как сделать чтобы работало прямо из файла? (т.е. без добавления в «Дополнительные отчеты и обработки»)

    P.S. На новых БСП (3.0.2 и выше)

    Reply
  19. max_zhilin

    (15) не работает, не показывает прогресс, да и интерфейс блокирует. БСП 3.0.1.351

    В Процедура СообщитьПрогресс срабатывает это:

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

    Upd: разобрался, был включен режим отладки

    Reply
  20. max_zhilin

    Кстати, вот этот фрагмент успевает за секунду назапускаться сотни раз. Надо как-то ограничить одним разом.

      Если НЕ (Процент % 10) И Процент Тогда
    ДлительныеОперации.СообщитьПрогресс(Процент, СтрШаблон(«Задание пройдено на %1 процентов», Процент));
    
    Reply
  21. Nuuq

    (7)

    Команда которая должна быть выполнена в фоне, должна иметь тип: ДополнительныеОтчетыИОбработкиКлиентСервер. ТипКомандыВызовСерверногоМетода();

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

    А можно по подробнее, как отладить?

    Я в параметр запуска написал «РежимОтладки», в Конфигураторе — Отладка — Подключение — Автоматическое подключение — галочки стоят Клиентские и внешние соединения на сервере и Фоновые задания (т.о. в табличке Сервер и Тонкий клиент 2 строчки). Открыл обработку в конфигураторе, поставил точку. Добавил (обновил) обработку в справочнике Дополнительных внешних обработок, тут же нажал «Выполнить» — но в отладку не попадает.

    ЧЯДНТ?

    Reply
  22. fuser

    (19) воспользоваться поиском по сайту (внешняя обработка длительные операции БСП)

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *