gifts2017

Прогресс бар 8.2

Опубликовал Иван Григорьев (Soloist) в раздел Программирование - Практика программирования

Данный приём позволяет показать текущее состояние выполнения серверного кода пользователю, запустившего его на выполнение.

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

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

Имея 4 модуля ксПрогрессБар, ксПрогрессБарВызовСервера, ксПрогрессБарКлиент, ксПрогрессБарКлиентСервер (которые можно достать из cf файла) функционал прогресса будет выглядеть примерно так:

1. Код модуля клиента

Основная идея заключается в том, что серверный код мы выполняем фоновым заданием ВыполнениеВФоне.ТестПрогрессБара. И при этом, подключив обработчик ожидания СобратьИнформациюХодаВыполнения, начинаем сбор состояния, пока метод ксПрогрессБарКлиент.ПоказатьСостояние не вернёт флаг о завершении фонового задания.

&НаКлиенте
Процедура Тест(Команда)

   
ТестНаСервере();

    Если
ВыполнятьВФоне() Тогда
       
ПодключитьОбработчикОжидания("СобратьИнформациюХодаВыполнения", ВремяОткликаСбораИнфорамацииХодаВыполнения());
    КонецЕсли;

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


Процедура
ТестНаСервере()

    Если
ВыполнятьВФоне() Тогда

       
Доступность = Ложь;

       
ФоновыеЗадания.Выполнить("ВыполнениеВФоне.ТестПрогрессБара", , УникальныйИдентификатор, НСтр("ru = 'Тест прогресс бара'"));

    Иначе

       
ВыполнениеВФоне.ТестПрогрессБара();

    КонецЕсли;

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




&НаКлиентеНаСервереБезКонтекста
Функция ВыполнятьВФоне()

    Возврат НЕ
ксКэшНаСеанс.ИнформационнаяБазаФайловая();

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


&НаКлиенте
Процедура СобратьИнформациюХодаВыполнения()

    Если НЕ
ксПрогрессБарКлиент.ПоказатьСостояние(УникальныйИдентификатор) Тогда
       
ОтключитьОбработчикОжидания("СобратьИнформациюХодаВыполнения");
       
Доступность = Истина;
    КонецЕсли;

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

&НаКлиенте
Функция ВремяОткликаСбораИнфорамацииХодаВыполнения()

    Возврат
1;

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

2. Код модуля сервера (основная часть вашего кода)

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


Процедура ТестПрогрессБара() Экспорт

   
количествоИтераций = 2000000;


   
текстСообщения = НСтр("ru = 'Тест прогресс бара'");
   
пояснение = НСтр("ru = 'Пустой цикл для тестирования'");
   
общееКоличество = количествоИтераций;
   
ц = 0;

    Для
цИтератор = 1 По количествоИтераций Цикл

       
ксПрогрессБар.УвеличитьСчетчикИУстановитьСостояние(ц, общееКоличество, текстСообщения, пояснение);


       
// Основной код.

   
КонецЦикла;

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

3. Результат

Итогом будет вот такой прогресс бар:

4. Заключение

Минусы:

-данный функционал получилось реализовать только для тонкого клиента. Хотя в основном для отрисовки прогресса используется типовой метод "Состояние".

Замечания:

-отладку фонового задания можно включить так:

-или, решением такой проблемы, может быть разработка дополнительного режима отладки, после включения которого, код будет выполняться не в фоне. Как пример, можно модернизировать метод ВыполнятьВФоне:

&НаКлиентеНаСервереБезКонтекста
Функция ВыполнятьВФоне(пТестирование)

    Возврат НЕ
пТестирование
        И НЕ ксКэшНаСеанс.ИнформационнаяБазаФайловая();

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

Плюсы:

+пользователь не только видит прогресс, но и может свободно пользоваться базой.

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Алексей Роза (DoctorRoza) 24.10.12 10:13
+ за информацию однозначно, может пригодится! :)
Добавьте вариант решения второго минуса, хотя бы наброски!
2. ksai ksai (ksai) 24.10.12 11:27
(1) DoctorRoza,
Добавьте вариант решения второго минуса, хотя бы наброски!

А галочка "Фоновые задания" в настройках отладки в конфигураторе чем не устраивает? Ну и ключ -debug в строке запуска сервера.
3. Иван Григорьев (Soloist) 24.10.12 11:48
(2) ksai, спасибо. Только что для себя открыл этот способ: Меню Отладка -> Подключение -> Автоматическое подключение -> Фоновые задания. http://screencast.com/t/eT0vgE9t
4. ksai ksai (ksai) 24.10.12 12:12
(3) Soloist,
Да не за что :) Я думал, факт общеизвестный.

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

1) Строка подключения инф. базы конфигуратора и строка подключения фонового задания должны совпадать посимвольно. Например: если конфигуратор подключается через строку "Srvr="server:1641";Ref="BASE";", а фоновое задание через "Srvr="server";Ref="BASE";", то отладка не подключится, хоть ты убейся.

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

P.S. Проверено на УПП 1.3
vasiliy_b; CratosX; JohnyDeath; mtv:); Soloist; +5 Ответить 1
5. ksai ksai (ksai) 24.10.12 12:32
(3) Soloist,
И, кстати, в свете последних событий:) стоит, наверно, изменить текст про минус в публикации.
6. Евгений Люлюк (Evg-Lylyk) 24.10.12 13:14
Еще один недостаток то что это фоновые задания следовательно без изменения конфы никак!!!
Вот по теме публикации: Эффективная индикация в 8.2
7. Александр Кияница (treedo) 24.10.12 18:42
Большое спасибо. Давно ломал голову как это реализовать в тонком клиенте)))
8. Иван Григорьев (Soloist) 25.10.12 07:57
(4) ksai, спасибо ещё раз. Попозже поправлю.
(6) Evg-Lylyk, даже и не думал искать по теме индикация... Извиняюсь. Да, конфу придётся изменить, поэтому старался минимизировать количество строк кода, инкапсулировав всё что можно, для пользования этим методом. Надеюсь удалось.
9. Александр Беляев (~gekK@~) 26.10.12 10:49
да уж...вроде задача должна решаться штатными методами, но...
10. ZOOBR (ZOOBR) 02.11.12 20:18
Идея вполне себе рабочая. Мысли в эту сторону были, но руки не дошли, так что автору+.
(9) А чем уж не штатный метод.) Добавить задание в конфигураторе по-моему не проблема, тем более, что обычно когда до таких "удобняков" дело доходит конфа уже изменена. А уж если вспомнить про всякие там XDTO и SOAP, то правка конфы это уже обыденность. Отладку тоже минусом врядли можно считать, скорее как и было озвучено просто нюансом.
(6) Там реализация сложновата не везде подойдет, хотя анализ приведён весьма показательный. Сляпать фоновое задание я думаю проще.
Правда как я понял без ложки дёгтя таки не обошлось. Проблема с реализацией будет в том случае если допустить, что прогресс-бар будет запускаться сразу от нескольких пользователей. Как быть в этом случае? Вот про это стоило бы упомянуть в статье.
11. Иван Григорьев (Soloist) 03.11.12 22:15
(10) ZOOBR, не понятно что за ложка? Прогресс рисует типовой метод "Состояние" и если операции непересекаемые, то вообще всё шикарно. Создадутся столько фоновых заданий сколько нужно, а они уж по уникальному идентификатору найдут своего пользователя. Допустим пакетное проведение документов по Организации1 для первого пользователя, и проведение по Организации2 для другого нам дадут вполне хороший результат. Если уж данные пересекаются, т.е. первый проводит по всем документам, а второй хочет по Организации2, то тут уж надо заботиться о захвате данных... но это не проблема прогресс бара... В худшем случае они оба увидят прогресс бар, но данные будут плохие. В хорошем, второму пользователю надо сказать - "подождите... данные захвачены другим пользователем".
12. Baza (pbazeliuk) 05.11.12 21:20
Идея хорошая, но как-то запутано все. Сделал проще для УТ 11 без изменения конфигурации (для SQL версии). Выложу сегодня в паблик.
13. Иван Григорьев (Soloist) 06.11.12 08:04
(12) Baza, чем проще - тем лучше:)
14. Олег Шалимов (CaSH_2004) 09.12.12 15:29
В публикации уточнение что работает только для тонкого, т.е. для ВЕБ не сработает? Проверено?
15. Иван Григорьев (Soloist) 11.12.12 11:18
(14) CaSH_2004, вы так интересно построили предложение-вопрос:)
Я был уверен, что работает, т.к. чему там не работать... Ради вас проверил на win 8)) Работает. И очень даже красиво http://screencast.com/t/N5u3ZHvrTRr
16. Олег Шалимов (CaSH_2004) 11.12.12 11:50
(15) Блогадарю, очень красиво, я так понимаю вы тоже пользуетесь Google Chrome вместо Internet Explorer? Просто по привычке или не устраивает что-то конкретное? Вопрос к тому что я долго не мог нормально запустить WEB базу нормально т.к. Explorer вис и сыпал кучей ошибок даже при авторизации, а при проведении группы документов так вообще полное повисание, а Chrome выполнил не задумываясь даже. Так что мы выбрали его для постоянного пользования.
17. Иван Григорьев (Soloist) 11.12.12 12:26
(16) CaSH_2004, раньше не устраивала актуальность, Chrome он ведь сам обновляется... а сейчас с него не слезу, т.к. у меня синхронизация закладок и паролей на нём, да и привык уже к нему.

Всё зависит от версии IE, у 1С есть минимальные требования к браузеру. На IE10 так же всё хорошо.
18. Дмитрий Чернавин (ddiimmaann) 04.04.13 15:17
Плюс за идею!
Прогресс-бар есс-но нужен, если выполняемое задание имеет значительную протяженность во времени. В жизни пользователи (а особенно мы, программисты, во время отладки) - не дожидаемся окончания процесса. Если закрыть "форму с кнопкой" к которой прикручен обработчик ожидания - то задание в фоне остается висеть и выполняется до конца. Предлагаю добавить в форму что-то типа:

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

&НаСервере
Функция ФоновоеЗаданиеАктивно()
	
	НашПроцесс = ксПрогрессБар.НайтиЗадание(УникальныйИдентификатор);
	
	Если НашПроцесс=Неопределено Тогда Возврат Ложь; КонецЕсли;
	
	Возврат НашПроцесс.Состояние=СостояниеФоновогоЗадания.Активно;
	
КонецФункции

&НаСервере
Процедура ОтменитьЗадание()
	
	НашПроцесс = ксПрогрессБар.НайтиЗадание(УникальныйИдентификатор);
	
	Если НашПроцесс=Неопределено Тогда
		Возврат;
	Иначе
		НашПроцесс.Отменить();
	КонецЕсли
	
КонецПроцедуры
...Показать Скрыть
19. Иван Григорьев (Soloist) 05.04.13 07:42
(18) ddiimmaann, спасибо. Скоро добавлю и обновлю.
20. Дмитрий Чернавин (ddiimmaann) 05.04.13 12:41
И еще, как идею, озвучу такую:
Для
1. упрощения интеграции
2. возможности использовать в командах (они запускаются вне контекста формы)
Я создал общую форму, в которую перенес часть методов из "1. Код модуля клиента".

В итоге удалось добиться вот такой минимизации (на примере команды)

В Модуле команды:
&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)

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


При таком подходе задача по управлению фоновым процессом иожно возложить на форму
-процесс запускается с ее идентификатором
-все обработки и проверки по завершению незаконченного процесса - можно написать 1 раз
-в форме можно реализовать проверки на тип платформы (в клиент-серверном - запускать в фоне, в файл серверном использовать другой механизм - если таковой имеется, я не в курсе :-) )
21. Дмитрий Чернавин (ddiimmaann) 05.04.13 12:57
Наверное стоит еще код общей формы привести.

Форма простая:
1. Кнопка "Отменить и закрыть" (по функциональности совпадает со штатным крестиком закрытия формы, но пользюкам так приятнее)
2. Два параметра формы (см. предыдущее сообщение)

Модуль формы:

////// События формы

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	
	ИмяПроцедуры		= Параметры.ИмяПроцедуры;
	ЭтаФорма.Заголовок	= Параметры.НаименованиеЗадачи;
	
КонецПроцедуры

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

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

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

&НаКлиенте
Процедура ОтменитьИЗакрыть(Команда)
	Закрыть();
КонецПроцедуры


///// Реализация прогресс-бара

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

&НаКлиенте
Процедура СобратьИнформациюХодаВыполнения() Экспорт
	
	Если НЕ ксПрогрессБарКлиент.ПоказатьСостояние(УникальныйИдентификатор) Тогда
		ОтключитьОбработчикОжидания("СобратьИнформациюХодаВыполнения");
		Доступность = Истина;
	КонецЕсли;
	
КонецПроцедуры

&НаСервереБезКонтекста
Функция ФоновоеЗаданиеАктивно(УникальныйИдентификатор)
	
	НашПроцесс = ксПрогрессБар.НайтиЗадание(УникальныйИдентификатор);
	
	Если НашПроцесс=Неопределено Тогда Возврат Ложь; КонецЕсли;
	
	Возврат НашПроцесс.Состояние=СостояниеФоновогоЗадания.Активно;
	
КонецФункции

&НаСервереБезКонтекста
Процедура ОтменитьЗадание(УникальныйИдентификатор)
	
	НашПроцесс = ксПрогрессБар.НайтиЗадание(УникальныйИдентификатор);
	
	Если НашПроцесс=Неопределено Тогда
		Возврат;
	Иначе
		НашПроцесс.Отменить();
	КонецЕсли
	
КонецПроцедуры
...Показать Скрыть
22. Алексей Опарихин (Al-X) 18.09.13 11:17
+ !!! Вот это точно пригодиться.
23. Сергей Галюк (dj_serega) 23.04.14 11:36
В cf нашел ошибку:

#Если Клинет Тогда

Состояние(пТекстСообщения, пПрогресс, пПояснение);

#КонецЕсли
24. Иван Григорьев (Soloist) 25.04.14 07:52
(23) dj_serega, спасибо большое.
Исправил. Обновил cf.