diff --git a/src/addins/tcp/Cargo.lock b/src/addins/tcp_client/Cargo.lock similarity index 100% rename from src/addins/tcp/Cargo.lock rename to src/addins/tcp_client/Cargo.lock diff --git a/src/addins/tcp/Cargo.toml b/src/addins/tcp_client/Cargo.toml similarity index 100% rename from src/addins/tcp/Cargo.toml rename to src/addins/tcp_client/Cargo.toml diff --git a/src/addins/tcp/MANIFEST.XML b/src/addins/tcp_client/MANIFEST.XML similarity index 100% rename from src/addins/tcp/MANIFEST.XML rename to src/addins/tcp_client/MANIFEST.XML diff --git a/src/addins/tcp/OPI_TCPClient.zip b/src/addins/tcp_client/OPI_TCPClient.zip similarity index 100% rename from src/addins/tcp/OPI_TCPClient.zip rename to src/addins/tcp_client/OPI_TCPClient.zip diff --git a/src/addins/tcp/release.bat b/src/addins/tcp_client/release.bat similarity index 100% rename from src/addins/tcp/release.bat rename to src/addins/tcp_client/release.bat diff --git a/src/addins/tcp/src/component/methods.rs b/src/addins/tcp_client/src/component/methods.rs similarity index 100% rename from src/addins/tcp/src/component/methods.rs rename to src/addins/tcp_client/src/component/methods.rs diff --git a/src/addins/tcp/src/component/mod.rs b/src/addins/tcp_client/src/component/mod.rs similarity index 100% rename from src/addins/tcp/src/component/mod.rs rename to src/addins/tcp_client/src/component/mod.rs diff --git a/src/addins/tcp/src/core/getset.rs b/src/addins/tcp_client/src/core/getset.rs similarity index 100% rename from src/addins/tcp/src/core/getset.rs rename to src/addins/tcp_client/src/core/getset.rs diff --git a/src/addins/tcp/src/core/mod.rs b/src/addins/tcp_client/src/core/mod.rs similarity index 100% rename from src/addins/tcp/src/core/mod.rs rename to src/addins/tcp_client/src/core/mod.rs diff --git a/src/addins/tcp/src/lib.rs b/src/addins/tcp_client/src/lib.rs similarity index 100% rename from src/addins/tcp/src/lib.rs rename to src/addins/tcp_client/src/lib.rs diff --git a/src/addins/tcp_server/Cargo.lock b/src/addins/tcp_server/Cargo.lock new file mode 100644 index 0000000000..5a8a42f299 --- /dev/null +++ b/src/addins/tcp_server/Cargo.lock @@ -0,0 +1,118 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addin1c" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef34e8b7ff4c43e87491a4cc30a4779a9f67c50db43378a36362c7a56246e05b" +dependencies = [ + "smallvec", + "utf16_lit", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "opi_addin" +version = "0.1.0" +dependencies = [ + "addin1c", + "serde_json", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "utf16_lit" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14706d2a800ee8ff38c1d3edb873cd616971ea59eb7c0d046bb44ef59b06a1ae" diff --git a/src/addins/tcp_server/Cargo.toml b/src/addins/tcp_server/Cargo.toml new file mode 100644 index 0000000000..f8e826bfd1 --- /dev/null +++ b/src/addins/tcp_server/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "opi_addin" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[profile.release] +lto = true # Enable Link Time Optimization +codegen-units = 1 # Reduce number of codegen units to increase optimizations. +panic = "abort" # Abort on panic +strip = true # Automatically strip symbols from the binary. + +[dependencies] +addin1c = "0.5.0" +serde_json = "1.0.133" \ No newline at end of file diff --git a/src/addins/tcp_server/MANIFEST.XML b/src/addins/tcp_server/MANIFEST.XML new file mode 100644 index 0000000000..eb28e4669f --- /dev/null +++ b/src/addins/tcp_server/MANIFEST.XML @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/addins/tcp_server/release.bat b/src/addins/tcp_server/release.bat new file mode 100644 index 0000000000..648eb36f3b --- /dev/null +++ b/src/addins/tcp_server/release.bat @@ -0,0 +1,63 @@ +@echo off + +:: Установить переменную +set LIB_NAME=opi_addin +set OPENSSL_DIR=C:\msys64\mingw64 +set OPENSSL_LIB_DIR=%OPENSSL_DIR%\lib +set OPENSSL_INCLUDE_DIR=%OPENSSL_DIR%\include + + +:: Перейти в директорию проекта +cd /d "%~dp0" + +:: Создать папку для артефактов +set OUTPUT_DIR=artifacts +if not exist "%OUTPUT_DIR%" mkdir "%OUTPUT_DIR%" + +:: Сборка для x86_64-pc-windows-msvc +cargo build --release --target x86_64-pc-windows-msvc +if errorlevel 1 goto :error + +:: Сборка для x86_64-unknown-linux-gnu +cargo zigbuild --release --target x86_64-unknown-linux-gnu +if errorlevel 1 goto :error + +:: Сборка для i686-pc-windows-msvc +cargo build --release --target i686-pc-windows-msvc +if errorlevel 1 goto :error + +:: Сборка для i686-unknown-linux-gnu +cargo zigbuild --release --target i686-unknown-linux-gnu +if errorlevel 1 goto :error + +:: Копирование файлов .dll и .so +copy /y target\x86_64-pc-windows-msvc\release\%LIB_NAME%.dll "%OUTPUT_DIR%\AddIn_x64_windows.dll" +if errorlevel 1 goto :error + +copy /y target\i686-pc-windows-msvc\release\%LIB_NAME%.dll "%OUTPUT_DIR%\AddIn_x86_windows.dll" +if errorlevel 1 goto :error + +copy /y target\x86_64-unknown-linux-gnu\release\lib%LIB_NAME%.so "%OUTPUT_DIR%\AddIn_x64_linux.so" +if errorlevel 1 goto :error + +copy /y target\i686-unknown-linux-gnu\release\lib%LIB_NAME%.so "%OUTPUT_DIR%\AddIn_x86_linux.so" +if errorlevel 1 goto :error + +copy /y MANIFEST.XML "%OUTPUT_DIR%\MANIFEST.XML" +if errorlevel 1 goto :error + +:: Архивация +set ZIP_NAME=addin.zip +powershell -Command "Compress-Archive -Path '%OUTPUT_DIR%\*' -Force -DestinationPath '%ZIP_NAME%'" +if errorlevel 1 goto :error + +if exist "%OUTPUT_DIR%" ( + rmdir /S /Q "%OUTPUT_DIR%" +) + +@echo Build and packaging completed successfully. +exit /b 0 + +:error +@echo An error occurred during the build or packaging process. +exit /b 1 diff --git a/src/addins/tcp_server/src/component/methods.rs b/src/addins/tcp_server/src/component/methods.rs new file mode 100644 index 0000000000..9b408f9d65 --- /dev/null +++ b/src/addins/tcp_server/src/component/methods.rs @@ -0,0 +1,101 @@ +use std::net::{TcpListener, TcpStream}; +use std::io::{Read, Write}; +use std::time::Instant; +use serde_json::json; +use crate::component::AddIn; + +pub enum ServerEvent { + NewConnection(String), + MessageReceived(String, Vec), +} + +pub fn start_server(addin: &mut AddIn) -> String { + + let addr = format!("0.0.0.0:{}", addin.port); + let listener = TcpListener::bind(&addr); + + match listener { + Ok(listener) => { + + let sender = addin.sender.clone(); + + std::thread::spawn(move || { + for stream in listener.incoming() { + if let Ok(mut stream) = stream { + + let addr = stream.peer_addr().unwrap().to_string(); + sender.send(ServerEvent::NewConnection(addr.clone())).unwrap(); + let sender = sender.clone(); + std::thread::spawn(move || { + let mut buffer = [0; 1024]; + while let Ok(bytes_read) = stream.read(&mut buffer) { + if bytes_read == 0 { + break; + } + sender + .send(ServerEvent::MessageReceived( + addr.clone(), + buffer[..bytes_read].to_vec(), + )) + .unwrap(); + } + }); + } + } + }); + "Success".to_string() + } + Err(e) => e.to_string(), + } +} + +pub fn await_connections(addin: &mut AddIn, timeout: i32) -> String { + let start_time = Instant::now(); + let mut active_connections = Vec::new(); + + while start_time.elapsed().as_secs() < timeout as u64 { + if let Ok(event) = addin.receiver.try_recv() { + match event { + ServerEvent::NewConnection(addr) => { + active_connections.push(addr.clone()); + addin.connections.insert(addr, TcpStream::connect("127.0.0.1:0").unwrap()); + } + ServerEvent::MessageReceived(addr, _data) => { + if !active_connections.contains(&addr) { + active_connections.push(addr); + } + } + } + } else { + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } + + json!(active_connections).to_string() +} + +pub fn receive_message(addin: &mut AddIn, addr: &str) -> Vec { + if let Some(stream) = addin.connections.get_mut(addr) { + let mut buffer = [0; 1024]; + if let Ok(bytes_read) = stream.read(&mut buffer) { + if bytes_read > 0 { + return buffer[..bytes_read].to_vec(); + } + } + } + vec![] +} + +pub fn send_response(addin: &mut AddIn, addr: &str, data: &[u8]) -> String { + if let Some(stream) = addin.connections.get_mut(addr) { + if stream.write_all(data).is_ok() { + return "Success".to_string(); + } + } + "Failed to send response".to_string() +} + +pub fn stop(addin: &mut AddIn) -> String { + addin.connections.clear(); + "Success".to_string() +} \ No newline at end of file diff --git a/src/addins/tcp_server/src/component/mod.rs b/src/addins/tcp_server/src/component/mod.rs new file mode 100644 index 0000000000..997df94e0e --- /dev/null +++ b/src/addins/tcp_server/src/component/mod.rs @@ -0,0 +1,108 @@ +mod methods; + +use addin1c::{name, Variant}; +use crate::core::getset; + +use std::collections::HashMap; +use std::net::TcpStream; +use std::sync::mpsc::{Sender, Receiver}; +use self::methods::{ServerEvent}; + + +// МЕТОДЫ КОМПОНЕНТЫ ------------------------------------------------------------------------------- + +// Синонимы +pub const METHODS: &[&[u16]] = &[ + name!("Start"), + name!("Await"), + name!("ReceiveMessage"), + name!("SendResponse"), + name!("Stop"), + +]; + +// Число параметров функций компоненты +pub fn get_params_amount(num: usize) -> usize { + match num { + 0 => 0, + 1 => 1, + 2 => 1, + 3 => 2, + 4 => 0, + _ => 0, + } +} + +// Соответствие функций Rust функциям компоненты +// Вызовы должны быть обернуты в Box::new +pub fn cal_func(obj: &mut AddIn, num: usize, params: &mut [Variant]) -> Box { + + match num { + + 0 => Box::new(methods::start_server(obj)), + 1 => { + let timeout = params[0].get_i32().unwrap_or(0); + Box::new(methods::await_connections(obj, timeout)) + } + 2 => { + let addr = params[0].get_string().unwrap_or("".to_string()); + Box::new(methods::receive_message(obj, &addr)) + } + 3 =>{ + let empty_array: [u8; 0] = []; + + let addr = params[0].get_string().unwrap_or("".to_string()); + let data = params[1].get_blob().unwrap_or(&empty_array); + + Box::new(methods::send_response(obj, &addr, data)) + } + 4 => Box::new(methods::stop(obj)), + _ => Box::new(false), // Неверный номер команды + } + +} + +// ------------------------------------------------------------------------------------------------- + +// ПОЛЯ КОМПОНЕНТЫ --------------------------------------------------------------------------------- + +// Синонимы +pub const PROPS: &[&[u16]] = &[ + name!("Port") +]; + + +pub struct AddIn { + pub port: i32, + pub connections: HashMap, + pub sender: Sender, + pub receiver: Receiver, +} + +impl AddIn { + pub fn new() -> Self { + let (sender, receiver) = std::sync::mpsc::channel(); + AddIn { + port: 0, + connections: HashMap::new(), + sender, + receiver, + } + } + pub fn get_field_ptr(&self, index: usize) -> *const dyn getset::ValueType { + match index { + 0 => &self.port as &dyn getset::ValueType as *const _, + _ => panic!("Index out of bounds"), + } + } + pub fn get_field_ptr_mut(&mut self, index: usize) -> *mut dyn getset::ValueType { self.get_field_ptr(index) as *mut _ } +} + +// ------------------------------------------------------------------------------------------------- + +// Обработка удаления объекта +impl Drop for AddIn { + fn drop(&mut self) { + methods::stop(self); + } +} \ No newline at end of file diff --git a/src/addins/tcp_server/src/core/getset.rs b/src/addins/tcp_server/src/core/getset.rs new file mode 100644 index 0000000000..dfb6f65bc8 --- /dev/null +++ b/src/addins/tcp_server/src/core/getset.rs @@ -0,0 +1,78 @@ +use addin1c::{Variant, Tm}; + + +pub trait ValueType { + fn get_value(&self, val: &mut Variant) -> bool; + fn set_value(&mut self, val: &Variant); +} + +// Реализация для i32 +impl ValueType for i32 { + fn get_value(&self, val: &mut Variant) -> bool { + val.set_i32(*self); + true + } + + fn set_value(&mut self, val: &Variant) { + *self = val.get_i32().unwrap_or(0); + } +} + +// Реализация для f64 +impl ValueType for f64 { + fn get_value(&self, val: &mut Variant) -> bool { + val.set_f64(*self); + true + } + + fn set_value(&mut self, val: &Variant) { + *self = val.get_f64().unwrap_or(0.0); + } +} + +// Реализация для bool +impl ValueType for bool { + fn get_value(&self, val: &mut Variant) -> bool { + val.set_bool(*self); + true + } + + fn set_value(&mut self, val: &Variant) { + *self = val.get_bool().unwrap_or(false); + } +} + +// Реализация для tm +impl ValueType for Tm { + fn get_value(&self, val: &mut Variant) -> bool { + val.set_date(*self); + true + } + + fn set_value(&mut self, val: &Variant) { + *self = val.get_date().unwrap_or(Tm::default()); + } +} + +// Реализация для String +impl ValueType for String { + fn get_value(&self, val: &mut Variant) -> bool { + let s: Vec = self.encode_utf16().collect(); + val.set_str1c(s.as_slice()).is_ok() + } + + fn set_value(&mut self, val: &Variant) { + *self = val.get_string().unwrap_or("".to_string()); + } +} + +// Реализация для Vec +impl ValueType for Vec { + fn get_value(&self, val: &mut Variant) -> bool { + val.set_blob(self.as_slice()).is_ok() + } + + fn set_value(&mut self, val: &Variant) { + *self = val.get_blob().unwrap_or(&[]).to_vec() + } +} \ No newline at end of file diff --git a/src/addins/tcp_server/src/core/mod.rs b/src/addins/tcp_server/src/core/mod.rs new file mode 100644 index 0000000000..c0f52eb751 --- /dev/null +++ b/src/addins/tcp_server/src/core/mod.rs @@ -0,0 +1,53 @@ +pub mod getset; + +use addin1c::{name, RawAddin, Variant}; + +use crate::component::METHODS; +use crate::component::PROPS; +use crate::component::get_params_amount; +use crate::component::cal_func; +use crate::component::AddIn; + +// Определение класса +impl RawAddin for AddIn { + + fn register_extension_as(&mut self) -> &'static [u16] { + name!("Main") + } + fn get_n_props(&mut self) -> usize { + PROPS.len() + } + fn find_prop(&mut self, name: &[u16]) -> Option { + PROPS.iter().position(|&x| x == name) + } + fn get_prop_name(&mut self, num: usize, _alias: usize) -> Option<&'static [u16]> { PROPS.get(num).copied() } + fn get_prop_val(&mut self, num: usize, val: &mut Variant) -> bool {let field: &dyn getset::ValueType = &self[num]; field.get_value(val) } + fn set_prop_val(&mut self, num: usize, val: &Variant) -> bool {let field: &mut dyn getset::ValueType = &mut self[num]; field.set_value(val); true } + fn is_prop_readable(&mut self, _nm: usize) -> bool { true } + fn is_prop_writable(&mut self, num: usize) -> bool { true } + fn get_n_methods(&mut self) -> usize { METHODS.len() } + fn find_method(&mut self, name: &[u16]) -> Option { METHODS.iter().position(|&x| x == name) } + fn get_method_name(&mut self, num: usize, _alias: usize) -> Option<&'static [u16]> { METHODS.get(num).copied() } + fn get_n_params(&mut self, num: usize) -> usize { get_params_amount(num) } + fn get_param_def_value(&mut self, _method_num: usize, _param_num: usize, _value: Variant, ) -> bool { true } + fn has_ret_val(&mut self, num: usize) -> bool { true } + fn call_as_proc(&mut self, _num: usize, _params: &mut [Variant]) -> bool { false } + fn call_as_func(&mut self, num: usize, params: &mut [Variant], ret_value: &mut Variant, ) -> bool { cal_func(self, num, params).get_value(ret_value) } + +} + +impl std::ops::Index for AddIn { + type Output = dyn getset::ValueType; + + fn index(&self, index: usize) -> &Self::Output { + unsafe { &*self.get_field_ptr(index) } + } +} + +impl std::ops::IndexMut for AddIn { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + unsafe { &mut *self.get_field_ptr_mut(index) } + } +} + + diff --git a/src/addins/tcp_server/src/lib.rs b/src/addins/tcp_server/src/lib.rs new file mode 100644 index 0000000000..d08fdf4a06 --- /dev/null +++ b/src/addins/tcp_server/src/lib.rs @@ -0,0 +1,48 @@ +pub mod component; +mod core; + + +use std::{ + ffi::{c_int, c_long, c_void}, + sync::atomic::{AtomicI32, Ordering}, +}; + +use component::AddIn; +use addin1c::{create_component, destroy_component, name, AttachType}; + +pub static mut PLATFORM_CAPABILITIES: AtomicI32 = AtomicI32::new(-1); + +#[allow(non_snake_case)] +#[no_mangle] +pub unsafe extern "C" fn GetClassObject(name: *const u16, component: *mut *mut c_void) -> c_long { + + let addin = AddIn::new(); + create_component(component, addin) + +} + +#[allow(non_snake_case)] +#[no_mangle] +pub unsafe extern "C" fn DestroyObject(component: *mut *mut c_void) -> c_long { + destroy_component(component) +} + +#[allow(non_snake_case)] +#[no_mangle] +pub extern "C" fn GetClassNames() -> *const u16 { + // small strings for performance + name!("Main").as_ptr() +} + +#[allow(non_snake_case)] +#[no_mangle] +pub unsafe extern "C" fn SetPlatformCapabilities(capabilities: c_int) -> c_int { + PLATFORM_CAPABILITIES.store(capabilities, Ordering::Relaxed); + 3 +} + +#[allow(non_snake_case)] +#[no_mangle] +pub extern "C" fn GetAttachType() -> AttachType { + AttachType::Any +} diff --git a/src/addins/tmpl/src/component/mod.rs b/src/addins/tmpl/src/component/mod.rs index 191677f60e..266bef7197 100644 --- a/src/addins/tmpl/src/component/mod.rs +++ b/src/addins/tmpl/src/component/mod.rs @@ -60,4 +60,11 @@ impl AddIn { } pub fn get_field_ptr_mut(&mut self, index: usize) -> *mut dyn getset::ValueType { self.get_field_ptr(index) as *mut _ } } -// ------------------------------------------------------------------------------------------------- \ No newline at end of file +// ------------------------------------------------------------------------------------------------- + +// УНИЧТОЖЕНИЕ ОБЪЕКТА ----------------------------------------------------------------------------- + +// Обработка удаления объекта +impl Drop for AddIn { + fn drop(&mut self) {} +} \ No newline at end of file diff --git a/src/addins/tmpl/src/core/mod.rs b/src/addins/tmpl/src/core/mod.rs index f8b632dcf0..1ab84b1e86 100644 --- a/src/addins/tmpl/src/core/mod.rs +++ b/src/addins/tmpl/src/core/mod.rs @@ -8,12 +8,6 @@ use crate::component::get_params_amount; use crate::component::cal_func; use crate::component::AddIn; - -// Обработка удаления объекта -impl Drop for AddIn { - fn drop(&mut self) {} -} - // Определение класса impl RawAddin for AddIn { diff --git a/src/addins/tmpl/src/lib.rs b/src/addins/tmpl/src/lib.rs index 50bfa2ae8f..d08fdf4a06 100644 --- a/src/addins/tmpl/src/lib.rs +++ b/src/addins/tmpl/src/lib.rs @@ -31,7 +31,7 @@ pub unsafe extern "C" fn DestroyObject(component: *mut *mut c_void) -> c_long { #[no_mangle] pub extern "C" fn GetClassNames() -> *const u16 { // small strings for performance - name!("Client").as_ptr() + name!("Main").as_ptr() } #[allow(non_snake_case)]