Полный модуль своего автономного мобильного Пульта: сканирование документов: реальные страницы, порядок и пять оценок качества. Подготовка, действительные источники данных, отмена, границы контекста и контроль результата описаны отдельно.
Где и как запускать
Полный модуль управляемой формы Пульт собственного автономного мобильного приложения УчебноеМультимедиа85; здесь не выполнялся.
Подготовка
Создать НОВУЮ отдельную конфигурацию УчебноеМультимедиа85 для автономного мобильного приложения8.5.1.1343; совместимость Не использовать. Не рабочую базу. Для опытов нужен свой Android/iOS и соответствующая мобильная платформа, не Windows desktop и не подключенный мобильный клиент.
Конфигуратор → Обработки → УчебноеМультимедиа85 → Формы → Пульт: управляемая форма. Вставить ВЕСЬ модуль только одного урока. Его ПриОткрытии и ПередЗакрытием назначить событиям формы через конструктор. ПриОткрытии имеет Отказ; ПередЗакрытием — четыре аргумента Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка.
Реквизиты формы: Согласие — Булево, начальное значение Ложь; Итог — Строка неограниченной длины, многострочное поле только просмотра. Для камеры/документов дополнительно Предпросмотр — Картинка и поле картинки с этим путем к данным.
Создать команды СканироватьЧерезОповещения, СканироватьАсинх, действия с точно теми же именами и кнопки. При снятом Согласие новая операция не начинается; результат сканирования также не принимается без согласия.
Для штрихкодового урока добавить РазрешенныйКод — Строка длины13 и поле ввода; до открытия сканера вписать цифры своей реальной упаковки. Для остальных уроков реквизит не нужен.
Сохранить свою конфигурацию, проверить и обновить собственную базу (F7). Включить требуемую функциональность мобильного приложения — камеру/микрофон для выбранного урока — и развернуть только собственный учебный пакет на своем устройстве обычным для вашей мобильной платформы способом. Здесь сборка пакета, установка и F7 не выполнялись.
На устройстве самостоятельно дать нужные разрешения ОС только перед своим опытом. Согласие на форме не заменяет разрешение ОС. Снимать только свою бумагу/упаковку, записывать свою короткую фразу; не чужие лица, разговоры, документы или экран.
Весь модуль содержит проверенные ветви препроцессора: мобильные API только в МобильноеПриложениеКлиент, имя базы — только в МобильноеПриложениеСервер. Проверка базы локальная для автономного приложения, не сетевой сервер. API также существует у мобильного клиента, но этот конкретный стенд намеренно автономный.
runtimeVerifiedScenarios=0. Проверена только установленная справка и исходный HTML/код. Камера, микрофон, голос, устройство, 1С и ОС-разрешения здесь не запускались; новых резервных копий не создано.
#Если МобильноеПриложениеКлиент Тогда
Перем СвойЗапуск, СвояОперация, СвояГотовность;
Перем СвоиСтраницы;
&НаКлиенте
Процедура ПриОткрытии(Отказ)
СвойЗапуск = Неопределено;
СвояОперация = "";
СвояГотовность = Ложь;
Согласие = Ложь;
Итог = "Установите согласие только перед собственным опытом";
Попытка
ПроверитьСвойМобильныйСтендНаСервере();
Если ТипЗнч(СредстваМультимедиа) <> Тип("СредстваМультимедиа") Тогда
ВызватьИсключение "Нужен реальный глобальный мобильный объект мультимедиа";
КонецЕсли;
СвояГотовность = Истина;
Исключение
Отказ = Истина;
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
СтандартнаяОбработка = Истина;
Если СвояОперация <> "" Тогда
Отказ = Истина;
ТекстПредупреждения = "Сначала завершите только свой опыт: " + СвояОперация;
Если Не ЗавершениеРаботы Тогда
Сообщить(ТекстПредупреждения);
КонецЕсли;
Возврат;
КонецЕсли;
СвояГотовность = Ложь;
СвойЗапуск = Неопределено;
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьСвоеСогласие()
Если Не СвояГотовность Или Не Согласие Тогда
ВызватьИсключение "Нужны открытое собственное окно и явное согласие на этот опыт";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция НачатьСвойОпыт(Операция)
ПроверитьСвоеСогласие();
Если СвояОперация <> "" Тогда
ВызватьИсключение "Не запускайте второй собственный опыт до завершения первого";
КонецЕсли;
СвойЗапуск = Новый УникальныйИдентификатор;
СвояОперация = Операция;
Итог = Операция;
Возврат СвойЗапуск;
КонецФункции
&НаКлиенте
Функция ЭтоСвойОпыт(Запуск)
Возврат СвояГотовность И СвойЗапуск <> Неопределено И Запуск = СвойЗапуск;
КонецФункции
&НаКлиенте
Процедура ЗавершитьСвойОпыт(Запуск)
Если ЭтоСвойОпыт(Запуск) Тогда
СвойЗапуск = Неопределено;
СвояОперация = "";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция СоздатьСвоиПараметрыСканирования()
Параметры = Новый ПараметрыСканированияДокументов;
Параметры.БелыйФонДокумента = Истина;
Параметры.ПрямоугольныйДокумент = Истина;
Параметры.СканированиеСериями = Истина;
Параметры.ПроверкаКачества = ПроверкаКачестваСканированияДокументов.ПредупреждатьНижеВысокого;
Возврат Параметры;
КонецФункции
&НаКлиенте
Процедура СканироватьЧерезОповещения(Команда)
Запуск = НачатьСвойОпыт("Сканирование с оповещениями");
Предпросмотр = Неопределено;
СвоиСтраницы = Новый Соответствие;
Попытка
Если Не СредстваМультимедиа.ПоддерживаетсяСканированиеДокументов() Тогда
ВызватьИсключение "Сканирование документов этим устройством не поддерживается";
КонецЕсли;
Страница = Новый ОписаниеОповещения("СвояСтраницаПолучена", ЭтаФорма, Запуск);
Завершение = Новый ОписаниеОповещения("СвояСерияЗавершена", ЭтаФорма, Запуск);
СредстваМультимедиа.ПоказатьСканированиеДокументов("Два собственных учебных листа", Страница, Завершение, СоздатьСвоиПараметрыСканирования());
Исключение
СвоиСтраницы = Неопределено;
ЗавершитьСвойОпыт(Запуск);
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура СвояСтраницаПолучена(Результат, ДополнительныеПараметры) Экспорт
Если Не ЭтоСвойОпыт(ДополнительныеПараметры) Тогда
Возврат;
КонецЕсли;
Если Не Согласие Тогда
СредстваМультимедиа.ЗакрытьСканированиеДокументов();
Возврат;
КонецЕсли;
Если ТипЗнч(Результат) <> Тип("СтраницаСканированияДокументов") Тогда
ВызватьИсключение "Нужна настоящая страница текущего сканирования";
КонецЕсли;
Идентификатор = Результат.Идентификатор;
Если ТипЗнч(Идентификатор) <> Тип("Число") Тогда
ВызватьИсключение "ID настоящей страницы должен быть числом, не номером строки своей таблицы";
КонецЕсли;
СвоиСтраницы.Вставить(Идентификатор, Результат);
Если СвоиСтраницы.Количество() > 2 Тогда
Итог = "В опыте принимаются не более двух разных полученных ID страниц; свое сканирование закрывается";
СредстваМультимедиа.ЗакрытьСканированиеДокументов();
Возврат;
КонецЕсли;
Если Не ПроверитьСвоюСтраницу(Результат) Тогда
Итог = Итог + "; не считается годной страницей пакета";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура СвояСерияЗавершена(ИдентификаторыСтраниц, ДополнительныеПараметры) Экспорт
Если Не ЭтоСвойОпыт(ДополнительныеПараметры) Тогда
Возврат;
КонецЕсли;
ЗавершитьСвойОпыт(ДополнительныеПараметры);
Если ИдентификаторыСтраниц = Неопределено Или Не Согласие Тогда
СвоиСтраницы = Неопределено;
Предпросмотр = Неопределено;
Итог = "Серия отменена или согласие снято: пакет не принят";
Возврат;
КонецЕсли;
Если ТипЗнч(ИдентификаторыСтраниц) <> Тип("Массив") Тогда
ВызватьИсключение "Завершение возвращает массив реальных ID, не массив картинок";
КонецЕсли;
Страницы = Новый Массив;
Для Каждого Идентификатор Из ИдентификаторыСтраниц Цикл
Страница = СвоиСтраницы.Получить(Идентификатор);
Если Страница = Неопределено Тогда
ВызватьИсключение "Для итогового ID нет реально полученной страницы; неизвестные данные не придумываются";
КонецЕсли;
Страницы.Добавить(Страница);
КонецЦикла;
СвоиСтраницы = Неопределено;
РазобратьСвойПакет(Страницы);
КонецПроцедуры
&НаКлиенте
Асинх Процедура СканироватьАсинх(Команда)
Запуск = НачатьСвойОпыт("Сканирование с Ждать");
Предпросмотр = Неопределено;
Попытка
Если Не СредстваМультимедиа.ПоддерживаетсяСканированиеДокументов() Тогда
ВызватьИсключение "Сканирование документов этим устройством не поддерживается";
КонецЕсли;
Страницы = Ждать СредстваМультимедиа.СканироватьДокументыАсинх("Два собственных учебных листа", СоздатьСвоиПараметрыСканирования());
Если Не ЭтоСвойОпыт(Запуск) Тогда
Возврат;
КонецЕсли;
ЗавершитьСвойОпыт(Запуск);
Если Страницы = Неопределено Или Не Согласие Тогда
Итог = "Серия отменена или согласие снято: пакет не принят";
Возврат;
КонецЕсли;
РазобратьСвойПакет(Страницы);
Исключение
ЗавершитьСвойОпыт(Запуск);
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Функция ПроверитьСвоюСтраницу(Страница)
Если ТипЗнч(Страница) <> Тип("СтраницаСканированияДокументов") Тогда
ВызватьИсключение "Проверяется только настоящая страница своего результата";
КонецЕсли;
Изображение = Страница.ДанныеИзображения;
Двоичные = ПолучитьСвоиНебольшиеДанные(Изображение, "image/");
Предпросмотр = Новый Картинка(Двоичные);
Если Не Страница.ДокументНайден Тогда
Итог = "ID " + Строка(Страница.Идентификатор) + ": граница документа не найдена; исходное фото показано, страница не принята";
Возврат Ложь;
КонецЕсли;
Качество = Страница.ПараметрыКачества;
Если ТипЗнч(Качество) <> Тип("ПараметрыКачестваСканированияДокументов") Тогда
ВызватьИсключение "Нужны реальные оценки качества полученной страницы";
КонецЕсли;
Оценки = Новый Структура("Белый,Граница,Текст,Общее,Перспектива", Качество.КачествоБелого,
Качество.КачествоГраницы, Качество.КонтрастностьТекста, Качество.ОбобщенноеКачество, Качество.ПерспективныеИскажения);
Принята = Истина;
ОтчетОценок = "";
Для Каждого Оценка Из Оценки Цикл
Если ТипЗнч(Оценка.Значение) <> Тип("Число") Или Оценка.Значение < 1 Или Оценка.Значение > 10 Тогда
ВызватьИсключение "Реальная нормированная оценка должна быть в диапазоне1..10";
КонецЕсли;
Если Оценка.Значение < 7 Тогда
Принята = Ложь;
КонецЕсли;
ОтчетОценок = ОтчетОценок + Оценка.Ключ + "=" + Строка(Оценка.Значение) + "; ";
КонецЦикла;
Итог = "ID " + Строка(Страница.Идентификатор) + ": " + ОтчетОценок + ?(Принята, "принята по собственной политике", "нужно переснять по собственной политике");
Сообщить(Итог);
Возврат Принята;
КонецФункции
&НаКлиенте
Процедура РазобратьСвойПакет(Страницы)
Если ТипЗнч(Страницы) <> Тип("Массив") Или Страницы.Количество() = 0 Или Страницы.Количество() > 2 Тогда
ВызватьИсключение "Собственный учебный пакет содержит одну или две реальные страницы";
КонецЕсли;
ID = Новый Соответствие;
ВсеПриняты = Истина;
Порядок = "";
Для Каждого Страница Из Страницы Цикл
Если ТипЗнч(Страница) <> Тип("СтраницаСканированияДокументов") Тогда
ВызватьИсключение "Пакет должен содержать настоящие страницы, не номера или свои пустые объекты";
КонецЕсли;
Идентификатор = Страница.Идентификатор;
Если ТипЗнч(Идентификатор) <> Тип("Число") Или ID.Получить(Идентификатор) <> Неопределено Тогда
ВызватьИсключение "Итоговые ID должны быть действительными и не повторяться";
КонецЕсли;
ID.Вставить(Идентификатор, Истина);
Если Не ПроверитьСвоюСтраницу(Страница) Тогда
ВсеПриняты = Ложь;
КонецЕсли;
Порядок = Порядок + Строка(Идентификатор) + "; ";
КонецЦикла;
Итог = "Порядок реальных ID: " + Порядок + ?(ВсеПриняты, "Весь собственный пакет принят", "Пакет не принят: есть страница для пересъемки");
КонецПроцедуры
&НаКлиенте
Функция ПолучитьСвоиНебольшиеДанные(Данные, СемействоMIME)
Если ТипЗнч(Данные) <> Тип("ДанныеМультимедиа") Тогда
ВызватьИсключение "Нужен настоящий результат своего мобильного захвата";
КонецЕсли;
Если Лев(Данные.ТипСодержимого, СтрДлина(СемействоMIME)) <> СемействоMIME Тогда
ВызватьИсключение "Получен другой вид мультимедиа, не " + СемействоMIME;
КонецЕсли;
Двоичные = Данные.ПолучитьДвоичныеДанные();
Если Двоичные.Размер() = 0 Или Двоичные.Размер() > 33554432 Тогда
ВызватьИсключение "Для этого собственного опыта нужен непустой результат не более32МиБ";
КонецЕсли;
Возврат Двоичные;
КонецФункции
#Иначе
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Отказ = Истина;
Сообщить("Откройте свою отдельную автономную мобильную базу УчебноеМультимедиа85, не desktop или подключенный мобильный клиент");
КонецПроцедуры
&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
Отказ = Ложь;
СтандартнаяОбработка = Истина;
КонецПроцедуры
&НаКлиенте
Процедура СканироватьЧерезОповещения(Команда)
ВызватьИсключение "Этот собственный урок только для автономного мобильного приложения";
КонецПроцедуры
&НаКлиенте
Процедура СканироватьАсинх(Команда)
ВызватьИсключение "Этот собственный урок только для автономного мобильного приложения";
КонецПроцедуры
#КонецЕсли
&НаСервереБезКонтекста
Процедура ПроверитьСвойМобильныйСтендНаСервере()
#Если МобильноеПриложениеСервер Тогда
Если ТранзакцияАктивна() Тогда
ВызватьИсключение "Собственный опыт не запускается внутри чужой транзакции";
КонецЕсли;
#Иначе
ВызватьИсключение "Этот стенд требует локального сервера автономного мобильного приложения";
#КонецЕсли
КонецПроцедурыРазбор по шагам
Подготовить один-два собственных белых прямоугольных листа с крупными надписями Учебная страница1 и2. Это не OCR: содержание ученик сверяет глазами по Предпросмотр.
Создать свои параметры: белый фон и прямоугольный документ включены, серии включены, native предупреждает ниже высокого качества. Открывать сканер только после проверки поддержки устройства и согласия.
В callback страницы приходит реальная СтраницаСканированияДокументов. Ее ID хранится ключом соответствия, сам объект — значением; повторный приход того же ID заменяет только последнюю полученную версию.
Callback завершения возвращает массив ID в порядке пользователя, не страницы. Собирать пакет строго по этому массиву; страницы, удаленные пользователем из итоговой серии, не переносятся из всего кэша по порядку обхода соответствия.
Этот собственный callback-опыт ограничен двумя различными полученными ID. Третий закрывает именно свой интерфейс через ЗакрытьСканированиеДокументов. Это лимит своего кэша, не количество текущих видимых страниц после пользовательского удаления.
Асинх-вариант использует Ждать: готовый результат уже является упорядоченным массивом реальных страниц. Не обращаться к Обещание как к массиву до завершения и не смешивать результаты двух вариантов.
ДанныеИзображения проверить и преобразовать в реальную Картинка. Если ДокументНайден=Ложь, показывается исходное фото, но страница не принимается как обнаруженный документ.
Для найденного документа прочитать реальные КачествоБелого, КачествоГраницы, КонтрастностьТекста, ОбобщенноеКачество и ПерспективныеИскажения. Диапазон каждой оценки1..10. Собственная строгая политика принимает только все пять>=7; это отдельное решение приложения, не замена native-алгоритма ОбобщенноеКачество.
Проверить отмену, порядок2→1, низкое качество одного листа и третий distinct-ID. ID не перенумеровываются и readonly-свойства не присваиваются; весь пакет принимается только при годности каждой его страницы.
Как проверить результат
Будущий ручной запуск должен получить настоящий результат выбранного API на своем устройстве и пройти условия своего урока. Положительный флаг поддержки или отсутствие исключения само по себе не означает, что данные получены.
Без согласия новая операция должна отказать. Отмена должна оставлять отмену, а ошибка запуска — ошибку; второе нажатие не должно подменять активный GUID собственного опыта.
Проверьте отдельный отрицательный опыт по шагам урока: отсутствие голосового пакета, отмену захвата, чужой код или низкое качество/лишнюю страницу. Не подставляйте фиктивный объект или ожидаемые байты вместо результата платформы.
После освобождения собственного сеанса форму можно закрыть. При собственном активном сеансе ПередЗакрытием должен предложить сначала завершить его; поздний callback другого GUID не должен изменить Итог.
runtimeVerifiedScenarios=0. Все аппаратные результаты, ошибки и проверки здесь — условия будущей ручной приемки, а не выполненный тест1С.
Ожидаемый результат
Подготовлен самостоятельный мобильный урок с собственным сеансом и действительными результатами платформы; аппаратно здесь не запускался.
На что обратить внимание
СредстваМультимедиа — глобальное readonly-свойство мобильного контекста. Не создавать этот объект через Новый, не передавать неизвестный ИсходныйОбъект и не переносить вызовы в серверный/desktop-код.
Этот полный стенд рассчитан на автономное мобильное приложение. Серверная проверка имени конфигурации работает локально на устройстве; Метаданные.Имя не читается в мобильной клиентской ветви.
Поддерживается... — проверка возможностей, не разрешений ОС, занятости камеры/микрофона, качества изображения или слышимости звука. Отказ ОС и отмена пользователя требуют отдельной проверки.
Имена публичных callback связаны с настоящими ОписаниеОповещения и проверяются по GUID. Для аудио, текста, страницы, завершения серии и штрихкода сигнатуры различны; их нельзя заменять одним универсальным обработчиком.
Код явно не сохраняет медиа, не вызывает HTTP, почту, печать или бизнес-запись. Платформа/ОС могут использовать свой временный кэш; очистка переменной не доказывает удаление этого кэша. Размер32МиБ проверяется после получения байтов и не обещает предотвратить их первоначальную аллокацию.
Нормированная оценка сканирования не является OCR, распознанным текстом или гарантией юридической пригодности документа. Строгая политика>=7 каждого показателя — решение этого собственного примера.
Callback остановки звука/текста не всегда означает окончательное завершение: уведомление может временно прервать звук с последующим возобновлением. В этих уроках владелец сеанса снимается явной собственной командой остановки, не предположением о причине callback.
ПередЗакрытием имеет четыре аргумента. При ЗавершениеРаботы=Истина нельзя открывать окна или вызывать сервер: заполняется ТекстПредупреждения. Пользователь или ОС могут принудительно завершить приложение; отказ от обычного закрытия не гарантирует запрет этого. Всегда явно остановите свой микрофон/звук до выхода.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.