Sebekerga / native_api_1c

Crate for simple implementation of Component for Native API 1C:Enterprise written in rust
https://crates.io/crates/native_api_1c
62 stars 13 forks source link

Вызов методов компоненты асинх #12

Closed Toveal closed 10 months ago

Toveal commented 10 months ago

Вопрос. Никак не могу найти нормальную информацию о том как сделать так, чтобы компонента могла работать в асинхе. Нашел информацию на ИТС:

Применение внешних компонент в асинхронном режиме (с помощью обещаний) имеет ряд особенностей:

1. Установка внешней компоненты выполняется с помощью асинхронной функции УстановитьВнешнююКомпонентуАсинх().

2. Подключение внешней компоненты выполняется с помощью асинхронного метода ПодключитьВнешнююКомпонентуАсинх().

3. Для обращения к свойствам внешней компоненты следует использовать асинхронные методы Получить<ИмяСвойства>Асинх() и Установить<ИмяСвойства>Асинх().

4. Вместо обращения к методу компоненты следует использовать асинхронные методы вида <ИмяМетода>Асинх().

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

Результатом ожидания обещания, которое возвращает метод <ИмяМетода>Асинх(), будет значение специального типа РезультатАсинхВызоваВнешнейКомпоненты. Значение этого типа будет содержать результат работы внешней компоненты (в свойстве РезультатАсинхВызоваВнешнейКомпоненты.Значение) и все параметры внешней компоненты (в том числе и измененные во время вызова метода). Эти параметры будут расположены в свойстве РезультатАсинхВызоваВнешнейКомпоненты.Параметры.

При использовании веб-браузеров Google Chrome и Mozilla Firefox поддерживается только асинхронное подключение и использование внешних компонент, в том время как для других веб-браузеров допустимо использование как синхронного, так и асинхронного способа работы. Это определяется свойством конфигурации Режим использования синхронных вызовов расширений платформы и внешних компонент (см. [здесь](https://its.1c.ru/db/v8321doc/content/src/%D1%80%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE%20%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%87%D0%B8%D0%BA%D0%B0/%D0%B3%D0%BB%D0%B0%D0%B2%D0%B0%205.%20%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B%20%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D0%B8.htm_?anchor=_ref370288523)).

Больше всего вопросов вызвал вот этот текст: Саму внешнюю компоненту не требуется отдельно перерабатывать для поддержки работы в асинхронном режиме, однако необходимо выполнить перекомпиляцию существующей внешней компоненты для поддержки асинхронной работы.

Подскажите если кто шарит

Sebekerga commented 10 months ago

Из синтаксис помощника:

Объект внешней компоненты (Add-in object) <ИмяМетода>Асинх (Async) Доступен, начиная с версии 8.3.18.

Платформа 1С сама добавляет эти функции к объекту компоненты, со стороны Rust ничего не требуется для реализаии асинх методов. Мб, имеется в виду для COM?

Toveal commented 10 months ago

Нет, именно для Native. Проблема в том что основной поток блокируется после вызова асинх функции компоненты. Либо я что-то делаю не так, либо асинх не имеет смысла. Ставить Tokio не имеет смысла т.к. 1с сама распоряжается как запускать компоненту (как я понимаю). Пример: хочу сделать GET запрос куда-либо и вызываю из 1С асинх. http://localhost:3001/ping просто mock сервер с задержкой в 5 сек

use native_api_1c::{native_api_1c_core::ffi::connection::Connection, native_api_1c_macro::AddIn};
use std::sync::Arc;

#[derive(AddIn)]
pub struct Addin {
    #[add_in_con] // соединение с 1С для вызова внешних событий
    connection: Arc<Option<&'static Connection>>, // Arc для возможности многопоточности

    #[add_in_func(name = "TestPing", name_ru = "ТестПинг")]
    #[returns(Str)]
    pub test_ping: fn(&Self) -> String,
}

impl Addin {
    pub fn new() -> Self {
        Self {
            connection: Arc::new(None),
            test_ping: Self::test_ping,
        }
    }

    pub fn test_ping(&self) -> String {
        return match reqwest::blocking::get("http://localhost:3001/ping") {
            Ok(_) => "OK".to_string(),
            Err(_) => "Err".to_string(),
        };
    }
}
&НаКлиенте
Асинх Процедура ГоАсинх()

    Имя = "Test";

    МакДВД = гетМак();

    Адрес = ПоместитьВоВременноеХранилище(МакДВД);

    Рез = Ждать УстановитьВнешнююКомпонентуАсинх(Адрес);
    Подключена = Ждать ПодключитьВнешнююКомпонентуАсинх(Адрес, Имя, ТипВнешнейКомпоненты.Native);
    СтруктураВозврата = Новый Структура("Ошибка, ОписаниеОшибки", Истина);

    Если Не Подключена Тогда
        СтруктураВозврата.ОписаниеОшибки = "Не подключился";
        Сообщить("Не подключили");
    КонецЕсли;

    КомпонентаОбъект = Новый ("Addin." + Имя + ".Addin");

    Результат = Ждать КомпонентаОбъект.ТестПингАсинх();

    Сообщить("Прошли по асинху");       
КонецПроцедуры

Функция гетМак()
    Возврат РеквизитФормыВЗначение("Объект").ПолучитьМакет("Макет");
КонецФункции
Sebekerga commented 10 months ago

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

Если вам нужна параллельность, то асинх не для этого, для этого есть внешние события компоненты, которые можно вызывать из параллельных потоков в Rust.

Sebekerga commented 10 months ago

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

Toveal commented 10 months ago

В принципе как и всегда, нашел информацию про 1С не на ИТС. В статье на инфостарте пишут:

...И в целом эти все потоки выполняются почти параллельно. Но есть в системе старый способ обеспечивать параллельность в работе, который пришел к нам из Windows 3.0, с помощью эмуляции параллелизма через очередь сообщений в Главные окна. Принцип работы заключается в том, что система выполняет какое-то задание через очередь сообщений и затем после возвращает управление системе, ожидая следующего задания. Так это работает и сейчас для взаимодействия с пользователем через GUI в окнах. Асинхронный режим, который реализуется в 1С, тоже использует очередь сообщений. Когда мы запускаем задание через асинхронную функцию, платформа 1С создает для неё обработчик и либо сразу запускает его, либо после того, как обработает текущее сообщение окна. Мы же получаем эмуляцию многопоточного режима...

Думал хоть где-то использовать Асинх. Придется юзать потоки. Спасибо