Прочитывает заголовок сообщения обмена данными и проверяет содержащиеся в заголовке данные. Если какие-либо данные указаны неправильно (задан неизвестный план обмена, указан узел, не в ходящий в план обмена, номер сообщения не соответствует ожидаемому), вызывается исключение. При начале чтения сообщения устанавливается блокировка на запись базы данных, соответствующую узлу плана обмена, чтобы предотвратить на время чтения сообщения другие операции чтения и записи сообщений, относящихся к данному узлу, а также не допустить изменения данных узла.
Синтаксис
НачатьЧтение(<ЧтениеXML>, <ДопустимыйНомерСообщения>)Примеры платформы
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайла);
ЧтениеСообщения.НачатьЧтение(ЧтениеXML, ДопустимыйНомерСообщения.Очередной);Полное описание, параметры и ограничения
ЧтениеСообщенияОбмена.НачатьЧтение (ExchangeMessageReader.BeginRead)
ЧтениеСообщенияОбмена (ExchangeMessageReader)
НачатьЧтение (BeginRead)
Доступен, начиная с версии 8.0.
Синтаксис:
НачатьЧтение(<ЧтениеXML>, <ДопустимыйНомерСообщения>)
Параметры:
<ЧтениеXML> (обязательный)
Тип: ЧтениеУзловDOM, ЧтениеFastInfoset, ЧтениеXML.
Объект, через который будет осуществляться чтение сообщения.
<ДопустимыйНомерСообщения> (необязательный)
Тип: ДопустимыйНомерСообщения.
Допустимый номер сообщения, которое может быть обработано методом НачатьЧтение.
Значение по умолчанию: Больший.
Описание:
Прочитывает заголовок сообщения обмена данными и проверяет содержащиеся в заголовке данные. Если какие-либо данные указаны неправильно (задан неизвестный план обмена, указан узел, не в ходящий в план обмена, номер сообщения не соответствует ожидаемому), вызывается исключение.
При начале чтения сообщения устанавливается блокировка на запись базы данных, соответствующую узлу плана обмена, чтобы предотвратить на время чтения сообщения другие операции чтения и записи сообщений, относящихся к данному узлу, а также не допустить изменения данных узла.
Доступность:
Сервер, толстый клиент, внешнее соединение, мобильное приложение (сервер), мобильный автономный сервер.
Пример:
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайла);
ЧтениеСообщения.НачатьЧтение(ЧтениеXML, ДопустимыйНомерСообщения.Очередной);
Использование в версии:
Доступен, начиная с версии 8.0.
Методическая информация
Практические примеры
Своя двухузловая РИБ: регистрация → пакет → actual чтение → ответ → безопасная очистка
Отдельная учебная РИБ A/B с одной собственной строкой и только собственными ссылками. Полный XML-протокол использует настоящие factory writer/reader, нативный начальный образ B и различает регистрацию, отправку, импорт и реально принятый ответ. Код только authored/static, не выполнялся в 1С.
Где и как запускать
Полный ordinary thick модуль собственной формы УчебнаяРИБ85, exact platform8.5.1.1343. Manual два файловых own A/B по указанным UUID путям; actual B только native initial imageA. Никаких actual действий агентом.
Подготовка
НИЧЕГО из этого урока не выполняйте в рабочей базе. Вручную создайте новый каталог C:\1C85_OwnExchangeLab\c88b7ab1-1d4c-4893-b6be-1f16f4b85293 и отдельную совершенно новую пустую файловую базу A внутри него. Каталог B должен отсутствовать полностью. Если UUID-папка уже содержит чужие данные или B существует — выберите другой учебный стенд и адаптируйте все связанные guards вместе; не удаляйте/не переиспользуйте содержимое.
В A, только платформа8.5.1.1343, Имя конфигурации=УчебнаяРИБ85; основное приложение Обычное. Ровно один неиерархический каталог МояСтрока85: Код Строка8, уникальность включена, автоматическая нумерация выключена, Наименование Строка40; собственные реквизиты Количество85 Число(9,0), Метка85 Строка36. Нет иных каталогов, документов, регистров, подписок, фоновых/регламентных заданий, обменов, расширений и рабочих modules.
Ровно один ПланОбмена МояРИБ85: РаспределеннаяИнформационнаяБаза=Да, Код Строка1, Наименование Строка40. В составе только каталог МояСтрока85, его АвтоРегистрация=Запретить. Не включайте узлы плана, константы или другие объекты в состав. После обновления только собственного A откройте его в обычном клиенте: Операции → Планы обмена → МояРИБ85. У своего предопределённого ЭтотУзел задайте Код A, Наименование Мой A c88b7ab1. В том же списке создайте второй actual узел Код B, Наименование Мой B c88b7ab1. Иные узлы/пометки удаления отсутствуют. Не присваивайте выдуманные UUID узлам.
Создайте обработку УчебнаяРИБ85 и Обычную Основную форму. Реквизиты: Исходящее85, Входящее85 и Состояние85 Строка неограниченной длины. Покажите первые два как большие многострочные поля: Исходящее85 только просмотр, Входящее85 принимает лишь XML, вручную скопированный из своей другой формы. Состояние85 только просмотр. Код целиком в модуле этой ordinary формы; ПриОткрытии() назначьте одноимённому событию.
Добавьте кнопки с одноимёнными действиями: СоздатьТолькоСвойНачальныйB85, ЗарегистрироватьТолькоСвоюСтроку85, ПрерватьСвойWriter85, ОтправитьСвоюСтрокуA85, ПосмотретьИПрерватьСвойReader85, ПринятьСвойПакет85, ОтветитьТолькоИзСвоегоB85, ОчиститьТолькоПодтвержденнуюСвоюСтроку85, ОтсоединитьИВосстановитьТолькоСвойChildB85. Обновите только свой A; открывайте thick ordinary клиентом.
Пока каталог МояСтрока85 пуст: A→СоздатьТолькоСвойНачальныйB85. Это настоящий native СоздатьНачальныйОбраз для своего actual nodeB и только несуществующего собственного UUID-dir B. Метод создаёт базу и удаляет регистрации элементов данных/конфигурации данного узла — не запускайте после рабочих регистраций. Агент НЕ выполнял это создание, файловые вызовы или копирование базы.
Зарегистрируйте B в своём launcher по ровно C:\1C85_OwnExchangeLab\c88b7ab1-1d4c-4893-b6be-1f16f4b85293\B, без серверного соединения. A/B пути укажите точно, без завершающего слеша, относительных путей и дополнительных частей File: guard требует точное буквальное имя вместе с Файл.ПолноеИмя; он не обещает разрешать symlinks или альтернативные написания. Откройте тот же код формы из реального образа B: native ЭтотУзел обязан стать B, native ГлавныйУзел=A. Не создавайте независимую B вручную — её actual ссылки/metadata происходят только из начального образа. Сначала обе базы имеют пустой каталог, sent/received counters0.
После создания B вручную только в A через Операции → Справочники → МояСтрока85 создайте единственную свою запись: Код Э85, Наименование Мой обмен85, Количество85=7, Метка85=c88b7ab1-1d4c-4893-b6be-1f16f4b85293. В B она появится только из real импорта. Авторегистрация запрещена, поэтому явную регистрацию выполняет own команда. До завершения упражнения строку, CF, extension, nodes, counters не редактируйте; A-форма остаётся открытой до собственного ответа B.
Два собственных клиента, по одному сеансу на каждую базу; никаких других пользователей, фоновых задач или параллельного обмена. Не вставляйте внешние/чужие XML. Сообщения передаются вручную между строками своих двух форм, не через сеть/файлы/production endpoints.
#Если ТолстыйКлиентОбычноеПриложение Тогда
Перем МояСторона85, МойЭтотУзел85, МойДругойУзел85, МояСсылка85, МойНомерОтправки85, МояВерсияОтправки85, МойПодтвержденныйНомер85;
Процедура ПриОткрытии()
ПроверитьСвойПустойСтенд();
ОпределитьСвоиУзлы85();
МойНомерОтправки85 = 0;
МояВерсияОтправки85 = "";
МойПодтвержденныйНомер85 = 0;
Исходящее85 = "";
Входящее85 = "";
Состояние85 = "Собственный узел " + МояСторона85 + ". Обмен только между собственными A/B; сообщения в строках формы.";
КонецПроцедуры
Процедура ОпределитьСвоиУзлы85()
План = Метаданные.ПланыОбмена.Найти("МояРИБ85");
Каталог = Метаданные.Справочники.Найти("МояСтрока85");
Если План = Неопределено Или Каталог = Неопределено Тогда
ВызватьИсключение "Нужны только собственные МояРИБ85 и МояСтрока85";
КонецЕсли;
Если Не План.РаспределеннаяИнформационнаяБаза Или План.Состав.Количество() <> 1 Или Не План.Состав.Содержит(Каталог) Тогда
ВызватьИсключение "Нужна настоящая РИБ, в составе только собственный каталог";
КонецЕсли;
Если План.Состав.Найти(Каталог).АвтоРегистрация <> АвтоРегистрацияИзменений.Запретить Тогда
ВызватьИсключение "Свой каталог должен иметь запрещённую авторегистрацию для явного учебного контроля";
КонецЕсли;
Проверка = Новый Запрос("ВЫБРАТЬ Ссылка, Код, Наименование, ПометкаУдаления ИЗ ПланОбмена.МояРИБ85");
Узлы = Проверка.Выполнить().Выгрузить();
Если Узлы.Количество() <> 2 Тогда
ВызватьИсключение "Нужны ровно два реально подготовленных собственных узла A/B";
КонецЕсли;
А = Узлы.Найти("A", "Код");
Б = Узлы.Найти("B", "Код");
Если А = Неопределено Или Б = Неопределено Тогда
ВызватьИсключение "Нет точных собственных узлов A/B";
КонецЕсли;
Если А.Наименование <> "Мой A c88b7ab1" Или Б.Наименование <> "Мой B c88b7ab1" Или А.ПометкаУдаления Или Б.ПометкаУдаления Тогда
ВызватьИсключение "Имена или состояние собственных узлов не совпали";
КонецЕсли;
МойЭтотУзел85 = ПланыОбмена.МояРИБ85.ЭтотУзел();
Если МойЭтотУзел85 = А.Ссылка Тогда
МояСторона85 = "A";
МойДругойУзел85 = Б.Ссылка;
ИначеЕсли МойЭтотУзел85 = Б.Ссылка Тогда
МояСторона85 = "B";
МойДругойУзел85 = А.Ссылка;
Иначе
ВызватьИсключение "ЭтотУзел не совпал с одной из двух actual собственных ссылок";
КонецЕсли;
Если МойДругойУзел85 = МойЭтотУзел85 Или Не ЗначениеЗаполнено(МойДругойУзел85) Тогда
ВызватьИсключение "Получатель должен быть реальным другим собственным узлом";
КонецЕсли;
ПроверитьСвойПуть85(МояСторона85);
Главный = ПланыОбмена.ГлавныйУзел();
Если (МояСторона85 = "A" И Главный <> Неопределено) Или (МояСторона85 = "B" И Главный <> МойДругойУзел85) Тогда
ВызватьИсключение "A должен быть actual корнем, B настоящим дочерним образом A";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьСвойПуть85(Сторона)
Части = СтрРазделить(СтрокаСоединенияИнформационнойБазы(), ";");
Если Части.Количество() <> 1 И Части.Количество() <> 2 Тогда
ВызватьИсключение "Разрешён только единственный File без дополнительных параметров";
КонецЕсли;
Если Части.Количество() = 2 И СокрЛП(Части[1]) <> "" Тогда
ВызватьИсключение "После собственного File допускается лишь пустой завершающий разделитель";
КонецЕсли;
Параметр = СокрЛП(Части[0]);
Если НРег(Лев(Параметр, 5)) <> "file=" Тогда
ВызватьИсключение "Серверная или чужая строка соединения запрещена";
КонецЕсли;
Путь = Сред(Параметр, 6);
Если Лев(Путь, 1) = """" И Прав(Путь, 1) = """" Тогда
Путь = Сред(Путь, 2, СтрДлина(Путь) - 2);
КонецЕсли;
ФайлБазы = Новый Файл(Путь);
Ожидание = "C:\1C85_OwnExchangeLab\c88b7ab1-1d4c-4893-b6be-1f16f4b85293\" + Сторона;
Если НРег(Путь) <> НРег(Ожидание) Или НРег(ФайлБазы.ПолноеИмя) <> НРег(Ожидание) Тогда
ВызватьИсключение "Разрешены только точные собственные UUID-каталоги A/B";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьСвоюСторону85(Сторона)
ПроверитьСвойПустойСтенд();
ОпределитьСвоиУзлы85();
Если МояСторона85 <> Сторона Тогда
ВызватьИсключение "Команда предназначена только своей стороне " + Сторона;
КонецЕсли;
КонецПроцедуры
Процедура СоздатьТолькоСвойНачальныйB85(Кнопка)
ПроверитьСвоюСторону85("A");
Каталог = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ Справочник.МояСтрока85");
Если Каталог.Выполнить().Выгрузить().Количество() <> 0 Тогда
ВызватьИсключение "Начальный образ создаётся до единственной своей строки, каталог обязан быть пустым";
КонецЕсли;
Цель = "C:\1C85_OwnExchangeLab\c88b7ab1-1d4c-4893-b6be-1f16f4b85293\B";
ФайлЦели = Новый Файл(Цель);
Если НРег(ФайлЦели.ПолноеИмя) <> НРег(Цель) Или ФайлЦели.Существует() Тогда
ВызватьИсключение "Только точный собственный ещё не существующий B; ничего не заменяем и не очищаем";
КонецЕсли;
Если МойДругойУзел85.Код <> "B" Или МойДругойУзел85.ЭтотУзел Или ПланыОбмена.ГлавныйУзел() <> Неопределено Тогда
ВызватьИсключение "Изображение разрешено только своему childB из настоящего корня A";
КонецЕсли;
ПланыОбмена.СоздатьНачальныйОбраз(МойДругойУзел85, "File=""" + Цель + """");
Если Не ФайлЦели.Существует() Тогда
ВызватьИсключение "Native создание образа не подтверждено существованием собственного B; дальнейший обмен запрещён";
КонецЕсли;
Состояние85 = "Создан только собственный начальный B. Native метод удаляет регистрации этого узла при создании образа; это не резервная копия. Откройте именно B, затем вручную подготовьте одну строку в A.";
КонецПроцедуры
Функция ПрочитатьСвоюСтроку85()
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Код, Наименование, Количество85, Метка85, ПометкаУдаления ИЗ Справочник.МояСтрока85");
Строки = Запрос.Выполнить().Выгрузить();
Если Строки.Количество() <> 1 Тогда
ВызватьИсключение "Разрешена ровно одна actual собственная строка";
КонецЕсли;
СтрокаДанных = Строки[0];
Если СтрокаДанных.Код <> "Э85" Или СтрокаДанных.Наименование <> "Мой обмен85" Или СтрокаДанных.Количество85 <> 7
Или СтрокаДанных.Метка85 <> "c88b7ab1-1d4c-4893-b6be-1f16f4b85293" Или СтрокаДанных.ПометкаУдаления Тогда
ВызватьИсключение "Own marker/code/name/value не совпали; чужие или изменённые строки запрещены";
КонецЕсли;
Возврат СтрокаДанных.Ссылка;
КонецФункции
Процедура ЗарегистрироватьТолькоСвоюСтроку85(Кнопка)
ПроверитьСвоюСторону85("A");
МояСсылка85 = ПрочитатьСвоюСтроку85();
Если ПланыОбмена.ИзменениеЗарегистрировано(МойДругойУзел85, МояСсылка85) Тогда
ВызватьИсключение "Для отдельного опыта нужна ещё не зарегистрированная own строка; ничего не сбрасываем автоматически";
КонецЕсли;
ПланыОбмена.ЗарегистрироватьИзменения(МойДругойУзел85, МояСсылка85);
Если Не ПланыОбмена.ИзменениеЗарегистрировано(МойДругойУзел85, МояСсылка85) Тогда
ВызватьИсключение "Actual регистрация своей строки не подтверждена readback";
КонецЕсли;
Состояние85 = "Зарегистрирована только одна own ссылка для своего B. Это ещё не отправка, не импорт и не ack.";
КонецПроцедуры
Процедура ПрерватьСвойWriter85(Кнопка)
ПроверитьСвойПустойСтенд();
ОпределитьСвоиУзлы85();
Было = МойДругойУзел85.ПолучитьОбъект().НомерОтправленного;
Поток = Новый ЗаписьXML;
Поток.УстановитьСтроку();
Writer = ПланыОбмена.СоздатьЗаписьСообщения();
Попытка
Writer.НачатьЗапись(Поток, МойДругойУзел85);
ПроверитьСвойАктивныйWriter85(Writer, Поток);
Если Writer.НомерСообщения <> Было + 1 Тогда
ВызватьИсключение "Начатый own writer должен предложить следующий номер";
КонецЕсли;
Writer.ПрерватьЗапись();
Исключение
ПрерватьИЗакрытьСвойWriterПослеОшибки85(Writer, Поток, ОписаниеОшибки());
КонецПопытки;
Поток.Закрыть();
ПроверитьСвойНеактивныйWriter85(Writer);
Если МойДругойУзел85.ПолучитьОбъект().НомерОтправленного <> Было Тогда
ВызватьИсключение "Прерванный writer не должен стать отправленным";
КонецЕсли;
Состояние85 = "Own writer прерван до выборки и записи тела. Номер отправленного не изменился; регистрационные номера выборки здесь вообще не менялись.";
КонецПроцедуры
Процедура ОтправитьСвоюСтрокуA85(Кнопка)
ПроверитьСвоюСторону85("A");
МояСсылка85 = ПрочитатьСвоюСтроку85();
Если МойНомерОтправки85 <> 0 Или Не ПланыОбмена.ИзменениеЗарегистрировано(МойДругойУзел85, МояСсылка85) Тогда
ВызватьИсключение "Сначала одна own регистрация; эта форма отправляет её один раз";
КонецЕсли;
МояВерсияОтправки85 = МояСсылка85.ПолучитьОбъект().ВерсияДанных;
Результат = ЗаписатьСвойПакет85(Истина);
МойНомерОтправки85 = Результат.Номер;
Исходящее85 = Результат.XML;
Если Не ПланыОбмена.ИзменениеЗарегистрировано(МойДругойУзел85, МояСсылка85) Тогда
ВызватьИсключение "Одна отправка не является подтверждённым приёмом, own регистрацию нельзя считать ack";
КонецЕсли;
Состояние85 = "Native пакет A номер " + Строка(МойНомерОтправки85) + " сформирован. Скопируйте только этот own XML в B.Входящее85; форма A должна оставаться открытой до её actual ответа.";
КонецПроцедуры
Функция ЗаписатьСвойПакет85(ВыбратьОднуСтроку)
Было = МойДругойУзел85.ПолучитьОбъект().НомерОтправленного;
Поток = Новый ЗаписьXML;
Поток.УстановитьСтроку();
Writer = ПланыОбмена.СоздатьЗаписьСообщения();
Попытка
Если ТипЗнч(Writer) <> Тип("ЗаписьСообщенияОбмена") Тогда
ВызватьИсключение "Native factory должен вернуть actual writer";
КонецЕсли;
Writer.НачатьЗапись(Поток, МойДругойУзел85);
ПроверитьСвойАктивныйWriter85(Writer, Поток);
Номер = Writer.НомерСообщения;
Ack = Writer.НомерПринятого;
Если ВыбратьОднуСтроку Тогда
Выборка = ПланыОбмена.ВыбратьИзменения(Writer.Получатель, Номер, МояСсылка85);
Количество = 0;
Пока Выборка.Следующий() Цикл
Объект = Выборка.Получить();
Если ТипЗнч(Объект) <> Тип("СправочникОбъект.МояСтрока85") Тогда
ВызватьИсключение "Own фильтр должен выбрать только actual объект своей строки, не удаление или другую metadata";
КонецЕсли;
Если Объект.Ссылка <> МояСсылка85 Или Объект.Метка85 <> "c88b7ab1-1d4c-4893-b6be-1f16f4b85293" Тогда
ВызватьИсключение "Actual выборка не соответствует own ссылке/маркеру";
КонецЕсли;
Количество = Количество + 1;
КонецЦикла;
Если Количество <> 1 Тогда
ВызватьИсключение "Должно быть ровно одно actual изменение своей строки";
КонецЕсли;
КонецЕсли;
ПланыОбмена.ЗаписатьИзменения(Writer, 0);
Writer.ЗакончитьЗапись();
Исключение
ПрерватьИЗакрытьСвойWriterПослеОшибки85(Writer, Поток, ОписаниеОшибки());
КонецПопытки;
XML = Поток.Закрыть();
ПроверитьСвойНеактивныйWriter85(Writer);
Если МойДругойУзел85.ПолучитьОбъект().НомерОтправленного <> Номер Или Номер <> Было + 1 Или СтрДлина(XML) = 0 Тогда
ВызватьИсключение "Own законченный writer/counter/XML не совпали; не объявляем доставку";
КонецЕсли;
Возврат Новый Структура("XML,Номер,Ack", XML, Номер, Ack);
КонецФункции
Процедура ПрерватьИЗакрытьСвойWriterПослеОшибки85(Writer, Поток, ИсходнаяОшибка)
ОшибкиОсвобождения = "";
Попытка
Если Writer.ЗаписьXML <> Неопределено Тогда
Writer.ПрерватьЗапись();
КонецЕсли;
Исключение
ОшибкиОсвобождения = "; прервать writer не удалось: " + ОписаниеОшибки();
КонецПопытки;
Попытка
Поток.Закрыть();
Исключение
ОшибкиОсвобождения = ОшибкиОсвобождения + "; закрыть XML writer не удалось: " + ОписаниеОшибки();
КонецПопытки;
ВызватьИсключение ИсходнаяОшибка + ОшибкиОсвобождения;
КонецПроцедуры
Процедура ПроверитьСвойАктивныйWriter85(Writer, Поток)
Если Writer.ЗаписьXML <> Поток Или Writer.Получатель <> МойДругойУзел85 Или Writer.Получатель = МойЭтотУзел85
Или Writer.НомерСообщения <= 0 Или Writer.НомерПринятого <> МойДругойУзел85.ПолучитьОбъект().НомерПринятого Тогда
ВызватьИсключение "Actual writer XML/recipient/number/own received ack не совпали";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьСвойНеактивныйWriter85(Writer)
Если Writer.ЗаписьXML <> Неопределено Или Writer.Получатель <> Неопределено
Или Writer.НомерСообщения <> Неопределено Или Writer.НомерПринятого <> 0 Тогда
ВызватьИсключение "После Finish/Abort native writer должен сбросить документированные временные свойства";
КонецЕсли;
КонецПроцедуры
Процедура ПосмотретьИПрерватьСвойReader85(Кнопка)
ПроверитьСвойПустойСтенд();
ОпределитьСвоиУзлы85();
ПроверитьОписаниеСвоегоПакета85(Входящее85);
Состояние85 = "Отдельный native reader прочитал описание и был явно прерван. Узел не принял сообщение; для импорта нужен НОВЫЙ reader на том же own XML.";
КонецПроцедуры
Процедура ПроверитьОписаниеСвоегоПакета85(XML)
Если СтрДлина(XML) = 0 Или СтрДлина(XML) > 2000000 Тогда
ВызватьИсключение "Разрешён только небольшой XML, скопированный из своей второй формы";
КонецЕсли;
Было = МойДругойУзел85.ПолучитьОбъект().НомерПринятого;
Поток = Новый ЧтениеXML;
Поток.УстановитьСтроку(XML);
Reader = ПланыОбмена.СоздатьЧтениеСообщения();
Попытка
Reader.НачатьЧтение(Поток, ДопустимыйНомерСообщения.Очередной);
ПроверитьСвойАктивныйReader85(Reader, Поток);
Описание = ПланыОбмена.ПрочитатьОписаниеИзменений(Reader);
ПроверитьСвоёОписаниеБезConfigChanges85(Описание);
Reader.ПрерватьЧтение();
Исключение
ПрерватьИЗакрытьСвойReaderПослеОшибки85(Reader, Поток, ОписаниеОшибки());
КонецПопытки;
Поток.Закрыть();
ПроверитьСвойНеактивныйReader85(Reader);
Если МойДругойУзел85.ПолучитьОбъект().НомерПринятого <> Было Тогда
ВызватьИсключение "Предварительный reader не должен подтвердить приём";
КонецЕсли;
КонецПроцедуры
Процедура ПринятьСвойПакет85(Кнопка)
ПроверитьСвойПустойСтенд();
ОпределитьСвоиУзлы85();
ПроверитьОписаниеСвоегоПакета85(Входящее85);
Поток = Новый ЧтениеXML;
Поток.УстановитьСтроку(Входящее85);
Reader = ПланыОбмена.СоздатьЧтениеСообщения();
Попытка
Если ТипЗнч(Reader) <> Тип("ЧтениеСообщенияОбмена") Тогда
ВызватьИсключение "Native factory должен вернуть actual reader";
КонецЕсли;
Reader.НачатьЧтение(Поток, ДопустимыйНомерСообщения.Очередной);
ПроверитьСвойАктивныйReader85(Reader, Поток);
Отправитель = Reader.Отправитель;
Номер = Reader.НомерСообщения;
Ack = Reader.НомерПринятого;
Описание = ПланыОбмена.ПрочитатьИзмененияКонфигурацииИРасширенийКонфигурации(Reader);
ПроверитьСвоёОписаниеБезConfigChanges85(Описание);
ПланыОбмена.ПрочитатьИзменения(Reader, 0);
СсылкаПослеЧтения = ПрочитатьСвоюСтроку85();
Если МояСторона85 = "A" И СсылкаПослеЧтения <> МояСсылка85 Тогда
ВызватьИсключение "Ответ B не должен подменить own ссылку A";
КонецЕсли;
Reader.ЗакончитьЧтение();
Исключение
ПрерватьИЗакрытьСвойReaderПослеОшибки85(Reader, Поток, ОписаниеОшибки());
КонецПопытки;
Поток.Закрыть();
ПроверитьСвойНеактивныйReader85(Reader);
Если Отправитель <> МойДругойУзел85 Или МойДругойУзел85.ПолучитьОбъект().НомерПринятого <> Номер Тогда
ВызватьИсключение "Actual FinishedRead не подтверждён своим node received counter";
КонецЕсли;
Если МояСторона85 = "A" Тогда
Если МойНомерОтправки85 = 0 Или Ack < МойНомерОтправки85 Или Ack > МойДругойУзел85.ПолучитьОбъект().НомерОтправленного Тогда
ВызватьИсключение "Реально принят own ответ, но он не подтверждает сохранённую отправку A; очистка запрещена";
КонецЕсли;
МойПодтвержденныйНомер85 = Ack;
КонецЕсли;
Состояние85 = "Native FinishedRead " + МояСторона85 + ": received=" + Строка(Номер) + ", actual peer ack=" + Строка(Ack) + ". Own строка=7. Abort после уже импортированных данных не обещает rollback.";
КонецПроцедуры
Процедура ОтветитьТолькоИзСвоегоB85(Кнопка)
ПроверитьСвоюСторону85("B");
ПрочитатьСвоюСтроку85();
Если МойДругойУзел85.ПолучитьОбъект().НомерПринятого <= 0 Тогда
ВызватьИсключение "B сначала должен закончить actual чтение пакета A; до этого подтверждения нет";
КонецЕсли;
Результат = ЗаписатьСвойПакет85(Ложь);
Если Результат.Ack <> МойДругойУзел85.ПолучитьОбъект().НомерПринятого Тогда
ВызватьИсключение "Own ответ должен нести настоящий accepted number B из node record";
КонецЕсли;
Исходящее85 = Результат.XML;
Состояние85 = "Собственный ответ B номер " + Строка(Результат.Номер) + ", ack=" + Строка(Результат.Ack) + ". Скопируйте его в остающуюся открытой форму A.Входящее85, затем ПринятьСвойПакет85.";
КонецПроцедуры
Процедура ОчиститьТолькоПодтвержденнуюСвоюСтроку85(Кнопка)
ПроверитьСвоюСторону85("A");
Если МояСсылка85 = Неопределено Или МойНомерОтправки85 = 0 Или МойПодтвержденныйНомер85 < МойНомерОтправки85 Тогда
ВызватьИсключение "Без сохранённой own отправки и actual FinishedRead peer ack очистка запрещена";
КонецЕсли;
Если ПрочитатьСвоюСтроку85() <> МояСсылка85 Или МояСсылка85.ПолучитьОбъект().ВерсияДанных <> МояВерсияОтправки85
Или МойДругойУзел85.ПолучитьОбъект().НомерОтправленного <> МойНомерОтправки85 Тогда
ВызватьИсключение "После отправки own объект/версия/номер изменились; нельзя удалить новую регистрацию старым ack";
КонецЕсли;
ПланыОбмена.УдалитьРегистрациюИзменений(МойДругойУзел85, МояСсылка85);
Если ПланыОбмена.ИзменениеЗарегистрировано(МойДругойУзел85, МояСсылка85) Тогда
ВызватьИсключение "Explicit own очистка не подтверждена readback";
КонецЕсли;
Если ПрочитатьСвоюСтроку85() <> МояСсылка85 Тогда
ВызватьИсключение "После очистки регистрации actual собственная запись должна сохраниться";
КонецЕсли;
Состояние85 = "Только одна own регистрация удалена после actual ack; сам объект со значением7 сохранён. Если native импорт уже снял её, эта точная cleanup операция идемпотентна. Blanket node/metadata/произвольный номер не использовались.";
КонецПроцедуры
Процедура ПрерватьИЗакрытьСвойReaderПослеОшибки85(Reader, Поток, ИсходнаяОшибка)
ОшибкиОсвобождения = "";
Попытка
Если Reader.ЧтениеXML <> Неопределено Тогда
Reader.ПрерватьЧтение();
КонецЕсли;
Исключение
ОшибкиОсвобождения = "; прервать reader не удалось: " + ОписаниеОшибки();
КонецПопытки;
Попытка
Поток.Закрыть();
Исключение
ОшибкиОсвобождения = ОшибкиОсвобождения + "; закрыть XML reader не удалось: " + ОписаниеОшибки();
КонецПопытки;
ВызватьИсключение ИсходнаяОшибка + ОшибкиОсвобождения;
КонецПроцедуры
Процедура ПроверитьСвойАктивныйReader85(Reader, Поток)
Если Reader.ЧтениеXML <> Поток Или Reader.Отправитель <> МойДругойУзел85 Или Reader.Отправитель = МойЭтотУзел85
Или Reader.НомерСообщения <= 0 Или Reader.НомерПринятого < 0
Или Reader.НомерПринятого > МойДругойУзел85.ПолучитьОбъект().НомерОтправленного Тогда
ВызватьИсключение "Actual reader source/XML/number/own ack не совпали; импорт запрещён";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьСвойНеактивныйReader85(Reader)
Если Reader.ЧтениеXML <> Неопределено Или Reader.Отправитель <> Неопределено
Или Reader.НомерСообщения <> Неопределено Или Reader.НомерПринятого <> Неопределено Тогда
ВызватьИсключение "После Finish/Abort native reader должен сбросить свои временные свойства";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьСвоёОписаниеБезConfigChanges85(Описание)
Если ТипЗнч(Описание) <> Тип("ОписаниеИзмененийКонфигурацииВСообщенииОбмена") Тогда
ВызватьИсключение "Нужен настоящий native description, не искусственные flags";
КонецЕсли;
Если Описание.КонфигурацияИзменена Или Описание.РасширенияКонфигурацииИзменены Или Описание.ИзмененныеМетаданныеРасширенийКонфигурацииИзменяютСтруктуруДанных Тогда
ВызватьИсключение "Свой первоначальный протокол не разрешает config/extensions changes; не продолжайте импорт";
КонецЕсли;
КонецПроцедуры
Процедура ОтсоединитьИВосстановитьТолькоСвойChildB85(Кнопка)
ПроверитьСвоюСторону85("B");
Сохраненный = ПланыОбмена.ГлавныйУзел();
Если Сохраненный <> МойДругойУзел85 Или Сохраненный.Код <> "A" Или МойЭтотУзел85.Код <> "B" Тогда
ВызватьИсключение "Изменение главного узла разрешено только actual childB своего A";
КонецЕсли;
Попытка
ПланыОбмена.УстановитьГлавныйУзел(Неопределено);
Если ПланыОбмена.ГлавныйУзел() <> Неопределено Тогда
ВызватьИсключение "Own detached промежуточное состояние не подтверждено";
КонецЕсли;
ПланыОбмена.УстановитьГлавныйУзел(Сохраненный);
Если ПланыОбмена.ГлавныйУзел() <> Сохраненный Тогда
ВызватьИсключение "Own parent не восстановлен";
КонецЕсли;
Исключение
ИсходнаяОшибка = ОписаниеОшибки();
Текущий = ПланыОбмена.ГлавныйУзел();
Если Текущий = Неопределено Тогда
Попытка
ПланыОбмена.УстановитьГлавныйУзел(Сохраненный);
Если ПланыОбмена.ГлавныйУзел() <> Сохраненный Тогда
ВызватьИсключение "Own восстановление parent не подтверждено";
КонецЕсли;
Исключение
ВызватьИсключение ИсходнаяОшибка + "; own восстановление тоже не удалось: " + ОписаниеОшибки();
КонецПопытки;
ИначеЕсли Текущий <> Сохраненный Тогда
ВызватьИсключение ИсходнаяОшибка + "; обнаружен другой current parent: автоматически не перезаписываем";
КонецЕсли;
ВызватьИсключение ИсходнаяОшибка;
КонецПопытки;
Состояние85 = "Только own childB временно detached и actual parentA восстановлен. Это не обмен, не ack и не откат данных; исходные ошибки не скрывались.";
КонецПроцедуры
Процедура ПроверитьСвойПустойСтенд()
Если Метаданные.Справочники.Количество() <> 1 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 1 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
КонецЕсли;
Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
КонецЕсли;
Источники = Новый Массив;
Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
Для Каждого Источник Из Источники Цикл
Расширения = РасширенияКонфигурации.Получить(, Источник);
Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
ВызватьИсключение "Native список расширений должен иметь тип Массив";
КонецЕсли;
Если Расширения.Количество() <> 0 Тогда
ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
КонецЕсли;
КонецПроцедуры
#КонецЕслиРазбор по шагам
ПриОткрытии проверяет exact пустую собственную metadata, единственный RIB состав и запрещённую autoreregistration, два actual узла, native ЭтотУзел и root/child условия. UUID-path guard разбирает собственный File= из текущей строки соединения и разрешает только точное буквальное имя и совпадающее Файл.ПолноеИмя для A/B; серверная или чужая база отклоняется.
A→ПрерватьСвойWriter85 демонстрирует actual Begin/Abort до выбора или записи тела. Сохраняется номер предыдущей отправки; Begin предлагает следующий номер, Abort освобождает writer и не объявляется отправкой. Временные readonly writer свойства после Abort сброшены по native описанию.
A→ЗарегистрироватьТолькоСвоюСтроку85 проверяет всю единственную own запись, сначала отсутствие регистрации, затем вызов ЗарегистрироватьИзменения только для actual nodeB+actual ownref и readback Истина. Это не отправка и не подтверждение доставки.
A→ОтправитьСвоюСтрокуA85: в настоящий writer попадает own nodeB. Пока writer активен, считываются readonly Получатель/ЗаписьXML/НомерСообщения/НомерПринятого. ВыбратьИзменения с фильтром одной actual ссылки читает реальный изменённый объект и проставляет номер в регистрации: эта выборка не read-only. Нативный ЗаписатьИзменения(Writer,0) затем записывает полное RIB тело; вручную дублировать выбранный объект в XML нельзя.
ЗакончитьЗапись делает сообщение считающимся отправленным и обновляет own sent counter; XML строка готова для копирования. Это не доказывает получение B. Номер и собственная версия объекта сохранены до/во время отправки, потому что после Finish временные свойства writer сбрасываются. При ошибке явно Abort только ещё активного writer; регистрационные номера, уже изменённые выборкой, не объявляются откатившимися.
Скопируйте A.Исходящее85 в B.Входящее85. B→ПосмотретьИПрерватьСвойReader85: настоящий reader с допустимым номером Очередной проверяет header/own sender, читает ПрочитатьОписаниеИзменений, требует все три native config/extensions flags Ложь и явно Abort. Номер принятого узла не меняется. После description этот reader НЕЛЬЗЯ использовать для ПрочитатьИзменения.
B→ПринятьСвойПакет85 сначала повторяет отдельный безопасный preview. Для самого импорта создаёт НОВЫЙ reader на том же own XML; active header readonly значения сохраняются заранее. ПрочитатьИзмененияКонфигурацииИРасширенийКонфигурации идёт до ПрочитатьИзменения; если какой-либо config/extension flag не Ложь, дальше не продолжаем. Наши одинаковые A/B не требуют изменения CF или перезапуска.
ПрочитатьИзменения(Reader,0) реально импортирует RIB данные, после чего actual query должен найти одну own запись со всеми точными значениями7/marker. Только затем ЗакончитьЧтение нормально принимает сообщение и обновляет own peer received counter. Отдельный final readback подтверждает этот counter; это не общая транзакция всех reader/DB/config стадий.
B→ОтветитьТолькоИзСвоегоB85 разрешён лишь после actual accepted number>0. Native writer сам помещает этот реальный НомерПринятого в header собственного ответа. Передайте B.Исходящее85 обратно в остающуюся открытой форму A.Входящее85 и выполните ПринятьСвойПакет85. Только после FinishedRead собственного ответа A сохраняет actual ack, подтверждающий её сохранённую отправку и не превышающий реальный sent counter.
A→ОчиститьТолькоПодтвержденнуюСвоюСтроку85 запрещён до actual ack, при новом номере отправки или изменившейся версии own объекта. УдалитьРегистрациюИзменений получает только actual nodeB+одну actual ownref — не весь узел, metadata, Неопределено или пользовательский номер. После этого readback регистрации Ложь, сам объект7 остаётся. Если native импорт уже снял регистрацию, точный cleanup идемпотентен; это прямо отличено от утверждения, будто только данная команда обязательно произвела удаление.
Отдельный опыт только B: ОтсоединитьИВосстановитьТолькоСвойChildB85 сохраняет реальный ГлавныйУзел=A, временно отменяет назначение через Неопределено и восстанавливает тот же actual A. В ошибке автоматическое восстановление разрешено только из собственного промежуточного состояния ГлавныйУзел=Неопределено; если появился другой parent, он не перезаписывается. Исходная ошибка и ошибка восстановления не скрываются. Этот опыт не является доставкой/ack/rollback.
Как проверить результат
В настоящем начальном B native ЭтотУзел=B и ГлавныйУзел=A; обе базы имеют только два own узла. Команда образа второй раз обязана отказать из-за существующей цели. Ничего не удаляется/не перезаписывается.
A registration: Ложь→явная регистрация→Истина. Writer Begin/Abort до тела не меняет sent counter. Нормальная отправка A увеличивает counter на1 и оставляет регистрационное состояние неравнозначным ack.
B preview/Abort не увеличивает accepted counter и не создаёт own строку. Потом новая actual full read/Finish должна создать только запись Э85/Мой обмен85/7/собственный marker и увеличить own accepted counter до номера входящего сообщения.
B reply header carries actual accepted A message; до чтения ответа A очистка запрещена. A после actual FinishedRead ответа может очистить только свою неизменённую отправленную запись; readback регистрации Ложь, число7 и ownref не удалены.
Для безопасного собственного отрицательного опыта: очистка до ack, повторное создание B, запустить A-команду в B, повторно принять тот же пакет с Очередной, поставить неправильный own marker в отдельном disposable стенде. Все нарушения должны прерывать дальнейшие стадии. Не используйте чужой XML/реальный узел для негативного опыта.
Если произошла ошибка после ПрочитатьИзменения, проверьте реальные данные и node counters отдельно: Abort не обещает отменить уже импортированные записи. Не повторяйте сообщение с Любой и не вручную обнуляйте counters для обхода ошибки. Собственные A/B должны быть приведены пользователем к понятному проверенному состоянию.
B detach/restore завершается с тем же actual parentA. При исключении убедитесь, какой parent реально остался; сообщение не должно скрыть частичное изменение или утверждать автоматический rollback. Никакого рабочего parent код не выбирает и не перезаписывает.
Ожидаемый результат
Полный собственный RIB lifecycle с actual A/B образа, реальной регистрацией/выборкой, native XML writer/reader и только подтверждённым завершённым ответом, дающим право на точную очистку собственной неизменённой записи.
На что обратить внимание
СоздатьНачальныйОбраз действительно создаёт базу и удаляет регистрацию изменений для данного узла. Это high-impact native операция, разрешённая здесь только для новой собственной CF и несуществующего точного UUID B. Агент ничего не создавал/копировал/удалял.
ВыбратьИзменения проставляет номер в регистрациях при обходе; это не немутирующая диагностическая выборка. Abort writer до тела и Abort после уже выполненной выборки — разные состояния; обратный rollback регистрационных номеров не обещается.
ПрочитатьОписаниеИзменений делает данный reader непригодным для ПрочитатьИзменения: всегда новый reader для actual import. ПрочитатьИзмененияКонфигурацииИРасширенийКонфигурации допускает последующий import только если не было изменений/исключений; наш protocol требует три flags Ложь.
Параметр ЭлементовВТранзакции=0 относится к данным внутри отдельного native WriteChanges/ReadChanges. Это НЕ доказательство одной транзакции header, configuration updates, импортированных данных, FinishRead, регистрации и последующей cleanup.
ПрерватьЧтение снимает блокировку и не меняет запись узла. Не расширяйте это на rollback всех уже импортированных данных или изменений конфигурации. FinishRead отдельно подтверждает нормальный конец XML тела; отсутствие полного конца не даёт подтверждённого приема.
Временные свойства writer/reader действуют только между Begin и Finish/Abort. Writer НомерПринятого после завершения=0; прочие временные поля сбрасываются в Неопределено. Сохраняйте actual значения заранее, не используйте сброшенные поля для ack.
Число во втором параметре УдалитьРегистрациюИзменений удаляет регистрации этого узла с номером <= указанного, а Неопределено может удалить все. Здесь оба blanket варианта намеренно не используются: только точная собственная ссылка и реальный ack+version gate.
Обмен читает только XML из собственной второй формы; этот пример не является безопасным приемником произвольных недоверенных payload из сети. Общее криптографическое подтверждение доставки, подписи, повторы и конфликтные версии здесь не реализованы и не обещаются.
Source/config/production только read-only. Изготовлены лишь lesson/test источники и статические проверки; реальные 1С, DB, UI, files, сеть и native начальный образ агентом не запускались. Новых backups/archives не создавалось.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.