diff --git a/README.md b/README.md index e29e11d..c0aa67b 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ * [addin1.rs](src/addin1.rs) - реализация компоненты с помощью низкоуровнего интерфейса, причем весь код безопасный. * [addin2.rs](src/addin2.rs) - упрощенный вариант, используется другой трейт. -### [conf1c](conf1c) - конфигурация 1С (выгрузка из конфигуратора 8.3.22), минимальный тестовый код. +### [conf1c](conf1c) - конфигурация 1С (выгрузка из конфигуратора 8.3.23), минимальный тестовый код. * [DataProcessors/Обработка1/Forms/Форма/Ext/Form/Module.bsl](conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form/Module.bsl) - тесты для ручного запуска. ## Разработка diff --git a/conf1c/Configuration.xml b/conf1c/Configuration.xml index ec71127..7405742 100644 --- a/conf1c/Configuration.xml +++ b/conf1c/Configuration.xml @@ -1,5 +1,5 @@  - + @@ -36,7 +36,7 @@ - Version8_3_22 + Version8_3_23 ManagedApplication PlatformApplication @@ -193,6 +193,14 @@ Videoconferences false + + NFC + false + + + DocumentScanning + false + @@ -210,6 +218,7 @@ DontUse DontUse Taxi + DontUse Version8_3_22 diff --git a/conf1c/DataProcessors/Обработка1.xml b/conf1c/DataProcessors/Обработка1.xml index 514d872..a4aa2c7 100644 --- a/conf1c/DataProcessors/Обработка1.xml +++ b/conf1c/DataProcessors/Обработка1.xml @@ -1,5 +1,5 @@  - + diff --git a/conf1c/DataProcessors/Обработка1/Forms/Форма.xml b/conf1c/DataProcessors/Обработка1/Forms/Форма.xml index ba12bd1..9c7f026 100644 --- a/conf1c/DataProcessors/Обработка1/Forms/Форма.xml +++ b/conf1c/DataProcessors/Обработка1/Forms/Форма.xml @@ -1,5 +1,5 @@  - +
Форма diff --git a/conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form.xml b/conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form.xml index 839135d..d940d34 100644 --- a/conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form.xml +++ b/conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form.xml @@ -1,7 +1,10 @@  - + Use + + ВнешнееСобытие + ИмяФайла @@ -31,6 +34,11 @@ Form.Command.Тест4 + @@ -119,5 +127,20 @@ Тест4 + + + <v8:item> + <v8:lang>ru</v8:lang> + <v8:content>Тест внешнее событие</v8:content> + </v8:item> + + + + ru + Тест внешнее событие + + + ТестВнешнееСобытие + \ No newline at end of file diff --git a/conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form/Module.bsl b/conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form/Module.bsl index 56f4a8b..48eb860 100644 --- a/conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form/Module.bsl +++ b/conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form/Module.bsl @@ -1,4 +1,14 @@  +&НаКлиенте +Перем КомпонентаДляВнешнегоСобытия; + +&НаКлиенте +Перем СчетчикВнешнихСобытий; + +&НаКлиенте +Перем ДатаНачалаСобытий; + + &НаКлиенте Процедура Тест1(Команда) Тест1НаСервере(ИмяФайла); @@ -149,3 +159,37 @@ Сообщить(СтрШаблон("Длительность: %1 мс", Конец - Начало)); КонецПроцедуры + +&НаКлиенте +Асинх Процедура ТестВнешнееСобытие(Команда) + + Если Не Ждать ПодключитьВнешнююКомпонентуАсинх(ИмяФайла, "Test", ТипВнешнейКомпоненты.Native, ТипПодключенияВнешнейКомпоненты.НеИзолированно) Тогда + ВызватьИсключение "Не удалось подключить"; + КонецЕсли; + + КомпонентаДляВнешнегоСобытия = Новый ("AddIn.Test.Class2"); + + СчетчикВнешнихСобытий = 0; + ДатаНачалаСобытий = ТекущаяУниверсальнаяДатаВМиллисекундах(); + Элементы.ТестВнешнееСобытие.Доступность = Ложь; + + Ждать КомпонентаДляВнешнегоСобытия.StartTimerAsync(1000); + +КонецПроцедуры + +&НаКлиенте +Асинх Процедура ВнешнееСобытие(Источник, Событие, Данные) + + Если Источник = "Class2" И Событие = "Timer" Тогда + СчетчикВнешнихСобытий = СчетчикВнешнихСобытий + 1; + Сообщить(СтрШаблон("%1 мс, внешнее событие № %2, данные: %3", ТекущаяУниверсальнаяДатаВМиллисекундах() - ДатаНачалаСобытий, СчетчикВнешнихСобытий, Данные)); + Если СчетчикВнешнихСобытий = 5 Тогда + Ждать КомпонентаДляВнешнегоСобытия.StopTimerAsync(); + КонецЕсли; + ИначеЕсли Источник = "Class2" И Событие = "TimerShutdown" Тогда + КомпонентаДляВнешнегоСобытия = Неопределено; + Сообщить(СтрШаблон("%1 мс, TimerShutdown", ТекущаяУниверсальнаяДатаВМиллисекундах() - ДатаНачалаСобытий)); + Элементы.ТестВнешнееСобытие.Доступность = Истина; + КонецЕсли; + +КонецПроцедуры diff --git a/conf1c/Ext/HomePageWorkArea.xml b/conf1c/Ext/HomePageWorkArea.xml index dc7c9e4..59338b4 100644 --- a/conf1c/Ext/HomePageWorkArea.xml +++ b/conf1c/Ext/HomePageWorkArea.xml @@ -1,5 +1,5 @@  - + TwoColumnsEqualWidth diff --git a/conf1c/Languages/Русский.xml b/conf1c/Languages/Русский.xml index a62fde0..412f65b 100644 --- a/conf1c/Languages/Русский.xml +++ b/conf1c/Languages/Русский.xml @@ -1,5 +1,5 @@  - + Русский diff --git a/src/addin2.rs b/src/addin2.rs index 6d9e5bd..1a2da2f 100644 --- a/src/addin2.rs +++ b/src/addin2.rs @@ -1,12 +1,42 @@ -use addin1c::{name, AddinResult, CStr1C, MethodInfo, Methods, PropInfo, SimpleAddin, Variant}; +use std::{ + error::Error, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread::{self, JoinHandle}, + time::Duration, +}; + +use addin1c::{ + cstr1c, name, AddinResult, CStr1C, CString1C, Connection, MethodInfo, Methods, PropInfo, + SimpleAddin, Variant, +}; pub struct Addin2 { + last_error: Option>, prop1: i32, + connection: Option<&'static Connection>, + timer_enabled: Arc, + thread_handle: Option>, } impl Addin2 { pub fn new() -> Addin2 { - Addin2 { prop1: 0 } + Addin2 { + last_error: None, + prop1: 0, + connection: None, + timer_enabled: Arc::new(AtomicBool::new(false)), + thread_handle: None, + } + } + + fn last_error(&mut self, value: &mut Variant) -> AddinResult { + match &self.last_error { + Some(err) => value.set_str1c(err.to_string()).map_err(|e| e.into()), + None => value.set_str1c("").map_err(|e| e.into()), + } } fn method1(&mut self, param: &mut Variant, ret_value: &mut Variant) -> AddinResult { @@ -29,6 +59,48 @@ impl Addin2 { Ok(()) } + fn start_timer(&mut self, duration: &mut Variant, _ret_value: &mut Variant) -> AddinResult { + let Some(connection) = self.connection else { + return Err("Нет интерфейса".into()); + }; + let duration = duration.get_i32()?; + let duration = duration.try_into()?; + + if let Some(handle) = self.thread_handle.as_ref() { + if !handle.is_finished() { + return Err("Timer is started".into()); + } + } + + self.timer_enabled.store(true, Ordering::Relaxed); + let enabled = self.timer_enabled.clone(); + connection.set_event_buffer_depth(100); + let handle = thread::spawn(move || { + let mut counter = 0; + loop { + thread::sleep(Duration::from_millis(duration)); + if enabled.load(Ordering::Relaxed) { + counter += 1; + connection.external_event( + cstr1c!("Class2"), + cstr1c!("Timer"), + CString1C::new(&format!("{counter}")), + ); + } else { + break; + } + } + connection.external_event(cstr1c!("Class2"), cstr1c!("TimerShutdown"), cstr1c!("")); + }); + self.thread_handle = Some(handle); + Ok(()) + } + + fn stop_timer(&mut self, _ret_value: &mut Variant) -> AddinResult { + self.timer_enabled.store(false, Ordering::Relaxed); + Ok(()) + } + fn set_prop1(&mut self, value: &Variant) -> AddinResult { self.prop1 = value.get_i32()?; Ok(()) @@ -40,11 +112,29 @@ impl Addin2 { } } +impl Drop for Addin2 { + fn drop(&mut self) { + self.timer_enabled.store(false, Ordering::Relaxed); + if let Some(handle) = self.thread_handle.take() { + let _ = handle.join(); + } + } +} + impl SimpleAddin for Addin2 { fn name() -> &'static CStr1C { name!("Class2") } + fn init(&mut self, interface: &'static Connection) -> bool { + self.connection = Some(interface); + true + } + + fn save_error(&mut self, err: Option>) { + self.last_error = err; + } + fn methods() -> &'static [MethodInfo] { &[ MethodInfo { @@ -55,14 +145,29 @@ impl SimpleAddin for Addin2 { name: name!("Method2"), method: Methods::Method2(Self::method2), }, + MethodInfo { + name: name!("StartTimer"), + method: Methods::Method1(Self::start_timer), + }, + MethodInfo { + name: name!("StopTimer"), + method: Methods::Method0(Self::stop_timer), + }, ] } fn properties() -> &'static [PropInfo] { - &[PropInfo { - name: name!("Prop1"), - getter: Some(Self::get_prop1), - setter: Some(Self::set_prop1), - }] + &[ + PropInfo { + name: name!("Prop1"), + getter: Some(Self::get_prop1), + setter: Some(Self::set_prop1), + }, + PropInfo { + name: name!("LastError"), + getter: Some(Self::last_error), + setter: None, + }, + ] } }